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  }