interanal/core/runtime: move building logic from cue package
- Simplify logic
- Lazy load packages
- Cleanup interim types used for cue package transition
The lazy loading of builtin packages allows:
- adding builtin packages to a used Runtime
- isolate performance measurements and metrics for
CUE code that doesn't use imports.
Change-Id: Idd4578100047f83397be7c1d69f66b083974e6c0
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7421
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index aa47081..637238b 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -223,7 +223,7 @@
if inst.Err != nil {
return &streamingIterator{e: inst.Err}
}
- i.r = internal.GetRuntimeNew(inst).(*cue.Runtime)
+ i.r = internal.GetRuntime(inst).(*cue.Runtime)
if b.schema == nil {
i.base = inst.Value()
} else {
diff --git a/cue/build.go b/cue/build.go
index 257b52c..bdaf00f 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -15,17 +15,12 @@
package cue
import (
- "strings"
- "sync"
-
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/compile"
"cuelang.org/go/internal/core/runtime"
)
@@ -41,7 +36,7 @@
}
func init() {
- internal.GetRuntimeNew = func(instance interface{}) interface{} {
+ internal.GetRuntime = func(instance interface{}) interface{} {
switch x := instance.(type) {
case Value:
return &Runtime{idx: x.idx}
@@ -54,18 +49,18 @@
}
}
- internal.CheckAndForkRuntimeNew = func(runtime, value interface{}) interface{} {
+ internal.CheckAndForkRuntime = func(runtime, value interface{}) interface{} {
r := runtime.(*Runtime)
idx := value.(Value).ctx().index
if idx != r.idx {
panic("value not from same runtime")
}
- return &Runtime{idx: newIndex(idx)}
+ return &Runtime{idx: newIndex()}
}
internal.CoreValue = func(value interface{}) (runtime, vertex interface{}) {
if v, ok := value.(Value); ok && v.v != nil {
- return v.idx.Index, v.v
+ return v.idx.Runtime, v.v
}
return nil, nil
}
@@ -75,7 +70,7 @@
func (r *Runtime) index() *index {
if r.idx == nil {
- r.idx = newIndex(sharedIndex)
+ r.idx = newIndex()
}
return r.idx
}
@@ -198,137 +193,43 @@
})
}
+type importIndex map[*build.Instance]*Instance
+
// index maps conversions from label names to internal codes.
//
// All instances belonging to the same package should share this index.
type index struct {
- adt.Runtime
- *runtime.Index
-
- loaded map[*build.Instance]*Instance
- mutex sync.Mutex
-}
-
-// sharedIndex is used for indexing builtins and any other labels common to
-// all instances.
-var sharedIndex = &index{
- Runtime: runtime.SharedRuntimeNew,
- Index: runtime.SharedIndexNew,
- loaded: map[*build.Instance]*Instance{},
+ *runtime.Runtime
+ loaded importIndex
}
// NewRuntime creates a *runtime.Runtime with builtins preloaded.
func NewRuntime() *runtime.Runtime {
- idx := runtime.NewIndex(sharedIndex.Index)
- r := runtime.NewWithIndex(idx)
+ r := runtime.New()
i := &index{
Runtime: r,
- Index: idx,
- loaded: map[*build.Instance]*Instance{},
+ loaded: importIndex{},
}
r.Data = i
return r
}
// newIndex creates a new index.
-func newIndex(parent *index) *index {
- idx := runtime.NewIndex(parent.Index)
- r := runtime.NewWithIndex(idx)
+func newIndex() *index {
+ r := runtime.New()
i := &index{
Runtime: r,
- Index: idx,
- loaded: map[*build.Instance]*Instance{},
+ loaded: importIndex{},
}
r.Data = i
return i
}
func isBuiltin(s string) bool {
- _, ok := builtins[s]
- return ok
+ return runtime.SharedRuntime.IsBuiltinPackage(s)
}
func (idx *index) loadInstance(p *build.Instance) *Instance {
- _ = visitInstances(p, func(p *build.Instance, errs errors.Error) errors.Error {
- if inst := idx.loaded[p]; inst != nil {
- if !inst.complete {
- // cycles should be detected by the builder and it should not be
- // possible to construct a build.Instance that has them.
- panic("cue: cycle")
- }
- return inst.Err
- }
-
- err := runtime.ResolveFiles(idx.Index, p, isBuiltin)
- errs = errors.Append(errs, err)
-
- v, err := compile.Files(nil, idx.Runtime, p.ID(), p.Files...)
- errs = errors.Append(errs, err)
-
- inst := newInstance(idx, p, v)
- idx.loaded[p] = inst
- inst.Err = errs
-
- inst.ImportPath = p.ImportPath
- inst.complete = true
-
- return inst.Err
- })
-
- return idx.loaded[p]
-}
-
-// TODO: runtime.Runtime has a similar, much simpler, implementation. This
-// code should go.
-
-type visitFunc func(b *build.Instance, err errors.Error) (errs errors.Error)
-
-// visitInstances calls f for each transitive dependency.
-//
-// It passes any errors that occur in transitive dependencies to the visitFunc.
-// visitFunc must return the errors it is passed or return nil to ignore it.
-func visitInstances(b *build.Instance, f visitFunc) (errs errors.Error) {
- v := visitor{b: b, f: f, errs: b.Err}
- for _, file := range b.Files {
- v.file(file)
- }
- return v.f(b, v.errs)
-}
-
-type visitor struct {
- b *build.Instance
- f visitFunc
- errs errors.Error
-}
-
-func (v *visitor) addErr(e errors.Error) {
- v.errs = errors.Append(v.errs, e)
-}
-
-func (v *visitor) file(file *ast.File) {
- file.VisitImports(func(x *ast.ImportDecl) {
- for _, s := range x.Specs {
- v.spec(s)
- }
- })
-}
-
-func (v *visitor) spec(spec *ast.ImportSpec) {
- info, err := astutil.ParseImportSpec(spec)
- if err != nil {
- v.addErr(errors.Promote(err, "invalid import path"))
- return
- }
-
- pkg := v.b.LookupImport(info.ID)
- if pkg == nil {
- if strings.Contains(info.ID, ".") {
- v.addErr(errors.Newf(spec.Pos(),
- "package %q imported but not defined in %s",
- info.ID, v.b.ImportPath))
- }
- return
- }
-
- v.addErr(visitInstances(pkg, v.f))
+ idx.Runtime.Build(p)
+ return idx.getImportFromBuild(p)
}
diff --git a/cue/build_test.go b/cue/build_test.go
index 4cf0ccb..55e4268 100644
--- a/cue/build_test.go
+++ b/cue/build_test.go
@@ -200,7 +200,7 @@
got = err.Error()
} else {
cfg := &debug.Config{Compact: true}
- got = debug.NodeString(insts[0].Index, insts[0].Value().v, cfg)
+ got = debug.NodeString(insts[0].Runtime, insts[0].Value().v, cfg)
}
if got != tc.emit {
t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
diff --git a/cue/builtin.go b/cue/builtin.go
index d3f2844..ef2754b 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -15,12 +15,12 @@
package cue
import (
- "path"
"strings"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
"cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/runtime"
)
func pos(n adt.Node) (p token.Pos) {
@@ -34,43 +34,15 @@
return src.Pos()
}
-var builtins = map[string]*Instance{}
-
-func AddBuiltinPackage(importPath string, f func(*adt.OpContext) (*adt.Vertex, error)) {
- ctx := sharedIndex.newContext().opCtx
-
- v, err := f(ctx)
- if err != nil {
- panic(err)
- }
-
- k := importPath
- i := sharedIndex.addInst(&Instance{
- ImportPath: k,
- PkgName: path.Base(k),
- root: v,
- })
-
- builtins[k] = i
- builtins["-/"+path.Base(k)] = i
-}
-
-func getBuiltinPkg(ctx *context, path string) *structLit {
- p, ok := builtins[path]
- if !ok {
- return nil
- }
- return p.root
-}
-
func init() {
+ // TODO: unroll this function. Should no longer be necessary to be internal.
internal.UnifyBuiltin = func(val interface{}, kind string) interface{} {
v := val.(Value)
ctx := v.ctx()
p := strings.Split(kind, ".")
pkg, name := p[0], p[1]
- s := getBuiltinPkg(ctx, pkg)
+ s, _ := runtime.SharedRuntime.LoadImport(pkg)
if s == nil {
return v
}
diff --git a/cue/instance.go b/cue/instance.go
index e6c9446..70a89b9 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -23,7 +23,6 @@
"cuelang.org/go/internal/core/compile"
"cuelang.org/go/internal/core/convert"
"cuelang.org/go/internal/core/eval"
- "cuelang.org/go/internal/core/runtime"
)
// An Instance defines a single configuration based on a collection of
@@ -43,29 +42,79 @@
inst *build.Instance
- complete bool // for cycle detection
+ // complete bool // for cycle detection
}
func (x *index) addInst(p *Instance) *Instance {
- x.Index.AddInst(p.ImportPath, p.root, p)
+ if p.inst == nil {
+ p.inst = &build.Instance{
+ ImportPath: p.ImportPath,
+ PkgName: p.PkgName,
+ }
+ }
+ // fmt.Println(p.ImportPath, "XXX")
+ x.AddInst(p.ImportPath, p.root, p.inst)
+ x.loaded[p.inst] = p
p.index = x
return p
}
+func (x *index) getImportFromBuild(p *build.Instance) *Instance {
+ inst := x.loaded[p]
+
+ if inst != nil {
+ return inst
+ }
+
+ v := x.GetNodeFromInstance(p)
+ if v == nil {
+ return nil
+ }
+
+ inst = &Instance{
+ ImportPath: p.ImportPath,
+ Dir: p.Dir,
+ PkgName: p.PkgName,
+ DisplayName: p.ImportPath,
+ root: v,
+ inst: p,
+ index: x,
+ }
+ if p.Err != nil {
+ inst.setListOrError(p.Err)
+ }
+
+ x.loaded[p] = inst
+
+ return inst
+}
+
func (x *index) getImportFromNode(v *adt.Vertex) *Instance {
- p := x.Index.GetImportFromNode(v)
+ p := x.GetInstanceFromNode(v)
if p == nil {
return nil
}
- return p.(*Instance)
+
+ return x.getImportFromBuild(p)
}
func (x *index) getImportFromPath(id string) *Instance {
- node := x.Index.GetImportFromPath(id)
+ node, _ := x.LoadImport(id)
if node == nil {
return nil
}
- return x.Index.GetImportFromNode(node).(*Instance)
+ b := x.GetInstanceFromNode(node)
+ inst := x.loaded[b]
+ if inst == nil {
+ inst = &Instance{
+ ImportPath: b.ImportPath,
+ PkgName: b.PkgName,
+ root: node,
+ inst: b,
+ index: x,
+ }
+ }
+ return inst
}
func init() {
@@ -86,20 +135,24 @@
// newInstance creates a new instance. Use Insert to populate the instance.
func newInstance(x *index, p *build.Instance, v *adt.Vertex) *Instance {
// TODO: associate root source with structLit.
- i := &Instance{
+ inst := &Instance{
root: v,
inst: p,
}
if p != nil {
- i.ImportPath = p.ImportPath
- i.Dir = p.Dir
- i.PkgName = p.PkgName
- i.DisplayName = p.ImportPath
+ inst.ImportPath = p.ImportPath
+ inst.Dir = p.Dir
+ inst.PkgName = p.PkgName
+ inst.DisplayName = p.ImportPath
if p.Err != nil {
- i.setListOrError(p.Err)
+ inst.setListOrError(p.Err)
}
}
- return x.addInst(i)
+
+ x.AddInst(p.ImportPath, v, p)
+ x.loaded[p] = inst
+ inst.index = x
+ return inst
}
func (inst *Instance) setListOrError(err errors.Error) {
@@ -137,7 +190,7 @@
cfg := &compile.Config{
Scope: scope,
Imports: func(x *ast.Ident) (pkgPath string) {
- if _, ok := builtins[x.Name]; !ok {
+ if !isBuiltin(x.Name) {
return ""
}
return x.Name
@@ -250,8 +303,8 @@
v.Finalize(ctx)
p := i.index.addInst(&Instance{
- root: v,
- complete: true,
+ root: v,
+ // complete: true,
})
return p
}
@@ -265,7 +318,7 @@
idx := inst.index
r := inst.index.Runtime
- rErr := runtime.ResolveFiles(idx.Index, p, isBuiltin)
+ rErr := r.ResolveFiles(p)
cfg := &compile.Config{Scope: inst.root}
v, err := compile.Files(cfg, r, p.ID(), p.Files...)
@@ -284,7 +337,7 @@
i.setListOrError(err)
}
- i.complete = true
+ // i.complete = true
return i
}
@@ -372,7 +425,7 @@
PkgName: inst.PkgName,
Incomplete: inst.Incomplete,
- complete: true,
+ // complete: true,
})
return inst, nil
}
diff --git a/cue/testdata/packages/issue398.txtar b/cue/testdata/packages/issue398.txtar
index 93bd039..5dfe8d8 100644
--- a/cue/testdata/packages/issue398.txtar
+++ b/cue/testdata/packages/issue398.txtar
@@ -15,13 +15,9 @@
xx: 1
-- out/eval --
(struct){
- foo: (struct){
- x: (int){ 1 }
- y: (int){ 2 }
- }
+ xx: (int){ 1 }
+ yy: (int){ 2 }
zz: (int){ 3 }
- x: (int){ 1 }
- y: (int){ 2 }
}
-- out/compile --
--- foo.cue
diff --git a/cue/types.go b/cue/types.go
index da8bc89..c0ed1d4 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -551,7 +551,7 @@
}
a = append(a, label)
default:
- a = append(a, f.SelectorString(v.idx.Index))
+ a = append(a, f.SelectorString(v.idx.Runtime))
}
}
return a
@@ -1756,7 +1756,7 @@
case *adt.FieldReference:
env := ctx.Env(x.UpCount)
inst, path = mkPath(c, nil, env.Vertex)
- path = append(path, x.Label.SelectorString(c.Index))
+ path = append(path, x.Label.SelectorString(c.Runtime))
case *adt.LabelReference:
env := ctx.Env(x.UpCount)
@@ -2216,7 +2216,7 @@
a = append(a, remakeValue(v, env, x.X))
// A string selector is quoted.
a = append(a, remakeValue(v, env, &adt.String{
- Str: x.Sel.SelectorString(v.idx.Index),
+ Str: x.Sel.SelectorString(v.idx.Runtime),
}))
op = SelectorOp
diff --git a/encoding/gocode/generator.go b/encoding/gocode/generator.go
index 451fa8f..b5f4476 100644
--- a/encoding/gocode/generator.go
+++ b/encoding/gocode/generator.go
@@ -159,7 +159,7 @@
g.decl(iter.Label(), iter.Value())
}
- r := internal.GetRuntimeNew(inst).(*cue.Runtime)
+ r := internal.GetRuntime(inst).(*cue.Runtime)
b, err = r.Marshal(inst)
g.addErr(err)
diff --git a/encoding/gocode/gocodec/codec.go b/encoding/gocode/gocodec/codec.go
index 6631683..bd4b083 100644
--- a/encoding/gocode/gocodec/codec.go
+++ b/encoding/gocode/gocodec/codec.go
@@ -177,5 +177,5 @@
}
func checkAndForkRuntime(r *cue.Runtime, v cue.Value) *cue.Runtime {
- return internal.CheckAndForkRuntimeNew(r, v).(*cue.Runtime)
+ return internal.CheckAndForkRuntime(r, v).(*cue.Runtime)
}
diff --git a/internal/builtin/registry.go b/internal/builtin/registry.go
deleted file mode 100644
index a90155e..0000000
--- a/internal/builtin/registry.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2020 CUE Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package builtin
-
-import (
- "sort"
-
- "cuelang.org/go/cue"
- "cuelang.org/go/internal/core/adt"
-)
-
-type PackageFunc func(ctx *adt.OpContext) (*adt.Vertex, error)
-
-// Register registers a builtin, the value of which will be built
-// on first use. All builtins must be registered before first use of a runtime.
-// This restriction may be eliminated in the future.
-func Register(importPath string, f PackageFunc) {
- builtins[importPath] = f
- // TODO: remove at some point.
- cue.AddBuiltinPackage(importPath, f)
-}
-
-var builtins = map[string]PackageFunc{}
-
-func ImportPaths() (a []string) {
- for s := range builtins {
- a = append(a, s)
- }
- sort.Strings(a)
- return a
-}
-
-// Get return the builder for the package with the given path.
-// It will panic if the path does not exist.
-func Get(path string) PackageFunc {
- return builtins[path]
-}
diff --git a/internal/core/convert/go.go b/internal/core/convert/go.go
index 1997136..6e74b9d 100644
--- a/internal/core/convert/go.go
+++ b/internal/core/convert/go.go
@@ -224,7 +224,7 @@
func convertRec(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Value {
if internal.CoreValue != nil {
if ii, iv := internal.CoreValue(x); ii != nil {
- i := ii.(*runtime.Index)
+ i := ii.(*runtime.Runtime)
v := iv.(*adt.Vertex)
// TODO: panic if nto the same runtime.
_ = i
diff --git a/internal/core/runtime/build.go b/internal/core/runtime/build.go
new file mode 100644
index 0000000..c167e16
--- /dev/null
+++ b/internal/core/runtime/build.go
@@ -0,0 +1,94 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runtime
+
+import (
+ "strings"
+
+ "cuelang.org/go/cue/ast"
+ "cuelang.org/go/cue/ast/astutil"
+ "cuelang.org/go/cue/build"
+ "cuelang.org/go/cue/errors"
+ "cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/compile"
+)
+
+// Build builds b and all its transitive dependencies, insofar they have not
+// been build yet.
+func (x *Runtime) Build(b *build.Instance) (v *adt.Vertex, errs errors.Error) {
+ if v := x.GetNodeFromInstance(b); v != nil {
+ return v, b.Err
+ }
+ // TODO: clear cache of old implementation.
+ // if s := b.ImportPath; s != "" {
+ // // Use cached result, if available.
+ // if v, err := x.LoadImport(s); v != nil || err != nil {
+ // return v, err
+ // }
+ // }
+
+ errs = b.Err
+
+ // Build transitive dependencies.
+ for _, file := range b.Files {
+ file.VisitImports(func(d *ast.ImportDecl) {
+ for _, s := range d.Specs {
+ errs = errors.Append(errs, x.buildSpec(b, s))
+ }
+ })
+ }
+
+ err := x.ResolveFiles(b)
+ errs = errors.Append(errs, err)
+
+ v, err = compile.Files(nil, x, b.ID(), b.Files...)
+ errs = errors.Append(errs, err)
+
+ if errs != nil {
+ v = adt.ToVertex(&adt.Bottom{Err: errs})
+ b.Err = errs
+ }
+
+ x.AddInst(b.ImportPath, v, b)
+
+ return v, errs
+}
+
+func (x *Runtime) buildSpec(b *build.Instance, spec *ast.ImportSpec) (errs errors.Error) {
+ info, err := astutil.ParseImportSpec(spec)
+ if err != nil {
+ return errors.Promote(err, "invalid import path")
+ }
+
+ pkg := b.LookupImport(info.ID)
+ if pkg == nil {
+ if strings.Contains(info.ID, ".") {
+ return errors.Newf(spec.Pos(),
+ "package %q imported but not defined in %s",
+ info.ID, b.ImportPath)
+ }
+ return nil // TODO: check the builtin package exists here.
+ }
+
+ if v := x.index.importsByBuild[pkg]; v != nil {
+ return pkg.Err
+ }
+
+ if _, err := x.Build(pkg); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/internal/core/runtime/go.go b/internal/core/runtime/go.go
new file mode 100644
index 0000000..4b84b3f
--- /dev/null
+++ b/internal/core/runtime/go.go
@@ -0,0 +1,53 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runtime
+
+import (
+ "reflect"
+
+ "cuelang.org/go/cue/ast"
+ "cuelang.org/go/internal/core/adt"
+)
+
+func (x *Runtime) StoreType(t reflect.Type, src ast.Expr, expr adt.Expr) {
+ if expr == nil {
+ x.index.StoreType(t, src)
+ } else {
+ x.index.StoreType(t, expr)
+ }
+}
+
+func (x *Runtime) LoadType(t reflect.Type) (src ast.Expr, expr adt.Expr, ok bool) {
+ v, ok := x.index.LoadType(t)
+ if ok {
+ switch x := v.(type) {
+ case ast.Expr:
+ return x, nil, true
+ case adt.Expr:
+ src, _ = x.Source().(ast.Expr)
+ return src, x, true
+ }
+ }
+ return nil, nil, false
+}
+
+func (x *index) StoreType(t reflect.Type, v interface{}) {
+ x.typeCache.Store(t, v)
+}
+
+func (x *index) LoadType(t reflect.Type) (v interface{}, ok bool) {
+ v, ok = x.typeCache.Load(t)
+ return v, ok
+}
diff --git a/internal/core/runtime/imports.go b/internal/core/runtime/imports.go
new file mode 100644
index 0000000..4f25c96
--- /dev/null
+++ b/internal/core/runtime/imports.go
@@ -0,0 +1,128 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runtime
+
+import (
+ "path"
+ "sync"
+
+ "cuelang.org/go/cue/build"
+ "cuelang.org/go/cue/errors"
+ "cuelang.org/go/internal/core/adt"
+)
+
+type PackageFunc func(ctx adt.Runtime) (*adt.Vertex, errors.Error)
+
+func RegisterBuiltin(importPath string, f PackageFunc) {
+ sharedIndex.RegisterBuiltin(importPath, f)
+}
+
+func (x *index) RegisterBuiltin(importPath string, f PackageFunc) {
+ if x.builtins == nil {
+ x.builtins = map[string]PackageFunc{}
+ }
+ x.builtins[importPath] = f
+}
+
+var SharedRuntime = &Runtime{index: sharedIndex}
+
+func (x *Runtime) IsBuiltinPackage(path string) bool {
+ return x.index.isBuiltin(path)
+}
+
+// sharedIndex is used for indexing builtins and any other labels common to
+// all instances.
+var sharedIndex = newIndex()
+
+// index maps conversions from label names to internal codes.
+//
+// All instances belonging to the same package should share this index.
+type index struct {
+ // Change this to Instance at some point.
+ // From *structLit/*Vertex -> Instance
+ imports map[*adt.Vertex]*build.Instance
+ importsByPath map[string]*adt.Vertex
+ importsByBuild map[*build.Instance]*adt.Vertex
+ builtins map[string]PackageFunc
+
+ // mutex sync.Mutex
+ typeCache sync.Map // map[reflect.Type]evaluated
+
+}
+
+func newIndex() *index {
+ i := &index{
+ imports: map[*adt.Vertex]*build.Instance{},
+ importsByPath: map[string]*adt.Vertex{},
+ importsByBuild: map[*build.Instance]*adt.Vertex{},
+ }
+ return i
+}
+
+func (x *index) isBuiltin(id string) bool {
+ if x == nil || x.builtins == nil {
+ return false
+ }
+ _, ok := x.builtins[id]
+ return ok
+}
+
+func (r *Runtime) AddInst(path string, key *adt.Vertex, p *build.Instance) {
+ x := r.index
+ if key == nil {
+ panic("key must not be nil")
+ }
+ x.imports[key] = p
+ x.importsByBuild[p] = key
+ if path != "" {
+ x.importsByPath[path] = key
+ }
+}
+
+func (r *Runtime) GetInstanceFromNode(key *adt.Vertex) *build.Instance {
+ return r.index.imports[key]
+}
+
+func (r *Runtime) GetNodeFromInstance(key *build.Instance) *adt.Vertex {
+ return r.index.importsByBuild[key]
+}
+
+func (r *Runtime) LoadImport(importPath string) (*adt.Vertex, errors.Error) {
+ x := r.index
+
+ key := x.importsByPath[importPath]
+ if key != nil {
+ return key, nil
+ }
+
+ if x.builtins != nil {
+ if f := x.builtins[importPath]; f != nil {
+ p, err := f(r)
+ if err != nil {
+ return adt.ToVertex(&adt.Bottom{Err: err}), nil
+ }
+ inst := &build.Instance{
+ ImportPath: importPath,
+ PkgName: path.Base(importPath),
+ }
+ x.imports[p] = inst
+ x.importsByPath[importPath] = p
+ x.importsByBuild[inst] = p
+ return p, nil
+ }
+ }
+
+ return key, nil
+}
diff --git a/internal/core/runtime/index.go b/internal/core/runtime/index.go
index a8e381c..bf5867c 100644
--- a/internal/core/runtime/index.go
+++ b/internal/core/runtime/index.go
@@ -15,122 +15,30 @@
package runtime
import (
- "reflect"
"sync"
- "cuelang.org/go/cue/ast"
"cuelang.org/go/internal"
"cuelang.org/go/internal/core/adt"
)
-// Index maps conversions from label names to internal codes.
-//
-// All instances belonging to the same package should share this Index.
-//
-// INDEX IS A TRANSITIONAL TYPE TO BRIDGE THE OLD AND NEW
-// IMPLEMENTATIONS. USE RUNTIME.
-type Index struct {
- labelMap map[string]int64
- labels []string
-
- // Change this to Instance at some point.
- // From *structLit/*Vertex -> Instance
- imports map[interface{}]interface{}
- importsByPath map[string]interface{}
- // imports map[string]*adt.Vertex
-
- offset int64
- parent *Index
-
- // mutex sync.Mutex
- typeCache sync.Map // map[reflect.Type]evaluated
+func (r *Runtime) IndexToString(i int64) string {
+ return r.index.IndexToString(i)
}
-// SharedIndex is used for indexing builtins and any other labels common to
-// all instances.
-var SharedIndex = newSharedIndex()
-
-var SharedIndexNew = newSharedIndex()
-
-var SharedRuntimeNew = &Runtime{index: SharedIndexNew}
-
-func newSharedIndex() *Index {
- i := &Index{
- labelMap: map[string]int64{"": 0},
- labels: []string{""},
- imports: map[interface{}]interface{}{},
- importsByPath: map[string]interface{}{},
- }
- return i
+func (r *Runtime) StringToIndex(s string) int64 {
+ return getKey(s)
}
-// NewIndex creates a new index.
-func NewIndex(parent *Index) *Index {
- i := &Index{
- labelMap: map[string]int64{},
- imports: map[interface{}]interface{}{},
- importsByPath: map[string]interface{}{},
- offset: int64(len(parent.labels)) + parent.offset,
- parent: parent,
- }
- return i
+func (r *Runtime) LabelStr(l adt.Feature) string {
+ return l.IdentString(r)
}
-func (x *Index) IndexToString(i int64) string {
- for ; i < x.offset; x = x.parent {
- }
- return x.labels[i-x.offset]
+func (r *Runtime) StrLabel(str string) adt.Feature {
+ return r.Label(str, false)
}
-func (x *Index) StringToIndex(s string) int64 {
- for p := x; p != nil; p = p.parent {
- if f, ok := p.labelMap[s]; ok {
- return int64(f)
- }
- }
- index := int64(len(x.labelMap)) + x.offset
- x.labelMap[s] = index
- x.labels = append(x.labels, s)
- return int64(index)
-}
-
-func (x *Index) HasLabel(s string) (ok bool) {
- for c := x; c != nil; c = c.parent {
- _, ok = c.labelMap[s]
- if ok {
- break
- }
- }
- return ok
-}
-
-func (x *Index) StoreType(t reflect.Type, v interface{}) {
- x.typeCache.Store(t, v)
-}
-
-func (x *Index) LoadType(t reflect.Type) (v interface{}, ok bool) {
- v, ok = x.typeCache.Load(t)
- return v, ok
-}
-
-func (x *Index) StrLabel(str string) adt.Feature {
- return x.Label(str, false)
-}
-
-func (x *Index) NodeLabel(n ast.Node) (f adt.Feature, ok bool) {
- switch label := n.(type) {
- case *ast.BasicLit:
- name, _, err := ast.LabelName(label)
- return x.Label(name, false), err == nil
- case *ast.Ident:
- name, err := ast.ParseIdent(label)
- return x.Label(name, true), err == nil
- }
- return 0, false
-}
-
-func (x *Index) Label(s string, isIdent bool) adt.Feature {
- index := x.StringToIndex(s)
+func (r *Runtime) Label(s string, isIdent bool) adt.Feature {
+ index := r.StringToIndex(s)
typ := adt.StringLabel
if isIdent {
switch {
@@ -146,32 +54,39 @@
return f
}
-func (idx *Index) LabelStr(l adt.Feature) string {
- return l.IdentString(idx)
+// TODO: move to Runtime as fields.
+var (
+ labelMap = map[string]int{}
+ labels = make([]string, 0, 1000)
+ mutex sync.RWMutex
+)
+
+func init() {
+ getKey("")
}
-func (x *Index) AddInst(path string, key, p interface{}) {
- if key == nil {
- panic("key must not be nil")
+func getKey(s string) int64 {
+ mutex.RLock()
+ p, ok := labelMap[s]
+ mutex.RUnlock()
+ if ok {
+ return int64(p)
}
- x.imports[key] = p
- if path != "" {
- x.importsByPath[path] = key
+ mutex.Lock()
+ defer mutex.Unlock()
+ p, ok = labelMap[s]
+ if ok {
+ return int64(p)
}
+ p = len(labels)
+ labels = append(labels, s)
+ labelMap[s] = p
+ return int64(p)
}
-func (x *Index) GetImportFromNode(key interface{}) interface{} {
- imp := x.imports[key]
- if imp == nil && x.parent != nil {
- return x.parent.GetImportFromNode(key)
- }
- return imp
-}
-
-func (x *Index) GetImportFromPath(id string) interface{} {
- key := x.importsByPath[id]
- if key == nil && x.parent != nil {
- return x.parent.GetImportFromPath(id)
- }
- return key
+func (x *index) IndexToString(i int64) string {
+ mutex.RLock()
+ s := labels[i]
+ mutex.RUnlock()
+ return s
}
diff --git a/internal/core/runtime/resolve.go b/internal/core/runtime/resolve.go
index 29f19e4..94188db 100644
--- a/internal/core/runtime/resolve.go
+++ b/internal/core/runtime/resolve.go
@@ -23,15 +23,9 @@
"cuelang.org/go/cue/errors"
)
-func lineStr(idx *Index, n ast.Node) string {
- return n.Pos().String()
-}
+func (r *Runtime) ResolveFiles(p *build.Instance) (errs errors.Error) {
+ idx := r.index
-func ResolveFiles(
- idx *Index,
- p *build.Instance,
- isBuiltin func(s string) bool,
-) (errs errors.Error) {
// Link top-level declarations. As top-level entries get unified, an entry
// may be linked to any top-level entry of any of the files.
allFields := map[string]ast.Node{}
@@ -45,18 +39,17 @@
}
}
for _, f := range p.Files {
- err := ResolveFile(idx, f, p, allFields, isBuiltin)
+ err := resolveFile(idx, f, p, allFields)
errs = errors.Append(errs, err)
}
return errs
}
-func ResolveFile(
- idx *Index,
+func resolveFile(
+ idx *index,
f *ast.File,
p *build.Instance,
allFields map[string]ast.Node,
- isBuiltin func(s string) bool,
) errors.Error {
unresolved := map[string][]*ast.Ident{}
for _, u := range f.Unresolved {
@@ -82,7 +75,7 @@
name := path.Base(id)
if imp := p.LookupImport(id); imp != nil {
name = imp.PkgName
- } else if !isBuiltin(id) {
+ } else if _, ok := idx.builtins[id]; !ok {
errs = errors.Append(errs,
nodeErrorf(spec, "package %q not found", id))
continue
@@ -162,3 +155,7 @@
// }
return errs
}
+
+func lineStr(idx *index, n ast.Node) string {
+ return n.Pos().String()
+}
diff --git a/internal/core/runtime/resolve_test.go b/internal/core/runtime/resolve_test.go
index 42ec1dd..9bc7240 100644
--- a/internal/core/runtime/resolve_test.go
+++ b/internal/core/runtime/resolve_test.go
@@ -33,8 +33,6 @@
Path: ast.NewString(importPath),
}
- isBuiltin := func(s string) bool { return false }
-
f := &ast.File{
Decls: []ast.Decl{
&ast.ImportDecl{Specs: []*ast.ImportSpec{spec1, spec2}},
@@ -50,12 +48,12 @@
Imports: []*ast.ImportSpec{spec1, spec2},
}
- err := ResolveFile(nil, f, &build.Instance{
+ err := resolveFile(nil, f, &build.Instance{
Imports: []*build.Instance{{
ImportPath: importPath,
PkgName: "foo",
}},
- }, map[string]ast.Node{}, isBuiltin)
+ }, map[string]ast.Node{})
if err != nil {
t.Errorf("exected no error, found %v", err)
diff --git a/internal/core/runtime/runtime.go b/internal/core/runtime/runtime.go
index 6361e6b..448dc30 100644
--- a/internal/core/runtime/runtime.go
+++ b/internal/core/runtime/runtime.go
@@ -14,124 +14,18 @@
package runtime
-import (
- "reflect"
- "strings"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/ast/astutil"
- "cuelang.org/go/cue/build"
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/compile"
-)
-
// A Runtime maintains data structures for indexing and resuse for evaluation.
type Runtime struct {
- index *Index
+ index *index
// Data holds the legacy index strut. It is for transitional purposes only.
Data interface{}
}
-// New creates a new Runtime.
+// New creates a new Runtime. The builtins registered with RegisterBuiltin
+// are available for
func New() *Runtime {
return &Runtime{
- index: NewIndex(SharedIndexNew),
+ index: sharedIndex,
}
}
-
-func NewWithIndex(x *Index) *Runtime {
- return &Runtime{index: x}
-}
-
-func (x *Runtime) IndexToString(i int64) string {
- return x.index.IndexToString(i)
-}
-
-func (x *Runtime) StringToIndex(s string) int64 {
- return x.index.StringToIndex(s)
-}
-
-func (x *Runtime) Build(b *build.Instance) (v *adt.Vertex, errs errors.Error) {
- if s := b.ImportPath; s != "" {
- // Use cached result, if available.
- if v, err := x.LoadImport(s); v != nil || err != nil {
- return v, err
- }
- // Cache the result if any.
- defer func() {
- if errs == nil && v != nil {
- x.index.AddInst(b.ImportPath, v, b)
- }
- }()
- }
-
- // Build transitive dependencies.
- for _, file := range b.Files {
- file.VisitImports(func(d *ast.ImportDecl) {
- for _, s := range d.Specs {
- errs = errors.Append(errs, x.buildSpec(b, s))
- }
- })
- }
-
- if errs != nil {
- return nil, errs
- }
-
- return compile.Files(nil, x, b.ID(), b.Files...)
-}
-
-func (x *Runtime) buildSpec(b *build.Instance, spec *ast.ImportSpec) (errs errors.Error) {
- info, err := astutil.ParseImportSpec(spec)
- if err != nil {
- return errors.Promote(err, "invalid import path")
- }
-
- pkg := b.LookupImport(info.ID)
- if pkg == nil {
- if strings.Contains(info.ID, ".") {
- return errors.Newf(spec.Pos(),
- "package %q imported but not defined in %s",
- info.ID, b.ImportPath)
- }
- return nil // TODO: check the builtin package exists here.
- }
-
- if _, err := x.Build(pkg); err != nil {
- return err
- }
-
- return nil
-}
-
-func (x *Runtime) LoadImport(importPath string) (*adt.Vertex, errors.Error) {
- v := x.index.GetImportFromPath(importPath)
- if v == nil {
- return nil, nil
- }
- return v.(*adt.Vertex), nil
-}
-
-func (x *Runtime) StoreType(t reflect.Type, src ast.Expr, expr adt.Expr) {
- if expr == nil {
- x.index.StoreType(t, src)
- } else {
- x.index.StoreType(t, expr)
- }
-}
-
-func (x *Runtime) LoadType(t reflect.Type) (src ast.Expr, expr adt.Expr, ok bool) {
- v, ok := x.index.LoadType(t)
- if ok {
- switch x := v.(type) {
- case ast.Expr:
- return x, nil, true
- case adt.Expr:
- src, _ = x.Source().(ast.Expr)
- return src, x, true
- }
- }
- return nil, nil, false
-}
diff --git a/internal/internal.go b/internal/internal.go
index 6e89bac..bc89cbd 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -72,10 +72,7 @@
var UnifyBuiltin func(v interface{}, kind string) interface{}
// GetRuntime reports the runtime for an Instance or Value.
-var GetRuntimeOld func(instance interface{}) interface{}
-
-// GetRuntime reports the runtime for an Instance or Value.
-var GetRuntimeNew func(instance interface{}) interface{}
+var GetRuntime func(instance interface{}) interface{}
// CoreValue returns an *runtime.Index and *adt.Vertex for a cue.Value.
// It returns nil if value is not a cue.Value.
@@ -87,12 +84,7 @@
// CheckAndForkRuntime checks that value is created using runtime, panicking
// if it does not, and returns a forked runtime that will discard additional
// keys.
-var CheckAndForkRuntimeOld func(runtime, value interface{}) interface{}
-
-// CheckAndForkRuntime checks that value is created using runtime, panicking
-// if it does not, and returns a forked runtime that will discard additional
-// keys.
-var CheckAndForkRuntimeNew func(runtime, value interface{}) interface{}
+var CheckAndForkRuntime func(runtime, value interface{}) interface{}
// BaseContext is used as CUEs default context for arbitrary-precision decimals
var BaseContext = apd.BaseContext.WithPrecision(24)
diff --git a/pkg/encoding/json/manual.go b/pkg/encoding/json/manual.go
index 96a9721..6ba9956 100644
--- a/pkg/encoding/json/manual.go
+++ b/pkg/encoding/json/manual.go
@@ -109,7 +109,7 @@
if !json.Valid(b) {
return false, fmt.Errorf("json: invalid JSON")
}
- r := internal.GetRuntimeNew(v).(*cue.Runtime)
+ r := internal.GetRuntime(v).(*cue.Runtime)
inst, err := r.Compile("json.Validate", b)
if err != nil {
return false, err
diff --git a/pkg/encoding/yaml/manual.go b/pkg/encoding/yaml/manual.go
index 4c8c05c..7e3f5ef 100644
--- a/pkg/encoding/yaml/manual.go
+++ b/pkg/encoding/yaml/manual.go
@@ -83,7 +83,7 @@
if err != nil {
return false, err
}
- r := internal.GetRuntimeNew(v).(*cue.Runtime)
+ r := internal.GetRuntime(v).(*cue.Runtime)
for {
expr, err := d.Decode()
if err != nil {
@@ -127,7 +127,7 @@
if err != nil {
return false, err
}
- r := internal.GetRuntimeNew(v).(*cue.Runtime)
+ r := internal.GetRuntime(v).(*cue.Runtime)
for {
expr, err := d.Decode()
if err != nil {
diff --git a/pkg/internal/register.go b/pkg/internal/register.go
index e25567e..c475476 100644
--- a/pkg/internal/register.go
+++ b/pkg/internal/register.go
@@ -15,13 +15,17 @@
package internal
import (
- "cuelang.org/go/internal/builtin"
+ "cuelang.org/go/cue/errors"
"cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/eval"
+ "cuelang.org/go/internal/core/runtime"
)
func Register(importPath string, p *Package) {
- f := func(ctx *adt.OpContext) (*adt.Vertex, error) {
+ f := func(r adt.Runtime) (*adt.Vertex, errors.Error) {
+ ctx := eval.NewContext(r, nil)
+
return p.MustCompile(ctx, importPath), nil
}
- builtin.Register(importPath, f)
+ runtime.RegisterBuiltin(importPath, f)
}