-
Notifications
You must be signed in to change notification settings - Fork 66
chore: Keep hidden field to std.objectRemoveKey and support inheritan… #586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -271,7 +271,8 @@ object Val { | |
| private val triggerAsserts: (Val.Obj, Val.Obj) => Unit, | ||
| `super`: Obj, | ||
| valueCache: util.HashMap[Any, Val] = new util.HashMap[Any, Val](), | ||
| private var allKeys: util.LinkedHashMap[String, java.lang.Boolean] = null) | ||
| private var allKeys: util.LinkedHashMap[String, java.lang.Boolean] = null, | ||
| private val excludedKeys: java.util.Set[String] = null) | ||
| extends Literal | ||
| with Expr.ObjBody { | ||
| private var asserting: Boolean = false | ||
|
|
@@ -321,33 +322,105 @@ object Val { | |
| current = current.getSuper | ||
| } | ||
|
|
||
| // Pre-collect all keys defined in this chain once (only needed if any obj has excludedKeys) | ||
| var keysInThisChain: java.util.Set[String] = null | ||
|
|
||
| current = lhs | ||
| for (s <- objs.reverse) { | ||
| current = new Val.Obj(s.pos, s.getValue0, false, s.triggerAsserts, current) | ||
| val filteredExcludedKeys = if (s.excludedKeys != null) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use scala sets?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default Scala Set is immutable, not easy to use here, the current value cache is a java linkedHashMap, and IIRC, Java's HashMap is faster than Scala's |
||
| // Lazily collect keys from the entire chain — computed at most once | ||
| if (keysInThisChain == null) { | ||
| keysInThisChain = Util.preSizedJavaHashSet[String](objs.length * 4) | ||
| for (o <- objs) keysInThisChain.addAll(o.getValue0.keySet()) | ||
| } | ||
| Util.interset(s.excludedKeys, keysInThisChain) | ||
| } else null | ||
| current = new Val.Obj( | ||
| s.pos, | ||
| s.getValue0, | ||
| false, | ||
| s.triggerAsserts, | ||
| current, | ||
| new util.HashMap[Any, Val](), | ||
| null, | ||
| filteredExcludedKeys | ||
| ) | ||
| } | ||
| current | ||
| } | ||
|
|
||
| /** | ||
| * Create a new object that removes the specified keys from this object. | ||
| * | ||
| * The implementation preserves both internal and external inheritance: | ||
| * 1. Internal: For `objectRemoveKey({ a: 1 } + { b: super.a }, 'a')`, the original object's | ||
| * internal super chain is preserved, so `b: super.a` can still access `a`. | ||
| * 2. External: For `{ a: 1 } + objectRemoveKey({ b: super.a }, 'a')`, the result can | ||
| * participate in a new inheritance chain, where `super.a` accesses the new super. | ||
| * | ||
| * The approach is to create a thin wrapper object with the original object as super, and mark | ||
| * the key as excluded via the excludedKeys set. The excluded key won't appear in | ||
| * allKeyNames/visibleKeyNames, but super.key can still access the value. | ||
| */ | ||
| def removeKeys(pos: Position, keys: String*): Val.Obj = { | ||
| val excluded = | ||
| if (keys.length == 1) java.util.Collections.singleton(keys.head) | ||
| else { | ||
| val set = Util.preSizedJavaHashSet[String](keys.length) | ||
| keys.foreach(set.add) | ||
| set | ||
| } | ||
| new Val.Obj( | ||
| pos, | ||
| Util.emptyJavaLinkedHashMap[String, Obj.Member], | ||
| false, | ||
| null, // No asserts in wrapper; original object's asserts are triggered via super chain | ||
| this, | ||
| new util.HashMap[Any, Val](), // NOTE: Must be a dedicated new value cache. | ||
| null, | ||
| excluded | ||
He-Pin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) | ||
| } | ||
|
|
||
| def prettyName = "object" | ||
| override def asObj: Val.Obj = this | ||
|
|
||
| private def gatherKeys(mapping: util.LinkedHashMap[String, java.lang.Boolean]): Unit = { | ||
| val objs = mutable.ArrayBuffer(this) | ||
| var current = this | ||
| while (current.getSuper != null) { | ||
| objs += current.getSuper | ||
| current = current.getSuper | ||
| var superObj = current.getSuper | ||
| while (superObj != null) { | ||
| objs += superObj | ||
| current = superObj | ||
| superObj = current.getSuper | ||
| } | ||
|
|
||
| // Collect all excluded keys from the entire chain | ||
| val allExcludedKeys = Util.preSizedJavaHashSet[String](objs.length) | ||
| for (s <- objs) { | ||
| val keys = s.excludedKeys | ||
| if (Util.isNotEmpty(keys)) { | ||
| allExcludedKeys.addAll(keys) | ||
| } | ||
| } | ||
|
|
||
| for (s <- objs.reverse) { | ||
| if (s.static) { | ||
| mapping.putAll(s.allKeys) | ||
| s.allKeys | ||
| .keySet() | ||
| .forEach(key => { | ||
| if (!allExcludedKeys.contains(key)) { | ||
| mapping.put(key, false) | ||
| } | ||
| }) | ||
| } else { | ||
| s.getValue0.forEach { (k, m) => | ||
| val vis = m.visibility | ||
| if (!mapping.containsKey(k)) mapping.put(k, vis == Visibility.Hidden) | ||
| else if (vis == Visibility.Hidden) mapping.put(k, true) | ||
| else if (vis == Visibility.Unhide) mapping.put(k, false) | ||
| if (!allExcludedKeys.contains(k)) { | ||
| val vis = m.visibility | ||
| if (!mapping.containsKey(k)) mapping.put(k, vis == Visibility.Hidden) | ||
| else if (vis == Visibility.Hidden) mapping.put(k, true) | ||
| else if (vis == Visibility.Unhide) mapping.put(k, false) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -411,6 +484,11 @@ object Val { | |
| case x => x | ||
| } | ||
| } else { | ||
| // Check if the key is excluded (used by objectRemoveKey) | ||
| // When self != this, we need to check if the key exists in self's visible keys | ||
He-Pin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if ((self eq this) && excludedKeys != null && excludedKeys.contains(k)) { | ||
| Error.fail("Field does not exist: " + k, pos) | ||
| } | ||
| val cacheKey = if (self eq this) k else (k, self) | ||
| val cachedValue = valueCache.get(cacheKey) | ||
| if (cachedValue != null) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| local o = std.objectRemoveKey({foo: 1, bar:: 2, baz: 3}, "foo"); | ||
| { | ||
| fields: std.objectFields(o), | ||
| all_fields: std.objectFieldsAll(o), | ||
| bar: o.bar, | ||
| object: o, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "all_fields": [ | ||
| "bar", | ||
| "baz" | ||
| ], | ||
| "bar": 2, | ||
| "fields": [ | ||
| "baz" | ||
| ], | ||
| "object": { | ||
| "baz": 3 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| local o1 = { | ||
| assert self.x : 'x', | ||
| assert super.y : 'y', | ||
| b: super.a, | ||
| }; | ||
| { a: 'begin' } + std.objectRemoveKey({ y: true } + o1 + { a: 'end', x: true }, 'y') |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "a": "end", | ||
| "b": "begin", | ||
| "x": true | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| local o1 = { | ||
| assert self.x : 'x', | ||
| assert super.y : 'y', | ||
| b: super.a, | ||
| }; | ||
| local o2 = { | ||
| x: true, | ||
| y: true, | ||
| a: 'one', | ||
| }; | ||
| { a: 'begin' } + std.objectRemoveKey({ y: std.isString('yyy') } + o1 + o2, 'x') |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| sjsonnet.Error: Field does not exist: x | ||
| at [Select x].(builtinObjectRemoveKey_super_assert.jsonnet:2:14) | ||
|
|
Uh oh!
There was an error while loading. Please reload this page.