cuelang.org/go@v0.10.1/internal/core/adt/share.go (about) 1 // Copyright 2024 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 // https://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 adt 16 17 // This file contains logic regarding structure sharing. 18 19 // Notes 20 // 21 // TODO: 22 // We may want to consider tracking closedness in parallel to the Vertex 23 // structure, for instance in a CloseInfo or in a cue.Value itself. 24 // 25 // reg: {} 26 // #def: sub: reg 27 // 28 // By tracking closedness inside the CloseInfo, we can still share the 29 // structure and only have to change 30 // 31 // Maybe this is okay, though, as #Def itself can be shared, at least. 32 33 func (n *nodeContext) unshare() { 34 n.noSharing = true 35 36 if !n.isShared { 37 return 38 } 39 n.isShared = false 40 n.node.IsShared = false 41 42 v := n.node.BaseValue.(*Vertex) 43 44 // TODO: the use of cycle for BaseValue is getting increasingly outdated. 45 // Find another mechanism once we get rid of the old evaluator. 46 n.node.BaseValue = n.origBaseValue 47 48 n.scheduleVertexConjuncts(n.shared, v, n.sharedID) 49 50 n.sharedID.cc.decDependent(n.ctx, SHARED, n.node.cc) 51 n.sharedID.cc = nil 52 } 53 54 // finalizeSharing should be called when it is known for sure a node can be 55 // shared. 56 func (n *nodeContext) finalizeSharing() { 57 if n.sharedID.cc != nil { 58 n.sharedID.cc.decDependent(n.ctx, SHARED, n.node.cc) 59 n.sharedID.cc = nil 60 } 61 } 62 63 func (n *nodeContext) share(c Conjunct, arc *Vertex, id CloseInfo) { 64 if n.isShared { 65 panic("already sharing") 66 } 67 n.origBaseValue = n.node.BaseValue 68 n.node.BaseValue = arc 69 n.node.IsShared = true 70 n.isShared = true 71 n.shared = c 72 n.sharedID = id 73 74 // At this point, the node may still be unshared at a later point. For this 75 // purpose we need to keep the retain count above zero until all conjuncts 76 // have been processed and it is clear that sharing is possible. Delaying 77 // such a count should not hurt performance, as a shared node is completed 78 // anyway. 79 id.cc.incDependent(n.ctx, SHARED, n.node.cc) 80 } 81 82 func (n *nodeContext) shareIfPossible(c Conjunct, arc *Vertex, id CloseInfo) bool { 83 // TODO: have an experiment here to enable or disable structure sharing. 84 // return false 85 if !n.ctx.Sharing { 86 return false 87 } 88 89 if n.noSharing || n.isShared || n.ctx.errs != nil { 90 return false 91 } 92 93 // This line is to deal with this case: 94 // 95 // reg: {} 96 // #def: sub: reg 97 // 98 // Ideally we find a different solution, like passing closedness 99 // down elsewhere. In fact, as we do this in closeContexts, it probably 100 // already works, it will just not be reflected in the debug output. 101 // We could fix that by not printing structure shared nodes, which is 102 // probably a good idea anyway. 103 // 104 // TODO: come up with a mechanism to allow this case. 105 if n.node.Closed && !arc.Closed { 106 return false 107 } 108 109 // Sharing let expressions is not supported and will result in unmarked 110 // structural cycles. Processing will still terminate, but printing the 111 // result will result in an infinite loop. 112 // 113 // TODO: allow this case. 114 if n.node.Label.IsLet() { 115 return false 116 } 117 118 // If an arc is a computed intermediate result and not part of a CUE output, 119 // it should not be shared. 120 if n.node.nonRooted || arc.nonRooted { 121 return false 122 } 123 124 n.share(c, arc, id) 125 return true 126 } 127 128 // Vertex values that are held in BaseValue will be wrapped in the following 129 // order: 130 // 131 // disjuncts -> (shared | computed | data) 132 // 133 // DerefDisjunct 134 // - get the current value under computation 135 // 136 // DerefValue 137 // - get the value the node ultimately represents. 138 // 139 140 // DerefValue unrolls indirections of Vertex values. These may be introduced, 141 // for instance, by temporary bindings such as comprehension values. 142 // It returns v itself if v does not point to another Vertex. 143 func (v *Vertex) DerefValue() *Vertex { 144 for { 145 arc, ok := v.BaseValue.(*Vertex) 146 if !ok { 147 return v 148 } 149 v = arc 150 } 151 } 152 153 // DerefDisjunct indirects a node that points to a disjunction. 154 func (v *Vertex) DerefDisjunct() *Vertex { 155 for { 156 arc, ok := v.BaseValue.(*Vertex) 157 if !ok || !arc.IsDisjunct { 158 return v 159 } 160 v = arc 161 } 162 } 163 164 // DerefNonDisjunct indirects a node that points to a disjunction. 165 func (v *Vertex) DerefNonDisjunct() *Vertex { 166 for { 167 arc, ok := v.BaseValue.(*Vertex) 168 if !ok || arc.IsDisjunct { 169 return v 170 } 171 v = arc 172 } 173 } 174 175 // DerefNonRooted indirects a node that points to a value that is not rooted. 176 // This includes structure-shared nodes that point to a let field: let fields 177 // may or may not be part of a struct, and thus should be treated as non-rooted. 178 func (v *Vertex) DerefNonRooted() *Vertex { 179 for { 180 arc, ok := v.BaseValue.(*Vertex) 181 if !ok || arc.IsDisjunct || (v.IsShared && !arc.Label.IsLet()) { 182 return v 183 } 184 v = arc 185 } 186 } 187 188 // DerefNonShared finds the indirection of an arc that is not the result of 189 // structure sharing. This is especially relevant when indirecting disjunction 190 // values. 191 func (v *Vertex) DerefNonShared() *Vertex { 192 if v.state != nil && v.state.isShared { 193 return v 194 } 195 for { 196 arc, ok := v.BaseValue.(*Vertex) 197 if !ok { 198 return v 199 } 200 v = arc 201 } 202 }