Skip to content

Commit 4f107a3

Browse files
implement std.objectRemoveKey as a builtin which retains hidden fields
The Go Jsonnet implementation already implements objectRemoveKey as a builtin and retains hidden fields, this brings the C++ Jsonnet into alignment. See google/go-jsonnet#830
1 parent bd1f67e commit 4f107a3

File tree

5 files changed

+67
-7
lines changed

5 files changed

+67
-7
lines changed

core/desugarer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct BuiltinDecl {
3636
std::vector<UString> params;
3737
};
3838

39-
static unsigned long max_builtin = 40;
39+
static unsigned long max_builtin = 41;
4040
BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
4141
{
4242
switch (builtin) {
@@ -81,6 +81,7 @@ BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
8181
case 38: return {U"decodeUTF8", {U"arr"}};
8282
case 39: return {U"atan2", {U"y", U"x"}};
8383
case 40: return {U"hypot", {U"a", U"b"}};
84+
case 41: return {U"objectRemoveKey", {U"obj", U"key"}};
8485
default:
8586
std::cerr << "INTERNAL ERROR: Unrecognized builtin function: " << builtin << std::endl;
8687
std::abort();

core/state.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct HeapEntity {
3535
SIMPLE_OBJECT,
3636
COMPREHENSION_OBJECT,
3737
EXTENDED_OBJECT,
38+
RESTRICTED_OBJECT,
3839
};
3940
GarbageCollectionMark mark;
4041
Type type;
@@ -210,6 +211,20 @@ struct HeapExtendedObject : public HeapObject {
210211
}
211212
};
212213

214+
/** Objects created by std.objectRemoveKey(s). */
215+
struct HeapRestrictedObject : public HeapLeafObject {
216+
/** The 'parent' object from which we inherit. */
217+
HeapObject *obj;
218+
219+
/** A 'filter list' of keys retained in the object. */
220+
std::map<const Identifier *, ObjectField::Hide> retainedKeys;
221+
222+
HeapRestrictedObject(HeapObject *obj, const std::map<const Identifier *, ObjectField::Hide> &retained_keys)
223+
: HeapLeafObject(RESTRICTED_OBJECT), obj(obj), retainedKeys(retained_keys)
224+
{
225+
}
226+
};
227+
213228
/** Objects created by the ObjectComprehensionSimple construct. */
214229
struct HeapComprehensionObject : public HeapLeafObject {
215230
/** The captured environment. */
@@ -382,6 +397,12 @@ class Heap {
382397
addIfHeapEntity(obj->right, s.children);
383398
break;
384399
}
400+
case HeapEntity::RESTRICTED_OBJECT: {
401+
assert(dynamic_cast<HeapRestrictedObject *>(curr));
402+
auto *obj = static_cast<HeapRestrictedObject *>(curr);
403+
addIfHeapEntity(obj->obj, s.children);
404+
break;
405+
}
385406
case HeapEntity::COMPREHENSION_OBJECT: {
386407
assert(dynamic_cast<HeapComprehensionObject *>(curr));
387408
auto *obj = static_cast<HeapComprehensionObject *>(curr);

core/vm.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,20 @@ class Interpreter {
688688
auto *l = findObject(f, ext->left, start_from, counter);
689689
if (l)
690690
return l;
691+
} else if (auto *ext = dynamic_cast<HeapRestrictedObject *>(curr)) {
692+
if (counter >= start_from) {
693+
++counter;
694+
const auto it = ext->retainedKeys.find(f);
695+
if (it != ext->retainedKeys.end()) {
696+
return findObject(f, ext->obj, start_from, counter);
697+
} else {
698+
counter += countLeaves(ext->obj);
699+
return nullptr;
700+
}
701+
} else {
702+
++counter;
703+
return findObject(f, ext->obj, start_from, counter);
704+
}
691705
} else {
692706
if (counter >= start_from) {
693707
if (auto *simp = dynamic_cast<HeapSimpleObject *>(curr)) {
@@ -719,6 +733,9 @@ class Interpreter {
719733
r[f.first] = f.second.hide;
720734
}
721735

736+
} else if (auto *obj = dynamic_cast<const HeapRestrictedObject *>(obj_)) {
737+
return obj->retainedKeys;
738+
722739
} else if (auto *obj = dynamic_cast<const HeapExtendedObject *>(obj_)) {
723740
r = objectFieldsAux(obj->right);
724741
for (const auto &pair : objectFieldsAux(obj->left)) {
@@ -848,6 +865,8 @@ class Interpreter {
848865
{
849866
if (auto *ext = dynamic_cast<HeapExtendedObject *>(obj)) {
850867
return countLeaves(ext->left) + countLeaves(ext->right);
868+
} else if (auto *ext = dynamic_cast<HeapRestrictedObject *>(obj)) {
869+
return countLeaves(ext->obj) + 1;
851870
} else {
852871
// Must be a HeapLeafObject.
853872
return 1;
@@ -944,6 +963,7 @@ class Interpreter {
944963
builtins["objectHasEx"] = &Interpreter::builtinObjectHasEx;
945964
builtins["length"] = &Interpreter::builtinLength;
946965
builtins["objectFieldsEx"] = &Interpreter::builtinObjectFieldsEx;
966+
builtins["objectRemoveKey"] = &Interpreter::builtinObjectRemoveKey;
947967
builtins["codepoint"] = &Interpreter::builtinCodepoint;
948968
builtins["char"] = &Interpreter::builtinChar;
949969
builtins["log"] = &Interpreter::builtinLog;
@@ -1263,6 +1283,22 @@ class Interpreter {
12631283
return nullptr;
12641284
}
12651285

1286+
const AST *builtinObjectRemoveKey(const LocationRange &loc, const std::vector<Value> &args)
1287+
{
1288+
validateBuiltinArgs(loc, "objectRemoveKey", args, {Value::OBJECT, Value::STRING});
1289+
auto *obj = static_cast<HeapObject *>(args[0].v.h);
1290+
const auto *key = static_cast<HeapString *>(args[1].v.h);
1291+
const auto *key_id = alloc->makeIdentifier(key->value);
1292+
auto fields = objectFieldsAux(obj);
1293+
const auto it = fields.find(key_id);
1294+
if (it != fields.end()) {
1295+
fields.erase(it);
1296+
}
1297+
// TODO: If the key isn't found perhaps we can evaluate to the existing `obj`?
1298+
scratch = makeObject<HeapRestrictedObject>(obj, fields);
1299+
return nullptr;
1300+
}
1301+
12661302
const AST *builtinCodepoint(const LocationRange &loc, const std::vector<Value> &args)
12671303
{
12681304
validateBuiltinArgs(loc, "codepoint", args, {Value::STRING});
@@ -1939,6 +1975,8 @@ class Interpreter {
19391975
if (auto *ext = dynamic_cast<HeapExtendedObject *>(curr)) {
19401976
objectInvariants(ext->right, self, counter, thunks);
19411977
objectInvariants(ext->left, self, counter, thunks);
1978+
} else if (auto *ext = dynamic_cast<HeapRestrictedObject *>(curr)) {
1979+
objectInvariants(ext->obj, self, counter, thunks);
19421980
} else {
19431981
if (auto *simp = dynamic_cast<HeapSimpleObject *>(curr)) {
19441982
for (AST *assert : simp->asserts) {

stdlib/std.jsonnet

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,12 +1786,6 @@ limitations under the License.
17861786
std.removeAt(arr, indexes[0])
17871787
,
17881788

1789-
objectRemoveKey(obj, key):: {
1790-
[k]: obj[k],
1791-
for k in std.objectFields(obj)
1792-
if k != key
1793-
},
1794-
17951789
sha1(str):: go_only_function,
17961790
sha256(str):: go_only_function,
17971791
sha512(str):: go_only_function,

test_suite/stdlib.jsonnet

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,12 @@ std.assertEqual(std.remove([1, 2, 3], 2), [1, 3]) &&
16301630
std.assertEqual(std.removeAt([1, 2, 3], 1), [1, 3]) &&
16311631

16321632
std.assertEqual(std.objectRemoveKey({ foo: 1, bar: 2, baz: 3 }, 'foo'), { bar: 2, baz: 3 }) &&
1633+
// objectRemoveKey should retain hidden fields as hidden fields.
1634+
std.assertEqual(std.objectRemoveKey({ foo: 1, bar: 2, baz:: 3 }, 'foo').baz, 3) &&
1635+
// objectRemoveKey doesn't break inheritance within the provided object.
1636+
std.assertEqual(std.objectRemoveKey({ a: 1 } + { b: super.a }, 'a'), { b: 1 }) &&
1637+
// objectRemoveKey works with inheritance outside of the object.
1638+
std.assertEqual({ a: 1 } + std.objectRemoveKey({ b: super.a }, 'a'), { a: 1, b: 1 }) &&
16331639

16341640
std.assertEqual(std.trim('already trimmed string'), 'already trimmed string') &&
16351641
std.assertEqual(std.trim(' string with spaces on both ends '), 'string with spaces on both ends') &&

0 commit comments

Comments
 (0)