From ae236ba329c6ad796f14d3888f65140595757406 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Thu, 20 May 2021 16:35:40 +0200 Subject: [PATCH 01/12] ADD: ES6 Object.values / Object.entries / Object.fromEntries --- src/org/mozilla/javascript/NativeObject.java | 67 +++++++- .../tests/es6/NativeObjectTest.java | 148 ++++++++++++++++++ 2 files changed, 214 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 91c913069a..9e8a3debf2 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; +import java.util.Map.Entry; import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex; @@ -54,6 +55,12 @@ protected void fillConstructorProperties(IdFunctionObject ctor) if (Context.getCurrentContext().version >= Context.VERSION_ES6) { addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_setPrototypeOf, "setPrototypeOf", 2); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_entries, + "entries", 1); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_fromEntries, + "fromEntries", 1); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_values, + "values", 1); } addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_keys, "keys", 1); @@ -368,6 +375,61 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } return cx.newArray(scope, ids); } + + case ConstructorId_entries: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = getCompatibleObject(cx, scope, arg); + Object[] ids = obj.getIds(); + for (int i = 0; i < ids.length; i++) { + if (ids[i] instanceof Integer) { + Integer key = (Integer) ids[i]; + ids[i] = cx.newArray(scope, new Object[]{ ids[i], obj.get(key, scope) }); + } else { + String key = ScriptRuntime.toString(ids[i]); + ids[i] = cx.newArray(scope, new Object[]{ ids[i], obj.get(key, scope) }); + } + } + return cx.newArray(scope, ids); + } + case ConstructorId_fromEntries: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = getCompatibleObject(cx, scope, arg); + Object enumeration = ScriptRuntime.enumInit(obj, cx, scope, ScriptRuntime.ENUMERATE_VALUES_IN_ORDER); + obj = cx.newObject(scope); + while (ScriptRuntime.enumNext(enumeration)) { + Scriptable entry = ensureScriptable(ScriptRuntime.enumId(enumeration, cx)); + Object key = entry.get(0, entry); + if (key == Scriptable.NOT_FOUND) { + key = Undefined.instance; + } + Object value = entry.get(1, entry); + if (value == Scriptable.NOT_FOUND) { + value = Undefined.instance; + } + if (key instanceof Integer) { + obj.put((Integer)key, obj, value); + } else { + obj.put(ScriptRuntime.toString(key), obj, value); + } + } + return obj; + } + case ConstructorId_values: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = getCompatibleObject(cx, scope, arg); + Object[] ids = obj.getIds(); + for (int i = 0; i < ids.length; i++) { + if (ids[i] instanceof Integer) { + ids[i] = obj.get((Integer) ids[i], scope); + } else { + ids[i] = obj.get(ScriptRuntime.toString(ids[i]), scope); + } + } + return cx.newArray(scope, ids); + } case ConstructorId_getOwnPropertyNames: { Object arg = args.length < 1 ? Undefined.instance : args[0]; @@ -676,7 +738,6 @@ public void clear() { throw new UnsupportedOperationException(); } - class EntrySet extends AbstractSet> { @Override public Iterator> iterator() { @@ -902,7 +963,11 @@ protected int findPrototypeId(String s) ConstructorId_getOwnPropertySymbols = -14, ConstructorId_assign = -15, ConstructorId_is = -16, + // ES6 ConstructorId_setPrototypeOf = -17, + ConstructorId_entries = -18, + ConstructorId_fromEntries = -19, + ConstructorId_values = -20, Id_constructor = 1, Id_toString = 2, diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java index 1a3bb66419..a4e8e6efd9 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java @@ -9,6 +9,10 @@ import static org.junit.Assert.assertEquals; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -146,4 +150,148 @@ public void testSetPrototypeOfMissingParameters() { assertEquals("Object.setPrototypeOf: At least 2 arguments required, but only 1 passed", result); } + + @Test + public void testKeysMissingParameter() { + Object result = cx.evaluateString( + scope, "try { " + + " Object.keys();" + + "} catch (e) { e.message }", + "test", 1, null + ); + + assertEquals("Cannot convert undefined to an object.", result); + } + + @Test + public void testKeysOnObjectParameter() { + evaluateAndAssert("Object.keys({'foo':'bar', 2: 'y', 1: 'x'})", + Arrays.asList("1", "2", "foo")); + } + + @Test + public void testKeysOnArray() { + evaluateAndAssert("Object.keys(['x','y','z'])", + Arrays.asList("0", "1", "2")); + } + + @Test + public void testKeysOnArrayWithProp() { + evaluateAndAssert("var arr = ['x','y','z'];\n" + + "arr['foo'] = 'bar'; Object.keys(arr)", + Arrays.asList("0", "1", "2", "foo")); + } + + @Test + public void testValuesMissingParameter() { + Object result = cx.evaluateString( + scope, "try { " + + " Object.values();" + + "} catch (e) { e.message }", + "test", 1, null + ); + + assertEquals("Cannot convert undefined to an object.", result); + } + + @Test + public void testValuesOnObjectParameter() { + evaluateAndAssert("Object.values({'foo':'bar', 2: 'y', 1: 'x'})", + Arrays.asList("x", "y", "bar")); + } + + @Test + public void testValuesOnArray() { + evaluateAndAssert("Object.values(['x','y','z'])", + Arrays.asList("x", "y", "z")); + } + + @Test + public void testValuesOnArrayWithProp() { + evaluateAndAssert("var arr = [3,4,5];\n" + + "arr['foo'] = 'bar'; Object.values(arr)", + Arrays.asList(3, 4, 5, "bar")); + } + + + @Test + public void testEntriesMissingParameter() { + Object result = cx.evaluateString( + scope, "try { " + + " Object.entries();" + + "} catch (e) { e.message }", + "test", 1, null + ); + + assertEquals("Cannot convert undefined to an object.", result); + } + + @Test + public void testEntriesOnObjectParameter() { + evaluateAndAssert("Object.entries({'foo':'bar', 2: 'y', 1: 'x'})", + Arrays.asList( + Arrays.asList(1, "x"), + Arrays.asList(2, "y"), + Arrays.asList("foo", "bar"))); + } + + @Test + public void testEntriesOnArray() { + evaluateAndAssert("Object.entries(['x','y','z'])", + Arrays.asList( + Arrays.asList(0,"x"), + Arrays.asList(1,"y"), + Arrays.asList(2,"z"))); + } + + @Test + public void testEntriesOnArrayWithProp() { + evaluateAndAssert("var arr = [3,4,5];\n" + + "arr['foo'] = 'bar'; Object.entries(arr)", + Arrays.asList( + Arrays.asList(0, 3), + Arrays.asList(1, 4), + Arrays.asList(2, 5), + Arrays.asList("foo", "bar"))); + } + + @Test + public void testFromEntriesMissingParameter() { + Object result = cx.evaluateString( + scope, "try { " + + " Object.fromEntries();" + + "} catch (e) { e.message }", + "test", 1, null + ); + + assertEquals("Cannot convert undefined to an object.", result); + } + + @Test + public void testFromEntriesOnObjectParameter() { + Map map = new HashMap<>(); + map.put("foo", "bar"); + map.put(2, "y"); + map.put(1, "x"); + evaluateAndAssert( + "Object.fromEntries(Object.entries({'foo':'bar', 2: 'y', 1: 'x'}))", + map); + } + + @Test + public void testFromEntriesOnArray() { + Map map = new HashMap<>(); + map.put(0, "x"); + map.put(1, "y"); + map.put(2, "z"); + evaluateAndAssert( + "Object.fromEntries(Object.entries(['x','y','z']))", + map); + } + + private void evaluateAndAssert(String script, Object expected) { + Object result = cx.evaluateString(scope, script, "test", 1, null); + assertEquals(expected, result); + } + } From 6a913ed72e3cd26759b6b42382a785b6d8af67b6 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Thu, 20 May 2021 20:03:12 +0200 Subject: [PATCH 02/12] enabled some tests --- testsrc/test262.properties | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/testsrc/test262.properties b/testsrc/test262.properties index ca219e0f95..b181a5dd04 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -729,7 +729,6 @@ built-ins/Object ! defineProperty/15.2.3.6-4-293-3.js ! defineProperty/15.2.3.6-4-293-4.js ! defineProperty/15.2.3.6-4-336.js - ! fromEntries/empty-iterable.js ! fromEntries/evaluation-order.js ! fromEntries/iterator-closed-for-null-entry.js ! fromEntries/iterator-closed-for-string-entry.js @@ -740,35 +739,12 @@ built-ins/Object ! fromEntries/iterator-not-closed-for-throwing-done-accessor.js ! fromEntries/iterator-not-closed-for-throwing-next.js ! fromEntries/iterator-not-closed-for-uncallable-next.js - ! fromEntries/key-order.js - ! fromEntries/length.js - ! fromEntries/name.js - ! fromEntries/prototype.js - ! fromEntries/requires-argument.js - ! fromEntries/simple-properties.js - ! fromEntries/string-entry-object-succeeds.js - ! fromEntries/string-entry-primitive-throws.js - ! fromEntries/string-entry-string-object-succeeds.js ! fromEntries/supports-symbols.js ! fromEntries/to-property-key.js - ! fromEntries/uses-define-semantics.js ! fromEntries/uses-keys-not-iterator.js - ! entries/exception-during-enumeration.js - ! entries/function-length.js - ! entries/function-name.js - ! entries/function-property-descriptor.js - ! entries/getter-adding-key.js ! entries/getter-making-future-key-nonenumerable.js ! entries/getter-removing-future-key.js - ! entries/inherited-properties-omitted.js - ! entries/observable-operations.js - ! entries/primitive-booleans.js - ! entries/primitive-numbers.js ! entries/primitive-strings.js - ! entries/primitive-symbols.js - ! entries/symbols-omitted.js - ! entries/tamper-with-global-object.js - ! entries/tamper-with-object-keys.js ! getOwnPropertyDescriptor/15.2.3.3-4-187.js ! getOwnPropertyDescriptor/15.2.3.3-4-212.js ! getOwnPropertyDescriptor/15.2.3.3-4-213.js @@ -823,22 +799,8 @@ built-ins/Object ! seal/symbol-object-contains-symbol-properties-non-strict.js ! seal/symbol-object-contains-symbol-properties-strict.js ! setPrototypeOf/set-error.js - ! values/exception-during-enumeration.js - ! values/function-length.js - ! values/function-name.js - ! values/function-property-descriptor.js - ! values/getter-adding-key.js ! values/getter-making-future-key-nonenumerable.js ! values/getter-removing-future-key.js - ! values/inherited-properties-omitted.js - ! values/observable-operations.js - ! values/primitive-booleans.js - ! values/primitive-numbers.js - ! values/primitive-strings.js - ! values/primitive-symbols.js - ! values/symbols-omitted.js - ! values/tamper-with-global-object.js - ! values/tamper-with-object-keys.js built-ins/parseFloat ! name.js From b4817f06c95e2160efc526e83bf500d950da2fc4 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Thu, 20 May 2021 20:09:33 +0200 Subject: [PATCH 03/12] spotless --- src/org/mozilla/javascript/NativeObject.java | 1384 +++++++++-------- .../tests/es6/NativeObjectTest.java | 293 ++-- 2 files changed, 858 insertions(+), 819 deletions(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 9e8a3debf2..06c3acb812 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -12,661 +12,696 @@ import java.util.Collection; import java.util.Iterator; import java.util.Map; +import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; -import java.util.Map.Entry; - import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex; /** - * This class implements the Object native object. - * See ECMA 15.2. + * This class implements the Object native object. See ECMA 15.2. + * * @author Norris Boyd */ -public class NativeObject extends IdScriptableObject implements Map -{ +public class NativeObject extends IdScriptableObject implements Map { private static final long serialVersionUID = -6345305608474346996L; private static final Object OBJECT_TAG = "Object"; - static void init(Scriptable scope, boolean sealed) - { + static void init(Scriptable scope, boolean sealed) { NativeObject obj = new NativeObject(); obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); } @Override - public String getClassName() - { + public String getClassName() { return "Object"; } @Override - public String toString() - { + public String toString() { return ScriptRuntime.defaultObjectToString(this); } @Override - protected void fillConstructorProperties(IdFunctionObject ctor) - { - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getPrototypeOf, - "getPrototypeOf", 1); + protected void fillConstructorProperties(IdFunctionObject ctor) { + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getPrototypeOf, "getPrototypeOf", 1); if (Context.getCurrentContext().version >= Context.VERSION_ES6) { - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_setPrototypeOf, - "setPrototypeOf", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_entries, - "entries", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_fromEntries, - "fromEntries", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_values, - "values", 1); + addIdFunctionProperty( + ctor, OBJECT_TAG, ConstructorId_setPrototypeOf, "setPrototypeOf", 2); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_entries, "entries", 1); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_fromEntries, "fromEntries", 1); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_values, "values", 1); } - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_keys, - "keys", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getOwnPropertyNames, - "getOwnPropertyNames", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getOwnPropertySymbols, - "getOwnPropertySymbols", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getOwnPropertyDescriptor, - "getOwnPropertyDescriptor", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_defineProperty, - "defineProperty", 3); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isExtensible, - "isExtensible", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_preventExtensions, - "preventExtensions", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_defineProperties, - "defineProperties", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_create, - "create", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isSealed, - "isSealed", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isFrozen, - "isFrozen", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_seal, - "seal", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_freeze, - "freeze", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_assign, - "assign", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_is, - "is", 2); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_keys, "keys", 1); + addIdFunctionProperty( + ctor, OBJECT_TAG, ConstructorId_getOwnPropertyNames, "getOwnPropertyNames", 1); + addIdFunctionProperty( + ctor, OBJECT_TAG, ConstructorId_getOwnPropertySymbols, "getOwnPropertySymbols", 1); + addIdFunctionProperty( + ctor, + OBJECT_TAG, + ConstructorId_getOwnPropertyDescriptor, + "getOwnPropertyDescriptor", + 2); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_defineProperty, "defineProperty", 3); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isExtensible, "isExtensible", 1); + addIdFunctionProperty( + ctor, OBJECT_TAG, ConstructorId_preventExtensions, "preventExtensions", 1); + addIdFunctionProperty( + ctor, OBJECT_TAG, ConstructorId_defineProperties, "defineProperties", 2); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_create, "create", 2); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isSealed, "isSealed", 1); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isFrozen, "isFrozen", 1); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_seal, "seal", 1); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_freeze, "freeze", 1); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_assign, "assign", 2); + addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_is, "is", 2); super.fillConstructorProperties(ctor); } @Override - protected void initPrototypeId(int id) - { + protected void initPrototypeId(int id) { String s; int arity; switch (id) { - case Id_constructor: arity=1; s="constructor"; break; - case Id_toString: arity=0; s="toString"; break; - case Id_toLocaleString: arity=0; s="toLocaleString"; break; - case Id_valueOf: arity=0; s="valueOf"; break; - case Id_hasOwnProperty: arity=1; s="hasOwnProperty"; break; - case Id_propertyIsEnumerable: - arity=1; s="propertyIsEnumerable"; break; - case Id_isPrototypeOf: arity=1; s="isPrototypeOf"; break; - case Id_toSource: arity=0; s="toSource"; break; - case Id___defineGetter__: - arity=2; s="__defineGetter__"; break; - case Id___defineSetter__: - arity=2; s="__defineSetter__"; break; - case Id___lookupGetter__: - arity=1; s="__lookupGetter__"; break; - case Id___lookupSetter__: - arity=1; s="__lookupSetter__"; break; - default: throw new IllegalArgumentException(String.valueOf(id)); + case Id_constructor: + arity = 1; + s = "constructor"; + break; + case Id_toString: + arity = 0; + s = "toString"; + break; + case Id_toLocaleString: + arity = 0; + s = "toLocaleString"; + break; + case Id_valueOf: + arity = 0; + s = "valueOf"; + break; + case Id_hasOwnProperty: + arity = 1; + s = "hasOwnProperty"; + break; + case Id_propertyIsEnumerable: + arity = 1; + s = "propertyIsEnumerable"; + break; + case Id_isPrototypeOf: + arity = 1; + s = "isPrototypeOf"; + break; + case Id_toSource: + arity = 0; + s = "toSource"; + break; + case Id___defineGetter__: + arity = 2; + s = "__defineGetter__"; + break; + case Id___defineSetter__: + arity = 2; + s = "__defineSetter__"; + break; + case Id___lookupGetter__: + arity = 1; + s = "__lookupGetter__"; + break; + case Id___lookupSetter__: + arity = 1; + s = "__lookupSetter__"; + break; + default: + throw new IllegalArgumentException(String.valueOf(id)); } initPrototypeMethod(OBJECT_TAG, id, s, arity); } @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { + public Object execIdCall( + IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (!f.hasTag(OBJECT_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); } int id = f.methodId(); switch (id) { - case Id_constructor: { - if (thisObj != null) { - // BaseFunction.construct will set up parent, proto - return f.construct(cx, scope, args); - } - if (args.length == 0 - || args[0] == null - || Undefined.isUndefined(args[0])) - { - return new NativeObject(); - } - return ScriptRuntime.toObject(cx, scope, args[0]); - } + case Id_constructor: + { + if (thisObj != null) { + // BaseFunction.construct will set up parent, proto + return f.construct(cx, scope, args); + } + if (args.length == 0 || args[0] == null || Undefined.isUndefined(args[0])) { + return new NativeObject(); + } + return ScriptRuntime.toObject(cx, scope, args[0]); + } - case Id_toLocaleString: { - Object toString = ScriptableObject.getProperty(thisObj, "toString"); - if(!(toString instanceof Callable)) { - throw ScriptRuntime.notFunctionError(toString); - } - Callable fun = (Callable)toString; - return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); - } - - case Id_toString: { - if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) { - String s = ScriptRuntime.defaultObjectToSource(cx, scope, - thisObj, args); - int L = s.length(); - if (L != 0 && s.charAt(0) == '(' && s.charAt(L - 1) == ')') { - // Strip () that surrounds toSource - s = s.substring(1, L - 1); - } - return s; - } - return ScriptRuntime.defaultObjectToString(thisObj); - } - - case Id_valueOf: - if (cx.getLanguageVersion() >= Context.VERSION_1_8 && (thisObj == null || Undefined.isUndefined(thisObj))) { - throw ScriptRuntime.typeErrorById("msg." + (thisObj == null ? "null" : "undef") + ".to.object"); - } - return thisObj; - - case Id_hasOwnProperty: { - if (cx.getLanguageVersion() >= Context.VERSION_1_8 && (thisObj == null || Undefined.isUndefined(thisObj))) { - throw ScriptRuntime.typeErrorById("msg." + (thisObj == null ? "null" : "undef") + ".to.object"); - } - boolean result; - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (arg instanceof Symbol) { - result = ensureSymbolScriptable(thisObj).has((Symbol) arg, thisObj); - } else { - StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg); - if (s.stringId == null) { - result = thisObj.has(s.index, thisObj); - } else { - result = thisObj.has(s.stringId, thisObj); - } - } - return ScriptRuntime.wrapBoolean(result); - } - - case Id_propertyIsEnumerable: { - if (cx.getLanguageVersion() >= Context.VERSION_1_8 && (thisObj == null || Undefined.isUndefined(thisObj))) { - throw ScriptRuntime.typeErrorById("msg." + (thisObj == null ? "null" : "undef") + ".to.object"); - } - - boolean result; - Object arg = args.length < 1 ? Undefined.instance : args[0]; - - if (arg instanceof Symbol) { - result = ((SymbolScriptable)thisObj).has((Symbol)arg, thisObj); - if (result && thisObj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject)thisObj; - int attrs = so.getAttributes((Symbol)arg); - result = ((attrs & ScriptableObject.DONTENUM) == 0); - } - } else { - StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg); - // When checking if a property is enumerable, a missing property should return "false" instead of - // throwing an exception. See: https://github.com/mozilla/rhino/issues/415 - try { - if (s.stringId == null) { - result = thisObj.has(s.index, thisObj); - if (result && thisObj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) thisObj; - int attrs = so.getAttributes(s.index); - result = ((attrs & ScriptableObject.DONTENUM) == 0); + case Id_toLocaleString: + { + Object toString = ScriptableObject.getProperty(thisObj, "toString"); + if (!(toString instanceof Callable)) { + throw ScriptRuntime.notFunctionError(toString); + } + Callable fun = (Callable) toString; + return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); + } + + case Id_toString: + { + if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) { + String s = + ScriptRuntime.defaultObjectToSource( + cx, scope, + thisObj, args); + int L = s.length(); + if (L != 0 && s.charAt(0) == '(' && s.charAt(L - 1) == ')') { + // Strip () that surrounds toSource + s = s.substring(1, L - 1); } + return s; + } + return ScriptRuntime.defaultObjectToString(thisObj); + } + + case Id_valueOf: + if (cx.getLanguageVersion() >= Context.VERSION_1_8 + && (thisObj == null || Undefined.isUndefined(thisObj))) { + throw ScriptRuntime.typeErrorById( + "msg." + (thisObj == null ? "null" : "undef") + ".to.object"); + } + return thisObj; + + case Id_hasOwnProperty: + { + if (cx.getLanguageVersion() >= Context.VERSION_1_8 + && (thisObj == null || Undefined.isUndefined(thisObj))) { + throw ScriptRuntime.typeErrorById( + "msg." + (thisObj == null ? "null" : "undef") + ".to.object"); + } + boolean result; + Object arg = args.length < 1 ? Undefined.instance : args[0]; + if (arg instanceof Symbol) { + result = ensureSymbolScriptable(thisObj).has((Symbol) arg, thisObj); } else { - result = thisObj.has(s.stringId, thisObj); + StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg); + if (s.stringId == null) { + result = thisObj.has(s.index, thisObj); + } else { + result = thisObj.has(s.stringId, thisObj); + } + } + return ScriptRuntime.wrapBoolean(result); + } + + case Id_propertyIsEnumerable: + { + if (cx.getLanguageVersion() >= Context.VERSION_1_8 + && (thisObj == null || Undefined.isUndefined(thisObj))) { + throw ScriptRuntime.typeErrorById( + "msg." + (thisObj == null ? "null" : "undef") + ".to.object"); + } + + boolean result; + Object arg = args.length < 1 ? Undefined.instance : args[0]; + + if (arg instanceof Symbol) { + result = ((SymbolScriptable) thisObj).has((Symbol) arg, thisObj); if (result && thisObj instanceof ScriptableObject) { ScriptableObject so = (ScriptableObject) thisObj; - int attrs = so.getAttributes(s.stringId); + int attrs = so.getAttributes((Symbol) arg); result = ((attrs & ScriptableObject.DONTENUM) == 0); } - } - } catch (EvaluatorException ee) { - if (ee.getMessage().startsWith(ScriptRuntime.getMessageById("msg.prop.not.found", - s.stringId == null ? Integer.toString(s.index) : s.stringId))) { - result = false; } else { - throw ee; + StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg); + // When checking if a property is enumerable, a missing property should + // return "false" instead of + // throwing an exception. See: https://github.com/mozilla/rhino/issues/415 + try { + if (s.stringId == null) { + result = thisObj.has(s.index, thisObj); + if (result && thisObj instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject) thisObj; + int attrs = so.getAttributes(s.index); + result = ((attrs & ScriptableObject.DONTENUM) == 0); + } + } else { + result = thisObj.has(s.stringId, thisObj); + if (result && thisObj instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject) thisObj; + int attrs = so.getAttributes(s.stringId); + result = ((attrs & ScriptableObject.DONTENUM) == 0); + } + } + } catch (EvaluatorException ee) { + if (ee.getMessage() + .startsWith( + ScriptRuntime.getMessageById( + "msg.prop.not.found", + s.stringId == null + ? Integer.toString(s.index) + : s.stringId))) { + result = false; + } else { + throw ee; + } + } } + return ScriptRuntime.wrapBoolean(result); } - } - return ScriptRuntime.wrapBoolean(result); - } - case Id_isPrototypeOf: { - if (cx.getLanguageVersion() >= Context.VERSION_1_8 && (thisObj == null || Undefined.isUndefined(thisObj))) { - throw ScriptRuntime.typeErrorById("msg." + (thisObj == null ? "null" : "undef") + ".to.object"); - } + case Id_isPrototypeOf: + { + if (cx.getLanguageVersion() >= Context.VERSION_1_8 + && (thisObj == null || Undefined.isUndefined(thisObj))) { + throw ScriptRuntime.typeErrorById( + "msg." + (thisObj == null ? "null" : "undef") + ".to.object"); + } - boolean result = false; - if (args.length != 0 && args[0] instanceof Scriptable) { - Scriptable v = (Scriptable) args[0]; - do { - v = v.getPrototype(); - if (v == thisObj) { - result = true; - break; + boolean result = false; + if (args.length != 0 && args[0] instanceof Scriptable) { + Scriptable v = (Scriptable) args[0]; + do { + v = v.getPrototype(); + if (v == thisObj) { + result = true; + break; + } + } while (v != null); } - } while (v != null); - } - return ScriptRuntime.wrapBoolean(result); - } - - case Id_toSource: - return ScriptRuntime.defaultObjectToSource(cx, scope, thisObj, - args); - case Id___defineGetter__: - case Id___defineSetter__: - { - if (args.length < 2 || !(args[1] instanceof Callable)) { - Object badArg = (args.length >= 2 ? args[1] : Undefined.instance); - throw ScriptRuntime.notFunctionError(badArg); - } - if (!(thisObj instanceof ScriptableObject)) { - throw Context.reportRuntimeErrorById( - "msg.extend.scriptable", - thisObj == null ? "null" : thisObj.getClass().getName(), - String.valueOf(args[0])); - } - ScriptableObject so = (ScriptableObject)thisObj; - StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); - int index = s.stringId != null ? 0 : s.index; - Callable getterOrSetter = (Callable)args[1]; - boolean isSetter = (id == Id___defineSetter__); - so.setGetterOrSetter(s.stringId, index, getterOrSetter, isSetter); - if (so instanceof NativeArray) - ((NativeArray)so).setDenseOnly(false); - } - return Undefined.instance; + return ScriptRuntime.wrapBoolean(result); + } + + case Id_toSource: + return ScriptRuntime.defaultObjectToSource(cx, scope, thisObj, args); + case Id___defineGetter__: + case Id___defineSetter__: + { + if (args.length < 2 || !(args[1] instanceof Callable)) { + Object badArg = (args.length >= 2 ? args[1] : Undefined.instance); + throw ScriptRuntime.notFunctionError(badArg); + } + if (!(thisObj instanceof ScriptableObject)) { + throw Context.reportRuntimeErrorById( + "msg.extend.scriptable", + thisObj == null ? "null" : thisObj.getClass().getName(), + String.valueOf(args[0])); + } + ScriptableObject so = (ScriptableObject) thisObj; + StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); + int index = s.stringId != null ? 0 : s.index; + Callable getterOrSetter = (Callable) args[1]; + boolean isSetter = (id == Id___defineSetter__); + so.setGetterOrSetter(s.stringId, index, getterOrSetter, isSetter); + if (so instanceof NativeArray) ((NativeArray) so).setDenseOnly(false); + } + return Undefined.instance; case Id___lookupGetter__: case Id___lookupSetter__: - { - if (args.length < 1 || - !(thisObj instanceof ScriptableObject)) - return Undefined.instance; - - ScriptableObject so = (ScriptableObject)thisObj; - StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); - int index = s.stringId != null ? 0 : s.index; - boolean isSetter = (id == Id___lookupSetter__); - Object gs; - for (;;) { - gs = so.getGetterOrSetter(s.stringId, index, isSetter); - if (gs != null) - break; - // If there is no getter or setter for the object itself, - // how about the prototype? - Scriptable v = so.getPrototype(); - if (v == null) - break; - if (v instanceof ScriptableObject) - so = (ScriptableObject)v; - else - break; - } - if (gs != null) { - if (gs instanceof MemberBox) { - if (isSetter) { - gs = ((MemberBox) gs).asSetterFunction(s.stringId, this); - } else { - gs = ((MemberBox) gs).asGetterFunction(s.stringId, this); - } - } - return gs; - } - } - return Undefined.instance; - - case ConstructorId_getPrototypeOf: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - return obj.getPrototype(); - } - case ConstructorId_setPrototypeOf: - { - if (args.length < 2) { - throw ScriptRuntime.typeErrorById("msg.method.missing.parameter", "Object.setPrototypeOf", "2", Integer.toString(args.length)); - } - Scriptable proto = (args[1] == null) ? null : ensureScriptable(args[1]); - if (proto instanceof Symbol) { - throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(proto)); - } - - final Object arg0 = args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6) { - ScriptRuntimeES6.requireObjectCoercible(cx, arg0, f); - } - if ( !(arg0 instanceof ScriptableObject) ) { - return arg0; - } - ScriptableObject obj = (ScriptableObject) arg0; - if (!obj.isExtensible()) { - throw ScriptRuntime.typeErrorById("msg.not.extensible"); - } - - // cycle detection - Scriptable prototypeProto = proto; - while (prototypeProto != null) { - if (prototypeProto == obj) { - throw ScriptRuntime.typeErrorById("msg.object.cyclic.prototype", obj.getClass().getSimpleName()); - } - prototypeProto = prototypeProto.getPrototype(); - } - obj.setPrototype(proto); - return obj; - } - case ConstructorId_keys: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - Object[] ids = obj.getIds(); - for (int i = 0; i < ids.length; i++) { - ids[i] = ScriptRuntime.toString(ids[i]); - } - return cx.newArray(scope, ids); - } - - case ConstructorId_entries: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - Object[] ids = obj.getIds(); - for (int i = 0; i < ids.length; i++) { - if (ids[i] instanceof Integer) { - Integer key = (Integer) ids[i]; - ids[i] = cx.newArray(scope, new Object[]{ ids[i], obj.get(key, scope) }); - } else { - String key = ScriptRuntime.toString(ids[i]); - ids[i] = cx.newArray(scope, new Object[]{ ids[i], obj.get(key, scope) }); - } - } - return cx.newArray(scope, ids); - } - case ConstructorId_fromEntries: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - Object enumeration = ScriptRuntime.enumInit(obj, cx, scope, ScriptRuntime.ENUMERATE_VALUES_IN_ORDER); - obj = cx.newObject(scope); - while (ScriptRuntime.enumNext(enumeration)) { - Scriptable entry = ensureScriptable(ScriptRuntime.enumId(enumeration, cx)); - Object key = entry.get(0, entry); - if (key == Scriptable.NOT_FOUND) { - key = Undefined.instance; - } - Object value = entry.get(1, entry); - if (value == Scriptable.NOT_FOUND) { - value = Undefined.instance; - } - if (key instanceof Integer) { - obj.put((Integer)key, obj, value); - } else { - obj.put(ScriptRuntime.toString(key), obj, value); - } - } - return obj; - } - case ConstructorId_values: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - Object[] ids = obj.getIds(); - for (int i = 0; i < ids.length; i++) { - if (ids[i] instanceof Integer) { - ids[i] = obj.get((Integer) ids[i], scope); - } else { - ids[i] = obj.get(ScriptRuntime.toString(ids[i]), scope); - } - } - return cx.newArray(scope, ids); - } - case ConstructorId_getOwnPropertyNames: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable s = getCompatibleObject(cx, scope, arg); - ScriptableObject obj = ensureScriptableObject(s); - Object[] ids = obj.getIds(true, false); - for (int i = 0; i < ids.length; i++) { - ids[i] = ScriptRuntime.toString(ids[i]); - } - return cx.newArray(scope, ids); - } + { + if (args.length < 1 || !(thisObj instanceof ScriptableObject)) + return Undefined.instance; + + ScriptableObject so = (ScriptableObject) thisObj; + StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); + int index = s.stringId != null ? 0 : s.index; + boolean isSetter = (id == Id___lookupSetter__); + Object gs; + for (; ; ) { + gs = so.getGetterOrSetter(s.stringId, index, isSetter); + if (gs != null) break; + // If there is no getter or setter for the object itself, + // how about the prototype? + Scriptable v = so.getPrototype(); + if (v == null) break; + if (v instanceof ScriptableObject) so = (ScriptableObject) v; + else break; + } + if (gs != null) { + if (gs instanceof MemberBox) { + if (isSetter) { + gs = ((MemberBox) gs).asSetterFunction(s.stringId, this); + } else { + gs = ((MemberBox) gs).asGetterFunction(s.stringId, this); + } + } + return gs; + } + } + return Undefined.instance; + + case ConstructorId_getPrototypeOf: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = getCompatibleObject(cx, scope, arg); + return obj.getPrototype(); + } + case ConstructorId_setPrototypeOf: + { + if (args.length < 2) { + throw ScriptRuntime.typeErrorById( + "msg.method.missing.parameter", + "Object.setPrototypeOf", + "2", + Integer.toString(args.length)); + } + Scriptable proto = (args[1] == null) ? null : ensureScriptable(args[1]); + if (proto instanceof Symbol) { + throw ScriptRuntime.typeErrorById( + "msg.arg.not.object", ScriptRuntime.typeof(proto)); + } + + final Object arg0 = args[0]; + if (cx.getLanguageVersion() >= Context.VERSION_ES6) { + ScriptRuntimeES6.requireObjectCoercible(cx, arg0, f); + } + if (!(arg0 instanceof ScriptableObject)) { + return arg0; + } + ScriptableObject obj = (ScriptableObject) arg0; + if (!obj.isExtensible()) { + throw ScriptRuntime.typeErrorById("msg.not.extensible"); + } + + // cycle detection + Scriptable prototypeProto = proto; + while (prototypeProto != null) { + if (prototypeProto == obj) { + throw ScriptRuntime.typeErrorById( + "msg.object.cyclic.prototype", obj.getClass().getSimpleName()); + } + prototypeProto = prototypeProto.getPrototype(); + } + obj.setPrototype(proto); + return obj; + } + case ConstructorId_keys: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = getCompatibleObject(cx, scope, arg); + Object[] ids = obj.getIds(); + for (int i = 0; i < ids.length; i++) { + ids[i] = ScriptRuntime.toString(ids[i]); + } + return cx.newArray(scope, ids); + } + + case ConstructorId_entries: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = getCompatibleObject(cx, scope, arg); + Object[] ids = obj.getIds(); + for (int i = 0; i < ids.length; i++) { + if (ids[i] instanceof Integer) { + Integer key = (Integer) ids[i]; + ids[i] = cx.newArray(scope, new Object[] {ids[i], obj.get(key, scope)}); + } else { + String key = ScriptRuntime.toString(ids[i]); + ids[i] = cx.newArray(scope, new Object[] {ids[i], obj.get(key, scope)}); + } + } + return cx.newArray(scope, ids); + } + case ConstructorId_fromEntries: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = getCompatibleObject(cx, scope, arg); + Object enumeration = + ScriptRuntime.enumInit( + obj, cx, scope, ScriptRuntime.ENUMERATE_VALUES_IN_ORDER); + obj = cx.newObject(scope); + while (ScriptRuntime.enumNext(enumeration)) { + Scriptable entry = ensureScriptable(ScriptRuntime.enumId(enumeration, cx)); + Object key = entry.get(0, entry); + if (key == Scriptable.NOT_FOUND) { + key = Undefined.instance; + } + Object value = entry.get(1, entry); + if (value == Scriptable.NOT_FOUND) { + value = Undefined.instance; + } + if (key instanceof Integer) { + obj.put((Integer) key, obj, value); + } else { + obj.put(ScriptRuntime.toString(key), obj, value); + } + } + return obj; + } + case ConstructorId_values: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = getCompatibleObject(cx, scope, arg); + Object[] ids = obj.getIds(); + for (int i = 0; i < ids.length; i++) { + if (ids[i] instanceof Integer) { + ids[i] = obj.get((Integer) ids[i], scope); + } else { + ids[i] = obj.get(ScriptRuntime.toString(ids[i]), scope); + } + } + return cx.newArray(scope, ids); + } + case ConstructorId_getOwnPropertyNames: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable s = getCompatibleObject(cx, scope, arg); + ScriptableObject obj = ensureScriptableObject(s); + Object[] ids = obj.getIds(true, false); + for (int i = 0; i < ids.length; i++) { + ids[i] = ScriptRuntime.toString(ids[i]); + } + return cx.newArray(scope, ids); + } case ConstructorId_getOwnPropertySymbols: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable s = getCompatibleObject(cx, scope, arg); - ScriptableObject obj = ensureScriptableObject(s); - Object[] ids = obj.getIds(true, true); - ArrayList syms = new ArrayList(); - for (int i = 0; i < ids.length; i++) { - if (ids[i] instanceof Symbol) { - syms.add(ids[i]); - } - } - return cx.newArray(scope, syms.toArray()); - } - case ConstructorId_getOwnPropertyDescriptor: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - // TODO(norris): There's a deeper issue here if - // arg instanceof Scriptable. Should we create a new - // interface to admit the new ECMAScript 5 operations? - Scriptable s = getCompatibleObject(cx, scope, arg); - ScriptableObject obj = ensureScriptableObject(s); - Object nameArg = args.length < 2 ? Undefined.instance : args[1]; - Scriptable desc = obj.getOwnPropertyDescriptor(cx, nameArg); - return desc == null ? Undefined.instance : desc; - } - case ConstructorId_defineProperty: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - ScriptableObject obj = ensureScriptableObject(arg); - Object name = args.length < 2 ? Undefined.instance : args[1]; - Object descArg = args.length < 3 ? Undefined.instance : args[2]; - ScriptableObject desc = ensureScriptableObject(descArg); - obj.defineOwnProperty(cx, name, desc); - return obj; - } - case ConstructorId_isExtensible: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return Boolean.FALSE; - } - - ScriptableObject obj = ensureScriptableObject(arg); - return Boolean.valueOf(obj.isExtensible()); - } - case ConstructorId_preventExtensions: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return arg; - } - - ScriptableObject obj = ensureScriptableObject(arg); - obj.preventExtensions(); - return obj; - } - case ConstructorId_defineProperties: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - ScriptableObject obj = ensureScriptableObject(arg); - Object propsObj = args.length < 2 ? Undefined.instance : args[1]; - Scriptable props = Context.toObject(propsObj, scope); - obj.defineOwnProperties(cx, ensureScriptableObject(props)); - return obj; - } - case ConstructorId_create: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = (arg == null) ? null : ensureScriptable(arg); - - ScriptableObject newObject = new NativeObject(); - newObject.setParentScope(scope); - newObject.setPrototype(obj); - - if (args.length > 1 && !Undefined.isUndefined(args[1])) { - Scriptable props = Context.toObject(args[1], scope); - newObject.defineOwnProperties(cx, ensureScriptableObject(props)); - } - - return newObject; - } - case ConstructorId_isSealed: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return Boolean.TRUE; - } - - ScriptableObject obj = ensureScriptableObject(arg); - - if (obj.isExtensible()) return Boolean.FALSE; - - for (Object name: obj.getAllIds()) { - Object configurable = obj.getOwnPropertyDescriptor(cx, name).get("configurable"); - if (Boolean.TRUE.equals(configurable)) - return Boolean.FALSE; - } - - return Boolean.TRUE; - } - case ConstructorId_isFrozen: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return Boolean.TRUE; - } - - ScriptableObject obj = ensureScriptableObject(arg); - - if (obj.isExtensible()) return Boolean.FALSE; - - for (Object name: obj.getAllIds()) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (Boolean.TRUE.equals(desc.get("configurable"))) - return Boolean.FALSE; - if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) - return Boolean.FALSE; - } - - return Boolean.TRUE; - } - case ConstructorId_seal: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return arg; - } - - ScriptableObject obj = ensureScriptableObject(arg); - - for (Object name: obj.getAllIds()) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (Boolean.TRUE.equals(desc.get("configurable"))) { - desc.put("configurable", desc, Boolean.FALSE); - obj.defineOwnProperty(cx, name, desc, false); - } - } - obj.preventExtensions(); - - return obj; - } - case ConstructorId_freeze: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return arg; - } - - ScriptableObject obj = ensureScriptableObject(arg); - - for (Object name: obj.getIds(true, true)) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) { - desc.put("writable", desc, Boolean.FALSE); - } - if (Boolean.TRUE.equals(desc.get("configurable"))) { - desc.put("configurable", desc, Boolean.FALSE); - } - obj.defineOwnProperty(cx, name, desc, false); + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable s = getCompatibleObject(cx, scope, arg); + ScriptableObject obj = ensureScriptableObject(s); + Object[] ids = obj.getIds(true, true); + ArrayList syms = new ArrayList(); + for (int i = 0; i < ids.length; i++) { + if (ids[i] instanceof Symbol) { + syms.add(ids[i]); + } + } + return cx.newArray(scope, syms.toArray()); + } + case ConstructorId_getOwnPropertyDescriptor: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + // TODO(norris): There's a deeper issue here if + // arg instanceof Scriptable. Should we create a new + // interface to admit the new ECMAScript 5 operations? + Scriptable s = getCompatibleObject(cx, scope, arg); + ScriptableObject obj = ensureScriptableObject(s); + Object nameArg = args.length < 2 ? Undefined.instance : args[1]; + Scriptable desc = obj.getOwnPropertyDescriptor(cx, nameArg); + return desc == null ? Undefined.instance : desc; + } + case ConstructorId_defineProperty: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + ScriptableObject obj = ensureScriptableObject(arg); + Object name = args.length < 2 ? Undefined.instance : args[1]; + Object descArg = args.length < 3 ? Undefined.instance : args[2]; + ScriptableObject desc = ensureScriptableObject(descArg); + obj.defineOwnProperty(cx, name, desc); + return obj; + } + case ConstructorId_isExtensible: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + if (cx.getLanguageVersion() >= Context.VERSION_ES6 + && !(arg instanceof ScriptableObject)) { + return Boolean.FALSE; + } + + ScriptableObject obj = ensureScriptableObject(arg); + return Boolean.valueOf(obj.isExtensible()); + } + case ConstructorId_preventExtensions: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + if (cx.getLanguageVersion() >= Context.VERSION_ES6 + && !(arg instanceof ScriptableObject)) { + return arg; + } + + ScriptableObject obj = ensureScriptableObject(arg); + obj.preventExtensions(); + return obj; + } + case ConstructorId_defineProperties: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + ScriptableObject obj = ensureScriptableObject(arg); + Object propsObj = args.length < 2 ? Undefined.instance : args[1]; + Scriptable props = Context.toObject(propsObj, scope); + obj.defineOwnProperties(cx, ensureScriptableObject(props)); + return obj; + } + case ConstructorId_create: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + Scriptable obj = (arg == null) ? null : ensureScriptable(arg); + + ScriptableObject newObject = new NativeObject(); + newObject.setParentScope(scope); + newObject.setPrototype(obj); + + if (args.length > 1 && !Undefined.isUndefined(args[1])) { + Scriptable props = Context.toObject(args[1], scope); + newObject.defineOwnProperties(cx, ensureScriptableObject(props)); + } + + return newObject; } - obj.preventExtensions(); + case ConstructorId_isSealed: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + if (cx.getLanguageVersion() >= Context.VERSION_ES6 + && !(arg instanceof ScriptableObject)) { + return Boolean.TRUE; + } - return obj; - } + ScriptableObject obj = ensureScriptableObject(arg); - case ConstructorId_assign: - { - Scriptable targetObj; - if (args.length > 0) { - targetObj = ScriptRuntime.toObject(cx, scope, args[0]); - } - else { - targetObj = ScriptRuntime.toObject(cx, scope, Undefined.instance); - } - for (int i = 1; i < args.length; i++) { - if ((args[i] == null) || Undefined.isUndefined(args[i])) { - continue; - } - Scriptable sourceObj = ScriptRuntime.toObject(cx, scope, args[i]); - Object[] ids = sourceObj.getIds(); - for (Object key : ids) { - if (targetObj instanceof ScriptableObject) { - ScriptableObject desc = ((ScriptableObject) targetObj).getOwnPropertyDescriptor(cx, key); - if (desc != null && isDataDescriptor(desc) && isFalse(desc.get("writable"))) { - throw ScriptRuntime.typeErrorById("msg.change.value.with.writable.false", key); - } - } - if (key instanceof String) { - Object val = sourceObj.get((String) key, sourceObj); - if ((val != Scriptable.NOT_FOUND) && !Undefined.isUndefined(val)) { - targetObj.put((String) key, targetObj, val); - } - } else if (key instanceof Number) { - int ii = ScriptRuntime.toInt32(key); - Object val = sourceObj.get(ii, sourceObj); - if ((val != Scriptable.NOT_FOUND) && !Undefined.isUndefined(val)) { - targetObj.put(ii, targetObj, val); - } - } - } - } - return targetObj; - } + if (obj.isExtensible()) return Boolean.FALSE; + + for (Object name : obj.getAllIds()) { + Object configurable = + obj.getOwnPropertyDescriptor(cx, name).get("configurable"); + if (Boolean.TRUE.equals(configurable)) return Boolean.FALSE; + } + + return Boolean.TRUE; + } + case ConstructorId_isFrozen: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + if (cx.getLanguageVersion() >= Context.VERSION_ES6 + && !(arg instanceof ScriptableObject)) { + return Boolean.TRUE; + } + + ScriptableObject obj = ensureScriptableObject(arg); + + if (obj.isExtensible()) return Boolean.FALSE; - case ConstructorId_is: - { - Object a1 = args.length < 1 ? Undefined.instance : args[0]; - Object a2 = args.length < 2 ? Undefined.instance : args[1]; - return ScriptRuntime.wrapBoolean(ScriptRuntime.same(a1, a2)); - } + for (Object name : obj.getAllIds()) { + ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); + if (Boolean.TRUE.equals(desc.get("configurable"))) return Boolean.FALSE; + if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) + return Boolean.FALSE; + } + + return Boolean.TRUE; + } + case ConstructorId_seal: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + if (cx.getLanguageVersion() >= Context.VERSION_ES6 + && !(arg instanceof ScriptableObject)) { + return arg; + } + + ScriptableObject obj = ensureScriptableObject(arg); + + for (Object name : obj.getAllIds()) { + ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); + if (Boolean.TRUE.equals(desc.get("configurable"))) { + desc.put("configurable", desc, Boolean.FALSE); + obj.defineOwnProperty(cx, name, desc, false); + } + } + obj.preventExtensions(); + + return obj; + } + case ConstructorId_freeze: + { + Object arg = args.length < 1 ? Undefined.instance : args[0]; + if (cx.getLanguageVersion() >= Context.VERSION_ES6 + && !(arg instanceof ScriptableObject)) { + return arg; + } + + ScriptableObject obj = ensureScriptableObject(arg); + + for (Object name : obj.getIds(true, true)) { + ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); + if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) { + desc.put("writable", desc, Boolean.FALSE); + } + if (Boolean.TRUE.equals(desc.get("configurable"))) { + desc.put("configurable", desc, Boolean.FALSE); + } + obj.defineOwnProperty(cx, name, desc, false); + } + obj.preventExtensions(); + return obj; + } - default: - throw new IllegalArgumentException(String.valueOf(id)); + case ConstructorId_assign: + { + Scriptable targetObj; + if (args.length > 0) { + targetObj = ScriptRuntime.toObject(cx, scope, args[0]); + } else { + targetObj = ScriptRuntime.toObject(cx, scope, Undefined.instance); + } + for (int i = 1; i < args.length; i++) { + if ((args[i] == null) || Undefined.isUndefined(args[i])) { + continue; + } + Scriptable sourceObj = ScriptRuntime.toObject(cx, scope, args[i]); + Object[] ids = sourceObj.getIds(); + for (Object key : ids) { + if (targetObj instanceof ScriptableObject) { + ScriptableObject desc = + ((ScriptableObject) targetObj) + .getOwnPropertyDescriptor(cx, key); + if (desc != null + && isDataDescriptor(desc) + && isFalse(desc.get("writable"))) { + throw ScriptRuntime.typeErrorById( + "msg.change.value.with.writable.false", key); + } + } + if (key instanceof String) { + Object val = sourceObj.get((String) key, sourceObj); + if ((val != Scriptable.NOT_FOUND) && !Undefined.isUndefined(val)) { + targetObj.put((String) key, targetObj, val); + } + } else if (key instanceof Number) { + int ii = ScriptRuntime.toInt32(key); + Object val = sourceObj.get(ii, sourceObj); + if ((val != Scriptable.NOT_FOUND) && !Undefined.isUndefined(val)) { + targetObj.put(ii, targetObj, val); + } + } + } + } + return targetObj; + } + + case ConstructorId_is: + { + Object a1 = args.length < 1 ? Undefined.instance : args[0]; + Object a2 = args.length < 2 ? Undefined.instance : args[1]; + return ScriptRuntime.wrapBoolean(ScriptRuntime.same(a1, a2)); + } + + default: + throw new IllegalArgumentException(String.valueOf(id)); } } - private static Scriptable getCompatibleObject(Context cx, Scriptable scope, Object arg) - { + private static Scriptable getCompatibleObject(Context cx, Scriptable scope, Object arg) { if (cx.getLanguageVersion() >= Context.VERSION_ES6) { Scriptable s = ScriptRuntime.toObject(cx, scope, arg); return ensureScriptable(s); @@ -689,8 +724,7 @@ public boolean containsKey(Object key) { @Override public boolean containsValue(Object value) { for (Object obj : values()) { - if (value == obj || - value != null && value.equals(obj)) { + if (value == obj || value != null && value.equals(obj)) { return true; } } @@ -778,13 +812,15 @@ public boolean equals(Object other) { } Map.Entry e = (Map.Entry) other; return (ekey == null ? e.getKey() == null : ekey.equals(e.getKey())) - && (value == null ? e.getValue() == null : value.equals(e.getValue())); + && (value == null + ? e.getValue() == null + : value.equals(e.getValue())); } @Override public int hashCode() { - return (ekey == null ? 0 : ekey.hashCode()) ^ - (value == null ? 0 : value.hashCode()); + return (ekey == null ? 0 : ekey.hashCode()) + ^ (value == null ? 0 : value.hashCode()); } @Override @@ -834,7 +870,7 @@ public boolean hasNext() { public Object next() { try { return (key = ids[index++]); - } catch(ArrayIndexOutOfBoundsException e) { + } catch (ArrayIndexOutOfBoundsException e) { key = null; throw new NoSuchElementException(); } @@ -848,7 +884,7 @@ public void remove() { NativeObject.this.remove(key); key = null; } - }; + }; } @Override @@ -893,95 +929,91 @@ public int size() { } } - -// #string_id_map# + // #string_id_map# @Override - protected int findPrototypeId(String s) - { + protected int findPrototypeId(String s) { int id; -// #generated# Last update: 2021-03-21 09:51:05 MEZ + // #generated# Last update: 2021-03-21 09:51:05 MEZ switch (s) { - case "constructor": - id = Id_constructor; - break; - case "toString": - id = Id_toString; - break; - case "toLocaleString": - id = Id_toLocaleString; - break; - case "valueOf": - id = Id_valueOf; - break; - case "hasOwnProperty": - id = Id_hasOwnProperty; - break; - case "propertyIsEnumerable": - id = Id_propertyIsEnumerable; - break; - case "isPrototypeOf": - id = Id_isPrototypeOf; - break; - case "toSource": - id = Id_toSource; - break; - case "__defineGetter__": - id = Id___defineGetter__; - break; - case "__defineSetter__": - id = Id___defineSetter__; - break; - case "__lookupGetter__": - id = Id___lookupGetter__; - break; - case "__lookupSetter__": - id = Id___lookupSetter__; - break; - default: - id = 0; - break; + case "constructor": + id = Id_constructor; + break; + case "toString": + id = Id_toString; + break; + case "toLocaleString": + id = Id_toLocaleString; + break; + case "valueOf": + id = Id_valueOf; + break; + case "hasOwnProperty": + id = Id_hasOwnProperty; + break; + case "propertyIsEnumerable": + id = Id_propertyIsEnumerable; + break; + case "isPrototypeOf": + id = Id_isPrototypeOf; + break; + case "toSource": + id = Id_toSource; + break; + case "__defineGetter__": + id = Id___defineGetter__; + break; + case "__defineSetter__": + id = Id___defineSetter__; + break; + case "__lookupGetter__": + id = Id___lookupGetter__; + break; + case "__lookupSetter__": + id = Id___lookupSetter__; + break; + default: + id = 0; + break; } -// #/generated# + // #/generated# return id; } - private static final int - ConstructorId_getPrototypeOf = -1, - ConstructorId_keys = -2, - ConstructorId_getOwnPropertyNames = -3, - ConstructorId_getOwnPropertyDescriptor = -4, - ConstructorId_defineProperty = -5, - ConstructorId_isExtensible = -6, - ConstructorId_preventExtensions = -7, - ConstructorId_defineProperties= -8, - ConstructorId_create = -9, - ConstructorId_isSealed = -10, - ConstructorId_isFrozen = -11, - ConstructorId_seal = -12, - ConstructorId_freeze = -13, - ConstructorId_getOwnPropertySymbols = -14, - ConstructorId_assign = -15, - ConstructorId_is = -16, - // ES6 - ConstructorId_setPrototypeOf = -17, - ConstructorId_entries = -18, - ConstructorId_fromEntries = -19, - ConstructorId_values = -20, - - Id_constructor = 1, - Id_toString = 2, - Id_toLocaleString = 3, - Id_valueOf = 4, - Id_hasOwnProperty = 5, - Id_propertyIsEnumerable = 6, - Id_isPrototypeOf = 7, - Id_toSource = 8, - Id___defineGetter__ = 9, - Id___defineSetter__ = 10, - Id___lookupGetter__ = 11, - Id___lookupSetter__ = 12, - MAX_PROTOTYPE_ID = 12; - -// #/string_id_map# + private static final int ConstructorId_getPrototypeOf = -1, + ConstructorId_keys = -2, + ConstructorId_getOwnPropertyNames = -3, + ConstructorId_getOwnPropertyDescriptor = -4, + ConstructorId_defineProperty = -5, + ConstructorId_isExtensible = -6, + ConstructorId_preventExtensions = -7, + ConstructorId_defineProperties = -8, + ConstructorId_create = -9, + ConstructorId_isSealed = -10, + ConstructorId_isFrozen = -11, + ConstructorId_seal = -12, + ConstructorId_freeze = -13, + ConstructorId_getOwnPropertySymbols = -14, + ConstructorId_assign = -15, + ConstructorId_is = -16, + // ES6 + ConstructorId_setPrototypeOf = -17, + ConstructorId_entries = -18, + ConstructorId_fromEntries = -19, + ConstructorId_values = -20, + Id_constructor = 1, + Id_toString = 2, + Id_toLocaleString = 3, + Id_valueOf = 4, + Id_hasOwnProperty = 5, + Id_propertyIsEnumerable = 6, + Id_isPrototypeOf = 7, + Id_toSource = 8, + Id___defineGetter__ = 9, + Id___defineSetter__ = 10, + Id___lookupGetter__ = 11, + Id___lookupSetter__ = 12, + MAX_PROTOTYPE_ID = 12; + + // #/string_id_map# } diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java index a4e8e6efd9..e61581a264 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java @@ -12,16 +12,13 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; - import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mozilla.javascript.Context; import org.mozilla.javascript.ScriptableObject; -/** - * Test for NativeObject. - */ +/** Test for NativeObject. */ public class NativeObjectTest { private Context cx; @@ -41,257 +38,267 @@ public void tearDown() { @Test public void testAssignPropertyGetter() { - Object result = cx.evaluateString( - scope, "var obj = Object.defineProperty({}, 'propA', {\n" - + " enumerable: true,\n" - + " get: function () {\n" - + " Object.defineProperty(this, 'propB', {\n" - + " value: 3,\n" - + " enumerable: false\n" - + " });\n" - + " }\n" - + " });\n" - + "Object.assign(obj, {propB: 2});\n" - + "Object.assign({propB: 1}, obj);\n" - + "'obj.propB = ' + obj.propB + ', enumerable = ' + Object.getOwnPropertyDescriptor(obj, 'propB').enumerable", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "var obj = Object.defineProperty({}, 'propA', {\n" + + " enumerable: true,\n" + + " get: function () {\n" + + " Object.defineProperty(this, 'propB', {\n" + + " value: 3,\n" + + " enumerable: false\n" + + " });\n" + + " }\n" + + " });\n" + + "Object.assign(obj, {propB: 2});\n" + + "Object.assign({propB: 1}, obj);\n" + + "'obj.propB = ' + obj.propB + ', enumerable = ' + Object.getOwnPropertyDescriptor(obj, 'propB').enumerable", + "test", + 1, + null); assertEquals("obj.propB = 3, enumerable = false", result); } @Test public void testAssignOneParameter() { - Object result = cx.evaluateString( - scope, "var obj = {};" - + "res = Object.assign(obj);" - + "res === obj;", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "var obj = {};" + "res = Object.assign(obj);" + "res === obj;", + "test", + 1, + null); assertEquals(true, result); } - @Test public void testAssignMissingParameters() { - Object result = cx.evaluateString( - scope, "try { " - + " Object.assign();" - + "} catch (e) { e.message }", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "try { " + " Object.assign();" + "} catch (e) { e.message }", + "test", + 1, + null); assertEquals("Cannot convert undefined to an object.", result); } @Test public void testAssignNumericPropertyGetter() { - Object result = cx.evaluateString( - scope, "var obj = Object.defineProperty({}, 1, {\n" - + " enumerable: true,\n" - + " get: function () {\n" - + " Object.defineProperty(this, 2, {\n" - + " value: 3,\n" - + " enumerable: false\n" - + " });\n" - + " }\n" - + " });\n" - + "Object.assign(obj, {2: 2});\n" - + "Object.assign({2: 1}, obj);\n" - + "'obj[2] = ' + obj[2] + ', enumerable = ' + Object.getOwnPropertyDescriptor(obj, 2).enumerable", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "var obj = Object.defineProperty({}, 1, {\n" + + " enumerable: true,\n" + + " get: function () {\n" + + " Object.defineProperty(this, 2, {\n" + + " value: 3,\n" + + " enumerable: false\n" + + " });\n" + + " }\n" + + " });\n" + + "Object.assign(obj, {2: 2});\n" + + "Object.assign({2: 1}, obj);\n" + + "'obj[2] = ' + obj[2] + ', enumerable = ' + Object.getOwnPropertyDescriptor(obj, 2).enumerable", + "test", + 1, + null); assertEquals("obj[2] = 3, enumerable = false", result); } @Test public void testSetPrototypeOfNull() { - Object result = cx.evaluateString( - scope, "try { " - + " Object.setPrototypeOf(null, new Object());" - + "} catch (e) { e.message }", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "try { " + + " Object.setPrototypeOf(null, new Object());" + + "} catch (e) { e.message }", + "test", + 1, + null); assertEquals("Object.prototype.setPrototypeOf method called on null or undefined", result); } @Test public void testSetPrototypeOfUndefined() { - Object result = cx.evaluateString( - scope, "try { " - + " Object.setPrototypeOf(undefined, new Object());" - + "} catch (e) { e.message }", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "try { " + + " Object.setPrototypeOf(undefined, new Object());" + + "} catch (e) { e.message }", + "test", + 1, + null); assertEquals("Object.prototype.setPrototypeOf method called on null or undefined", result); } @Test public void testSetPrototypeOfMissingParameters() { - Object result = cx.evaluateString( - scope, "try { " - + " Object.setPrototypeOf();" - + "} catch (e) { e.message }", - "test", 1, null - ); - - assertEquals("Object.setPrototypeOf: At least 2 arguments required, but only 0 passed", result); - - result = cx.evaluateString( - scope, "try { " - + " Object.setPrototypeOf({});" - + "} catch (e) { e.message }", - "test", 1, null - ); - - assertEquals("Object.setPrototypeOf: At least 2 arguments required, but only 1 passed", result); + Object result = + cx.evaluateString( + scope, + "try { " + " Object.setPrototypeOf();" + "} catch (e) { e.message }", + "test", + 1, + null); + + assertEquals( + "Object.setPrototypeOf: At least 2 arguments required, but only 0 passed", result); + + result = + cx.evaluateString( + scope, + "try { " + " Object.setPrototypeOf({});" + "} catch (e) { e.message }", + "test", + 1, + null); + + assertEquals( + "Object.setPrototypeOf: At least 2 arguments required, but only 1 passed", result); } - + @Test public void testKeysMissingParameter() { - Object result = cx.evaluateString( - scope, "try { " - + " Object.keys();" - + "} catch (e) { e.message }", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "try { " + " Object.keys();" + "} catch (e) { e.message }", + "test", + 1, + null); assertEquals("Cannot convert undefined to an object.", result); } - + @Test public void testKeysOnObjectParameter() { - evaluateAndAssert("Object.keys({'foo':'bar', 2: 'y', 1: 'x'})", - Arrays.asList("1", "2", "foo")); + evaluateAndAssert( + "Object.keys({'foo':'bar', 2: 'y', 1: 'x'})", Arrays.asList("1", "2", "foo")); } - + @Test public void testKeysOnArray() { - evaluateAndAssert("Object.keys(['x','y','z'])", - Arrays.asList("0", "1", "2")); + evaluateAndAssert("Object.keys(['x','y','z'])", Arrays.asList("0", "1", "2")); } - + @Test public void testKeysOnArrayWithProp() { - evaluateAndAssert("var arr = ['x','y','z'];\n" - + "arr['foo'] = 'bar'; Object.keys(arr)", + evaluateAndAssert( + "var arr = ['x','y','z'];\n" + "arr['foo'] = 'bar'; Object.keys(arr)", Arrays.asList("0", "1", "2", "foo")); } - + @Test public void testValuesMissingParameter() { - Object result = cx.evaluateString( - scope, "try { " - + " Object.values();" - + "} catch (e) { e.message }", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "try { " + " Object.values();" + "} catch (e) { e.message }", + "test", + 1, + null); assertEquals("Cannot convert undefined to an object.", result); } @Test public void testValuesOnObjectParameter() { - evaluateAndAssert("Object.values({'foo':'bar', 2: 'y', 1: 'x'})", - Arrays.asList("x", "y", "bar")); + evaluateAndAssert( + "Object.values({'foo':'bar', 2: 'y', 1: 'x'})", Arrays.asList("x", "y", "bar")); } @Test public void testValuesOnArray() { - evaluateAndAssert("Object.values(['x','y','z'])", - Arrays.asList("x", "y", "z")); + evaluateAndAssert("Object.values(['x','y','z'])", Arrays.asList("x", "y", "z")); } @Test public void testValuesOnArrayWithProp() { - evaluateAndAssert("var arr = [3,4,5];\n" - + "arr['foo'] = 'bar'; Object.values(arr)", + evaluateAndAssert( + "var arr = [3,4,5];\n" + "arr['foo'] = 'bar'; Object.values(arr)", Arrays.asList(3, 4, 5, "bar")); } - - + @Test public void testEntriesMissingParameter() { - Object result = cx.evaluateString( - scope, "try { " - + " Object.entries();" - + "} catch (e) { e.message }", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "try { " + " Object.entries();" + "} catch (e) { e.message }", + "test", + 1, + null); assertEquals("Cannot convert undefined to an object.", result); } - + @Test public void testEntriesOnObjectParameter() { - evaluateAndAssert("Object.entries({'foo':'bar', 2: 'y', 1: 'x'})", + evaluateAndAssert( + "Object.entries({'foo':'bar', 2: 'y', 1: 'x'})", Arrays.asList( - Arrays.asList(1, "x"), - Arrays.asList(2, "y"), - Arrays.asList("foo", "bar"))); + Arrays.asList(1, "x"), Arrays.asList(2, "y"), Arrays.asList("foo", "bar"))); } - + @Test public void testEntriesOnArray() { - evaluateAndAssert("Object.entries(['x','y','z'])", - Arrays.asList( - Arrays.asList(0,"x"), - Arrays.asList(1,"y"), - Arrays.asList(2,"z"))); + evaluateAndAssert( + "Object.entries(['x','y','z'])", + Arrays.asList(Arrays.asList(0, "x"), Arrays.asList(1, "y"), Arrays.asList(2, "z"))); } - + @Test public void testEntriesOnArrayWithProp() { - evaluateAndAssert("var arr = [3,4,5];\n" - + "arr['foo'] = 'bar'; Object.entries(arr)", + evaluateAndAssert( + "var arr = [3,4,5];\n" + "arr['foo'] = 'bar'; Object.entries(arr)", Arrays.asList( Arrays.asList(0, 3), Arrays.asList(1, 4), Arrays.asList(2, 5), Arrays.asList("foo", "bar"))); } - + @Test public void testFromEntriesMissingParameter() { - Object result = cx.evaluateString( - scope, "try { " - + " Object.fromEntries();" - + "} catch (e) { e.message }", - "test", 1, null - ); + Object result = + cx.evaluateString( + scope, + "try { " + " Object.fromEntries();" + "} catch (e) { e.message }", + "test", + 1, + null); assertEquals("Cannot convert undefined to an object.", result); } - + @Test public void testFromEntriesOnObjectParameter() { Map map = new HashMap<>(); map.put("foo", "bar"); map.put(2, "y"); map.put(1, "x"); - evaluateAndAssert( - "Object.fromEntries(Object.entries({'foo':'bar', 2: 'y', 1: 'x'}))", - map); + evaluateAndAssert("Object.fromEntries(Object.entries({'foo':'bar', 2: 'y', 1: 'x'}))", map); } - + @Test public void testFromEntriesOnArray() { Map map = new HashMap<>(); map.put(0, "x"); map.put(1, "y"); map.put(2, "z"); - evaluateAndAssert( - "Object.fromEntries(Object.entries(['x','y','z']))", - map); + evaluateAndAssert("Object.fromEntries(Object.entries(['x','y','z']))", map); } private void evaluateAndAssert(String script, Object expected) { Object result = cx.evaluateString(scope, script, "test", 1, null); assertEquals(expected, result); } - } From 0fed865eca190d337b7d2d6256d8bd067cda3459 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 21 May 2021 12:14:40 +0200 Subject: [PATCH 04/12] enabled test262 for entries/vaules --- src/org/mozilla/javascript/NativeObject.java | 83 ++++++++++++++----- .../tests/es6/NativeObjectTest.java | 25 ++---- testsrc/test262.properties | 5 -- 3 files changed, 70 insertions(+), 43 deletions(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 06c3acb812..fb729950c3 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -9,6 +9,7 @@ import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -228,11 +229,7 @@ public Object execIdCall( if (arg instanceof Symbol) { result = ((SymbolScriptable) thisObj).has((Symbol) arg, thisObj); - if (result && thisObj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) thisObj; - int attrs = so.getAttributes((Symbol) arg); - result = ((attrs & ScriptableObject.DONTENUM) == 0); - } + result = result && isEnumerable((Symbol) arg, thisObj); } else { StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg); // When checking if a property is enumerable, a missing property should @@ -241,18 +238,10 @@ public Object execIdCall( try { if (s.stringId == null) { result = thisObj.has(s.index, thisObj); - if (result && thisObj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) thisObj; - int attrs = so.getAttributes(s.index); - result = ((attrs & ScriptableObject.DONTENUM) == 0); - } + result = result && isEnumerable(s.index, thisObj); } else { result = thisObj.has(s.stringId, thisObj); - if (result && thisObj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) thisObj; - int attrs = so.getAttributes(s.stringId); - result = ((attrs & ScriptableObject.DONTENUM) == 0); - } + result = result && isEnumerable(s.stringId, thisObj); } } catch (EvaluatorException ee) { if (ee.getMessage() @@ -413,15 +402,26 @@ public Object execIdCall( Object arg = args.length < 1 ? Undefined.instance : args[0]; Scriptable obj = getCompatibleObject(cx, scope, arg); Object[] ids = obj.getIds(); + int j = 0; for (int i = 0; i < ids.length; i++) { if (ids[i] instanceof Integer) { - Integer key = (Integer) ids[i]; - ids[i] = cx.newArray(scope, new Object[] {ids[i], obj.get(key, scope)}); + int intId = (Integer) ids[i]; + if (obj.has(intId, obj) && isEnumerable(intId, obj)) { + String stringId = ScriptRuntime.toString(ids[i]); + Object[] entry = new Object[] {stringId, obj.get(intId, obj)}; + ids[j++] = cx.newArray(scope, entry); + } } else { - String key = ScriptRuntime.toString(ids[i]); - ids[i] = cx.newArray(scope, new Object[] {ids[i], obj.get(key, scope)}); + String stringId = ScriptRuntime.toString(ids[i]); + if (obj.has(stringId, obj) && isEnumerable(stringId, obj)) { + Object[] entry = new Object[] {stringId, obj.get(stringId, obj)}; + ids[j++] = cx.newArray(scope, entry); + } } } + if (j != ids.length) { + ids = Arrays.copyOf(ids, j); + } return cx.newArray(scope, ids); } case ConstructorId_fromEntries: @@ -455,13 +455,24 @@ public Object execIdCall( Object arg = args.length < 1 ? Undefined.instance : args[0]; Scriptable obj = getCompatibleObject(cx, scope, arg); Object[] ids = obj.getIds(); + int j = 0; for (int i = 0; i < ids.length; i++) { if (ids[i] instanceof Integer) { - ids[i] = obj.get((Integer) ids[i], scope); + int intId = (Integer) ids[i]; + if (obj.has(intId, obj) && isEnumerable(intId, obj)) { + ids[j++] = obj.get(intId, obj); + } } else { - ids[i] = obj.get(ScriptRuntime.toString(ids[i]), scope); + String stringId = ScriptRuntime.toString(ids[i]); + // getter may remove keys + if (obj.has(stringId, obj) && isEnumerable(stringId, obj)) { + ids[j++] = obj.get(stringId, obj); + } } } + if (j != ids.length) { + ids = Arrays.copyOf(ids, j); + } return cx.newArray(scope, ids); } case ConstructorId_getOwnPropertyNames: @@ -701,6 +712,36 @@ && isFalse(desc.get("writable"))) { } } + private boolean isEnumerable(int index, Object obj) { + if (obj instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject) obj; + int attrs = so.getAttributes(index); + return (attrs & ScriptableObject.DONTENUM) == 0; + } else { + return true; + } + } + + private boolean isEnumerable(String key, Object obj) { + if (obj instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject) obj; + int attrs = so.getAttributes(key); + return (attrs & ScriptableObject.DONTENUM) == 0; + } else { + return true; + } + } + + private boolean isEnumerable(Symbol sym, Object obj) { + if (obj instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject) obj; + int attrs = so.getAttributes(sym); + return (attrs & ScriptableObject.DONTENUM) == 0; + } else { + return true; + } + } + private static Scriptable getCompatibleObject(Context cx, Scriptable scope, Object arg) { if (cx.getLanguageVersion() >= Context.VERSION_ES6) { Scriptable s = ScriptRuntime.toObject(cx, scope, arg); diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java index e61581a264..103fc12199 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java @@ -245,14 +245,14 @@ public void testEntriesOnObjectParameter() { evaluateAndAssert( "Object.entries({'foo':'bar', 2: 'y', 1: 'x'})", Arrays.asList( - Arrays.asList(1, "x"), Arrays.asList(2, "y"), Arrays.asList("foo", "bar"))); + Arrays.asList("1", "x"), Arrays.asList("2", "y"), Arrays.asList("foo", "bar"))); } @Test public void testEntriesOnArray() { evaluateAndAssert( "Object.entries(['x','y','z'])", - Arrays.asList(Arrays.asList(0, "x"), Arrays.asList(1, "y"), Arrays.asList(2, "z"))); + Arrays.asList(Arrays.asList("0", "x"), Arrays.asList("1", "y"), Arrays.asList("2", "z"))); } @Test @@ -260,9 +260,9 @@ public void testEntriesOnArrayWithProp() { evaluateAndAssert( "var arr = [3,4,5];\n" + "arr['foo'] = 'bar'; Object.entries(arr)", Arrays.asList( - Arrays.asList(0, 3), - Arrays.asList(1, 4), - Arrays.asList(2, 5), + Arrays.asList("0", 3), + Arrays.asList("1", 4), + Arrays.asList("2", 5), Arrays.asList("foo", "bar"))); } @@ -279,21 +279,12 @@ public void testFromEntriesMissingParameter() { assertEquals("Cannot convert undefined to an object.", result); } - @Test - public void testFromEntriesOnObjectParameter() { - Map map = new HashMap<>(); - map.put("foo", "bar"); - map.put(2, "y"); - map.put(1, "x"); - evaluateAndAssert("Object.fromEntries(Object.entries({'foo':'bar', 2: 'y', 1: 'x'}))", map); - } - @Test public void testFromEntriesOnArray() { Map map = new HashMap<>(); - map.put(0, "x"); - map.put(1, "y"); - map.put(2, "z"); + map.put("0", "x"); + map.put("1", "y"); + map.put("2", "z"); evaluateAndAssert("Object.fromEntries(Object.entries(['x','y','z']))", map); } diff --git a/testsrc/test262.properties b/testsrc/test262.properties index b181a5dd04..ee5b5334b4 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -742,9 +742,6 @@ built-ins/Object ! fromEntries/supports-symbols.js ! fromEntries/to-property-key.js ! fromEntries/uses-keys-not-iterator.js - ! entries/getter-making-future-key-nonenumerable.js - ! entries/getter-removing-future-key.js - ! entries/primitive-strings.js ! getOwnPropertyDescriptor/15.2.3.3-4-187.js ! getOwnPropertyDescriptor/15.2.3.3-4-212.js ! getOwnPropertyDescriptor/15.2.3.3-4-213.js @@ -799,8 +796,6 @@ built-ins/Object ! seal/symbol-object-contains-symbol-properties-non-strict.js ! seal/symbol-object-contains-symbol-properties-strict.js ! setPrototypeOf/set-error.js - ! values/getter-making-future-key-nonenumerable.js - ! values/getter-removing-future-key.js built-ins/parseFloat ! name.js From d3c13ea167dc96b67c2df2b9a2d87766d143a0c2 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 21 May 2021 13:00:43 +0200 Subject: [PATCH 05/12] spotless --- .../mozilla/javascript/tests/es6/NativeObjectTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java index 103fc12199..19cbe88995 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* - * Tests for the Object.getOwnPropertyDescriptor(obj, prop) method + * Tests for the Object.getOwnPropertyDescriptor(obj, prop), Object.keys, Object.values and Object.entries method */ package org.mozilla.javascript.tests.es6; @@ -245,14 +245,17 @@ public void testEntriesOnObjectParameter() { evaluateAndAssert( "Object.entries({'foo':'bar', 2: 'y', 1: 'x'})", Arrays.asList( - Arrays.asList("1", "x"), Arrays.asList("2", "y"), Arrays.asList("foo", "bar"))); + Arrays.asList("1", "x"), + Arrays.asList("2", "y"), + Arrays.asList("foo", "bar"))); } @Test public void testEntriesOnArray() { evaluateAndAssert( "Object.entries(['x','y','z'])", - Arrays.asList(Arrays.asList("0", "x"), Arrays.asList("1", "y"), Arrays.asList("2", "z"))); + Arrays.asList( + Arrays.asList("0", "x"), Arrays.asList("1", "y"), Arrays.asList("2", "z"))); } @Test From 2fb4633808d23e4296721f5540ec178c4e892d14 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 25 May 2021 08:52:45 +0200 Subject: [PATCH 06/12] FIX: fromEntries/evaluation-order.js --- src/org/mozilla/javascript/NativeObject.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 54a81fa138..c4ca50fb92 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -438,10 +438,18 @@ public Object execIdCall( if (key == Scriptable.NOT_FOUND) { key = Undefined.instance; } + if (key instanceof Function) { + Function fun = (Function)key; + key = fun.call(cx, fun.getParentScope(), entry, ScriptRuntime.emptyArgs); + } Object value = entry.get(1, entry); if (value == Scriptable.NOT_FOUND) { value = Undefined.instance; } + if (value instanceof Function) { + Function fun = (Function)value; + value = fun.call(cx, fun.getParentScope(), entry, ScriptRuntime.emptyArgs); + } if (key instanceof Integer) { obj.put((Integer) key, obj, value); } else { From 5cdf4313cd5c0a99e909ac629b51d6bbe14b674c Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 25 May 2021 09:21:06 +0200 Subject: [PATCH 07/12] FIX: fromEntries/to-property-key.js --- src/org/mozilla/javascript/NativeObject.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index c4ca50fb92..f8ef932e79 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -438,9 +438,15 @@ public Object execIdCall( if (key == Scriptable.NOT_FOUND) { key = Undefined.instance; } + if (key instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject) key; + if (so.has(SymbolKey.TO_PRIMITIVE, so)) { + key = so.get(SymbolKey.TO_PRIMITIVE, so); + } + } if (key instanceof Function) { Function fun = (Function)key; - key = fun.call(cx, fun.getParentScope(), entry, ScriptRuntime.emptyArgs); + key = fun.call(cx, fun.getParentScope(), entry, new Object[]{"string"}); } Object value = entry.get(1, entry); if (value == Scriptable.NOT_FOUND) { From 88d40c67da8feb7ab85d3a13858fec9c305f581c Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 25 May 2021 09:39:12 +0200 Subject: [PATCH 08/12] FIX: Support for symbols --- src/org/mozilla/javascript/NativeObject.java | 21 +++++++++++++++---- src/org/mozilla/javascript/ScriptRuntime.java | 20 +++++++++++------- testsrc/test262.properties | 1 - 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index f8ef932e79..cd3328b829 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -445,19 +445,32 @@ public Object execIdCall( } } if (key instanceof Function) { - Function fun = (Function)key; - key = fun.call(cx, fun.getParentScope(), entry, new Object[]{"string"}); + Function fun = (Function) key; + key = + fun.call( + cx, + fun.getParentScope(), + entry, + new Object[] {"string"}); } Object value = entry.get(1, entry); if (value == Scriptable.NOT_FOUND) { value = Undefined.instance; } if (value instanceof Function) { - Function fun = (Function)value; - value = fun.call(cx, fun.getParentScope(), entry, ScriptRuntime.emptyArgs); + Function fun = (Function) value; + value = + fun.call( + cx, + fun.getParentScope(), + entry, + ScriptRuntime.emptyArgs); } + if (key instanceof Integer) { obj.put((Integer) key, obj, value); + } else if (key instanceof Symbol && obj instanceof SymbolScriptable) { + ((SymbolScriptable) obj).put((Symbol) key, obj, value); } else { obj.put(ScriptRuntime.toString(key), obj, value); } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index cc83925582..e1ad97f16c 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -4291,13 +4291,22 @@ public static Scriptable newObjectLiteral( Object id = propertyIds[i]; int getterSetter = getterSetters == null ? 0 : getterSetters[i]; Object value = propertyValues[i]; - if (id instanceof String) { + if (id instanceof Symbol) { + Symbol sym = (Symbol) id; + SymbolScriptable so = (SymbolScriptable) object; + so.put(sym, object, value); + } else if (id instanceof Integer) { + int index = ((Integer) id).intValue(); + object.put(index, object, value); + } else { + // we might get a computed double. + String stringId = ScriptRuntime.toString(id); if (getterSetter == 0) { - if (isSpecialProperty((String) id)) { - Ref ref = specialRef(object, (String) id, cx, scope); + if (isSpecialProperty(stringId)) { + Ref ref = specialRef(object, stringId, cx, scope); ref.set(cx, scope, value); } else { - object.put((String) id, object, value); + object.put(stringId, object, value); } } else { ScriptableObject so = (ScriptableObject) object; @@ -4305,9 +4314,6 @@ public static Scriptable newObjectLiteral( boolean isSetter = getterSetter == 1; so.setGetterOrSetter((String) id, 0, getterOrSetter, isSetter); } - } else { - int index = ((Integer) id).intValue(); - object.put(index, object, value); } } return object; diff --git a/testsrc/test262.properties b/testsrc/test262.properties index ee5b5334b4..19568bf069 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -739,7 +739,6 @@ built-ins/Object ! fromEntries/iterator-not-closed-for-throwing-done-accessor.js ! fromEntries/iterator-not-closed-for-throwing-next.js ! fromEntries/iterator-not-closed-for-uncallable-next.js - ! fromEntries/supports-symbols.js ! fromEntries/to-property-key.js ! fromEntries/uses-keys-not-iterator.js ! getOwnPropertyDescriptor/15.2.3.3-4-187.js From 265f5caeadd9cb9c02430faed6622957d1c4f89d Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 26 May 2021 11:56:19 +0200 Subject: [PATCH 09/12] used common "loadFromIterable" code --- src/org/mozilla/javascript/NativeMap.java | 26 ++------ src/org/mozilla/javascript/NativeObject.java | 64 +++++-------------- src/org/mozilla/javascript/ScriptRuntime.java | 45 +++++++++++++ 3 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/org/mozilla/javascript/NativeMap.java b/src/org/mozilla/javascript/NativeMap.java index 9eb7f93338..9d5aac4236 100644 --- a/src/org/mozilla/javascript/NativeMap.java +++ b/src/org/mozilla/javascript/NativeMap.java @@ -191,29 +191,13 @@ static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, // Find the "add" function of our own prototype, since it might have // been replaced. Since we're not fully constructed yet, create a dummy instance // so that we can get our own prototype. - ScriptableObject dummy = ensureScriptableObject(cx.newObject(scope, map.getClassName())); - final Callable set = - ScriptRuntime.getPropFunctionAndThis(dummy.getPrototype(), "set", cx, scope); + Scriptable proto = ScriptableObject.getClassPrototype(scope, map.getClassName()); + final Callable set = ScriptRuntime.getPropFunctionAndThis(proto, "set", cx, scope); ScriptRuntime.lastStoredScriptable(cx); - // Finally, run through all the iterated values and add them! - try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) { - for (Object val : it) { - Scriptable sVal = ScriptableObject.ensureScriptable(val); - if (sVal instanceof Symbol) { - throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(sVal)); - } - Object finalKey = sVal.get(0, sVal); - if (finalKey == NOT_FOUND) { - finalKey = Undefined.instance; - } - Object finalVal = sVal.get(1, sVal); - if (finalVal == NOT_FOUND) { - finalVal = Undefined.instance; - } - set.call(cx, scope, map, new Object[] { finalKey, finalVal }); - } - } + ScriptRuntime.loadFromIterable(cx, scope, arg1, (key, value) -> { + set.call(cx, scope, map, new Object[] { key, value }); + }); } private static NativeMap realThis(Scriptable thisObj, IdFunctionObject f) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index cd3328b829..42e8c8ce06 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -427,54 +427,22 @@ public Object execIdCall( case ConstructorId_fromEntries: { Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - Object enumeration = - ScriptRuntime.enumInit( - obj, cx, scope, ScriptRuntime.ENUMERATE_VALUES_IN_ORDER); - obj = cx.newObject(scope); - while (ScriptRuntime.enumNext(enumeration)) { - Scriptable entry = ensureScriptable(ScriptRuntime.enumId(enumeration, cx)); - Object key = entry.get(0, entry); - if (key == Scriptable.NOT_FOUND) { - key = Undefined.instance; - } - if (key instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) key; - if (so.has(SymbolKey.TO_PRIMITIVE, so)) { - key = so.get(SymbolKey.TO_PRIMITIVE, so); - } - } - if (key instanceof Function) { - Function fun = (Function) key; - key = - fun.call( - cx, - fun.getParentScope(), - entry, - new Object[] {"string"}); - } - Object value = entry.get(1, entry); - if (value == Scriptable.NOT_FOUND) { - value = Undefined.instance; - } - if (value instanceof Function) { - Function fun = (Function) value; - value = - fun.call( - cx, - fun.getParentScope(), - entry, - ScriptRuntime.emptyArgs); - } - - if (key instanceof Integer) { - obj.put((Integer) key, obj, value); - } else if (key instanceof Symbol && obj instanceof SymbolScriptable) { - ((SymbolScriptable) obj).put((Symbol) key, obj, value); - } else { - obj.put(ScriptRuntime.toString(key), obj, value); - } - } + arg = getCompatibleObject(cx, scope, arg); + Scriptable obj = cx.newObject(scope); + ScriptRuntime.loadFromIterable( + cx, + scope, + arg, + (key, value) -> { + if (key instanceof Integer) { + obj.put((Integer) key, obj, value); + } else if (key instanceof Symbol + && obj instanceof SymbolScriptable) { + ((SymbolScriptable) obj).put((Symbol) key, obj, value); + } else { + obj.put(ScriptRuntime.toString(key), obj, value); + } + }); return obj; } case ConstructorId_values: diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index c698b064d6..31b37ea792 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.Locale; import java.util.ResourceBundle; +import java.util.function.BiConsumer; import org.mozilla.javascript.ast.FunctionNode; import org.mozilla.javascript.v8dtoa.DoubleConversion; import org.mozilla.javascript.v8dtoa.FastDtoa; @@ -2417,6 +2418,50 @@ private static void enumChangeObject(IdEnumeration x) { x.index = 0; } + /** + * This is used to handle all the special cases that are required when invoking + * Object.fromEntries or constructing a NativeMap or NativeWeakMap from an iterable. + * + * @param cx the current context + * @param scope the current scope + * @param arg1 the iterable object. + * @param setter the setter to set the value + * @return true, if arg1 was iterable. + */ + public static boolean loadFromIterable( + Context cx, Scriptable scope, Object arg1, BiConsumer setter) { + if ((arg1 == null) || Undefined.instance.equals(arg1)) { + return false; + } + + // Call the "[Symbol.iterator]" property as a function. + final Object ito = ScriptRuntime.callIterator(arg1, cx, scope); + if (Undefined.instance.equals(ito)) { + // Per spec, ignore if the iterator is undefined + return false; + } + + // Finally, run through all the iterated values and add them! + try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) { + for (Object val : it) { + Scriptable sVal = ScriptableObject.ensureScriptable(val); + if (sVal instanceof Symbol) { + throw ScriptRuntime.typeErrorById( + "msg.arg.not.object", ScriptRuntime.typeof(sVal)); + } + Object finalKey = sVal.get(0, sVal); + if (finalKey == Scriptable.NOT_FOUND) { + finalKey = Undefined.instance; + } + Object finalVal = sVal.get(1, sVal); + if (finalVal == Scriptable.NOT_FOUND) { + finalVal = Undefined.instance; + } + setter.accept(finalKey, finalVal); + } + } + return true; + } /** * Prepare for calling name(...): return function corresponding to name and make current top * scope available as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The From 8fa46fac26471438669a180ec282e918830d19db Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 26 May 2021 11:56:30 +0200 Subject: [PATCH 10/12] spotless NativeMap --- src/org/mozilla/javascript/NativeMap.java | 255 ++++++++++++---------- 1 file changed, 142 insertions(+), 113 deletions(-) diff --git a/src/org/mozilla/javascript/NativeMap.java b/src/org/mozilla/javascript/NativeMap.java index 9d5aac4236..f930cd13b7 100644 --- a/src/org/mozilla/javascript/NativeMap.java +++ b/src/org/mozilla/javascript/NativeMap.java @@ -40,9 +40,8 @@ public String getClassName() { } @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { + public Object execIdCall( + IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (!f.hasTag(MAP_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); } @@ -59,11 +58,13 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } throw ScriptRuntime.typeErrorById("msg.no.new", "Map"); case Id_set: - return realThis(thisObj, f).js_set( - args.length > 0 ? args[0] : Undefined.instance, - args.length > 1 ? args[1] : Undefined.instance); + return realThis(thisObj, f) + .js_set( + args.length > 0 ? args[0] : Undefined.instance, + args.length > 1 ? args[1] : Undefined.instance); case Id_delete: - return realThis(thisObj, f).js_delete(args.length > 0 ? args[0] : Undefined.instance); + return realThis(thisObj, f) + .js_delete(args.length > 0 ? args[0] : Undefined.instance); case Id_get: return realThis(thisObj, f).js_get(args.length > 0 ? args[0] : Undefined.instance); case Id_has: @@ -73,42 +74,42 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, case Id_keys: return realThis(thisObj, f).js_iterator(scope, NativeCollectionIterator.Type.KEYS); case Id_values: - return realThis(thisObj, f).js_iterator(scope, NativeCollectionIterator.Type.VALUES); + return realThis(thisObj, f) + .js_iterator(scope, NativeCollectionIterator.Type.VALUES); case Id_entries: return realThis(thisObj, f).js_iterator(scope, NativeCollectionIterator.Type.BOTH); case Id_forEach: - return realThis(thisObj, f).js_forEach(cx, scope, - args.length > 0 ? args[0] : Undefined.instance, - args.length > 1 ? args[1] : Undefined.instance); + return realThis(thisObj, f) + .js_forEach( + cx, + scope, + args.length > 0 ? args[0] : Undefined.instance, + args.length > 1 ? args[1] : Undefined.instance); case SymbolId_getSize: return realThis(thisObj, f).js_getSize(); } throw new IllegalArgumentException("Map.prototype has no method: " + f.getFunctionName()); } - private Object js_set(Object k, Object v) - { + private Object js_set(Object k, Object v) { // Map.get() does not distinguish between "not found" and a null value. So, // replace true null here with a marker so that we can re-convert in "get". final Object value = (v == null ? NULL_VALUE : v); // Special handling of "negative zero" from the spec. Object key = k; - if ((key instanceof Number) && - ((Number)key).doubleValue() == ScriptRuntime.negativeZero) { + if ((key instanceof Number) && ((Number) key).doubleValue() == ScriptRuntime.negativeZero) { key = ScriptRuntime.zeroObj; } entries.put(key, value); return this; } - private Object js_delete(Object arg) - { + private Object js_delete(Object arg) { final Object e = entries.delete(arg); return Boolean.valueOf(e != null); } - private Object js_get(Object arg) - { + private Object js_get(Object arg) { final Object val = entries.get(arg); if (val == null) { return Undefined.instance; @@ -119,33 +120,29 @@ private Object js_get(Object arg) return val; } - private Object js_has(Object arg) - { + private Object js_has(Object arg) { return Boolean.valueOf(entries.has(arg)); } - private Object js_getSize() - { + private Object js_getSize() { return Integer.valueOf(entries.size()); } - private Object js_iterator(Scriptable scope, NativeCollectionIterator.Type type) - { + private Object js_iterator(Scriptable scope, NativeCollectionIterator.Type type) { return new NativeCollectionIterator(scope, ITERATOR_TAG, type, entries.iterator()); } - private Object js_clear() - { + private Object js_clear() { entries.clear(); return Undefined.instance; } - private Object js_forEach(Context cx, Scriptable scope, Object arg1, Object arg2) - { + private Object js_forEach(Context cx, Scriptable scope, Object arg1, Object arg2) { if (!(arg1 instanceof Callable)) { - throw ScriptRuntime.typeErrorById("msg.isnt.function", arg1, ScriptRuntime.typeof(arg1)); + throw ScriptRuntime.typeErrorById( + "msg.isnt.function", arg1, ScriptRuntime.typeof(arg1)); } - final Callable f = (Callable)arg1; + final Callable f = (Callable) arg1; boolean isStrict = cx.isStrictMode(); Iterator i = entries.iterator(); @@ -166,17 +163,16 @@ private Object js_forEach(Context cx, Scriptable scope, Object arg1, Object arg2 val = null; } - f.call(cx, scope, thisObj, new Object[] { val, e.key, this }); + f.call(cx, scope, thisObj, new Object[] {val, e.key, this}); } return Undefined.instance; } /** - * If an "iterable" object was passed to the constructor, there are many many things - * to do... Make this static because NativeWeakMap has the exact same requirement. + * If an "iterable" object was passed to the constructor, there are many many things to do... + * Make this static because NativeWeakMap has the exact same requirement. */ - static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, Object arg1) - { + static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, Object arg1) { if ((arg1 == null) || Undefined.instance.equals(arg1)) { return; } @@ -195,13 +191,16 @@ static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, final Callable set = ScriptRuntime.getPropFunctionAndThis(proto, "set", cx, scope); ScriptRuntime.lastStoredScriptable(cx); - ScriptRuntime.loadFromIterable(cx, scope, arg1, (key, value) -> { - set.call(cx, scope, map, new Object[] { key, value }); - }); + ScriptRuntime.loadFromIterable( + cx, + scope, + arg1, + (key, value) -> { + set.call(cx, scope, map, new Object[] {key, value}); + }); } - private static NativeMap realThis(Scriptable thisObj, IdFunctionObject f) - { + private static NativeMap realThis(Scriptable thisObj, IdFunctionObject f) { final NativeMap nm = ensureType(thisObj, NativeMap.class, f); if (!nm.instanceOfMap) { // Check for "Map internal data tag" @@ -212,40 +211,72 @@ private static NativeMap realThis(Scriptable thisObj, IdFunctionObject f) } @Override - protected void initPrototypeId(int id) - { + protected void initPrototypeId(int id) { switch (id) { case SymbolId_getSize: initPrototypeMethod(MAP_TAG, id, NativeSet.GETSIZE, "get size", 0); return; case SymbolId_toStringTag: - initPrototypeValue(SymbolId_toStringTag, SymbolKey.TO_STRING_TAG, - getClassName(), DONTENUM | READONLY); + initPrototypeValue( + SymbolId_toStringTag, + SymbolKey.TO_STRING_TAG, + getClassName(), + DONTENUM | READONLY); return; - // fallthrough + // fallthrough } String s, fnName = null; int arity; switch (id) { - case Id_constructor: arity=0; s="constructor"; break; - case Id_set: arity=2; s="set"; break; - case Id_get: arity=1; s="get"; break; - case Id_delete: arity=1; s="delete"; break; - case Id_has: arity=1; s="has"; break; - case Id_clear: arity=0; s="clear"; break; - case Id_keys: arity=0; s="keys"; break; - case Id_values: arity=0; s="values"; break; - case Id_entries: arity=0; s="entries"; break; - case Id_forEach: arity=1; s="forEach"; break; - default: throw new IllegalArgumentException(String.valueOf(id)); + case Id_constructor: + arity = 0; + s = "constructor"; + break; + case Id_set: + arity = 2; + s = "set"; + break; + case Id_get: + arity = 1; + s = "get"; + break; + case Id_delete: + arity = 1; + s = "delete"; + break; + case Id_has: + arity = 1; + s = "has"; + break; + case Id_clear: + arity = 0; + s = "clear"; + break; + case Id_keys: + arity = 0; + s = "keys"; + break; + case Id_values: + arity = 0; + s = "values"; + break; + case Id_entries: + arity = 0; + s = "entries"; + break; + case Id_forEach: + arity = 1; + s = "forEach"; + break; + default: + throw new IllegalArgumentException(String.valueOf(id)); } initPrototypeMethod(MAP_TAG, id, s, fnName, arity); } @Override - protected int findPrototypeId(Symbol k) - { + protected int findPrototypeId(Symbol k) { if (NativeSet.GETSIZE.equals(k)) { return SymbolId_getSize; } @@ -261,68 +292,66 @@ protected int findPrototypeId(Symbol k) return 0; } -// #string_id_map# + // #string_id_map# @Override - protected int findPrototypeId(String s) - { + protected int findPrototypeId(String s) { int id; -// #generated# Last update: 2021-03-21 09:51:14 MEZ + // #generated# Last update: 2021-03-21 09:51:14 MEZ switch (s) { - case "constructor": - id = Id_constructor; - break; - case "set": - id = Id_set; - break; - case "get": - id = Id_get; - break; - case "delete": - id = Id_delete; - break; - case "has": - id = Id_has; - break; - case "clear": - id = Id_clear; - break; - case "keys": - id = Id_keys; - break; - case "values": - id = Id_values; - break; - case "entries": - id = Id_entries; - break; - case "forEach": - id = Id_forEach; - break; - default: - id = 0; - break; + case "constructor": + id = Id_constructor; + break; + case "set": + id = Id_set; + break; + case "get": + id = Id_get; + break; + case "delete": + id = Id_delete; + break; + case "has": + id = Id_has; + break; + case "clear": + id = Id_clear; + break; + case "keys": + id = Id_keys; + break; + case "values": + id = Id_values; + break; + case "entries": + id = Id_entries; + break; + case "forEach": + id = Id_forEach; + break; + default: + id = 0; + break; } -// #/generated# + // #/generated# return id; } // Note that "SymbolId_iterator" is not present here. That's because the spec // requires that it be the same value as the "entries" prototype property. - private static final int - Id_constructor = 1, - Id_set = 2, - Id_get = 3, - Id_delete = 4, - Id_has = 5, - Id_clear = 6, - Id_keys = 7, - Id_values = 8, - Id_entries = 9, - Id_forEach = 10, - SymbolId_getSize = 11, - SymbolId_toStringTag = 12, - MAX_PROTOTYPE_ID = SymbolId_toStringTag; - -// #/string_id_map# + private static final int Id_constructor = 1, + Id_set = 2, + Id_get = 3, + Id_delete = 4, + Id_has = 5, + Id_clear = 6, + Id_keys = 7, + Id_values = 8, + Id_entries = 9, + Id_forEach = 10, + SymbolId_getSize = 11, + SymbolId_toStringTag = 12, + MAX_PROTOTYPE_ID = SymbolId_toStringTag; + + // #/string_id_map# } From c915eea3ccd681169c11bd89a39844b1e7a5ab37 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 2 Jun 2021 07:48:13 +0200 Subject: [PATCH 11/12] Spotless NativeMap --- src/org/mozilla/javascript/NativeMap.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/org/mozilla/javascript/NativeMap.java b/src/org/mozilla/javascript/NativeMap.java index 772ae4c704..ab74769f1e 100644 --- a/src/org/mozilla/javascript/NativeMap.java +++ b/src/org/mozilla/javascript/NativeMap.java @@ -190,9 +190,13 @@ static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, Scriptable proto = ScriptableObject.getClassPrototype(scope, map.getClassName()); final Callable set = ScriptRuntime.getPropFunctionAndThis(proto, "set", cx, scope); - ScriptRuntime.loadFromIterable(cx, scope, arg1, (key, value) -> { - set.call(cx, scope, map, new Object[] { key, value }); - }); + ScriptRuntime.loadFromIterable( + cx, + scope, + arg1, + (key, value) -> { + set.call(cx, scope, map, new Object[] {key, value}); + }); } private static NativeMap realThis(Scriptable thisObj, IdFunctionObject f) { From 37b6fee15cf9c89fbe1b9ab58a159dc4b589fbd3 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 2 Jun 2021 07:58:49 +0200 Subject: [PATCH 12/12] FIX: lastStoredScriptable got lost during merge resolve --- src/org/mozilla/javascript/NativeMap.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/mozilla/javascript/NativeMap.java b/src/org/mozilla/javascript/NativeMap.java index ab74769f1e..495ff7380d 100644 --- a/src/org/mozilla/javascript/NativeMap.java +++ b/src/org/mozilla/javascript/NativeMap.java @@ -189,6 +189,7 @@ static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, // so that we can get our own prototype. Scriptable proto = ScriptableObject.getClassPrototype(scope, map.getClassName()); final Callable set = ScriptRuntime.getPropFunctionAndThis(proto, "set", cx, scope); + ScriptRuntime.lastStoredScriptable(cx); ScriptRuntime.loadFromIterable( cx,