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)
 }