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 export 16 17 import ( 18 "crypto/md5" 19 "fmt" 20 "io" 21 "strconv" 22 "strings" 23 24 "cuelang.org/go/cue/ast" 25 "cuelang.org/go/cue/literal" 26 "cuelang.org/go/cue/token" 27 "cuelang.org/go/internal/core/adt" 28 ) 29 30 func (e *exporter) stringLabel(f adt.Feature) ast.Label { 31 x := f.Index() 32 switch f.Typ() { 33 case adt.IntLabel: 34 return ast.NewLit(token.INT, strconv.Itoa(int(x))) 35 36 case adt.DefinitionLabel, adt.HiddenLabel, adt.HiddenDefinitionLabel: 37 s := e.identString(f) 38 return ast.NewIdent(s) 39 40 case adt.StringLabel: 41 s := e.ctx.IndexToString(int64(x)) 42 if f == 0 || !ast.IsValidIdent(s) || 43 strings.HasPrefix(s, "#") || strings.HasPrefix(s, "_") { 44 return ast.NewLit(token.STRING, literal.Label.Quote(s)) 45 } 46 fallthrough 47 48 default: 49 return ast.NewIdent(e.ctx.IndexToString(int64(x))) 50 } 51 } 52 53 // identString converts the given Feature f to an identifier string. 54 // 55 // Hidden field mangling: 56 // 57 // If f is a hidden field that comes from an expanded package, it will mangle 58 // the name by expending it with the MD5 hash of the package name. This is to 59 // avoid collisions of the hidden identifiers when namespaces are merged. 60 // It uses the MD5 hash to allow hidden fields from the same package to 61 // still match across inlining and unifications. 62 // 63 // TODO: Alternatives approaches to consider: 64 // 1. Rewrite to unique hidden field names. This means, though, that hidden 65 // fields may not match across unifications. That may be okay. 66 // 2. Force inline hidden fields from such packages, or translate them to let 67 // expressions when necessary. This should generally preserve semantics 68 // quite well. 69 // 3. Allow addressing hidden fields across packages, for instance by allowing 70 // `_(hiddenField, pkg: "import/path")`. This kind of defeats the purpose 71 // of hidden fields, though. 72 // 73 // Option 2 seems the best long-term solution. It should be possible to 74 // piggyback on the self containment algorithm for this: basically, whenever 75 // we see a hidden reference of an inlined package, we treat it as an 76 // external reference itself. 77 // 78 // For now, as this only can occur when the InlineImports feature is used, 79 // we use this simpler approach. 80 func (e *exporter) identString(f adt.Feature) string { 81 s := f.IdentString(e.ctx) 82 83 if !f.IsHidden() || !e.cfg.InlineImports { 84 return s 85 } 86 87 pkg := f.PkgID(e.ctx) 88 if pkg == "" || pkg == "_" || pkg == e.pkgID { 89 return s 90 } 91 92 if e.pkgHash == nil { 93 e.pkgHash = map[string]string{} 94 } 95 id, ok := e.pkgHash[pkg] 96 if !ok { 97 h := md5.New() 98 io.WriteString(h, pkg) 99 b := h.Sum(nil) 100 id = fmt.Sprintf("_%8X", b[:4]) 101 e.pkgHash[pkg] = id 102 } 103 s += id 104 105 return s 106 } 107