113 lines
3.7 KiB
Go
113 lines
3.7 KiB
Go
// Copyright 2016 Pulumi, Inc. All rights reserved.
|
|
|
|
package resource
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/golang/glog"
|
|
|
|
"github.com/pulumi/coconut/pkg/compiler"
|
|
"github.com/pulumi/coconut/pkg/compiler/binder"
|
|
"github.com/pulumi/coconut/pkg/compiler/errors"
|
|
"github.com/pulumi/coconut/pkg/compiler/symbols"
|
|
"github.com/pulumi/coconut/pkg/diag"
|
|
"github.com/pulumi/coconut/pkg/eval"
|
|
"github.com/pulumi/coconut/pkg/eval/rt"
|
|
"github.com/pulumi/coconut/pkg/tokens"
|
|
"github.com/pulumi/coconut/pkg/util/contract"
|
|
)
|
|
|
|
// ConfigMap contains a mapping from variable token to the value to poke into that variable.
|
|
type ConfigMap map[tokens.Token]interface{}
|
|
|
|
// ConfigApplier returns a compiler preexec function that applies the configuration when invoked, and copies the
|
|
// resulting configuration variables into the supplied map (if it is non-nil).
|
|
func (cfg *ConfigMap) ConfigApplier(vars map[tokens.Token]*rt.Object) compiler.Preexec {
|
|
return func(ctx *binder.Context, pkg *symbols.Package, e eval.Interpreter) {
|
|
configs := cfg.ApplyConfig(ctx, pkg, e)
|
|
contract.Assert(configs != nil)
|
|
if vars != nil {
|
|
for k, v := range configs {
|
|
vars[k] = v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ApplyConfig applies the configuration map to an existing interpreter context. The map is simply a map of tokens --
|
|
// which must be globally settable variables (module properties or static class properties) -- to serializable constant
|
|
// values. The routine simply walks these tokens in sorted order, and assigns the constant objects. Note that, because
|
|
// we are accessing module and class members, this routine will also trigger the relevant initialization routines.
|
|
func (cfg *ConfigMap) ApplyConfig(ctx *binder.Context, pkg *symbols.Package,
|
|
e eval.Interpreter) map[tokens.Token]*rt.Object {
|
|
glog.V(5).Infof("Applying configuration values for package '%v'", pkg)
|
|
|
|
// Track all configuration variables that get set, for diagnostics and plumbing.
|
|
vars := make(map[tokens.Token]*rt.Object)
|
|
|
|
if cfg != nil {
|
|
// For each config entry, bind the token to its symbol, and then attempt to assign to it.
|
|
for _, tok := range StableConfigKeys(*cfg) {
|
|
glog.V(5).Infof("Applying configuration value for token '%v'", tok)
|
|
|
|
// Bind to the symbol; if it returns nil, this means an error has resulted, and we can skip it.
|
|
var tree diag.Diagable = nil // there is no source info for this; eventually we may assign one.
|
|
if sym := ctx.LookupSymbol(tree, tokens.Token(tok), true); sym != nil {
|
|
var ok bool
|
|
switch s := sym.(type) {
|
|
case *symbols.ModuleProperty:
|
|
ok = true
|
|
case *symbols.ClassProperty:
|
|
// class properties are ok, so long as they are static.
|
|
ok = s.Static()
|
|
default:
|
|
ok = false
|
|
}
|
|
if !ok {
|
|
ctx.Diag.Errorf(errors.ErrorIllegalConfigToken, tok)
|
|
continue // skip to the next one
|
|
}
|
|
|
|
// Load up the location as an l-value; because we don't support instance properties, this is nil.
|
|
if loc := e.LoadLocation(tree, sym, nil, true); loc != nil {
|
|
// Allocate a new constant for the value we are about to assign, and assign it to the location.
|
|
v := (*cfg)[tok]
|
|
obj := e.NewConstantObject(nil, v)
|
|
loc.Assign(tree, obj)
|
|
vars[tok] = obj
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !ctx.Diag.Success() {
|
|
ctx.Diag.Errorf(errors.ErrorConfigApplyFailure, pkg)
|
|
}
|
|
|
|
return vars
|
|
}
|
|
|
|
func StableConfigKeys(config ConfigMap) []tokens.Token {
|
|
sorted := make(configKeys, 0, len(config))
|
|
for key := range config {
|
|
sorted = append(sorted, key)
|
|
}
|
|
sort.Sort(sorted)
|
|
return sorted
|
|
}
|
|
|
|
type configKeys []tokens.Token
|
|
|
|
func (s configKeys) Len() int {
|
|
return len(s)
|
|
}
|
|
|
|
func (s configKeys) Swap(i, j int) {
|
|
s[i], s[j] = s[j], s[i]
|
|
}
|
|
|
|
func (s configKeys) Less(i, j int) bool {
|
|
return s[i] < s[j]
|
|
}
|