cuelang.org/go@v0.13.0/internal/core/runtime/imports.go (about) 1 // Copyright 2020 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package runtime 16 17 import ( 18 "path" 19 "sync" 20 21 "cuelang.org/go/cue/build" 22 "cuelang.org/go/cue/errors" 23 "cuelang.org/go/internal" 24 "cuelang.org/go/internal/core/adt" 25 ) 26 27 type PackageFunc func(ctx adt.Runtime) (*adt.Vertex, errors.Error) 28 29 func RegisterBuiltin(importPath string, f PackageFunc) { 30 sharedIndex.RegisterBuiltin(importPath, f) 31 } 32 33 func (x *index) RegisterBuiltin(importPath string, f PackageFunc) { 34 if x.builtinPaths == nil { 35 x.builtinPaths = map[string]PackageFunc{} 36 x.builtinShort = map[string]string{} 37 } 38 x.builtinPaths[importPath] = f 39 base := path.Base(importPath) 40 if _, ok := x.builtinShort[base]; ok { 41 importPath = "" // Don't allow ambiguous base paths. 42 } 43 x.builtinShort[base] = importPath 44 } 45 46 // We use a sync.OnceValue below so that cueexperiment.Init is only called 47 // the first time that the API is used, letting the user set $CUE_EXPERIMENT globally 48 // as part of their package init if they want to. 49 var SharedRuntime = sync.OnceValue(func() *Runtime { 50 r := &Runtime{index: sharedIndex} 51 // The version logic below is copied from [Runtime.Init]; 52 // consider refactoring to share the code if it gets any more complicated. 53 // 54 // TODO(mvdan,mpvl): Note that SharedRuntime follows the globally set evaluator version, 55 // which may be different than what was supplied via Go code for each context like 56 // via cuecontext.EvaluatorVersion(cuecontext.EvalV3). 57 // This does not cause issues between evalv2 and evalv3 as they use the same ADT, 58 // but future evaluator versions may not be compatible at that level. 59 // We should consider using one SharedRuntime per evaluator version, 60 // or getting rid of SharedRuntime altogether. 61 r.SetVersion(internal.DefaultVersion) 62 return r 63 }) 64 65 // BuiltinPackagePath converts a short-form builtin package identifier to its 66 // full path or "" if this doesn't exist. 67 func (x *Runtime) BuiltinPackagePath(path string) string { 68 return x.index.shortBuiltinToPath(path) 69 } 70 71 // sharedIndex is used for indexing builtins and any other labels common to 72 // all instances. 73 var sharedIndex = newIndex() 74 75 // index maps conversions from label names to internal codes. 76 // 77 // All instances belonging to the same package should share this index. 78 type index struct { 79 // lock is used to guard imports-related maps. 80 // TODO: makes these per cuecontext. 81 lock sync.RWMutex 82 imports map[*adt.Vertex]*build.Instance 83 importsByPath map[string]*adt.Vertex 84 importsByBuild map[*build.Instance]*adt.Vertex 85 86 nextUniqueID uint64 87 88 // These are initialized during Go package initialization time and do not 89 // need to be guarded. 90 builtinPaths map[string]PackageFunc // Full path 91 builtinShort map[string]string // Commandline shorthand 92 93 typeCache sync.Map // map[reflect.Type]evaluated 94 } 95 96 func (i *index) getNextUniqueID() uint64 { 97 // TODO: use atomic increment instead. 98 i.lock.Lock() 99 i.nextUniqueID++ 100 x := i.nextUniqueID 101 i.lock.Unlock() 102 return x 103 } 104 105 func newIndex() *index { 106 i := &index{ 107 imports: map[*adt.Vertex]*build.Instance{}, 108 importsByPath: map[string]*adt.Vertex{}, 109 importsByBuild: map[*build.Instance]*adt.Vertex{}, 110 } 111 return i 112 } 113 114 func (x *index) shortBuiltinToPath(id string) string { 115 if x == nil || x.builtinPaths == nil { 116 return "" 117 } 118 return x.builtinShort[id] 119 } 120 121 func (r *Runtime) AddInst(path string, key *adt.Vertex, p *build.Instance) { 122 r.index.lock.Lock() 123 defer r.index.lock.Unlock() 124 125 x := r.index 126 if key == nil { 127 panic("key must not be nil") 128 } 129 x.imports[key] = p 130 x.importsByBuild[p] = key 131 if path != "" { 132 x.importsByPath[path] = key 133 } 134 } 135 136 func (r *Runtime) GetInstanceFromNode(key *adt.Vertex) *build.Instance { 137 r.index.lock.RLock() 138 defer r.index.lock.RUnlock() 139 140 return r.index.imports[key] 141 } 142 143 func (r *Runtime) getNodeFromInstance(key *build.Instance) *adt.Vertex { 144 r.index.lock.RLock() 145 defer r.index.lock.RUnlock() 146 147 return r.index.importsByBuild[key] 148 } 149 150 func (r *Runtime) LoadImport(importPath string) *adt.Vertex { 151 r.index.lock.Lock() 152 defer r.index.lock.Unlock() 153 154 x := r.index 155 156 key := x.importsByPath[importPath] 157 if key != nil { 158 return key 159 } 160 161 if x.builtinPaths != nil { 162 if f := x.builtinPaths[importPath]; f != nil { 163 p, err := f(r) 164 if err != nil { 165 return adt.ToVertex(&adt.Bottom{Err: err}) 166 } 167 inst := &build.Instance{ 168 ImportPath: importPath, 169 PkgName: path.Base(importPath), 170 } 171 x.imports[p] = inst 172 x.importsByPath[importPath] = p 173 x.importsByBuild[inst] = p 174 return p 175 } 176 } 177 178 return key 179 }