Skip to content
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

FEAT: Implement Symbol.unscopables #1556

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion rhino/src/main/java/org/mozilla/javascript/NativeArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,30 @@ public class NativeArray extends IdScriptableObject implements List {

private static final Object ARRAY_TAG = "Array";
private static final Long NEGATIVE_ONE = Long.valueOf(-1);
private static final String[] UNSCOPABLES = {
"at",
"copyWithin",
"entries",
"fill",
"find",
"findIndex",
"findLast",
"findLastIndex",
"flat",
"flatMap",
"includes",
"keys",
"toReversed",
"toSorted",
"toSpliced",
"values"
};

static void init(Context cx, Scriptable scope, boolean sealed) {
NativeArray obj = new NativeArray(0);
IdFunctionObject constructor = obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
ScriptRuntimeES6.addSymbolSpecies(cx, scope, constructor);
ScriptRuntimeES6.addSymbolUnscopables(cx, scope, constructor);
}

static int getMaximumInitialCapacity() {
Expand Down Expand Up @@ -160,6 +179,11 @@ protected void fillConstructorProperties(IdFunctionObject ctor) {

@Override
protected void initPrototypeId(int id) {
if (id == SymbolId_unscopables) {
initPrototypeValue(id, SymbolKey.UNSCOPABLES, makeUnscopables(), DONTENUM | READONLY);
return;
}

String s, fnName = null;
int arity;
switch (id) {
Expand Down Expand Up @@ -502,6 +526,25 @@ public void setPrototype(Scriptable p) {
}
}

private Object makeUnscopables() {
Context cx = Context.getCurrentContext();
NativeObject obj;

if (cx != null) {
Scriptable scope = this.getParentScope();
obj = (NativeObject) cx.newObject(scope);
} else {
obj = new NativeObject();
}

ScriptableObject desc = ScriptableObject.buildDataDescriptor(obj, true, EMPTY);
for (var k : UNSCOPABLES) {
obj.defineOwnProperty(cx, k, desc);
}
obj.setPrototype(null); // unscopables don't have any prototype
return obj;
}

@Override
public Object get(int index, Scriptable start) {
if (!denseOnly && isGetterOrSetter(null, index, false)) return super.get(index, start);
Expand Down Expand Up @@ -2382,6 +2425,8 @@ protected int findPrototypeId(Symbol k) {
// as the "values" property. We implement this by returning the
// ID of "values" when the iterator symbol is accessed.
return Id_values;
} else if (SymbolKey.UNSCOPABLES.equals(k)) {
return SymbolId_unscopables;
}
return 0;
}
Expand Down Expand Up @@ -2533,7 +2578,8 @@ protected int findPrototypeId(String s) {
Id_at = 32,
Id_flat = 33,
Id_flatMap = 34,
MAX_PROTOTYPE_ID = Id_flatMap;
SymbolId_unscopables = 35,
MAX_PROTOTYPE_ID = SymbolId_unscopables;
private static final int ConstructorId_join = -Id_join,
ConstructorId_reverse = -Id_reverse,
ConstructorId_sort = -Id_sort,
Expand Down
10 changes: 10 additions & 0 deletions rhino/src/main/java/org/mozilla/javascript/ScriptRuntimeES6.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,14 @@ public static void addSymbolSpecies(
thisObj));
constructor.defineOwnProperty(cx, SymbolKey.SPECIES, speciesDescriptor, false);
}

/** Registers the symbol <code>[Symbol.unscopables]</code> on the given constructor function. */
public static void addSymbolUnscopables(
Context cx, Scriptable scope, IdScriptableObject constructor) {
ScriptableObject unScopablesDescriptor = (ScriptableObject) cx.newObject(scope);
ScriptableObject.putProperty(unScopablesDescriptor, "enumerable", false);
ScriptableObject.putProperty(unScopablesDescriptor, "configurable", false);
ScriptableObject.putProperty(unScopablesDescriptor, "writable", false);
constructor.defineOwnProperty(cx, SymbolKey.UNSCOPABLES, unScopablesDescriptor, false);
}
}
28 changes: 28 additions & 0 deletions tests/testsrc/jstests/es6/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,32 @@ assertEquals(a.map(_ => 'a'), ['a', 'a']);
assertEquals(Array, symbolSpeciesValue);
})();

(function TestSymbolUnScopables() {
var symbolUnscopablesValue = Array[Symbol.unscopables];
assertEquals(undefined, symbolUnscopablesValue);
})();

(function TestSymbolUnScopablesOnArray() {
var symbolValuesToAssert = '{' +
'"at":true,' +
'"copyWithin":true,' +
'"entries":true,' +
'"fill":true,' +
'"find":true,' +
'"findIndex":true,' +
'"findLast":true,' +
'"findLastIndex":true,' +
'"flat":true,' +
'"flatMap":true,' +
'"includes":true,' +
'"keys":true,' +
'"toReversed":true,' +
'"toSorted":true,' +
'"toSpliced":true,' +
'"values":true' +
'}';
var symbolValues = Array.prototype[Symbol.unscopables];
assertEquals(JSON.stringify(symbolValues), symbolValuesToAssert);
})();

"success";
4 changes: 2 additions & 2 deletions tests/testsrc/test262.properties
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ harness 22/115 (19.13%)
isConstructor.js {unsupported: [Reflect.construct]}
nativeFunctionMatcher.js

built-ins/Array 471/3055 (15.42%)
built-ins/Array 468/3055 (15.32%)
fromAsync 94/94 (100.0%)
from/calling-from-valid-1-noStrict.js non-strict Spec pretty clearly says this should be undefined
from/elements-deleted-after.js Checking to see if length changed, but spec says it should not
Expand Down Expand Up @@ -302,7 +302,7 @@ built-ins/Array 471/3055 (15.42%)
prototype/splice/target-array-non-extensible.js
prototype/splice/target-array-with-non-configurable-property.js
prototype/Symbol.iterator/not-a-constructor.js {unsupported: [Reflect.construct]}
prototype/Symbol.unscopables 4/4 (100.0%)
prototype/Symbol.unscopables/change-array-by-copy.js
prototype/toLocaleString/invoke-element-tolocalestring.js
prototype/toLocaleString/not-a-constructor.js {unsupported: [Reflect.construct]}
prototype/toLocaleString/primitive_this_value.js strict
Expand Down
Loading