From 90ac93690f15b6187cf57852772e626472fe99c2 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Tue, 6 Jun 2023 18:08:47 +0200 Subject: [PATCH 01/24] first implementation of NativeReflect --- examples/Matrix.java | 20 + src/org/mozilla/javascript/NativeReflect.java | 466 ++++++++++++++++++ src/org/mozilla/javascript/ScriptRuntime.java | 2 + src/org/mozilla/javascript/Scriptable.java | 6 + .../mozilla/javascript/ScriptableObject.java | 8 + src/org/mozilla/javascript/Undefined.java | 8 + .../javascript/tests/IterableTest.java | 10 + .../javascript/tests/Test262SuiteTest.java | 4 - .../tests/es6/NativeReflectTest.java | 357 ++++++++++++++ testsrc/test262.properties | 293 ++++++----- 10 files changed, 1045 insertions(+), 129 deletions(-) create mode 100644 src/org/mozilla/javascript/NativeReflect.java create mode 100644 testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java diff --git a/examples/Matrix.java b/examples/Matrix.java index cea70015df..baf0810c8d 100644 --- a/examples/Matrix.java +++ b/examples/Matrix.java @@ -9,6 +9,7 @@ import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Symbol; /** * Matrix: An example host object class that implements the Scriptable interface. @@ -93,6 +94,17 @@ public boolean has(int index, Scriptable start) { return true; } + /** + * Defines all numeric properties by returning true. + * + * @param key the key of the property + * @param start the object where lookup began + */ + @Override + public boolean has(Symbol key, Scriptable start) { + return true; + } + /** * Get the named property. * @@ -172,6 +184,14 @@ public void delete(String id) {} @Override public void delete(int index) {} + /** + * Remove an symbol property. + * + *

This method shouldn't even be called since we define all properties as PERMANENT. + */ + @Override + public void delete(Symbol key) {} + /** Get prototype. */ @Override public Scriptable getPrototype() { diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java new file mode 100644 index 0000000000..cc34b8eaae --- /dev/null +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -0,0 +1,466 @@ +/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class implements the Reflect object. + * + * @author Ronald Brill + */ +final class NativeReflect extends IdScriptableObject { + private static final long serialVersionUID = 2920773905356325445L; + + private static final Object REFLECT_TAG = "Reflect"; + + static void init(Scriptable scope, boolean sealed) { + NativeReflect obj = new NativeReflect(); + obj.activatePrototypeMap(LAST_METHOD_ID); + obj.setPrototype(getObjectPrototype(scope)); + obj.setParentScope(scope); + if (sealed) { + obj.sealObject(); + } + ScriptableObject.defineProperty(scope, "Reflect", obj, ScriptableObject.DONTENUM); + } + + private NativeReflect() {} + + @Override + public String getClassName() { + return "Reflect"; + } + + @Override + protected void initPrototypeId(int id) { + if (id <= LAST_METHOD_ID) { + String name; + int arity; + switch (id) { + case Id_toSource: + arity = 0; + name = "toSource"; + break; + case Id_apply: + arity = 3; + name = "apply"; + break; + case Id_construct: + arity = 2; + name = "construct"; + break; + case Id_defineProperty: + arity = 3; + name = "defineProperty"; + break; + case Id_deleteProperty: + arity = 2; + name = "deleteProperty"; + break; + case Id_get: + arity = 2; + name = "get"; + break; + case Id_getOwnPropertyDescriptor: + arity = 2; + name = "getOwnPropertyDescriptor"; + break; + case Id_getPrototypeOf: + arity = 1; + name = "getPrototypeOf"; + break; + case Id_has: + arity = 2; + name = "has"; + break; + case Id_isExtensible: + arity = 1; + name = "isExtensible"; + break; + case Id_ownKeys: + arity = 1; + name = "ownKeys"; + break; + case Id_preventExtensions: + arity = 1; + name = "preventExtensions"; + break; + case Id_set: + arity = 3; + name = "set"; + break; + case Id_setPrototypeOf: + arity = 2; + name = "setPrototypeOf"; + break; + default: + throw new IllegalStateException(String.valueOf(id)); + } + initPrototypeMethod(REFLECT_TAG, id, name, arity); + } + } + + @Override + public Object execIdCall( + IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + if (!f.hasTag(REFLECT_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + + int methodId = f.methodId(); + switch (methodId) { + case Id_toSource: + return "Reflect"; + + case Id_apply: + return js_apply(cx, scope, args); + case Id_construct: + return js_construct(cx, scope, args); + case Id_defineProperty: + return js_defineProperty(cx, args); + case Id_deleteProperty: + return js_deleteProperty(args); + case Id_get: + return js_get(args); + case Id_getOwnPropertyDescriptor: + return js_getOwnPropertyDescriptor(cx, args); + case Id_getPrototypeOf: + return js_getPrototypeOf(args); + case Id_has: + return js_has(args); + case Id_isExtensible: + return js_isExtensible(args); + case Id_ownKeys: + return js_ownKeys(cx, scope, args); + case Id_preventExtensions: + return js_preventExtensions(args); + case Id_set: + return js_set(args); + case Id_setPrototypeOf: + return js_setPrototypeOf(args); + + default: + throw new IllegalStateException(String.valueOf(methodId)); + } + } + + private static Object js_apply(Context cx, Scriptable scope, Object[] args) { + if (args.length < 3) { + throw ScriptRuntime.typeErrorById( + "msg.method.missing.parameter", + "Reflect.apply", + "3", + Integer.toString(args.length)); + } + + Scriptable callable = ScriptableObject.ensureScriptable(args[0]); + + Scriptable thisObj = Undefined.SCRIPTABLE_UNDEFINED; + if (args[1] instanceof Scriptable) { + thisObj = (Scriptable) args[1]; + } + + if (ScriptRuntime.isSymbol(args[2])) { + throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(args[2])); + } + ScriptableObject argumentsList = ScriptableObject.ensureScriptableObject(args[2]); + + return ScriptRuntime.applyOrCall( + true, cx, scope, callable, new Object[] {thisObj, argumentsList}); + } + + private static Scriptable js_construct(Context cx, Scriptable scope, Object[] args) { + if (args.length < 1) { + throw ScriptRuntime.typeErrorById( + "msg.method.missing.parameter", + "Reflect.construct", + "3", + Integer.toString(args.length)); + } + + if (!(args[0] instanceof Function)) { + throw ScriptRuntime.typeErrorById("msg.not.ctor", ScriptRuntime.typeof(args[0])); + } + + Function ctor = (Function) args[0]; + if (args.length < 2) { + return ctor.construct(cx, scope, ScriptRuntime.emptyArgs); + } + Object[] callArgs = ScriptRuntime.getApplyArguments(cx, args[1]); + return ctor.construct(cx, scope, callArgs); + } + + private static boolean js_defineProperty(Context cx, Object[] args) { + if (args.length < 3) { + throw ScriptRuntime.typeErrorById( + "msg.method.missing.parameter", + "Reflect.apply", + "3", + Integer.toString(args.length)); + } + + ScriptableObject obj = checkTarget(args); + ScriptableObject desc = ScriptableObject.ensureScriptableObject(args[2]); + + try { + obj.defineOwnProperty(cx, args[1], desc); + return true; + } catch (EcmaError e) { + return false; + } + } + + private static boolean js_deleteProperty(Object[] args) { + ScriptableObject obj = checkTarget(args); + + if (args.length > 1) { + if (ScriptRuntime.isSymbol(args[1])) { + return ScriptableObject.deleteProperty(obj, (Symbol) args[1]); + } + + return ScriptableObject.deleteProperty(obj, ScriptRuntime.toString(args[1])); + } + return false; + } + + private static Object js_get(Object[] args) { + ScriptableObject obj = checkTarget(args); + + if (args.length > 1) { + if (ScriptRuntime.isSymbol(args[1])) { + Object prop = ScriptableObject.getProperty(obj, (Symbol) args[1]); + return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; + } + if (args[1] instanceof Double) { + Object prop = ScriptableObject.getProperty(obj, ScriptRuntime.toIndex(args[1])); + return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; + } + + Object prop = ScriptableObject.getProperty(obj, ScriptRuntime.toString(args[1])); + return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; + } + return Undefined.SCRIPTABLE_UNDEFINED; + } + + private static Scriptable js_getOwnPropertyDescriptor(Context cx, Object[] args) { + ScriptableObject obj = checkTarget(args); + + if (args.length > 1) { + if (ScriptRuntime.isSymbol(args[1])) { + ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, args[1]); + return desc == null ? Undefined.SCRIPTABLE_UNDEFINED : desc; + } + + ScriptableObject desc = + obj.getOwnPropertyDescriptor(cx, ScriptRuntime.toString(args[1])); + return desc == null ? Undefined.SCRIPTABLE_UNDEFINED : desc; + } + return Undefined.SCRIPTABLE_UNDEFINED; + } + + private static Scriptable js_getPrototypeOf(Object[] args) { + ScriptableObject obj = checkTarget(args); + + return obj.getPrototype(); + } + + private static boolean js_has(Object[] args) { + ScriptableObject obj = checkTarget(args); + + if (args.length > 1) { + if (ScriptRuntime.isSymbol(args[1])) { + return ScriptableObject.hasProperty(obj, (Symbol) args[1]); + } + + return ScriptableObject.hasProperty(obj, ScriptRuntime.toString(args[1])); + } + return false; + } + + private static boolean js_isExtensible(Object[] args) { + ScriptableObject obj = checkTarget(args); + return obj.isExtensible(); + } + + private static Scriptable js_ownKeys(Context cx, Scriptable scope, Object[] args) { + ScriptableObject obj = checkTarget(args); + + final List strings = new ArrayList<>(); + final List symbols = new ArrayList<>(); + + for (Object o : obj.getIds(true, true)) { + if (o instanceof Symbol) { + symbols.add(o); + } else { + strings.add(ScriptRuntime.toString(o)); + } + } + + Object[] keys = new Object[strings.size() + symbols.size()]; + System.arraycopy(strings.toArray(), 0, keys, 0, strings.size()); + System.arraycopy(symbols.toArray(), 0, keys, strings.size(), symbols.size()); + + return cx.newArray(scope, keys); + } + + private static boolean js_preventExtensions(Object[] args) { + ScriptableObject obj = checkTarget(args); + + obj.preventExtensions(); + return true; + } + + private static boolean js_set(Object[] args) { + ScriptableObject obj = checkTarget(args); + + if (args.length > 1) { + if (ScriptRuntime.isSymbol(args[1])) { + obj.put((Symbol) args[1], obj, args[2]); + return true; + } + if (args[1] instanceof Double) { + obj.put(ScriptRuntime.toIndex(args[1]), obj, args[2]); + return true; + } + + obj.put(ScriptRuntime.toString(args[1]), obj, args[2]); + return true; + } + return false; + } + + private static boolean js_setPrototypeOf(Object[] args) { + if (args.length < 2) { + throw ScriptRuntime.typeErrorById( + "msg.method.missing.parameter", + "Reflect.js_setPrototypeOf", + "2", + Integer.toString(args.length)); + } + + ScriptableObject obj = checkTarget(args); + + if (obj.getPrototype() == args[1]) { + return true; + } + + if (!obj.isExtensible()) { + return false; + } + + if (args[1] == null) { + obj.setPrototype(null); + return true; + } + + if (ScriptRuntime.isSymbol(args[1])) { + throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(args[0])); + } + + ScriptableObject proto = ScriptableObject.ensureScriptableObject(args[1]); + if (obj.getPrototype() == proto) { + return true; + } + + // avoid cycles + Scriptable p = proto; + while (p != null) { + if (obj == p) { + return false; + } + p = p.getPrototype(); + } + + obj.setPrototype(proto); + return true; + } + + private static ScriptableObject checkTarget(Object[] args) { + if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) { + Object argument = args.length == 0 ? Undefined.instance : args[0]; + throw ScriptRuntime.typeErrorById( + "msg.no.properties", ScriptRuntime.toString(argument)); + } + + if (ScriptRuntime.isSymbol(args[0])) { + throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(args[0])); + } + return ScriptableObject.ensureScriptableObject(args[0]); + } + + @Override + protected int findPrototypeId(String s) { + int id; + switch (s) { + case "toSource": + id = Id_toSource; + break; + case "apply": + id = Id_apply; + break; + case "construct": + id = Id_construct; + break; + case "defineProperty": + id = Id_defineProperty; + break; + case "deleteProperty": + id = Id_deleteProperty; + break; + case "get": + id = Id_get; + break; + case "getOwnPropertyDescriptor": + id = Id_getOwnPropertyDescriptor; + break; + case "getPrototypeOf": + id = Id_getPrototypeOf; + break; + case "has": + id = Id_has; + break; + case "isExtensible": + id = Id_isExtensible; + break; + case "ownKeys": + id = Id_ownKeys; + break; + case "preventExtensions": + id = Id_preventExtensions; + break; + case "set": + id = Id_set; + break; + case "setPrototypeOf": + id = Id_setPrototypeOf; + break; + + default: + id = 0; + break; + } + return id; + } + + private static final int Id_toSource = 1, + Id_apply = 2, + Id_construct = 3, + Id_defineProperty = 4, + Id_deleteProperty = 5, + Id_get = 6, + Id_getOwnPropertyDescriptor = 7, + Id_getPrototypeOf = 8, + Id_has = 9, + Id_isExtensible = 10, + Id_ownKeys = 11, + Id_preventExtensions = 12, + Id_set = 13, + Id_setPrototypeOf = 14, + LAST_METHOD_ID = Id_setPrototypeOf; +} diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 825eb2537c..1ad0fc8e16 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -286,6 +286,8 @@ public static ScriptableObject initSafeStandardObjects( NativeWeakMap.init(scope, sealed); NativeWeakSet.init(scope, sealed); NativeBigInt.init(scope, sealed); + + NativeReflect.init(scope, sealed); } if (scope instanceof TopLevel) { diff --git a/src/org/mozilla/javascript/Scriptable.java b/src/org/mozilla/javascript/Scriptable.java index cc37012fb3..26b7f7f240 100644 --- a/src/org/mozilla/javascript/Scriptable.java +++ b/src/org/mozilla/javascript/Scriptable.java @@ -126,6 +126,9 @@ public interface Scriptable { */ public boolean has(int index, Scriptable start); + /** A version of delete for properties with Symbol keys. */ + public boolean has(Symbol key, Scriptable start); + /** * Sets a named property in this object. * @@ -228,6 +231,9 @@ public interface Scriptable { */ public void delete(int index); + /** A version of delete for properties with Symbol keys. */ + public void delete(Symbol key); + /** * Get the prototype of the object. * diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 6ff475eaab..33f2604387 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -2293,6 +2293,14 @@ public static boolean deleteProperty(Scriptable obj, int index) { return !base.has(index, obj); } + /** A version of deleteProperty for properties with Symbol keys. */ + public static boolean deleteProperty(Scriptable obj, Symbol key) { + Scriptable base = getBase(obj, key); + if (base == null) return true; + base.delete(key); + return !base.has(key, obj); + } + /** * Returns an array of all ids from an object and its prototypes. * diff --git a/src/org/mozilla/javascript/Undefined.java b/src/org/mozilla/javascript/Undefined.java index 3b30bac193..c9c79266b3 100644 --- a/src/org/mozilla/javascript/Undefined.java +++ b/src/org/mozilla/javascript/Undefined.java @@ -79,6 +79,11 @@ public boolean has(int index, Scriptable start) { return false; } + @Override + public boolean has(Symbol key, Scriptable start) { + return false; + } + @Override public void put(String name, Scriptable start, Object value) {} @@ -91,6 +96,9 @@ public void delete(String name) {} @Override public void delete(int index) {} + @Override + public void delete(Symbol key) {} + @Override public Scriptable getPrototype() { return null; diff --git a/testsrc/org/mozilla/javascript/tests/IterableTest.java b/testsrc/org/mozilla/javascript/tests/IterableTest.java index 5e3d8ad4bd..e4a46fc776 100644 --- a/testsrc/org/mozilla/javascript/tests/IterableTest.java +++ b/testsrc/org/mozilla/javascript/tests/IterableTest.java @@ -239,6 +239,11 @@ public boolean has(int index, Scriptable start) { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public boolean has(Symbol key, Scriptable start) { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public void put(String name, Scriptable start, Object value) { throw new UnsupportedOperationException("Not supported yet."); @@ -259,6 +264,11 @@ public void delete(int index) { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public void delete(Symbol key) { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public void setPrototype(Scriptable prototype) { throw new UnsupportedOperationException("Not supported yet."); diff --git a/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java b/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java index db31713b23..c519350caa 100644 --- a/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java +++ b/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java @@ -91,10 +91,6 @@ public class Test262SuiteTest { "Atomics", "IsHTMLDDA", "Proxy", - "Reflect", - "Reflect.construct", - "Reflect.set", - "Reflect.setPrototypeOf", "SharedArrayBuffer", "Symbol.species", "async-functions", diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java new file mode 100644 index 0000000000..5cdeb5dab8 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java @@ -0,0 +1,357 @@ +package org.mozilla.javascript.tests.es6; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.tests.Utils; + +public class NativeReflectTest { + + @Test + public void testToString() { + testString("[object Reflect]", "Reflect.toString()"); + } + + @Test + public void apply() { + testDouble(1.0, "Reflect.apply(Math.floor, undefined, [1.75])"); + } + + @Test + public void applyDetails() { + String js = + "var o = {};\n" + + "var count = 0;\n" + + "var results, args;\n" + + "function fn() {\n" + + " count++;\n" + + " results = {\n" + + " thisArg: this,\n" + + " args: arguments\n" + + " };\n" + + "}\n" + + "Reflect.apply(fn, o, ['arg1', 2, , null]);\n" + + "'' + count " + + "+ ' ' + (results.thisArg === o)" + + "+ ' ' + results.args.length" + + "+ ' ' + results.args[0]" + + "+ ' ' + results.args[1]" + + "+ ' ' + results.args[2]" + + "+ ' ' + results.args[3]"; + testString("1 true 4 arg1 2 undefined null", js); + } + + @Test + public void applyMissingArgs() { + String js = "try {\n" + " Reflect.apply();\n" + "} catch(e) {" + " '' + e;\n" + "}"; + testString( + "TypeError: Reflect.apply: At least 3 arguments required, but only 0 passed", js); + } + + @Test + public void applyTargetNotFunction() { + String js = + "try {\n" + + " Reflect.apply({}, undefined, [1.75]);\n" + + "} catch(e) {" + + " '' + e;\n" + + "}"; + testString("TypeError: [object Object] is not a function, it is object.", js); + } + + @Test + public void applyArgumentsListNotFunction() { + String js = + "var s1 = Symbol('1');" + + "try {\n" + + " Reflect.apply(Math.floor, undefined, s1);\n" + + "} catch(e) {" + + " '' + e;\n" + + "}"; + testString("TypeError: Expected argument of type object, but instead had type symbol", js); + } + + @Test + public void construct() { + String js = + "var d = Reflect.construct(Date, [1776, 6, 4]);\n" + + "'' + (d instanceof Date) + ' ' + d.getFullYear();"; + testString("true 1776", js); + } + + @Test + public void defineProperty() { + String js = + "var o = {};\n" + "'' + Reflect.defineProperty(o, 'p', { value: 42 }) + ' ' + o.p;"; + testString("true 42", js); + } + + @Test + public void definePropertyWithoutValue() { + String js = + "var o = {};\n" + + "'' + Reflect.defineProperty(o, 'p', {})" + + "+ ' ' + Reflect.has(o, 'p')" + + "+ ' ' + o.p;"; + testString("true true undefined", js); + } + + @Test + public void definePropertyFreezed() { + String js = + "var o = {};\n" + + "Object.freeze(o);\n" + + "'' + Reflect.defineProperty(o, 'p', { value: 42 }) + ' ' + o.p;"; + testString("false undefined", js); + } + + @Test + public void getOwnPropertyDescriptor() { + String js = + "var o1 = {};\n" + + "var fn = function() {};\n" + + "Object.defineProperty(o1, 'p', {\n" + + " get: fn,\n" + + " configurable: true\n" + + "});\n" + + "var result = Reflect.getOwnPropertyDescriptor(o1, 'p');\n" + + "'[' + Object.getOwnPropertyNames(result) + ']'" + + "+ ' ' + result.enumerable" + + "+ ' ' + result.configurable" + + "+ ' ' + (result.get === fn)" + + "+ ' ' + (result.set === undefined)"; + testString("[get,set,enumerable,configurable] false true true true", js); + } + + @Test + public void isExtensible() { + String js = + "var o1 = {};\n" + + "var result = '' + Reflect.isExtensible(o1);\n" + + "Reflect.preventExtensions(o1);\n" + + "result += ' ' + Reflect.isExtensible(o1);\n" + + "var o2 = Object.seal({});\n" + + "result += ' ' + Reflect.isExtensible(o2);\n"; + + testString("true false false", js); + } + + @Test + public void ownKeys() { + String js = + "var o1 = {\n" + + " p1: 42,\n" + + " p2: 'one'\n" + + "};\n" + + "var a1 = [];\n" + + "'' + Reflect.ownKeys(o1)" + + "+ ' ' + Reflect.ownKeys(a1)"; + testString("p1,p2 length", js); + } + + @Test + public void ownKeys2() { + String js = + "let s1 = Symbol.for('foo');\n" + + "let s2 = Symbol.for('bar');\n" + + "var o1 = {\n" + + " s1: 0,\n" + + " 'str': 0,\n" + + " 773: 0,\n" + + " '55': 0,\n" + + " 0: 0,\n" + + " '-1': 0,\n" + + " 8: 0,\n" + + " '6': 8,\n" + + " s2: 0,\n" + + " 'str2': 0\n" + + "};\n" + + "var a1 = [];\n" + + "'' + Reflect.ownKeys(o1)"; + // FF: 0,6,8,55,773,s1,str,-1,s2,str2 + testString("-1,0,6,8,55,773,s1,str,s2,str2", js); + } + + @Test + public void ownKeysEmptyObj() { + String js = "'' + Reflect.ownKeys({}).length"; + testString("0", js); + } + + @Test + public void ownKeysDeleteObj() { + String js = "var o = { d: 42 };\n" + "delete o.d;\n" + "'' + Reflect.ownKeys({}).length"; + testString("0", js); + } + + @Test + public void ownKeysEmptyArray() { + String js = "'' + Reflect.ownKeys([])"; + testString("length", js); + } + + @Test + public void ownKeysArray() { + String js = "'' + Reflect.ownKeys([, , 2])"; + testString("2,length", js); + } + + @Test + public void ownKeysNotEnumerable() { + String js = + "var o = {};\n" + + "Object.defineProperty(o, 'p1', { value: 42, enumerable: false });\n" + + "Object.defineProperty(o, 'p2', { get: function() {}, enumerable: false });\n" + + "'' + Reflect.ownKeys(o)"; + testString("p1,p2", js); + } + + @Test + public void has() { + String js = + "var o1 = { p: 42 }\n" + + "'' + Reflect.has(o1, 'p')" + + "+ ' ' + Reflect.has(o1, 'p2')" + + "+ ' ' + Reflect.has(o1, 'toString')"; + testString("true false true", js); + } + + @Test + public void hasSymbol() { + String js = + "var s1 = Symbol('1');\n" + + "var s2 = Symbol('1');\n" + + "var o = {};\n" + + "o[s1] = 42;\n" + + "'' + Reflect.has(o, s1)" + + "+ ' ' + Reflect.has(o, 2)"; + testString("true false", js); + } + + @Test + public void hasProto() { + String js = "var o1 = { p: 42 }\n" + "'' + typeof Reflect.has.__proto__"; + testString("function", js); + } + + @Test + public void getOwnPropertyDescriptorSymbol() { + String js = + "var s = Symbol('sym');\n" + + "var o = {};\n" + + "o[s] = 42;\n" + + "var result = Reflect.getOwnPropertyDescriptor(o, s);\n" + + "'' + result.value" + + "+ ' ' + result.enumerable" + + "+ ' ' + result.configurable" + + "+ ' ' + result.writable"; + testString("42 true true true", js); + } + + @Test + public void getOwnPropertyDescriptorUndefinedProperty() { + String js = + "var o = Object.create({p: 1});\n" + + "var result = Reflect.getOwnPropertyDescriptor(o, 'p');\n" + + "'' + (result === undefined)"; + testString("true", js); + } + + @Test + public void getPropertyByInt() { + String js = "var a = ['zero', 'one']\n" + "Reflect.get(a, 1);"; + testString("one", js); + } + + @Test + public void getProperty() { + String js = + "var o = {};\n" + + "o.p1 = 'value 1';\n" + + "var result = '';" + + "result += Reflect.get(o, 'p1');\n" + + "Object.defineProperty(o, 'p2', { get: undefined });\n" + + "result += ', ' + Reflect.get(o, 'p2');\n" + + "Object.defineProperty(o, 'p3', { get: function() { return 'foo'; } });\n" + + "result += ', ' + Reflect.get(o, 'p3');\n" + + "var o2 = Object.create({ p: 42 });\n" + + "result += ', ' + Reflect.get(o2, 'p');\n" + + "result += ', ' + Reflect.get(o2, 'u');\n"; + + testString("value 1, undefined, foo, 42, undefined", js); + } + + @Test + public void setPrototypeOf() { + String js = + "var o1 = {};\n" + + "var result = '';\n" + + "result += Reflect.setPrototypeOf(o1, Object.prototype);\n" + + "result += ' ' + Reflect.setPrototypeOf(o1, null);\n" + + "var o2 = {};\n" + + "result += ' ' + Reflect.setPrototypeOf(Object.freeze(o2), null);\n"; + testString("true true false", js); + } + + @Test + public void setPrototypeOfCycle() { + String js = "var o1 = {};\n" + "'' + Reflect.setPrototypeOf(o1, o1);\n"; + testString("false", js); + } + + @Test + public void setPrototypeOfCycleComplex() { + String js = + "var o1 = {};\n" + + "var o2 = {};\n" + + "var o3 = {};\n" + + "'' + Reflect.setPrototypeOf(o1, o2)" + + "+ ' ' + Reflect.setPrototypeOf(o2, o3)" + + "+ ' ' + Reflect.setPrototypeOf(o3, o1)"; + testString("true true false", js); + } + + @Test + public void setPrototypeOfSame() { + String js = + "var o1 = {};\n" + + "Object.preventExtensions(o1);\n" + + "var o2 = Object.create(null);\n" + + "Object.preventExtensions(o2);\n" + + "var proto = {};\n" + + "var o3 = Object.create(proto);\n" + + "Object.preventExtensions(o3);\n" + + "'' + Reflect.setPrototypeOf(o1, Object.prototype)" + + "+ ' ' + Reflect.setPrototypeOf(o2, null)" + + "+ ' ' + Reflect.setPrototypeOf(o3, proto)"; + testString("true true true", js); + } + + private static void testString(String expected, String js) { + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + final Scriptable scope = cx.initStandardObjects(); + + Object result = cx.evaluateString(scope, js, "test", 1, null); + assertEquals(expected, result); + + return null; + }); + } + + private static void testDouble(double expected, String js) { + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + final Scriptable scope = cx.initStandardObjects(); + + Object result = cx.evaluateString(scope, js, "test", 1, null); + assertEquals(expected, ((Double) result).doubleValue(), 0.00001); + + return null; + }); + } +} diff --git a/testsrc/test262.properties b/testsrc/test262.properties index 57d2fba45b..836afc37d0 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -14,9 +14,9 @@ built-ins/Array 177/2670 (6.63%) from/source-object-length-set-elem-prop-non-writable.js isArray/proxy.js {unsupported: [Proxy]} isArray/proxy-revoked.js {unsupported: [Proxy]} - length/define-own-prop-length-coercion-order.js {unsupported: [Reflect]} - length/define-own-prop-length-coercion-order-set.js {unsupported: [Reflect, Reflect.set]} - length/define-own-prop-length-no-value-order.js {unsupported: [Reflect]} + length/define-own-prop-length-coercion-order.js + length/define-own-prop-length-coercion-order-set.js + length/define-own-prop-length-no-value-order.js length/define-own-prop-length-overflow-order.js of/proto-from-ctor-realm.js of/return-abrupt-from-data-property-using-proxy.js {unsupported: [Proxy]} @@ -171,11 +171,11 @@ built-ins/Array 177/2670 (6.63%) prototype/methods-called-as-functions.js {unsupported: [Symbol.species]} prototype/Symbol.iterator.js Expects a particular string value Symbol.species 4/4 (100.0%) - proto-from-ctor-realm-one.js {unsupported: [Reflect]} - proto-from-ctor-realm-two.js {unsupported: [Reflect]} - proto-from-ctor-realm-zero.js {unsupported: [Reflect]} + proto-from-ctor-realm-one.js + proto-from-ctor-realm-two.js + proto-from-ctor-realm-zero.js -built-ins/ArrayBuffer 30/80 (37.5%) +built-ins/ArrayBuffer 29/80 (36.25%) isView/arg-is-dataview-subclass-instance.js {unsupported: [class]} isView/arg-is-typedarray-subclass-instance.js {unsupported: [class]} prototype/byteLength/detached-buffer.js @@ -198,10 +198,9 @@ built-ins/ArrayBuffer 30/80 (37.5%) prototype/slice/this-is-sharedarraybuffer.js {unsupported: [SharedArrayBuffer]} prototype/Symbol.toStringTag.js Symbol.species 4/4 (100.0%) - data-allocation-after-object-creation.js {unsupported: [Reflect.construct]} - newtarget-prototype-is-not-object.js {unsupported: [Reflect.construct]} - proto-from-ctor-realm.js {unsupported: [Reflect]} - prototype-from-newtarget.js {unsupported: [Reflect.construct]} + data-allocation-after-object-creation.js + proto-from-ctor-realm.js + prototype-from-newtarget.js undefined-newtarget-throws.js built-ins/ArrayIteratorPrototype 1/27 (3.7%) @@ -228,9 +227,9 @@ built-ins/BigInt 14/68 (20.59%) prototype/toString/thisbigintvalue-not-valid-throws.js Computed property is not support built-ins/Boolean 1/49 (2.04%) - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js -built-ins/DataView 166/455 (36.48%) +built-ins/DataView 165/455 (36.26%) prototype/buffer/detached-buffer.js prototype/buffer/invoked-as-accessor.js prototype/buffer/length.js @@ -370,13 +369,12 @@ built-ins/DataView 166/455 (36.48%) buffer-does-not-have-arraybuffer-data-throws-sab.js {unsupported: [SharedArrayBuffer]} buffer-reference-sab.js {unsupported: [SharedArrayBuffer]} byteoffset-is-negative-throws-sab.js {unsupported: [SharedArrayBuffer]} - custom-proto-access-detaches-buffer.js {unsupported: [Reflect.construct]} - custom-proto-access-throws.js {unsupported: [Reflect.construct]} - custom-proto-access-throws-sab.js {unsupported: [Reflect.construct, SharedArrayBuffer]} - custom-proto-if-not-object-fallbacks-to-default-prototype.js {unsupported: [Reflect.construct]} - custom-proto-if-not-object-fallbacks-to-default-prototype-sab.js {unsupported: [Reflect.construct, SharedArrayBuffer]} - custom-proto-if-object-is-used.js {unsupported: [Reflect.construct]} - custom-proto-if-object-is-used-sab.js {unsupported: [Reflect.construct, SharedArrayBuffer]} + custom-proto-access-detaches-buffer.js + custom-proto-access-throws.js + custom-proto-access-throws-sab.js {unsupported: [SharedArrayBuffer]} + custom-proto-if-not-object-fallbacks-to-default-prototype-sab.js {unsupported: [SharedArrayBuffer]} + custom-proto-if-object-is-used.js + custom-proto-if-object-is-used-sab.js {unsupported: [SharedArrayBuffer]} defined-bytelength-and-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} defined-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} defined-byteoffset-undefined-bytelength-sab.js {unsupported: [SharedArrayBuffer]} @@ -388,8 +386,8 @@ built-ins/DataView 166/455 (36.48%) negative-byteoffset-throws-sab.js {unsupported: [SharedArrayBuffer]} newtarget-undefined-throws.js newtarget-undefined-throws-sab.js {unsupported: [SharedArrayBuffer]} - proto-from-ctor-realm.js {unsupported: [Reflect]} - proto-from-ctor-realm-sab.js {unsupported: [SharedArrayBuffer, Reflect]} + proto-from-ctor-realm.js + proto-from-ctor-realm-sab.js {unsupported: [SharedArrayBuffer]} return-abrupt-tonumber-bytelength-sab.js {unsupported: [SharedArrayBuffer]} return-abrupt-tonumber-bytelength-symbol-sab.js {unsupported: [SharedArrayBuffer]} return-abrupt-tonumber-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} @@ -414,7 +412,7 @@ built-ins/Date 39/707 (5.52%) prototype/Symbol.toPrimitive/name.js prototype/Symbol.toPrimitive/prop-desc.js prototype/Symbol.toPrimitive/this-val-non-obj.js - prototype/toJSON/builtin.js {unsupported: [Reflect.construct]} + prototype/toJSON/builtin.js prototype/toJSON/called-as-function.js prototype/toJSON/invoke-result.js prototype/toJSON/to-primitive-symbol.js @@ -423,10 +421,10 @@ built-ins/Date 39/707 (5.52%) prototype/no-date-value.js UTC/coercion-order.js coercion-order.js - proto-from-ctor-realm-one.js {unsupported: [Reflect]} - proto-from-ctor-realm-two.js {unsupported: [Reflect]} - proto-from-ctor-realm-zero.js {unsupported: [Reflect]} - subclassing.js {unsupported: [Reflect]} + proto-from-ctor-realm-one.js + proto-from-ctor-realm-two.js + proto-from-ctor-realm-zero.js + subclassing.js value-get-symbol-to-prim-err.js value-symbol-to-prim-err.js value-symbol-to-prim-invocation.js @@ -462,7 +460,7 @@ built-ins/Error 5/42 (11.9%) prototype/toString/invalid-receiver.js prototype/no-error-data.js prototype/S15.11.4_A2.js - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js built-ins/eval 3/9 (33.33%) length-non-configurable.js @@ -496,18 +494,18 @@ built-ins/Function 186/505 (36.83%) prototype/apply/S15.3.4.3_A7_T7.js non-interpreted prototype/apply/this-not-callable-realm.js prototype/bind/BoundFunction_restricted-properties.js - prototype/bind/get-fn-realm.js {unsupported: [Reflect]} - prototype/bind/get-fn-realm-recursive.js {unsupported: [Reflect]} - prototype/bind/instance-construct-newtarget-boundtarget.js {unsupported: [Reflect, new.target]} - prototype/bind/instance-construct-newtarget-boundtarget-bound.js {unsupported: [Reflect, new.target]} + prototype/bind/get-fn-realm.js + prototype/bind/get-fn-realm-recursive.js + prototype/bind/instance-construct-newtarget-boundtarget.js {unsupported: [new.target]} + prototype/bind/instance-construct-newtarget-boundtarget-bound.js {unsupported: [new.target]} prototype/bind/instance-construct-newtarget-self-new.js {unsupported: [new.target]} - prototype/bind/instance-construct-newtarget-self-reflect.js {unsupported: [Reflect, new.target]} + prototype/bind/instance-construct-newtarget-self-reflect.js {unsupported: [new.target]} prototype/bind/instance-length-exceeds-int32.js prototype/bind/instance-length-tointeger.js prototype/bind/instance-name.js prototype/bind/instance-name-chained.js prototype/bind/instance-name-error.js - prototype/bind/proto-from-ctor-realm.js {unsupported: [Reflect]} + prototype/bind/proto-from-ctor-realm.js prototype/call/15.3.4.4-1-s.js strict prototype/call/15.3.4.4-2-s.js strict prototype/call/15.3.4.4-3-s.js strict @@ -553,7 +551,7 @@ built-ins/Function 186/505 (36.83%) prototype/toString/AsyncFunction.js {unsupported: [async-functions]} prototype/toString/AsyncGenerator.js {unsupported: [async-iteration]} prototype/toString/bound-function.js - prototype/toString/built-in-function-object.js {unsupported: [Reflect]} + prototype/toString/built-in-function-object.js prototype/toString/class-declaration-complex-heritage.js prototype/toString/class-declaration-explicit-ctor.js prototype/toString/class-declaration-implicit-ctor.js @@ -647,8 +645,8 @@ built-ins/Function 186/505 (36.83%) call-bind-this-realm-undef.js call-bind-this-realm-value.js private-identifiers-not-empty.js {unsupported: [class-fields-private]} - proto-from-ctor-realm.js {unsupported: [Reflect]} - proto-from-ctor-realm-prototype.js {unsupported: [Reflect]} + proto-from-ctor-realm.js + proto-from-ctor-realm-prototype.js StrictFunction_restricted-properties.js strict ~built-ins/GeneratorFunction @@ -696,8 +694,8 @@ built-ins/isNaN 8/16 (50.0%) ~built-ins/IteratorPrototype -built-ins/JSON 35/140 (25.0%) - parse/builtin.js {unsupported: [Reflect.construct]} +built-ins/JSON 36/140 (25.71%) + parse/builtin.js parse/revived-proxy.js {unsupported: [Proxy]} parse/revived-proxy-revoked.js {unsupported: [Proxy]} parse/reviver-array-define-prop-err.js {unsupported: [Proxy]} @@ -713,7 +711,7 @@ built-ins/JSON 35/140 (25.0%) parse/reviver-object-non-configurable-prop-delete.js strict parse/reviver-object-own-keys-err.js {unsupported: [Proxy]} parse/text-negative-zero.js - stringify/builtin.js {unsupported: [Reflect.construct]} + stringify/builtin.js stringify/replacer-array-abrupt.js {unsupported: [Proxy]} stringify/replacer-array-number.js stringify/replacer-array-proxy.js {unsupported: [Proxy]} @@ -736,7 +734,7 @@ built-ins/JSON 35/140 (25.0%) built-ins/Map 6/145 (4.14%) Symbol.species 4/4 (100.0%) iterator-is-undefined-throws.js - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js built-ins/MapIteratorPrototype 0/11 (0.0%) @@ -749,17 +747,17 @@ built-ins/NativeErrors 35/108 (32.41%) AggregateError/prototype 6/6 (100.0%) AggregateError 17/17 (100.0%) EvalError/prototype/not-error-object.js - EvalError/proto-from-ctor-realm.js {unsupported: [Reflect]} + EvalError/proto-from-ctor-realm.js RangeError/prototype/not-error-object.js - RangeError/proto-from-ctor-realm.js {unsupported: [Reflect]} + RangeError/proto-from-ctor-realm.js ReferenceError/prototype/not-error-object.js - ReferenceError/proto-from-ctor-realm.js {unsupported: [Reflect]} + ReferenceError/proto-from-ctor-realm.js SyntaxError/prototype/not-error-object.js - SyntaxError/proto-from-ctor-realm.js {unsupported: [Reflect]} + SyntaxError/proto-from-ctor-realm.js TypeError/prototype/not-error-object.js - TypeError/proto-from-ctor-realm.js {unsupported: [Reflect]} + TypeError/proto-from-ctor-realm.js URIError/prototype/not-error-object.js - URIError/proto-from-ctor-realm.js {unsupported: [Reflect]} + URIError/proto-from-ctor-realm.js built-ins/Number 9/283 (3.18%) prototype/toExponential/return-abrupt-tointeger-fractiondigits.js @@ -767,12 +765,12 @@ built-ins/Number 9/283 (3.18%) prototype/toExponential/undefined-fractiondigits.js prototype/toLocaleString/length.js prototype/toPrecision/nan.js - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js S9.3.1_A2_U180E.js {unsupported: [u180e]} S9.3.1_A3_T1_U180E.js {unsupported: [u180e]} S9.3.1_A3_T2_U180E.js {unsupported: [u180e]} -built-ins/Object 127/3150 (4.03%) +built-ins/Object 122/3150 (3.87%) assign/source-own-prop-desc-missing.js {unsupported: [Proxy]} assign/source-own-prop-error.js {unsupported: [Proxy]} assign/source-own-prop-keys-error.js {unsupported: [Proxy]} @@ -816,7 +814,7 @@ built-ins/Object 127/3150 (4.03%) entries/observable-operations.js {unsupported: [Proxy]} entries/order-after-define-property.js freeze/abrupt-completion.js {unsupported: [Proxy]} - freeze/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy, Reflect]} + freeze/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} freeze/throws-when-false.js fromEntries/evaluation-order.js fromEntries/iterator-closed-for-null-entry.js @@ -831,7 +829,7 @@ built-ins/Object 127/3150 (4.03%) fromEntries/to-property-key.js fromEntries/uses-keys-not-iterator.js getOwnPropertyDescriptors/observable-operations.js {unsupported: [Proxy]} - getOwnPropertyDescriptors/order-after-define-property.js {unsupported: [Reflect]} + getOwnPropertyDescriptors/order-after-define-property.js getOwnPropertyDescriptors/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} getOwnPropertyDescriptors/proxy-undefined-descriptor.js {unsupported: [Proxy]} getOwnPropertyDescriptor/15.2.3.3-4-212.js @@ -848,12 +846,9 @@ built-ins/Object 127/3150 (4.03%) getOwnPropertySymbols/proxy-invariant-duplicate-string-entry.js {unsupported: [Proxy]} getOwnPropertySymbols/proxy-invariant-not-extensible-absent-string-key.js {unsupported: [Proxy]} getOwnPropertySymbols/proxy-invariant-not-extensible-extra-string-key.js {unsupported: [Proxy]} - internals/DefineOwnProperty/consistent-value-function-arguments.js - internals/DefineOwnProperty/consistent-value-function-caller.js internals/DefineOwnProperty/consistent-value-regexp-dollar1.js - internals/DefineOwnProperty/consistent-writable-regexp-dollar1.js - isFrozen/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy, Reflect]} - isSealed/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy, Reflect]} + isFrozen/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} + isSealed/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} keys/order-after-define-property.js {unsupported: [Proxy]} keys/property-traps-order-with-proxied-array.js {unsupported: [Proxy]} keys/proxy-keys.js @@ -867,7 +862,7 @@ built-ins/Object 127/3150 (4.03%) prototype/hasOwnProperty/symbol_property_valueOf.js prototype/hasOwnProperty/topropertykey_before_toobject.js prototype/isPrototypeOf/arg-is-proxy.js {unsupported: [Proxy]} - prototype/isPrototypeOf/builtin.js {unsupported: [Reflect.construct]} + prototype/isPrototypeOf/builtin.js prototype/isPrototypeOf/null-this-and-primitive-arg-returns-false.js prototype/isPrototypeOf/undefined-this-and-primitive-arg-returns-false.js prototype/propertyIsEnumerable/symbol_property_toPrimitive.js @@ -890,16 +885,14 @@ built-ins/Object 127/3150 (4.03%) prototype/toString/symbol-tag-str.js prototype/valueOf/S15.2.4.4_A14.js prototype/valueOf/S15.2.4.4_A15.js - prototype/setPrototypeOf-with-different-values.js {unsupported: [Reflect.setPrototypeOf]} - prototype/setPrototypeOf-with-same-value.js {unsupported: [Reflect.setPrototypeOf]} seal/abrupt-completion.js {unsupported: [Proxy]} - seal/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy, Reflect]} + seal/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} seal/throws-when-false.js setPrototypeOf/set-error.js {unsupported: [Proxy]} values/observable-operations.js {unsupported: [Proxy]} values/order-after-define-property.js - proto-from-ctor-realm.js {unsupported: [Reflect]} - subclass-object-arg.js {unsupported: [Reflect.construct, Reflect, class]} + proto-from-ctor-realm.js + subclass-object-arg.js {unsupported: [class]} built-ins/parseFloat 3/58 (5.17%) name.js @@ -911,7 +904,7 @@ built-ins/parseInt 3/60 (5.0%) S15.1.2.2_A2_T10_U180E.js {unsupported: [u180e]} S15.1.2.2_A9.2.js -built-ins/Promise 405/599 (67.61%) +built-ins/Promise 404/599 (67.45%) allSettled/capability-resolve-throws-reject.js {unsupported: [async]} allSettled/ctx-ctor.js {unsupported: [class]} allSettled/does-not-invoke-array-setters.js {unsupported: [async]} @@ -1121,15 +1114,15 @@ built-ins/Promise 405/599 (67.61%) any/species-get-error.js {unsupported: [Symbol.species]} prototype/catch/S25.4.5.1_A3.1_T1.js {unsupported: [async]} prototype/catch/S25.4.5.1_A3.1_T2.js {unsupported: [async]} - prototype/finally/invokes-then-with-function.js {unsupported: [Reflect.construct]} + prototype/finally/invokes-then-with-function.js prototype/finally/rejected-observable-then-calls.js {unsupported: [async]} - prototype/finally/rejected-observable-then-calls-argument.js {unsupported: [Reflect.construct, async]} + prototype/finally/rejected-observable-then-calls-argument.js {unsupported: [async]} prototype/finally/rejected-observable-then-calls-PromiseResolve.js {unsupported: [async]} prototype/finally/rejection-reason-no-fulfill.js {unsupported: [async]} prototype/finally/rejection-reason-override-with-throw.js {unsupported: [async]} prototype/finally/resolution-value-no-override.js {unsupported: [async]} prototype/finally/resolved-observable-then-calls.js {unsupported: [async]} - prototype/finally/resolved-observable-then-calls-argument.js {unsupported: [Reflect.construct, async]} + prototype/finally/resolved-observable-then-calls-argument.js {unsupported: [async]} prototype/finally/resolved-observable-then-calls-PromiseResolve.js {unsupported: [async]} prototype/finally/species-constructor.js {unsupported: [async]} prototype/finally/subclass-reject-count.js {unsupported: [async]} @@ -1283,14 +1276,13 @@ built-ins/Promise 405/599 (67.61%) resolve/S25.Promise_resolve_foreign_thenable_1.js {unsupported: [async]} resolve/S25.Promise_resolve_foreign_thenable_2.js {unsupported: [async]} Symbol.species 5/5 (100.0%) - create-resolving-functions-reject.js {unsupported: [Reflect.construct, async]} - create-resolving-functions-resolve.js {unsupported: [Reflect.construct, async]} + create-resolving-functions-reject.js {unsupported: [async]} + create-resolving-functions-resolve.js {unsupported: [async]} exception-after-resolve-in-executor.js {unsupported: [async]} exception-after-resolve-in-thenable-job.js {unsupported: [async]} - executor-function-nonconstructor.js {unsupported: [Reflect.construct]} - get-prototype-abrupt.js {unsupported: [Reflect.construct, Reflect]} - get-prototype-abrupt-executor-not-callable.js {unsupported: [Reflect.construct, Reflect]} - proto-from-ctor-realm.js {unsupported: [Reflect]} + executor-function-nonconstructor.js + get-prototype-abrupt.js + proto-from-ctor-realm.js reject-ignored-via-abrupt.js {unsupported: [async]} reject-ignored-via-fn-deferred.js {unsupported: [async]} reject-ignored-via-fn-immed.js {unsupported: [async]} @@ -1316,7 +1308,36 @@ built-ins/Promise 405/599 (67.61%) ~built-ins/Proxy -~built-ins/Reflect +built-ins/Reflect 29/139 (20.86%) + construct/newtarget-is-not-constructor-throws.js + construct/return-with-newtarget-argument.js + defineProperty/return-abrupt-from-property-key.js + defineProperty/return-abrupt-from-result.js {unsupported: [Proxy]} + deleteProperty/return-abrupt-from-result.js {unsupported: [Proxy]} + deleteProperty/return-boolean.js strict + getOwnPropertyDescriptor/return-abrupt-from-result.js {unsupported: [Proxy]} + getPrototypeOf/return-abrupt-from-result.js {unsupported: [Proxy]} + get/return-value-from-receiver.js + has/return-abrupt-from-result.js {unsupported: [Proxy]} + isExtensible/return-abrupt-from-result.js {unsupported: [Proxy]} + ownKeys/order-after-define-property.js + ownKeys/return-abrupt-from-result.js {unsupported: [Proxy]} + ownKeys/return-on-corresponding-order-large-index.js {unsupported: [computed-property-names]} + preventExtensions/return-abrupt-from-result.js {unsupported: [Proxy]} + preventExtensions/return-boolean-from-proxy-object.js {unsupported: [Proxy]} + setPrototypeOf/return-abrupt-from-result.js {unsupported: [Proxy]} + set/call-prototype-property-set.js + set/creates-a-data-descriptor.js + set/different-property-descriptors.js + set/receiver-is-not-object.js + set/return-abrupt-from-property-key.js + set/return-abrupt-from-result.js + set/return-false-if-receiver-is-not-writable.js + set/return-false-if-target-is-not-writable.js + set/set-value-on-accessor-descriptor-with-receiver.js + set/set-value-on-data-descriptor.js + set/symbol-property.js + Symbol.toStringTag.js built-ins/RegExp 897/1464 (61.27%) CharacterClassEscapes 24/24 (100.0%) @@ -1538,7 +1559,7 @@ built-ins/RegExp 897/1464 (61.27%) from-regexp-like-get-flags-err.js from-regexp-like-get-source-err.js from-regexp-like-short-circuit.js - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js quantifier-integer-limit.js S15.10.1_A1_T13.js S15.10.1_A1_T14.js @@ -1555,7 +1576,7 @@ built-ins/RegExp 897/1464 (61.27%) built-ins/Set 5/188 (2.66%) Symbol.species 4/4 (100.0%) - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js built-ins/SetIteratorPrototype 0/11 (0.0%) @@ -1624,11 +1645,11 @@ built-ins/String 120/1114 (10.77%) prototype/trimStart/this-value-object-valueof-returns-object-err.js prototype/trim/u180e.js {unsupported: [u180e]} prototype/valueOf/non-generic-realm.js - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js built-ins/StringIteratorPrototype 0/7 (0.0%) -built-ins/Symbol 29/85 (34.12%) +built-ins/Symbol 28/85 (32.94%) asyncIterator/prop-desc.js for/cross-realm.js hasInstance/cross-realm.js @@ -1653,7 +1674,6 @@ built-ins/Symbol 29/85 (34.12%) toPrimitive/cross-realm.js toStringTag/cross-realm.js unscopables/cross-realm.js - is-constructor.js {unsupported: [Reflect.construct]} built-ins/ThrowTypeError 7/13 (53.85%) extensible.js @@ -1741,7 +1761,7 @@ built-ins/TypedArray 992/1070 (92.71%) prototype/every/callbackfn-not-called-on-empty.js prototype/every/callbackfn-return-does-not-change-instance.js prototype/every/callbackfn-returns-abrupt.js - prototype/every/callbackfn-set-value-during-interaction.js {unsupported: [Reflect.set]} + prototype/every/callbackfn-set-value-during-interaction.js prototype/every/callbackfn-this.js prototype/every/detached-buffer.js prototype/every/get-length-uses-internal-arraylength.js @@ -1789,7 +1809,7 @@ built-ins/TypedArray 992/1070 (92.71%) prototype/filter/callbackfn-not-called-on-empty.js prototype/filter/callbackfn-return-does-not-change-instance.js prototype/filter/callbackfn-returns-abrupt.js - prototype/filter/callbackfn-set-value-during-iteration.js {unsupported: [Reflect.set]} + prototype/filter/callbackfn-set-value-during-iteration.js prototype/filter/callbackfn-this.js prototype/filter/detached-buffer.js prototype/filter/invoked-as-func.js @@ -1864,7 +1884,7 @@ built-ins/TypedArray 992/1070 (92.71%) prototype/forEach/callbackfn-not-called-on-empty.js prototype/forEach/callbackfn-return-does-not-change-instance.js prototype/forEach/callbackfn-returns-abrupt.js - prototype/forEach/callbackfn-set-value-during-interaction.js {unsupported: [Reflect.set]} + prototype/forEach/callbackfn-set-value-during-interaction.js prototype/forEach/callbackfn-this.js prototype/forEach/detached-buffer.js prototype/forEach/invoked-as-func.js @@ -1972,7 +1992,7 @@ built-ins/TypedArray 992/1070 (92.71%) prototype/map/callbackfn-return-does-not-change-instance.js prototype/map/callbackfn-return-does-not-copy-non-integer-properties.js prototype/map/callbackfn-returns-abrupt.js - prototype/map/callbackfn-set-value-during-interaction.js {unsupported: [Reflect.set]} + prototype/map/callbackfn-set-value-during-interaction.js prototype/map/callbackfn-this.js prototype/map/detached-buffer.js prototype/map/invoked-as-func.js @@ -2009,7 +2029,7 @@ built-ins/TypedArray 992/1070 (92.71%) prototype/reduceRight/callbackfn-not-called-on-empty.js prototype/reduceRight/callbackfn-return-does-not-change-instance.js prototype/reduceRight/callbackfn-returns-abrupt.js - prototype/reduceRight/callbackfn-set-value-during-iteration.js {unsupported: [Reflect.set]} + prototype/reduceRight/callbackfn-set-value-during-iteration.js prototype/reduceRight/callbackfn-this.js prototype/reduceRight/detached-buffer.js prototype/reduceRight/empty-instance-return-initialvalue.js @@ -2032,7 +2052,7 @@ built-ins/TypedArray 992/1070 (92.71%) prototype/reduce/callbackfn-not-called-on-empty.js prototype/reduce/callbackfn-return-does-not-change-instance.js prototype/reduce/callbackfn-returns-abrupt.js - prototype/reduce/callbackfn-set-value-during-iteration.js {unsupported: [Reflect.set]} + prototype/reduce/callbackfn-set-value-during-iteration.js prototype/reduce/callbackfn-this.js prototype/reduce/detached-buffer.js prototype/reduce/empty-instance-return-initialvalue.js @@ -2137,7 +2157,7 @@ built-ins/TypedArray 992/1070 (92.71%) prototype/some/callbackfn-not-called-on-empty.js prototype/some/callbackfn-return-does-not-change-instance.js prototype/some/callbackfn-returns-abrupt.js - prototype/some/callbackfn-set-value-during-interaction.js {unsupported: [Reflect.set]} + prototype/some/callbackfn-set-value-during-interaction.js prototype/some/callbackfn-this.js prototype/some/detached-buffer.js prototype/some/get-length-uses-internal-arraylength.js @@ -2224,7 +2244,7 @@ built-ins/TypedArray 992/1070 (92.71%) name.js prototype.js -built-ins/TypedArrayConstructors 529/684 (77.34%) +built-ins/TypedArrayConstructors 518/684 (75.73%) BigInt64Array/prototype 4/4 (100.0%) BigInt64Array 7/7 (100.0%) BigUint64Array/prototype 4/4 (100.0%) @@ -2242,8 +2262,8 @@ built-ins/TypedArrayConstructors 529/684 (77.34%) ctors/buffer-arg/byteoffset-throws-from-modulo-element-size-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/byteoffset-to-number-detachbuffer.js ctors/buffer-arg/byteoffset-to-number-throws-sab.js {unsupported: [SharedArrayBuffer]} - ctors/buffer-arg/custom-proto-access-throws.js {unsupported: [Reflect]} - ctors/buffer-arg/custom-proto-access-throws-sab.js {unsupported: [SharedArrayBuffer, Reflect]} + ctors/buffer-arg/custom-proto-access-throws.js + ctors/buffer-arg/custom-proto-access-throws-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/defined-length-and-offset-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/defined-length-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/defined-negative-length.js @@ -2258,30 +2278,27 @@ built-ins/TypedArrayConstructors 529/684 (77.34%) ctors/buffer-arg/length-is-symbol-throws-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/length-to-number-detachbuffer.js ctors/buffer-arg/new-instance-extensibility-sab.js {unsupported: [SharedArrayBuffer]} - ctors/buffer-arg/proto-from-ctor-realm.js {unsupported: [Reflect]} - ctors/buffer-arg/proto-from-ctor-realm-sab.js {unsupported: [SharedArrayBuffer, Reflect]} + ctors/buffer-arg/proto-from-ctor-realm.js + ctors/buffer-arg/proto-from-ctor-realm-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/returns-new-instance-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/toindex-bytelength-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/toindex-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/typedarray-backed-by-sharedarraybuffer.js {unsupported: [SharedArrayBuffer]} - ctors/buffer-arg/use-custom-proto-if-object.js {unsupported: [Reflect]} - ctors/buffer-arg/use-custom-proto-if-object-sab.js {unsupported: [SharedArrayBuffer, Reflect]} - ctors/buffer-arg/use-default-proto-if-custom-proto-is-not-object.js + ctors/buffer-arg/use-custom-proto-if-object.js + ctors/buffer-arg/use-custom-proto-if-object-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/use-default-proto-if-custom-proto-is-not-object-sab.js {unsupported: [SharedArrayBuffer]} - ctors/length-arg/custom-proto-access-throws.js {unsupported: [Reflect]} + ctors/length-arg/custom-proto-access-throws.js ctors/length-arg/is-infinity-throws-rangeerror.js ctors/length-arg/is-negative-integer-throws-rangeerror.js ctors/length-arg/is-symbol-throws.js - ctors/length-arg/proto-from-ctor-realm.js {unsupported: [Reflect]} + ctors/length-arg/proto-from-ctor-realm.js ctors/length-arg/toindex-length.js - ctors/length-arg/use-custom-proto-if-object.js {unsupported: [Reflect]} - ctors/length-arg/use-default-proto-if-custom-proto-is-not-object.js - ctors/no-args/custom-proto-access-throws.js {unsupported: [Reflect]} - ctors/no-args/proto-from-ctor-realm.js {unsupported: [Reflect]} - ctors/no-args/use-custom-proto-if-object.js {unsupported: [Reflect]} - ctors/no-args/use-default-proto-if-custom-proto-is-not-object.js + ctors/length-arg/use-custom-proto-if-object.js + ctors/no-args/custom-proto-access-throws.js + ctors/no-args/proto-from-ctor-realm.js + ctors/no-args/use-custom-proto-if-object.js ctors/object-arg/as-generator-iterable-returns.js - ctors/object-arg/custom-proto-access-throws.js {unsupported: [Reflect]} + ctors/object-arg/custom-proto-access-throws.js ctors/object-arg/iterating-throws.js ctors/object-arg/iterator-is-null-as-array-like.js ctors/object-arg/iterator-not-callable-throws.js @@ -2290,16 +2307,15 @@ built-ins/TypedArrayConstructors 529/684 (77.34%) ctors/object-arg/length-is-symbol-throws.js ctors/object-arg/length-throws.js ctors/object-arg/new-instance-extensibility.js - ctors/object-arg/proto-from-ctor-realm.js {unsupported: [Reflect]} + ctors/object-arg/proto-from-ctor-realm.js ctors/object-arg/returns.js ctors/object-arg/throws-from-property.js ctors/object-arg/throws-setting-obj-to-primitive.js ctors/object-arg/throws-setting-obj-to-primitive-typeerror.js ctors/object-arg/throws-setting-property.js ctors/object-arg/throws-setting-symbol-property.js - ctors/object-arg/use-custom-proto-if-object.js {unsupported: [Reflect]} - ctors/object-arg/use-default-proto-if-custom-proto-is-not-object.js - ctors/typedarray-arg/custom-proto-access-throws.js {unsupported: [Reflect]} + ctors/object-arg/use-custom-proto-if-object.js + ctors/typedarray-arg/custom-proto-access-throws.js ctors/typedarray-arg/detached-when-species-retrieved-different-type.js {unsupported: [Symbol.species]} ctors/typedarray-arg/detached-when-species-retrieved-same-type.js {unsupported: [Symbol.species]} ctors/typedarray-arg/other-ctor-buffer-ctor-access-throws.js @@ -2311,7 +2327,7 @@ built-ins/TypedArrayConstructors 529/684 (77.34%) ctors/typedarray-arg/other-ctor-buffer-ctor-species-null.js {unsupported: [Symbol.species]} ctors/typedarray-arg/other-ctor-buffer-ctor-species-prototype-throws.js {unsupported: [Symbol.species]} ctors/typedarray-arg/other-ctor-buffer-ctor-species-undefined.js {unsupported: [Symbol.species]} - ctors/typedarray-arg/proto-from-ctor-realm.js {unsupported: [Reflect]} + ctors/typedarray-arg/proto-from-ctor-realm.js ctors/typedarray-arg/same-ctor-buffer-ctor-access-throws.js ctors/typedarray-arg/same-ctor-buffer-ctor-species-custom.js {unsupported: [Symbol.species]} ctors/typedarray-arg/same-ctor-buffer-ctor-species-custom-proto-from-ctor-realm.js {unsupported: [Symbol.species]} @@ -2322,8 +2338,7 @@ built-ins/TypedArrayConstructors 529/684 (77.34%) ctors/typedarray-arg/same-ctor-buffer-ctor-species-undefined.js {unsupported: [Symbol.species]} ctors/typedarray-arg/same-ctor-buffer-ctor-value-not-obj-throws.js ctors/typedarray-arg/src-typedarray-big-throws.js - ctors/typedarray-arg/use-custom-proto-if-object.js {unsupported: [Reflect]} - ctors/typedarray-arg/use-default-proto-if-custom-proto-is-not-object.js + ctors/typedarray-arg/use-custom-proto-if-object.js Float32Array/prototype/not-typedarray-object.js Float32Array/prototype/proto.js Float64Array/prototype/not-typedarray-object.js @@ -2359,7 +2374,24 @@ built-ins/TypedArrayConstructors 529/684 (77.34%) Int8Array/prototype/not-typedarray-object.js Int8Array/prototype/proto.js internals/DefineOwnProperty/BigInt 20/20 (100.0%) - internals/DefineOwnProperty 22/22 (100.0%) + internals/DefineOwnProperty/conversion-operation.js + internals/DefineOwnProperty/conversion-operation-consistent-nan.js + internals/DefineOwnProperty/desc-value-throws.js + internals/DefineOwnProperty/detached-buffer.js + internals/DefineOwnProperty/detached-buffer-realm.js + internals/DefineOwnProperty/key-is-greater-than-last-index.js + internals/DefineOwnProperty/key-is-lower-than-zero.js + internals/DefineOwnProperty/key-is-minus-zero.js + internals/DefineOwnProperty/key-is-not-canonical-index.js + internals/DefineOwnProperty/key-is-not-integer.js + internals/DefineOwnProperty/key-is-numericindex.js + internals/DefineOwnProperty/key-is-numericindex-accessor-desc.js + internals/DefineOwnProperty/key-is-numericindex-desc-configurable.js + internals/DefineOwnProperty/key-is-numericindex-desc-not-enumerable.js + internals/DefineOwnProperty/key-is-numericindex-desc-not-writable.js + internals/DefineOwnProperty/non-extensible-redefine-key.js + internals/DefineOwnProperty/set-value.js + internals/DefineOwnProperty/tonumber-value-detached-buffer.js internals/Get/BigInt 14/14 (100.0%) internals/GetOwnProperty/BigInt 12/12 (100.0%) internals/GetOwnProperty/detached-buffer.js @@ -2382,22 +2414,34 @@ built-ins/TypedArrayConstructors 529/684 (77.34%) internals/Get/key-is-out-of-bounds.js internals/Get/key-is-symbol.js internals/HasProperty/BigInt 15/15 (100.0%) - internals/HasProperty 15/15 (100.0%) + internals/HasProperty/abrupt-from-ordinary-has-parent-hasproperty.js {unsupported: [Proxy]} + internals/HasProperty/detached-buffer.js + internals/HasProperty/detached-buffer-key-is-not-number.js + internals/HasProperty/detached-buffer-key-is-symbol.js + internals/HasProperty/detached-buffer-realm.js + internals/HasProperty/indexed-value.js + internals/HasProperty/infinity-with-detached-buffer.js non-strict + internals/HasProperty/inherited-property.js + internals/HasProperty/key-is-greater-than-last-index.js + internals/HasProperty/key-is-lower-than-zero.js + internals/HasProperty/key-is-minus-zero.js + internals/HasProperty/key-is-not-canonical-index.js + internals/HasProperty/key-is-not-integer.js internals/OwnPropertyKeys/BigInt 4/4 (100.0%) internals/OwnPropertyKeys 4/4 (100.0%) internals/Set/BigInt 23/23 (100.0%) internals/Set/detached-buffer.js - internals/Set/detached-buffer-key-is-not-numeric-index.js {unsupported: [Reflect]} - internals/Set/detached-buffer-key-is-symbol.js {unsupported: [Reflect]} + internals/Set/detached-buffer-key-is-not-numeric-index.js + internals/Set/detached-buffer-key-is-symbol.js internals/Set/detached-buffer-realm.js - internals/Set/indexed-value.js {unsupported: [Reflect]} - internals/Set/key-is-minus-zero.js {unsupported: [Reflect]} - internals/Set/key-is-not-canonical-index.js {unsupported: [Reflect]} - internals/Set/key-is-not-integer.js {unsupported: [Reflect]} - internals/Set/key-is-not-numeric-index.js {unsupported: [Reflect]} - internals/Set/key-is-out-of-bounds.js {unsupported: [Reflect]} - internals/Set/key-is-symbol.js {unsupported: [Reflect]} - internals/Set/tonumber-value-detached-buffer.js {unsupported: [Reflect]} + internals/Set/indexed-value.js + internals/Set/key-is-minus-zero.js + internals/Set/key-is-not-canonical-index.js + internals/Set/key-is-not-integer.js + internals/Set/key-is-not-numeric-index.js + internals/Set/key-is-out-of-bounds.js + internals/Set/key-is-symbol.js + internals/Set/tonumber-value-detached-buffer.js internals/Set/tonumber-value-throws.js of/BigInt 12/12 (100.0%) of/argument-number-value-throws.js @@ -2450,10 +2494,10 @@ built-ins/TypedArrayConstructors 529/684 (77.34%) built-ins/undefined 0/8 (0.0%) built-ins/WeakMap 1/88 (1.14%) - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js built-ins/WeakSet 1/75 (1.33%) - proto-from-ctor-realm.js {unsupported: [Reflect]} + proto-from-ctor-realm.js language/arguments-object 191/260 (73.46%) mapped/mapped-arguments-nonconfigurable-3.js non-strict @@ -4881,8 +4925,7 @@ language/expressions/template-literal 2/57 (3.51%) language/expressions/this 0/6 (0.0%) -language/expressions/typeof 2/16 (12.5%) - built-in-ordinary-objects-no-call.js +language/expressions/typeof 1/16 (6.25%) proxy.js {unsupported: [Proxy]} language/expressions/unary-minus 1/14 (7.14%) From e405429c1d735017377dcf9c90b09a21f134fbbc Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Wed, 7 Jun 2023 07:57:13 +0200 Subject: [PATCH 02/24] fix broken backward compatibility (use the same approach as for all the other symbol based methods) --- examples/Matrix.java | 20 ------------------- src/org/mozilla/javascript/Scriptable.java | 6 ------ .../mozilla/javascript/ScriptableObject.java | 5 +++-- src/org/mozilla/javascript/Undefined.java | 8 -------- .../javascript/tests/IterableTest.java | 10 ---------- 5 files changed, 3 insertions(+), 46 deletions(-) diff --git a/examples/Matrix.java b/examples/Matrix.java index baf0810c8d..cea70015df 100644 --- a/examples/Matrix.java +++ b/examples/Matrix.java @@ -9,7 +9,6 @@ import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Symbol; /** * Matrix: An example host object class that implements the Scriptable interface. @@ -94,17 +93,6 @@ public boolean has(int index, Scriptable start) { return true; } - /** - * Defines all numeric properties by returning true. - * - * @param key the key of the property - * @param start the object where lookup began - */ - @Override - public boolean has(Symbol key, Scriptable start) { - return true; - } - /** * Get the named property. * @@ -184,14 +172,6 @@ public void delete(String id) {} @Override public void delete(int index) {} - /** - * Remove an symbol property. - * - *

This method shouldn't even be called since we define all properties as PERMANENT. - */ - @Override - public void delete(Symbol key) {} - /** Get prototype. */ @Override public Scriptable getPrototype() { diff --git a/src/org/mozilla/javascript/Scriptable.java b/src/org/mozilla/javascript/Scriptable.java index 26b7f7f240..cc37012fb3 100644 --- a/src/org/mozilla/javascript/Scriptable.java +++ b/src/org/mozilla/javascript/Scriptable.java @@ -126,9 +126,6 @@ public interface Scriptable { */ public boolean has(int index, Scriptable start); - /** A version of delete for properties with Symbol keys. */ - public boolean has(Symbol key, Scriptable start); - /** * Sets a named property in this object. * @@ -231,9 +228,6 @@ public interface Scriptable { */ public void delete(int index); - /** A version of delete for properties with Symbol keys. */ - public void delete(Symbol key); - /** * Get the prototype of the object. * diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 33f2604387..f404aeaa1e 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -2297,8 +2297,9 @@ public static boolean deleteProperty(Scriptable obj, int index) { public static boolean deleteProperty(Scriptable obj, Symbol key) { Scriptable base = getBase(obj, key); if (base == null) return true; - base.delete(key); - return !base.has(key, obj); + SymbolScriptable scriptable = ensureSymbolScriptable(base); + scriptable.delete(key); + return !scriptable.has(key, obj); } /** diff --git a/src/org/mozilla/javascript/Undefined.java b/src/org/mozilla/javascript/Undefined.java index c9c79266b3..3b30bac193 100644 --- a/src/org/mozilla/javascript/Undefined.java +++ b/src/org/mozilla/javascript/Undefined.java @@ -79,11 +79,6 @@ public boolean has(int index, Scriptable start) { return false; } - @Override - public boolean has(Symbol key, Scriptable start) { - return false; - } - @Override public void put(String name, Scriptable start, Object value) {} @@ -96,9 +91,6 @@ public void delete(String name) {} @Override public void delete(int index) {} - @Override - public void delete(Symbol key) {} - @Override public Scriptable getPrototype() { return null; diff --git a/testsrc/org/mozilla/javascript/tests/IterableTest.java b/testsrc/org/mozilla/javascript/tests/IterableTest.java index e4a46fc776..5e3d8ad4bd 100644 --- a/testsrc/org/mozilla/javascript/tests/IterableTest.java +++ b/testsrc/org/mozilla/javascript/tests/IterableTest.java @@ -239,11 +239,6 @@ public boolean has(int index, Scriptable start) { throw new UnsupportedOperationException("Not supported yet."); } - @Override - public boolean has(Symbol key, Scriptable start) { - throw new UnsupportedOperationException("Not supported yet."); - } - @Override public void put(String name, Scriptable start, Object value) { throw new UnsupportedOperationException("Not supported yet."); @@ -264,11 +259,6 @@ public void delete(int index) { throw new UnsupportedOperationException("Not supported yet."); } - @Override - public void delete(Symbol key) { - throw new UnsupportedOperationException("Not supported yet."); - } - @Override public void setPrototype(Scriptable prototype) { throw new UnsupportedOperationException("Not supported yet."); From f90fb11e5569c8916d9958c3a2f3f7ee1e15f74a Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Wed, 7 Jun 2023 15:16:39 +0200 Subject: [PATCH 03/24] fix typos --- src/org/mozilla/javascript/NativeReflect.java | 2 +- testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index cc34b8eaae..459a5ae2e9 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -200,7 +200,7 @@ private static boolean js_defineProperty(Context cx, Object[] args) { if (args.length < 3) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", - "Reflect.apply", + "Reflect.defineProperty", "3", Integer.toString(args.length)); } diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java index 5cdeb5dab8..c740372d79 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java @@ -182,7 +182,7 @@ public void ownKeysEmptyObj() { @Test public void ownKeysDeleteObj() { - String js = "var o = { d: 42 };\n" + "delete o.d;\n" + "'' + Reflect.ownKeys({}).length"; + String js = "var o = { d: 42 };\n" + "delete o.d;\n" + "'' + Reflect.ownKeys(o).length"; testString("0", js); } From 010bd10d4aa758f28ae00adc27344814b2b417b0 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Sun, 13 Aug 2023 18:25:35 +0200 Subject: [PATCH 04/24] working on proxy support (wip) --- src/org/mozilla/javascript/NativeProxy.java | 453 +++++++++++++ src/org/mozilla/javascript/NativeReflect.java | 73 +- src/org/mozilla/javascript/ScriptRuntime.java | 1 + .../javascript/tests/Test262SuiteTest.java | 1 - .../javascript/tests/es6/NativeProxyTest.java | 631 ++++++++++++++++++ .../tests/es6/NativeReflectTest.java | 62 +- testsrc/test262.properties | 3 +- 7 files changed, 1184 insertions(+), 40 deletions(-) create mode 100644 src/org/mozilla/javascript/NativeProxy.java create mode 100644 testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java new file mode 100644 index 0000000000..ac2aa9eecd --- /dev/null +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -0,0 +1,453 @@ +/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript; + +/** + * This class implements the Proxy object. + * + * @author Ronald Brill + */ +final class NativeProxy extends IdScriptableObject { + private static final long serialVersionUID = 6676871870513494844L; + + private static final Object PROXY_TAG = "Proxy"; + + private static final String TRAP_GET_PROTOTYPE_OF = "getPrototypeOf"; + private static final String TRAP_SET_PROTOTYPE_OF = "setPrototypeOf"; + private static final String TRAP_IS_EXTENSIBLE = "isExtensible"; + private static final String TRAP_PREVENT_EXTENSIONS = "preventExtensions"; + private static final String TRAP_GET_OWN_PROPERTY_DESCRIPTOR = "getOwnPropertyDescriptor"; + private static final String TRAP_DEFINE_PROPERTY = "defineProperty"; + private static final String TRAP_HAS = "has"; + private static final String TRAP_GET = "get"; + private static final String TRAP_SET = "set"; + private static final String TRAP_DELETE_PROPERTY = "deleteProperty"; + private static final String TRAP_OWN_KEYS = "ownKeys"; + private static final String TRAP_APPLY = "apply"; + private static final String TRAP_CONSTRUCT = "construct"; + + private final boolean isCtor; + private ScriptableObject target; + private Scriptable handler; + private final String typeOf; + + private static final class Revoker implements Callable { + private NativeProxy revocableProxy = null; + + public Revoker(NativeProxy proxy){ + revocableProxy = proxy; + } + + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) { + if (revocableProxy != null) { + revocableProxy.handler = null; + revocableProxy.target = null; + revocableProxy = null; + } + return Undefined.instance; + } + } + + static void init(Scriptable scope, boolean sealed) { + NativeProxy constructor = new NativeProxy(null, null, true); + IdFunctionObject ctor = constructor.exportAsJSClass(MAX_PROTOTYPE_ID, scope, false); + ctor.setPrototypeProperty(null); + } + + private NativeProxy(ScriptableObject target, Scriptable handler, boolean isCtor) { + this.target = target; + this.handler = handler; + this.isCtor = isCtor; + + if (target == null || !(target instanceof Callable)) { + typeOf = super.getTypeOf(); + } + else { + typeOf = target.getTypeOf(); + } + } + + @Override + public String getClassName() { + if (isCtor) { + return "Proxy"; + } + + assertNotRevoked(); + return target.getClassName(); + } + + @Override + protected void initPrototypeId(int id) { + if (id <= MAX_PROTOTYPE_ID) { + String name; + int arity; + switch (id) { + case Id_constructor: + arity = 2; + name = "constructor"; + break; + + default: + throw new IllegalStateException(String.valueOf(id)); + } + initPrototypeMethod(PROXY_TAG, id, name, arity); + } + } + + @Override + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + if (!f.hasTag(PROXY_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + + int methodId = f.methodId(); + switch (methodId) { + case Id_constructor: + if (thisObj != null && cx.getLanguageVersion() >= Context.VERSION_ES6) { + throw ScriptRuntime.typeErrorById("msg.only.from.new", getClassName()); + } + return js_constructor(cx, scope, args); + + case ConstructorId_revocable: + return js_revocable(cx, scope, args); + + default: + throw new IllegalStateException(String.valueOf(methodId)); + } + } + + @Override + public boolean has(String name, Scriptable start) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_HAS); + if (trap != null) { + return (boolean) callTrap(trap, new Object[] {target, name}); + } + + return ScriptableObject.hasProperty(target, name); + } + + @Override + public boolean has(int index, Scriptable start) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_HAS); + if (trap != null) { + return (boolean) callTrap(trap, new Object[] {target, index}); + } + + return ScriptableObject.hasProperty(target, index); + } + + @Override + public boolean has(Symbol key, Scriptable start) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_HAS); + if (trap != null) { + return (boolean) callTrap(trap, new Object[] {target, key}); + } + + return ScriptableObject.hasProperty(target, key); + } + + @Override + public Object[] getIds() { + if (isCtor) { + return super.getIds(); + } + + assertNotRevoked(); + + Callable trap = getTrap(TRAP_OWN_KEYS); + if (trap != null) { + Object res = callTrap(trap, new Object[] {target}); + if (res instanceof NativeArray) { + return ((NativeArray) res).toArray(); + } + return (Object[]) res; + } + + return target.getIds(); + } + + @Override + public Object[] getAllIds() { + assertNotRevoked(); + + return target.getAllIds(); + } + + @Override + public Object get(String name, Scriptable start) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_GET); + if (trap != null) { + return callTrap(trap, new Object[] {target, name}); + } + + return ScriptRuntime.getObjectProp(target, name, Context.getContext()); + } + + @Override + public Object get(int index, Scriptable start) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_GET); + if (trap != null) { + return callTrap(trap, new Object[] {target, index}); + } + return ScriptRuntime.getObjectIndex(target, index, Context.getContext()); + } + + @Override + public Object get(Symbol key, Scriptable start) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_GET); + if (trap != null) { + return callTrap(trap, new Object[] {target, key}); + } + + if (start == this) { + start = target; + } + SymbolScriptable symbolScriptableTarget = ensureSymbolScriptable(target); + return symbolScriptableTarget.get(key, start); + } + + @Override + public void put(String name, Scriptable start, Object value) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_SET); + if (trap != null) { + ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); + callTrap(trap, new Object[] {target, name, desc}); + } + + ScriptableObject.putProperty(target, name, value); + } + + @Override + public void put(int index, Scriptable start, Object value) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_SET); + if (trap != null) { + ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); + callTrap(trap, new Object[] {target, index, desc}); + } + + ScriptableObject.putProperty(target, index, value); + } + + @Override + public void put(Symbol key, Scriptable start, Object value) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_SET); + if (trap != null) { + ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); + callTrap(trap, new Object[] {target, key, desc}); + } + + if (start == this) { + start = target; + } + SymbolScriptable symbolScriptableTarget = ensureSymbolScriptable(target); + symbolScriptableTarget.put(key, start, value); + } + + @Override + public void delete(String name) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_DELETE_PROPERTY); + if (trap != null) { + callTrap(trap, new Object[] {target, name}); + } + + target.delete(name); + } + + @Override + public void delete(int index) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_DELETE_PROPERTY); + if (trap != null) { + callTrap(trap, new Object[] {target, index}); + } + + target.delete(index); + } + + @Override + public void delete(Symbol key) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_DELETE_PROPERTY); + if (trap != null) { + callTrap(trap, new Object[] {target, key}); + } + + SymbolScriptable symbolScriptableTarget = ensureSymbolScriptable(target); + symbolScriptableTarget.delete(key); + } + + @Override + protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_GET_OWN_PROPERTY_DESCRIPTOR); + if (trap != null) { + ScriptableObject proxiedDescriptor = (ScriptableObject) callTrap(trap, new Object[] {target, id}); + if (proxiedDescriptor != null) { + Object value = ScriptableObject.getProperty(proxiedDescriptor, "value"); + int attributes = applyDescriptorToAttributeBitset(DONTENUM | READONLY | PERMANENT, proxiedDescriptor); + + ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, attributes); + return desc; + } + // TODO + return null; + } + + if (ScriptRuntime.isSymbol(id)) { + return target.getOwnPropertyDescriptor(cx, id); + } + + return target.getOwnPropertyDescriptor(cx, ScriptRuntime.toString(id)); + } + + @Override + public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_DEFINE_PROPERTY); + if (trap != null) { + callTrap(trap, new Object[] {target, id, desc}); + } + + target.defineOwnProperty(cx, id, desc); + } + + @Override + public boolean isExtensible() { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_IS_EXTENSIBLE); + if (trap != null) { + callTrap(trap, new Object[] {target}); + } + + return target.isExtensible(); + } + + @Override + public void preventExtensions() { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_PREVENT_EXTENSIONS); + if (trap != null) { + callTrap(trap, new Object[] {target}); + } + + target.preventExtensions(); + } + + @Override + public String getTypeOf() { + return typeOf; + } + + private NativeProxy js_constructor(Context cx, Scriptable scope, Object[] args) { + if (args.length < 2) { + throw ScriptRuntime.typeErrorById( + "msg.method.missing.parameter", + "Proxy.ctor", + "2", + Integer.toString(args.length)); + } + Scriptable s = ScriptRuntime.toObject(cx, scope, args[0]); + ScriptableObject trgt = ensureScriptableObject(s); + + s = ScriptRuntime.toObject(cx, scope, args[1]); + ScriptableObject hndlr = ensureScriptableObject(s); + + NativeProxy proxy = new NativeProxy(trgt, hndlr, false); + proxy.setPrototype(ScriptableObject.getClassPrototype(scope, proxy.getClassName())); + proxy.setParentScope(scope); + return proxy; + } + + private NativeObject js_revocable(Context cx, Scriptable scope, Object[] args) { + NativeProxy proxy = js_constructor(cx, scope, args); + + NativeObject revocable = (NativeObject) cx.newObject(scope); + + revocable.put("proxy", revocable, proxy); + revocable.put("revoke", revocable, new Revoker(proxy)); + return revocable; + } + + private Callable getTrap(String trapName) { + Object handlerProp = ScriptableObject.getProperty(handler, trapName); + if (Scriptable.NOT_FOUND == handlerProp) { + return null; + } + if (handlerProp == null || Undefined.isUndefined(handlerProp)) { + return null; + } + if (!(handlerProp instanceof Callable)) { + throw ScriptRuntime.notFunctionError(handlerProp, trapName); + } + + return (Callable) handlerProp; + } + + private Object callTrap(Callable trap, Object[] args) { + return trap.call(Context.getCurrentContext(), handler, handler, args); + } + + private void assertNotRevoked() { + if (!isCtor && target == null) { + throw ScriptRuntime.typeError("Illegal operation attempted on a revoked proxy"); + } + } + + @Override + protected int findPrototypeId(String s) { + int id; + switch (s) { + case "constructor": + id = Id_constructor; + break; + + default: + id = 0; + break; + } + return id; + } + + @Override + protected void fillConstructorProperties(IdFunctionObject ctor) { + addIdFunctionProperty(ctor, PROXY_TAG, ConstructorId_revocable, "revocable", 2); + super.fillConstructorProperties(ctor); + } + + private static final int ConstructorId_revocable = -1, + Id_constructor = 1, + MAX_PROTOTYPE_ID = Id_constructor; +} diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index 459a5ae2e9..c4d5c32e37 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -192,6 +192,11 @@ private static Scriptable js_construct(Context cx, Scriptable scope, Object[] ar if (args.length < 2) { return ctor.construct(cx, scope, ScriptRuntime.emptyArgs); } + + if (args.length > 2 && !(args[2] instanceof Function)) { + throw ScriptRuntime.typeErrorById("msg.not.ctor", ScriptRuntime.typeof(args[2])); + } + Object[] callArgs = ScriptRuntime.getApplyArguments(cx, args[1]); return ctor.construct(cx, scope, callArgs); } @@ -205,11 +210,11 @@ private static boolean js_defineProperty(Context cx, Object[] args) { Integer.toString(args.length)); } - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); ScriptableObject desc = ScriptableObject.ensureScriptableObject(args[2]); try { - obj.defineOwnProperty(cx, args[1], desc); + target.defineOwnProperty(cx, args[1], desc); return true; } catch (EcmaError e) { return false; @@ -217,84 +222,84 @@ private static boolean js_defineProperty(Context cx, Object[] args) { } private static boolean js_deleteProperty(Object[] args) { - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); if (args.length > 1) { if (ScriptRuntime.isSymbol(args[1])) { - return ScriptableObject.deleteProperty(obj, (Symbol) args[1]); + return ScriptableObject.deleteProperty(target, (Symbol) args[1]); } - return ScriptableObject.deleteProperty(obj, ScriptRuntime.toString(args[1])); + return ScriptableObject.deleteProperty(target, ScriptRuntime.toString(args[1])); } return false; } private static Object js_get(Object[] args) { - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); if (args.length > 1) { if (ScriptRuntime.isSymbol(args[1])) { - Object prop = ScriptableObject.getProperty(obj, (Symbol) args[1]); + Object prop = ScriptableObject.getProperty(target, (Symbol) args[1]); return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; } if (args[1] instanceof Double) { - Object prop = ScriptableObject.getProperty(obj, ScriptRuntime.toIndex(args[1])); + Object prop = ScriptableObject.getProperty(target, ScriptRuntime.toIndex(args[1])); return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; } - Object prop = ScriptableObject.getProperty(obj, ScriptRuntime.toString(args[1])); + Object prop = ScriptableObject.getProperty(target, ScriptRuntime.toString(args[1])); return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; } return Undefined.SCRIPTABLE_UNDEFINED; } private static Scriptable js_getOwnPropertyDescriptor(Context cx, Object[] args) { - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); if (args.length > 1) { if (ScriptRuntime.isSymbol(args[1])) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, args[1]); + ScriptableObject desc = target.getOwnPropertyDescriptor(cx, args[1]); return desc == null ? Undefined.SCRIPTABLE_UNDEFINED : desc; } ScriptableObject desc = - obj.getOwnPropertyDescriptor(cx, ScriptRuntime.toString(args[1])); + target.getOwnPropertyDescriptor(cx, ScriptRuntime.toString(args[1])); return desc == null ? Undefined.SCRIPTABLE_UNDEFINED : desc; } return Undefined.SCRIPTABLE_UNDEFINED; } private static Scriptable js_getPrototypeOf(Object[] args) { - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); - return obj.getPrototype(); + return target.getPrototype(); } private static boolean js_has(Object[] args) { - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); if (args.length > 1) { if (ScriptRuntime.isSymbol(args[1])) { - return ScriptableObject.hasProperty(obj, (Symbol) args[1]); + return ScriptableObject.hasProperty(target, (Symbol) args[1]); } - return ScriptableObject.hasProperty(obj, ScriptRuntime.toString(args[1])); + return ScriptableObject.hasProperty(target, ScriptRuntime.toString(args[1])); } return false; } private static boolean js_isExtensible(Object[] args) { - ScriptableObject obj = checkTarget(args); - return obj.isExtensible(); + ScriptableObject target = checkTarget(args); + return target.isExtensible(); } private static Scriptable js_ownKeys(Context cx, Scriptable scope, Object[] args) { - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); final List strings = new ArrayList<>(); final List symbols = new ArrayList<>(); - for (Object o : obj.getIds(true, true)) { + for (Object o : target.getIds(true, true)) { if (o instanceof Symbol) { symbols.add(o); } else { @@ -310,26 +315,26 @@ private static Scriptable js_ownKeys(Context cx, Scriptable scope, Object[] args } private static boolean js_preventExtensions(Object[] args) { - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); - obj.preventExtensions(); + target.preventExtensions(); return true; } private static boolean js_set(Object[] args) { - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); if (args.length > 1) { if (ScriptRuntime.isSymbol(args[1])) { - obj.put((Symbol) args[1], obj, args[2]); + target.put((Symbol) args[1], target, args[2]); return true; } if (args[1] instanceof Double) { - obj.put(ScriptRuntime.toIndex(args[1]), obj, args[2]); + target.put(ScriptRuntime.toIndex(args[1]), target, args[2]); return true; } - obj.put(ScriptRuntime.toString(args[1]), obj, args[2]); + target.put(ScriptRuntime.toString(args[1]), target, args[2]); return true; } return false; @@ -344,18 +349,18 @@ private static boolean js_setPrototypeOf(Object[] args) { Integer.toString(args.length)); } - ScriptableObject obj = checkTarget(args); + ScriptableObject target = checkTarget(args); - if (obj.getPrototype() == args[1]) { + if (target.getPrototype() == args[1]) { return true; } - if (!obj.isExtensible()) { + if (!target.isExtensible()) { return false; } if (args[1] == null) { - obj.setPrototype(null); + target.setPrototype(null); return true; } @@ -364,20 +369,20 @@ private static boolean js_setPrototypeOf(Object[] args) { } ScriptableObject proto = ScriptableObject.ensureScriptableObject(args[1]); - if (obj.getPrototype() == proto) { + if (target.getPrototype() == proto) { return true; } // avoid cycles Scriptable p = proto; while (p != null) { - if (obj == p) { + if (target == p) { return false; } p = p.getPrototype(); } - obj.setPrototype(proto); + target.setPrototype(proto); return true; } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 1ad0fc8e16..72873ccd8d 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -287,6 +287,7 @@ public static ScriptableObject initSafeStandardObjects( NativeWeakSet.init(scope, sealed); NativeBigInt.init(scope, sealed); + NativeProxy.init(scope, sealed); NativeReflect.init(scope, sealed); } diff --git a/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java b/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java index c519350caa..6c6e9e4be7 100644 --- a/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java +++ b/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java @@ -90,7 +90,6 @@ public class Test262SuiteTest { Arrays.asList( "Atomics", "IsHTMLDDA", - "Proxy", "SharedArrayBuffer", "Symbol.species", "async-functions", diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java new file mode 100644 index 0000000000..f091b11d5d --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -0,0 +1,631 @@ +package org.mozilla.javascript.tests.es6; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.tests.Utils; + +public class NativeProxyTest { + + @Test + public void testToString() { + testString("function Proxy() { [native code for Proxy.Proxy, arity=2] }\n", "Proxy.toString()"); + + testString("[object Object]", "Object.prototype.toString.call(new Proxy({}, {}))"); + testString("[object Array]", "Object.prototype.toString.call(new Proxy([], {}))"); + testString("[object Array]", "Object.prototype.toString.call(new Proxy(new Proxy([], {}), {}))"); + } + + @Test + public void testToStringRevoke() { + String js = "var rev = Proxy.revocable(%s, {});\n" + + "rev.revoke();\n" + + "try {" + + " Object.prototype.toString.call(%s);\n" + + "} catch(e) {" + + " '' + e;" + + "}"; + + testString("TypeError: Illegal operation attempted on a revoked proxy", String.format(js, "{}", "rev.proxy")); + testString("TypeError: Illegal operation attempted on a revoked proxy", String.format(js, "[]", "rev.proxy")); + } + + @Test + public void prototype() { + testString("false", "'' + Object.hasOwnProperty.call(Proxy, 'prototype')"); + + testString("2", "'' + Proxy.length"); + + } + + @Test + public void ctorMissingArgs() { + testString("TypeError: Proxy.ctor: At least 2 arguments required, but only 0 passed", "try { new Proxy() } catch(e) { '' + e }"); + testString("TypeError: Proxy.ctor: At least 2 arguments required, but only 1 passed", "try { new Proxy({}) } catch(e) { '' + e }"); + + testString("TypeError: Cannot convert undefined to an object.", "try { new Proxy(undefined, {}) } catch(e) { '' + e }"); + testString("TypeError: Cannot convert null to an object.", "try { new Proxy(null, {}) } catch(e) { '' + e }"); + } + + @Test + public void ctorAsFunction() { + testString("TypeError: \"Constructor Proxy\" may only be invoked from a \"new\" expression.", "try { Proxy() } catch(e) { '' + e }"); + } + +// +// @Test +// public void applyWithoutHandler() { +// String js = "function sum(a, b) { return a + b; }\n" +// + "var proxy1 = new Proxy(sum, {});\n" +// + "proxy1(1, 2)"; +// testDouble(3.0, js); +// } +// +// @Test +// public void applyDetails() { +// String js = +// "var o = {};\n" +// + "var count = 0;\n" +// + "var results, args;\n" +// + "function fn() {\n" +// + " count++;\n" +// + " results = {\n" +// + " thisArg: this,\n" +// + " args: arguments\n" +// + " };\n" +// + "}\n" +// + "Reflect.apply(fn, o, ['arg1', 2, , null]);\n" +// + "'' + count " +// + "+ ' ' + (results.thisArg === o)" +// + "+ ' ' + results.args.length" +// + "+ ' ' + results.args[0]" +// + "+ ' ' + results.args[1]" +// + "+ ' ' + results.args[2]" +// + "+ ' ' + results.args[3]"; +// testString("1 true 4 arg1 2 undefined null", js); +// } +// +// @Test +// public void applyMissingArgs() { +// String js = +// "try {\n" +// + " Reflect.apply();\n" +// + "} catch(e) {\n" +// + " '' + e;\n" +// + "}"; +// testString( +// "TypeError: Reflect.apply: At least 3 arguments required, but only 0 passed", js); +// } +// +// @Test +// public void applyTargetNotFunction() { +// String js = +// "try {\n" +// + " Reflect.apply({}, undefined, [1.75]);\n" +// + "} catch(e) {\n" +// + " '' + e;\n" +// + "}"; +// testString("TypeError: [object Object] is not a function, it is object.", js); +// } +// +// @Test +// public void applyArgumentsListNotFunction() { +// String js = +// "var s1 = Symbol('1');" +// + "try {\n" +// + " Reflect.apply(Math.floor, undefined, s1);\n" +// + "} catch(e) {\n" +// + " '' + e;\n" +// + "}"; +// testString("TypeError: Expected argument of type object, but instead had type symbol", js); +// } +// +// @Test +// public void construct() { +// String js = +// "var d = Reflect.construct(Date, [1776, 6, 4]);\n" +// + "'' + (d instanceof Date) + ' ' + d.getFullYear();"; +// testString("true 1776", js); +// } +// +// @Test +// public void constructNoConstructorNumber() { +// String js = "try {\n" +// + " Reflect.construct(function() {}, [], 1);\n" +// + "} catch(e) {\n" +// + " '' + e;\n" +// + "}"; +// testString("TypeError: \"number\" is not a constructor.", js); +// } +// +// @Test +// public void constructNoConstructorNull() { +// String js = "try {\n" +// + " Reflect.construct(function() {}, [], null);\n" +// + "} catch(e) {\n" +// + " '' + e;\n" +// + "}"; +// testString("TypeError: \"object\" is not a constructor.", js); +// } +// +// @Test +// public void constructNoConstructorObject() { +// String js = "try {\n" +// + " Reflect.construct(function() {}, [], {});\n" +// + "} catch(e) {\n" +// + " '' + e;\n" +// + "}"; +// testString("TypeError: \"object\" is not a constructor.", js); +// } +// +// @Test +// public void constructNoConstructorFunction() { +// String js = "try {\n" +// + " Reflect.construct(function() {}, [], Date.now);\n" +// + "} catch(e) {\n" +// + " '' + e;\n" +// + "}"; +// // testString("TypeError: \"object\" is not a constructor.", js); +// // found no way to check a function for constructor +// } + + @Test + public void defineProperty() { + String js = + "var o = {};\n" + + "var res = '';\n" + + "var handler = {\n" + + " defineProperty(target, key, desc) {\n" + + " res = res + target + ' ' + key + ' '\n" + + " + desc.writable + ' ' + desc.configurable + ' ' + desc.enumerable;\n" + + " return true;\n" + + " }\n" + + " };\n" + + "var proxy1 = new Proxy(o, handler);\n" + + "Object.defineProperty(proxy1, 'p', { value: 42, writable: false });\n" + + "res;"; + testString("[object Object] p false undefined undefined", js); + } + + @Test + public void definePropertyWithoutHandler() { + String js = + "var o = {};\n" + + "var proxy1 = new Proxy(o, {});\n" + + "proxy1.p = 42;\n" + + "'' + o.p;"; + testString("42", js); + } + + @Test + public void definePropertyFreezedWithoutHandler() { + String js = + "var o = {};\n" + + "Object.freeze(o);\n" + + "var proxy1 = new Proxy(o, {});\n" + + "try {\n" + + " Object.defineProperty(proxy1, 'p', { value: 42, writable: false });\n" + + " '' + o.p;\n" + + "} catch(e) {\n" + + " '' + e;" + + "}\n"; + testString("TypeError: Cannot add properties to this object because extensible is false.", js); + } + + @Test + public void definePropertyHandlerNotFunction() { + String js = + "var o = {};\n" + + "var proxy1 = new Proxy(o, { defineProperty: 7 });\n" + + "try {\n" + + " Object.defineProperty(proxy1, 'p', { value: 42, writable: false });\n" + + " '' + o.p;\n" + + "} catch(e) {\n" + + " '' + e;" + + "}\n"; + testString("TypeError: defineProperty is not a function, it is number.", js); + } + + @Test + public void definePropertyHandlerNull() { + String js = + "var o = {};\n" + + "var proxy1 = new Proxy(o, { defineProperty: null });\n" + + "try {\n" + + " Object.defineProperty(proxy1, 'p', { value: 42, writable: false });\n" + + " '' + o.p;\n" + + "} catch(e) {\n" + + " '' + e;" + + "}\n"; + testString("42", js); + } + + @Test + public void definePropertyHandlerUndefined() { + String js = + "var o = {};\n" + + "var proxy1 = new Proxy(o, { defineProperty: undefined });\n" + + "try {\n" + + " Object.defineProperty(proxy1, 'p', { value: 42, writable: false });\n" + + " '' + o.p;\n" + + "} catch(e) {\n" + + " '' + e;" + + "}\n"; + testString("42", js); + } + + @Test + public void deletePropertyWithoutHandler() { + String js = + "var o = { p: 42 };\n" + + "var proxy1 = new Proxy(o, {});\n" + + "delete proxy1.p;\n" + + "'' + o.p;"; + testString("undefined", js); + } + + @Test + public void getOwnPropertyDescriptor() { + String js = + "var o1 = {};\n" + + "var fn = function() {};\n" + + "Object.defineProperty(o1, 'p', {\n" + + " get: fn,\n" + + " configurable: true\n" + + "});\n" + + "var proxy1 = new Proxy(o1, {\n" + + " getOwnPropertyDescriptor(target, prop) {\n" + + " return { configurable: true, enumerable: true, value: 7 };\n" + + " }});\n" + + "var result = Object.getOwnPropertyDescriptor(proxy1, 'p');\n" + + "'' + o1.p + ' ' + result.value \n" + + "+ ' [' + Object.getOwnPropertyNames(result) + ']' " + + "+ ' ' + result.enumerable " + + "+ ' ' + result.configurable " + + "+ ' ' + result.writable " + + "+ ' ' + (result.get === fn) " + + "+ ' ' + (result.set === undefined)"; + testString("undefined 7 [value,writable,enumerable,configurable] true true false false true", js); + } + + @Test + public void getOwnPropertyDescriptorWithoutHandler() { + String js = + "var o1 = {};\n" + + "var fn = function() {};\n" + + "Object.defineProperty(o1, 'p', {\n" + + " get: fn,\n" + + " configurable: true\n" + + "});\n" + + "var proxy1 = new Proxy(o1, {});\n" + + "var result = Object.getOwnPropertyDescriptor(proxy1, 'p');\n" + + "'[' + Object.getOwnPropertyNames(result) + ']'" + + "+ ' ' + result.enumerable" + + "+ ' ' + result.configurable" + + "+ ' ' + (result.get === fn)" + + "+ ' ' + (result.set === undefined)"; + testString("[get,set,enumerable,configurable] false true true true", js); + } + + @Test + public void isExtensible() { + String js = + "var o = {};\n" + + "var res = '';\n" + + "var handler = {\n" + + " isExtensible(target) {\n" + + " res += ' a ' + (target == o);\n" + + " },\n" + + " preventExtensions(target) {\n" + + " res += ' o ' + (target == o);\n" + + " }\n" + + " };\n" + + "var proxy1 = new Proxy(o, handler);\n" + + "var x = Object.isExtensible(proxy1);\n" + + "res += ' ' + x;\n" + + "x = Object.preventExtensions(proxy1);\n" + + "res += ' ' + x;\n" + + "x = Object.isExtensible(proxy1);\n" + + "res += ' ' + x;\n" + + + "var o2 = Object.seal({});\n" + + "var proxy2 = new Proxy(o2, handler);\n" + + "x = Object.isExtensible(proxy2);\n" + + "res += ' ' + x;\n"; + testString(" a true true o true [object Object] a true false a false false", js); + } + + @Test + public void isExtensibleWithoutHandler() { + String js = + "var o1 = {};\n" + + "var proxy1 = new Proxy(o1, {});\n" + + "var result = '' + Object.isExtensible(o1) + '-' + Object.isExtensible(proxy1);\n" + + "Object.preventExtensions(proxy1);\n" + + "result += ' ' + Object.isExtensible(o1) + '-' + Object.isExtensible(proxy1);\n" + + + "var o2 = Object.seal({});\n" + + "var proxy2 = new Proxy(o2, {});\n" + + "result += ' ' + Object.isExtensible(o2) + '-' + Object.isExtensible(proxy2);\n"; + + testString("true-true false-false false-false", js); + } + + @Test + public void ownKeys() { + String js = "var o = { d: 42 };\n" + + "var res = '';\n" + + "var handler = {\n" + + " ownKeys(target) {\n" + + " res += (target == o);\n" + + " return Reflect.ownKeys(target);" + + " }\n" + + " };\n" + + "var proxy1 = new Proxy(o, handler);\n" + + "var x = Object.keys(proxy1);\n" + + "res += ' ' + x;\n"; + testString("true d", js); + } + + @Test + public void ownKeysWithoutHandler() { + String js = + "var o1 = {\n" + + " p1: 42,\n" + + " p2: 'one'\n" + + "};\n" + + "var a1 = [];\n" + + "var proxy1 = new Proxy(o1, {});\n" + + "var proxy2 = new Proxy(a1, {});\n" + + "'' + Object.keys(proxy1)" + + "+ ' ' + Object.keys(proxy2)"; + testString("p1,p2 ", js); + } + + @Test + public void ownKeysWithoutHandler2() { + String js = + "let s1 = Symbol.for('foo');\n" + + "let s2 = Symbol.for('bar');\n" + + "var o1 = {\n" + + " s1: 0,\n" + + " 'str': 0,\n" + + " 773: 0,\n" + + " '55': 0,\n" + + " 0: 0,\n" + + " '-1': 0,\n" + + " 8: 0,\n" + + " '6': 8,\n" + + " s2: 0,\n" + + " 'str2': 0\n" + + "};\n" + + "var a1 = [];\n" + + "var proxy1 = new Proxy(o1, {});\n" + + "'' + Object.keys(proxy1)"; + // FF: 0,6,8,55,773,s1,str,-1,s2,str2 + testString("-1,0,6,8,55,773,s1,str,s2,str2", js); + } + + @Test + public void ownKeysWithoutHandlerEmptyObj() { + String js = "var proxy1 = new Proxy({}, {});\n" + + "'' + Object.keys(proxy1).length"; + testString("0", js); + } + + @Test + public void ownKeysWithoutHandlerDeleteObj() { + String js = "var o = { d: 42 };\n" + + "delete o.d;\n" + + "var proxy1 = new Proxy(o, {});\n" + + "'' + Object.keys(proxy1).length"; + testString("0", js); + } + + @Test + public void ownKeysWithoutHandlerEmptyArray() { + String js = "var proxy1 = new Proxy([], {});\n" + + "'' + Object.keys(proxy1)"; + testString("", js); + } + + @Test + public void ownKeysWithoutHandlerArray() { + String js = "var proxy1 = new Proxy([, , 2], {});\n" + + "'' + Object.keys(proxy1)"; + testString("2", js); + } + + @Test + public void ownKeysWithoutHandlerNotEnumerable() { + String js = + "var o = {};\n" + + "Object.defineProperty(o, 'p1', { value: 42, enumerable: false });\n" + + "Object.defineProperty(o, 'p2', { get: function() {}, enumerable: false });\n" + + "var proxy1 = new Proxy(o, {});\n" + + "'' + Object.keys(proxy1)"; + testString("", js); + } + + @Test + public void hasWithoutHandler() { + String js = + "var o1 = { p: 42 }\n" + + "var proxy1 = new Proxy(o1, {});\n" + + "'' + ('p' in proxy1)" + + "+ ' ' + ('p2' in proxy1)" + + "+ ' ' + ('toString' in proxy1)"; + testString("true false true", js); + } + + @Test + public void hasSymbolWithoutHandler() { + String js = + "var s1 = Symbol('1');\n" + + "var s2 = Symbol('1');\n" + + "var o = {};\n" + + "o[s1] = 42;\n" + + "var proxy1 = new Proxy(o, {});\n" + + "'' + (s1 in proxy1)" + + "+ ' ' + (2 in proxy1)"; + testString("true false", js); + } + +// @Test +// public void getOwnPropertyDescriptorSymbol() { +// String js = +// "var s = Symbol('sym');\n" +// + "var o = {};\n" +// + "o[s] = 42;\n" +// + "var result = Reflect.getOwnPropertyDescriptor(o, s);\n" +// + "'' + result.value" +// + "+ ' ' + result.enumerable" +// + "+ ' ' + result.configurable" +// + "+ ' ' + result.writable"; +// testString("42 true true true", js); +// } +// +// @Test +// public void getOwnPropertyDescriptorUndefinedProperty() { +// String js = +// "var o = Object.create({p: 1});\n" +// + "var result = Reflect.getOwnPropertyDescriptor(o, 'p');\n" +// + "'' + (result === undefined)"; +// testString("true", js); +// } + + @Test + public void getPropertyByIntWithoutHandler() { + String js = "var a = ['zero', 'one'];" + + "var proxy1 = new Proxy(a, {});\n" + + "proxy1[1];"; + testString("one", js); + } + + @Test + public void getProperty() { + String js = + "" + + "var o = {};\n" + + "o.p1 = 'value 1';\n" + + "var proxy1 = new Proxy(o, { get: function(t, prop) {\n" + + " return t[prop] + '!';\n" + + " }});\n" + + "var result = ''\n;" + + "result += proxy1.p1;\n" + + "Object.defineProperty(o, 'p3', { get: function() { return 'foo'; } });\n" + + "result += ', ' + proxy1.p3;\n" + + "var o2 = Object.create({ p: 42 });\n" + + "var proxy2 = new Proxy(o2, {});\n" + + "result += ', ' + proxy2.p;\n" + + "result += ', ' + proxy2.u;\n"; + + testString("value 1!, foo!, 42, undefined", js); + } + + @Test + public void getPropertyWithoutHandler() { + String js = + "var o = {};\n" + + "o.p1 = 'value 1';\n" + + "var proxy1 = new Proxy(o, {});\n" + + "var result = ''\n;" + + "result += proxy1.p1;\n" + + "Object.defineProperty(o, 'p2', { get: undefined });\n" + + "result += ', ' + proxy1.p2;\n" + + "Object.defineProperty(o, 'p3', { get: function() { return 'foo'; } });\n" + + "result += ', ' + proxy1.p3;\n" + + "var o2 = Object.create({ p: 42 });\n" + + "var proxy2 = new Proxy(o2, {});\n" + + "result += ', ' + proxy2.p;\n" + + "result += ', ' + proxy2.u;\n"; + + testString("value 1, undefined, foo, 42, undefined", js); + } + + @Test + public void setPrototypeOfWithoutHandler() { + String js = + "var o1 = {};\n" + + "var result = '';\n" + + "result += Reflect.setPrototypeOf(o1, Object.prototype);\n" + + "result += ' ' + Reflect.setPrototypeOf(o1, null);\n" + + "var o2 = {};\n" + + "result += ' ' + Reflect.setPrototypeOf(Object.freeze(o2), null);\n"; + testString("true true false", js); + } + + @Test + public void setPrototypeOfCycleWithoutHandler() { + String js = "var o1 = {};\n" + "'' + Reflect.setPrototypeOf(o1, o1);\n"; + testString("false", js); + } + + @Test + public void setPrototypeOfCycleComplexWithoutHandler() { + String js = + "var o1 = {};\n" + + "var o2 = {};\n" + + "var o3 = {};\n" + + "'' + Reflect.setPrototypeOf(o1, o2)" + + "+ ' ' + Reflect.setPrototypeOf(o2, o3)" + + "+ ' ' + Reflect.setPrototypeOf(o3, o1)"; + testString("true true false", js); + } + + @Test + public void setPrototypeOfSameWithoutHandler() { + String js = + "var o1 = {};\n" + + "Object.preventExtensions(o1);\n" + + "var o2 = Object.create(null);\n" + + "Object.preventExtensions(o2);\n" + + "var proto = {};\n" + + "var o3 = Object.create(proto);\n" + + "Object.preventExtensions(o3);\n" + + "'' + Reflect.setPrototypeOf(o1, Object.prototype)" + + "+ ' ' + Reflect.setPrototypeOf(o2, null)" + + "+ ' ' + Reflect.setPrototypeOf(o3, proto)"; + testString("true true true", js); + } + + @Test + public void typeof() { + testString("object", "typeof new Proxy({}, {})"); + testString("function", "typeof new Proxy(function() {}, {})"); + } + + @Test + public void typeofRevocable() { + testString("object", "var rev = Proxy.revocable({}, {}); rev.revoke(); typeof rev.proxy"); + testString("function", "var rev = Proxy.revocable(function() {}, {}); rev.revoke(); typeof rev.proxy"); + } + + private static void testString(String expected, String js) { + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + final Scriptable scope = cx.initStandardObjects(); + + Object result = cx.evaluateString(scope, js, "test", 1, null); + assertEquals(expected, result); + + return null; + }); + } + + private static void testDouble(double expected, String js) { + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + final Scriptable scope = cx.initStandardObjects(); + + Object result = cx.evaluateString(scope, js, "test", 1, null); + assertEquals(expected, ((Double) result).doubleValue(), 0.00001); + + return null; + }); + } +} diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java index c740372d79..b3cc0416ed 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java @@ -45,7 +45,12 @@ public void applyDetails() { @Test public void applyMissingArgs() { - String js = "try {\n" + " Reflect.apply();\n" + "} catch(e) {" + " '' + e;\n" + "}"; + String js = + "try {\n" + + " Reflect.apply();\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; testString( "TypeError: Reflect.apply: At least 3 arguments required, but only 0 passed", js); } @@ -55,7 +60,7 @@ public void applyTargetNotFunction() { String js = "try {\n" + " Reflect.apply({}, undefined, [1.75]);\n" - + "} catch(e) {" + + "} catch(e) {\n" + " '' + e;\n" + "}"; testString("TypeError: [object Object] is not a function, it is object.", js); @@ -67,7 +72,7 @@ public void applyArgumentsListNotFunction() { "var s1 = Symbol('1');" + "try {\n" + " Reflect.apply(Math.floor, undefined, s1);\n" - + "} catch(e) {" + + "} catch(e) {\n" + " '' + e;\n" + "}"; testString("TypeError: Expected argument of type object, but instead had type symbol", js); @@ -81,6 +86,47 @@ public void construct() { testString("true 1776", js); } + @Test + public void constructNoConstructorNumber() { + String js = "try {\n" + + " Reflect.construct(function() {}, [], 1);\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; + testString("TypeError: \"number\" is not a constructor.", js); + } + + @Test + public void constructNoConstructorNull() { + String js = "try {\n" + + " Reflect.construct(function() {}, [], null);\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; + testString("TypeError: \"object\" is not a constructor.", js); + } + + @Test + public void constructNoConstructorObject() { + String js = "try {\n" + + " Reflect.construct(function() {}, [], {});\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; + testString("TypeError: \"object\" is not a constructor.", js); + } + + @Test + public void constructNoConstructorFunction() { + String js = "try {\n" + + " Reflect.construct(function() {}, [], Date.now);\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; + // testString("TypeError: \"object\" is not a constructor.", js); + // found no way to check a function for constructor + } + @Test public void defineProperty() { String js = @@ -107,6 +153,16 @@ public void definePropertyFreezed() { testString("false undefined", js); } + @Test + public void deleteProperty() { + String js = + "var o = { p: 42 };\n" + + "'' + Reflect.deleteProperty(o, 'p')" + + "+ ' ' + Reflect.has(o, 'p')" + + "+ ' ' + o.p;"; + testString("true false undefined", js); + } + @Test public void getOwnPropertyDescriptor() { String js = diff --git a/testsrc/test262.properties b/testsrc/test262.properties index 836afc37d0..d0e1048dcd 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -1308,7 +1308,7 @@ built-ins/Promise 404/599 (67.45%) ~built-ins/Proxy -built-ins/Reflect 29/139 (20.86%) +built-ins/Reflect 28/139 (20.14%) construct/newtarget-is-not-constructor-throws.js construct/return-with-newtarget-argument.js defineProperty/return-abrupt-from-property-key.js @@ -1330,7 +1330,6 @@ built-ins/Reflect 29/139 (20.86%) set/creates-a-data-descriptor.js set/different-property-descriptors.js set/receiver-is-not-object.js - set/return-abrupt-from-property-key.js set/return-abrupt-from-result.js set/return-false-if-receiver-is-not-writable.js set/return-false-if-target-is-not-writable.js From 00e944421efb0cfa76d6f32ea18ee2cd014da01f Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Mon, 14 Aug 2023 19:00:27 +0200 Subject: [PATCH 05/24] apply implemented --- src/org/mozilla/javascript/NativeProxy.java | 16 +- .../javascript/tests/es6/NativeProxyTest.java | 160 ++++++++++-------- 2 files changed, 107 insertions(+), 69 deletions(-) diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index ac2aa9eecd..986ddef7d0 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -11,7 +11,7 @@ * * @author Ronald Brill */ -final class NativeProxy extends IdScriptableObject { +final class NativeProxy extends IdScriptableObject implements Callable { private static final long serialVersionUID = 6676871870513494844L; private static final Object PROXY_TAG = "Proxy"; @@ -366,6 +366,20 @@ public void preventExtensions() { target.preventExtensions(); } + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + assertNotRevoked(); + + Scriptable argumentsList = cx.newArray(scope, args); + + Callable trap = getTrap(TRAP_APPLY); + if (trap != null) { + return callTrap(trap, new Object[] {target, thisObj, argumentsList}); + } + + return ScriptRuntime.applyOrCall(true, cx, scope, target, new Object[] {thisObj, argumentsList}); + } + @Override public String getTypeOf() { return typeOf; diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index f091b11d5d..04e8f4e2e1 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -54,74 +54,98 @@ public void ctorAsFunction() { testString("TypeError: \"Constructor Proxy\" may only be invoked from a \"new\" expression.", "try { Proxy() } catch(e) { '' + e }"); } -// -// @Test -// public void applyWithoutHandler() { -// String js = "function sum(a, b) { return a + b; }\n" -// + "var proxy1 = new Proxy(sum, {});\n" -// + "proxy1(1, 2)"; -// testDouble(3.0, js); -// } -// -// @Test -// public void applyDetails() { -// String js = -// "var o = {};\n" -// + "var count = 0;\n" -// + "var results, args;\n" -// + "function fn() {\n" -// + " count++;\n" -// + " results = {\n" -// + " thisArg: this,\n" -// + " args: arguments\n" -// + " };\n" -// + "}\n" -// + "Reflect.apply(fn, o, ['arg1', 2, , null]);\n" -// + "'' + count " -// + "+ ' ' + (results.thisArg === o)" -// + "+ ' ' + results.args.length" -// + "+ ' ' + results.args[0]" -// + "+ ' ' + results.args[1]" -// + "+ ' ' + results.args[2]" -// + "+ ' ' + results.args[3]"; -// testString("1 true 4 arg1 2 undefined null", js); -// } -// -// @Test -// public void applyMissingArgs() { -// String js = -// "try {\n" -// + " Reflect.apply();\n" -// + "} catch(e) {\n" -// + " '' + e;\n" -// + "}"; -// testString( -// "TypeError: Reflect.apply: At least 3 arguments required, but only 0 passed", js); -// } -// -// @Test -// public void applyTargetNotFunction() { -// String js = -// "try {\n" -// + " Reflect.apply({}, undefined, [1.75]);\n" -// + "} catch(e) {\n" -// + " '' + e;\n" -// + "}"; -// testString("TypeError: [object Object] is not a function, it is object.", js); -// } -// -// @Test -// public void applyArgumentsListNotFunction() { -// String js = -// "var s1 = Symbol('1');" -// + "try {\n" -// + " Reflect.apply(Math.floor, undefined, s1);\n" -// + "} catch(e) {\n" -// + " '' + e;\n" -// + "}"; -// testString("TypeError: Expected argument of type object, but instead had type symbol", js); -// } -// + @Test + public void apply() { + String js = + "function sum(a, b) {\n" + + " return a + b;\n" + + "}\n" + + + "var res = '';\n" + + "var handler = {\n" + + " apply: function (target, thisArg, argumentsList) {\n" + + " res += ' ' + `Calculate sum: ${argumentsList}`;\n" + + " return target(argumentsList[0], argumentsList[1]) * 7;\n" + + " },\n" + + "};\n" + + + "var proxy1 = new Proxy(sum, handler);\n" + + "var x = ' ' + proxy1(1, 2);\n" + + "res + x"; + + testString(" Calculate sum: 1,2 21", js); + } + + @Test + public void applyParameters() { + String js = + "var _target, _args, _handler, _context;\n" + + "var target = function() {\n" + + " throw new Error('target should not be called');\n" + + "};\n" + + "var handler = {\n" + + " apply: function(t, c, args) {\n" + + " _handler = this;\n" + + " _target = t;\n" + + " _context = c;\n" + + " _args = args;\n" + + " }\n" + + "};\n" + + + "var p = new Proxy(target, handler);\n" + + "var context = {};\n" + + + "p.call(context, 1, 4);\n" + + + "'' + (_handler === handler)\n" + + "+ ' ' + (_target === target)" + + "+ ' ' + (_context === context)" + + "+ ' ' + _args.length + ' ' + _args[0] + ' ' + _args[1]"; + + testString("true true true 2 1 4", js); + } + + @Test + public void applyTrapIsNull() { + String js = + "var calls = 0;\n" + + "var _context;\n" + + + "var target = new Proxy(function() {}, {\n" + + " apply: function(_target, context, args) {\n" + + " calls++;\n" + + " _context = context;\n" + + " return args[0] + args[1];\n" + + " }\n" + + "})\n" + + + "var p = new Proxy(target, {\n" + + " apply: null\n" + + "});\n" + + + "var context = {};\n" + + "var res = p.call(context, 1, 2);\n" + + + "'' + calls\n" + + "+ ' ' + (_context === context)" + + "+ ' ' + res"; + + testString("1 true 3", js); + } + + @Test + public void applyWithoutHandler() { + String js = + "function sum(a, b) {\n" + + " return a + b;\n" + + "}\n" + + + "var proxy1 = new Proxy(sum, {});\n" + + "proxy1(1, 2);"; + + testDouble(3.0, js); + } + // @Test // public void construct() { // String js = From 2599d9a5316c5435b815c83c9b8b4246a9d8fab2 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Mon, 14 Aug 2023 19:50:58 +0200 Subject: [PATCH 06/24] built-ins/Proxy 152/306 (49.67%) milestone I reached --- src/org/mozilla/javascript/NativeProxy.java | 28 ++++++++---- .../javascript/tests/es6/NativeProxyTest.java | 44 +++++++++++++++++-- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index 986ddef7d0..be58169dac 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -192,7 +192,7 @@ public Object get(String name, Scriptable start) { Callable trap = getTrap(TRAP_GET); if (trap != null) { - return callTrap(trap, new Object[] {target, name}); + return callTrap(trap, new Object[] {target, name, this}); } return ScriptRuntime.getObjectProp(target, name, Context.getContext()); @@ -204,7 +204,7 @@ public Object get(int index, Scriptable start) { Callable trap = getTrap(TRAP_GET); if (trap != null) { - return callTrap(trap, new Object[] {target, index}); + return callTrap(trap, new Object[] {target, index, this}); } return ScriptRuntime.getObjectIndex(target, index, Context.getContext()); } @@ -215,7 +215,7 @@ public Object get(Symbol key, Scriptable start) { Callable trap = getTrap(TRAP_GET); if (trap != null) { - return callTrap(trap, new Object[] {target, key}); + return callTrap(trap, new Object[] {target, key, this}); } if (start == this) { @@ -366,6 +366,23 @@ public void preventExtensions() { target.preventExtensions(); } + @Override + public String getTypeOf() { + return typeOf; + } + + @Override + public Scriptable getPrototype() { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_GET_PROTOTYPE_OF); + if (trap != null) { + return (ScriptableObject) callTrap(trap, new Object[] {target}); + } + + return target.getPrototype(); + } + @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { assertNotRevoked(); @@ -380,11 +397,6 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar return ScriptRuntime.applyOrCall(true, cx, scope, target, new Object[] {thisObj, argumentsList}); } - @Override - public String getTypeOf() { - return typeOf; - } - private NativeProxy js_constructor(Context cx, Scriptable scope, Object[] args) { if (args.length < 2) { throw ScriptRuntime.typeErrorById( diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index 04e8f4e2e1..a2b3b2b4f6 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -530,9 +530,7 @@ public void getPropertyByIntWithoutHandler() { @Test public void getProperty() { - String js = - "" - + "var o = {};\n" + String js = "var o = {};\n" + "o.p1 = 'value 1';\n" + "var proxy1 = new Proxy(o, { get: function(t, prop) {\n" + " return t[prop] + '!';\n" @@ -549,6 +547,39 @@ public void getProperty() { testString("value 1!, foo!, 42, undefined", js); } + @Test + public void getPropertyParameters() { + String js = "var _target, _handler, _prop, _receiver;\n" + + "var target = {\n" + + " attr: 1\n" + + "};\n" + + + "var handler = {\n" + + " get: function(t, prop, receiver) {\n" + + " _handler = this;\n" + + " _target = t;\n" + + " _prop = prop;\n" + + " _receiver = receiver;\n" + + " }\n" + + "};\n" + + + "var p = new Proxy(target, handler);\r\n" + + + "p.attr;\n" + + + "var res = '' + (_handler === handler)\n" + + "+ ' ' + (_target === target)" + + "+ ' ' + (_prop == 'attr')" + + "+ ' ' + (_receiver === p);" + + + "_prop = null;\n" + + "p['attr'];\n" + + + "res + ' ' + (_prop == 'attr')"; + + testString("true true true true true", js); + } + @Test public void getPropertyWithoutHandler() { String js = @@ -627,6 +658,13 @@ public void typeofRevocable() { testString("function", "var rev = Proxy.revocable(function() {}, {}); rev.revoke(); typeof rev.proxy"); } + @Test + public void revocableGetPrototypeOf() { + testString("TypeError: Illegal operation attempted on a revoked proxy", + "var rev = Proxy.revocable({}, {}); rev.revoke(); " + + "try { Object.getPrototypeOf(rev.proxy); } catch(e) { '' + e }"); + } + private static void testString(String expected, String js) { Utils.runWithAllOptimizationLevels( cx -> { From 32e99459172ec55c5b965f734e53d3b62219e6dc Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Tue, 15 Aug 2023 19:12:55 +0200 Subject: [PATCH 07/24] built-ins/Proxy 134/306 (43.79%) --- src/org/mozilla/javascript/Interpreter.java | 4 +- .../mozilla/javascript/LambdaConstructor.java | 4 + src/org/mozilla/javascript/NativeProxy.java | 151 ++++++++---------- src/org/mozilla/javascript/ScriptRuntime.java | 6 +- .../javascript/tests/es6/NativeProxyTest.java | 51 +++++- 5 files changed, 127 insertions(+), 89 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index af904e2686..4b2a374516 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1909,12 +1909,12 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl continue StateLoop; } } - if (!(lhs instanceof Function)) { + if (!(lhs instanceof Constructable)) { if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); throw ScriptRuntime.notFunctionError(lhs); } - Function fun = (Function) lhs; + Constructable fun = (Constructable) lhs; if (fun instanceof IdFunctionObject) { IdFunctionObject ifun = (IdFunctionObject) fun; diff --git a/src/org/mozilla/javascript/LambdaConstructor.java b/src/org/mozilla/javascript/LambdaConstructor.java index 4c9ef8f4f4..910a42faed 100644 --- a/src/org/mozilla/javascript/LambdaConstructor.java +++ b/src/org/mozilla/javascript/LambdaConstructor.java @@ -61,6 +61,10 @@ public LambdaConstructor( this.flags = flags; } + protected Constructable getTargetConstructor() { + return targetConstructor; + } + @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if ((flags & CONSTRUCTOR_FUNCTION) == 0) { diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index be58169dac..01b2df61ba 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -11,10 +11,10 @@ * * @author Ronald Brill */ -final class NativeProxy extends IdScriptableObject implements Callable { +final class NativeProxy extends ScriptableObject implements Callable, Constructable { private static final long serialVersionUID = 6676871870513494844L; - private static final Object PROXY_TAG = "Proxy"; + private static final String PROXY_TAG = "Proxy"; private static final String TRAP_GET_PROTOTYPE_OF = "getPrototypeOf"; private static final String TRAP_SET_PROTOTYPE_OF = "setPrototypeOf"; @@ -30,7 +30,6 @@ final class NativeProxy extends IdScriptableObject implements Callable { private static final String TRAP_APPLY = "apply"; private static final String TRAP_CONSTRUCT = "construct"; - private final boolean isCtor; private ScriptableObject target; private Scriptable handler; private final String typeOf; @@ -54,16 +53,40 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, } } - static void init(Scriptable scope, boolean sealed) { - NativeProxy constructor = new NativeProxy(null, null, true); - IdFunctionObject ctor = constructor.exportAsJSClass(MAX_PROTOTYPE_ID, scope, false); - ctor.setPrototypeProperty(null); + + public static void init(Context cx, Scriptable scope, boolean sealed) { + LambdaConstructor constructor = + new LambdaConstructor( + scope, + PROXY_TAG, + 2, + LambdaConstructor.CONSTRUCTOR_NEW, + NativeProxy::constructor) { + + @Override + public Scriptable construct(Context cx, Scriptable scope, Object[] args) { + NativeProxy obj = (NativeProxy) getTargetConstructor().construct(cx, scope, args); + // avoid getting trapped + obj.setPrototypeDirect(getClassPrototype()); + obj.setParentScope(scope); + return obj; + } + }; + // constructor.setPrototypePropertyAttributes(DONTENUM | READONLY | PERMANENT); + constructor.setPrototypeProperty(null); + + constructor.defineConstructorMethod( + scope, "revocable", 2, NativeProxy::revocable, DONTENUM, DONTENUM | READONLY); + + ScriptableObject.defineProperty(scope, PROXY_TAG, constructor, DONTENUM); + if (sealed) { + constructor.sealObject(); + } } - private NativeProxy(ScriptableObject target, Scriptable handler, boolean isCtor) { + private NativeProxy(ScriptableObject target, Scriptable handler) { this.target = target; this.handler = handler; - this.isCtor = isCtor; if (target == null || !(target instanceof Callable)) { typeOf = super.getTypeOf(); @@ -75,52 +98,24 @@ private NativeProxy(ScriptableObject target, Scriptable handler, boolean isCtor) @Override public String getClassName() { - if (isCtor) { - return "Proxy"; - } - assertNotRevoked(); return target.getClassName(); } @Override - protected void initPrototypeId(int id) { - if (id <= MAX_PROTOTYPE_ID) { - String name; - int arity; - switch (id) { - case Id_constructor: - arity = 2; - name = "constructor"; - break; - - default: - throw new IllegalStateException(String.valueOf(id)); - } - initPrototypeMethod(PROXY_TAG, id, name, arity); - } - } + public Scriptable construct(Context cx, Scriptable scope, Object[] args) { + assertNotRevoked(); - @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(PROXY_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); + Callable trap = getTrap(TRAP_CONSTRUCT); + if (trap != null) { + Object result = callTrap(trap, new Object[] {target, args, this}); + if (!(result instanceof Scriptable) || ScriptRuntime.isSymbol(result)) { + throw ScriptRuntime.typeError("Constructor trap has to return a scriptable."); + } + return (ScriptableObject) result; } - int methodId = f.methodId(); - switch (methodId) { - case Id_constructor: - if (thisObj != null && cx.getLanguageVersion() >= Context.VERSION_ES6) { - throw ScriptRuntime.typeErrorById("msg.only.from.new", getClassName()); - } - return js_constructor(cx, scope, args); - - case ConstructorId_revocable: - return js_revocable(cx, scope, args); - - default: - throw new IllegalStateException(String.valueOf(methodId)); - } + return ((Constructable) target).construct(cx, scope, args); } @Override @@ -161,10 +156,6 @@ public boolean has(Symbol key, Scriptable start) { @Override public Object[] getIds() { - if (isCtor) { - return super.getIds(); - } - assertNotRevoked(); Callable trap = getTrap(TRAP_OWN_KEYS); @@ -383,6 +374,23 @@ public Scriptable getPrototype() { return target.getPrototype(); } + private void setPrototypeDirect(Scriptable prototype) { + super.setPrototype(prototype); + } + + @Override + public void setPrototype(Scriptable prototype) { + assertNotRevoked(); + + Callable trap = getTrap(TRAP_SET_PROTOTYPE_OF); + if (trap != null) { + callTrap(trap, new Object[] {target, prototype}); + return; + } + + target.setPrototype(prototype); + } + @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { assertNotRevoked(); @@ -397,7 +405,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar return ScriptRuntime.applyOrCall(true, cx, scope, target, new Object[] {thisObj, argumentsList}); } - private NativeProxy js_constructor(Context cx, Scriptable scope, Object[] args) { + private static NativeProxy constructor(Context cx, Scriptable scope, Object[] args) { if (args.length < 2) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -411,14 +419,18 @@ private NativeProxy js_constructor(Context cx, Scriptable scope, Object[] args) s = ScriptRuntime.toObject(cx, scope, args[1]); ScriptableObject hndlr = ensureScriptableObject(s); - NativeProxy proxy = new NativeProxy(trgt, hndlr, false); - proxy.setPrototype(ScriptableObject.getClassPrototype(scope, proxy.getClassName())); + NativeProxy proxy = new NativeProxy(trgt, hndlr); + proxy.setPrototypeDirect(ScriptableObject.getClassPrototype(scope, PROXY_TAG)); proxy.setParentScope(scope); return proxy; } - private NativeObject js_revocable(Context cx, Scriptable scope, Object[] args) { - NativeProxy proxy = js_constructor(cx, scope, args); + // Proxy.revocable + private static Object revocable(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + if (!ScriptRuntime.isObject(thisObj)) { + throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(thisObj)); + } + NativeProxy proxy = constructor(cx, scope, args); NativeObject revocable = (NativeObject) cx.newObject(scope); @@ -447,33 +459,8 @@ private Object callTrap(Callable trap, Object[] args) { } private void assertNotRevoked() { - if (!isCtor && target == null) { + if (target == null) { throw ScriptRuntime.typeError("Illegal operation attempted on a revoked proxy"); } } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - - default: - id = 0; - break; - } - return id; - } - - @Override - protected void fillConstructorProperties(IdFunctionObject ctor) { - addIdFunctionProperty(ctor, PROXY_TAG, ConstructorId_revocable, "revocable", 2); - super.fillConstructorProperties(ctor); - } - - private static final int ConstructorId_revocable = -1, - Id_constructor = 1, - MAX_PROTOTYPE_ID = Id_constructor; } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 72873ccd8d..63fb4c945f 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -287,7 +287,7 @@ public static ScriptableObject initSafeStandardObjects( NativeWeakSet.init(scope, sealed); NativeBigInt.init(scope, sealed); - NativeProxy.init(scope, sealed); + NativeProxy.init(cx, scope, sealed); NativeReflect.init(scope, sealed); } @@ -2683,10 +2683,10 @@ public static Ref callRef(Callable function, Scriptable thisObj, Object[] args, *

See ECMA 11.2.2 */ public static Scriptable newObject(Object fun, Context cx, Scriptable scope, Object[] args) { - if (!(fun instanceof Function)) { + if (!(fun instanceof Constructable)) { throw notFunctionError(fun); } - Function function = (Function) fun; + Constructable function = (Constructable) fun; return function.construct(cx, scope, args); } diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index a2b3b2b4f6..c4c0224cac 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -11,7 +11,7 @@ public class NativeProxyTest { @Test public void testToString() { - testString("function Proxy() { [native code for Proxy.Proxy, arity=2] }\n", "Proxy.toString()"); + testString("function Proxy() {\n\t[native code, arity=2]\n}\n", "Proxy.toString()"); testString("[object Object]", "Object.prototype.toString.call(new Proxy({}, {}))"); testString("[object Array]", "Object.prototype.toString.call(new Proxy([], {}))"); @@ -51,7 +51,35 @@ public void ctorMissingArgs() { @Test public void ctorAsFunction() { - testString("TypeError: \"Constructor Proxy\" may only be invoked from a \"new\" expression.", "try { Proxy() } catch(e) { '' + e }"); + testString("TypeError: The constructor for Proxy may not be invoked as a function", "try { Proxy() } catch(e) { '' + e }"); + } + + @Test + public void construct() { + String js = + "var _target, _handler, _args, _P;\n" + + + "function Target() {}\n" + + + "var handler = {\n" + + " construct: function(t, args, newTarget) {\n" + + " _handler = this;\n" + + " _target = t;\n" + + " _args = args;\n" + + " _P = newTarget;\n" + + " return new t(args[0], args[1]);\n" + + " }\n" + + "};\n" + + + "var P = new Proxy(Target, handler);\n" + + + "new P(1, 4);\n" + + "'' + (_handler === handler)\n" + + "+ ' ' + (_target === Target)" + + "+ ' ' + (_P === P)" + + "+ ' ' + _args.length + ' ' + _args[0] + ' ' + _args[1]"; + + testString("true true true 2 1 4", js); } @Test @@ -600,6 +628,18 @@ public void getPropertyWithoutHandler() { testString("value 1, undefined, foo, 42, undefined", js); } + @Test + public void getPrototypeOfNull() { + String js = + "var plainObjectTarget = new Proxy(Object.create(null), {});\n" + + "var plainObjectProxy = new Proxy(plainObjectTarget, {\n" + + " getPrototypeOf: null,\n" + + "});\n" + + "'' + Object.getPrototypeOf(plainObjectProxy);\n"; + testString("null", js); + } + + @Test public void setPrototypeOfWithoutHandler() { String js = @@ -656,6 +696,13 @@ public void typeof() { public void typeofRevocable() { testString("object", "var rev = Proxy.revocable({}, {}); rev.revoke(); typeof rev.proxy"); testString("function", "var rev = Proxy.revocable(function() {}, {}); rev.revoke(); typeof rev.proxy"); + + String js = "var revocableTarget = Proxy.revocable(function() {}, {});\n" + + "revocableTarget.revoke();\n" + + + "var revocable = Proxy.revocable(revocableTarget.proxy, {});\n" + + "'' + typeof revocable.proxy;\n"; + testString("function", js); } @Test From e6e51dedde769e4933b1a58078d3aeeb6bb10a62 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Wed, 16 Aug 2023 18:59:25 +0200 Subject: [PATCH 08/24] convert to lambda --- src/org/mozilla/javascript/NativeReflect.java | 264 +++++------------- src/org/mozilla/javascript/ScriptRuntime.java | 4 +- 2 files changed, 65 insertions(+), 203 deletions(-) diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index c4d5c32e37..ca12a275be 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -14,20 +14,34 @@ * * @author Ronald Brill */ -final class NativeReflect extends IdScriptableObject { +final class NativeReflect extends ScriptableObject { private static final long serialVersionUID = 2920773905356325445L; - private static final Object REFLECT_TAG = "Reflect"; + private static final String REFLECT_TAG = "Reflect"; - static void init(Scriptable scope, boolean sealed) { - NativeReflect obj = new NativeReflect(); - obj.activatePrototypeMap(LAST_METHOD_ID); - obj.setPrototype(getObjectPrototype(scope)); - obj.setParentScope(scope); + public static void init(Context cx, Scriptable scope, boolean sealed) { + NativeReflect reflect = new NativeReflect(); + reflect.setPrototype(getObjectPrototype(scope)); + reflect.setParentScope(scope); if (sealed) { - obj.sealObject(); + reflect.sealObject(); } - ScriptableObject.defineProperty(scope, "Reflect", obj, ScriptableObject.DONTENUM); + + reflect.defineProperty(scope, "apply", 3, NativeReflect::apply, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "construct", 2, NativeReflect::construct, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "defineProperty", 3, NativeReflect::defineProperty, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "deleteProperty", 2, NativeReflect::deleteProperty, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "get", 2, NativeReflect::get, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "getOwnPropertyDescriptor", 2, NativeReflect::getOwnPropertyDescriptor, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "getPrototypeOf", 1, NativeReflect::getPrototypeOf, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "has", 2, NativeReflect::has, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "isExtensible", 1, NativeReflect::isExtensible, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "ownKeys", 1, NativeReflect::ownKeys, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "preventExtensions", 1, NativeReflect::preventExtensions, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "set", 3, NativeReflect::set, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(scope, "setPrototypeOf", 2, NativeReflect::setPrototypeOf, DONTENUM, DONTENUM | READONLY); + + ScriptableObject.defineProperty(scope, REFLECT_TAG, reflect, DONTENUM); } private NativeReflect() {} @@ -37,120 +51,27 @@ public String getClassName() { return "Reflect"; } - @Override - protected void initPrototypeId(int id) { - if (id <= LAST_METHOD_ID) { - String name; - int arity; - switch (id) { - case Id_toSource: - arity = 0; - name = "toSource"; - break; - case Id_apply: - arity = 3; - name = "apply"; - break; - case Id_construct: - arity = 2; - name = "construct"; - break; - case Id_defineProperty: - arity = 3; - name = "defineProperty"; - break; - case Id_deleteProperty: - arity = 2; - name = "deleteProperty"; - break; - case Id_get: - arity = 2; - name = "get"; - break; - case Id_getOwnPropertyDescriptor: - arity = 2; - name = "getOwnPropertyDescriptor"; - break; - case Id_getPrototypeOf: - arity = 1; - name = "getPrototypeOf"; - break; - case Id_has: - arity = 2; - name = "has"; - break; - case Id_isExtensible: - arity = 1; - name = "isExtensible"; - break; - case Id_ownKeys: - arity = 1; - name = "ownKeys"; - break; - case Id_preventExtensions: - arity = 1; - name = "preventExtensions"; - break; - case Id_set: - arity = 3; - name = "set"; - break; - case Id_setPrototypeOf: - arity = 2; - name = "setPrototypeOf"; - break; - default: - throw new IllegalStateException(String.valueOf(id)); - } - initPrototypeMethod(REFLECT_TAG, id, name, arity); + private static NativeReflect constructor(Context cx, Scriptable scope, Object[] args) { + if (args.length < 2) { + throw ScriptRuntime.typeErrorById( + "msg.method.missing.parameter", + "Proxy.ctor", + "2", + Integer.toString(args.length)); } - } + Scriptable s = ScriptRuntime.toObject(cx, scope, args[0]); + ScriptableObject trgt = ensureScriptableObject(s); - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(REFLECT_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } + s = ScriptRuntime.toObject(cx, scope, args[1]); + ScriptableObject hndlr = ensureScriptableObject(s); - int methodId = f.methodId(); - switch (methodId) { - case Id_toSource: - return "Reflect"; - - case Id_apply: - return js_apply(cx, scope, args); - case Id_construct: - return js_construct(cx, scope, args); - case Id_defineProperty: - return js_defineProperty(cx, args); - case Id_deleteProperty: - return js_deleteProperty(args); - case Id_get: - return js_get(args); - case Id_getOwnPropertyDescriptor: - return js_getOwnPropertyDescriptor(cx, args); - case Id_getPrototypeOf: - return js_getPrototypeOf(args); - case Id_has: - return js_has(args); - case Id_isExtensible: - return js_isExtensible(args); - case Id_ownKeys: - return js_ownKeys(cx, scope, args); - case Id_preventExtensions: - return js_preventExtensions(args); - case Id_set: - return js_set(args); - case Id_setPrototypeOf: - return js_setPrototypeOf(args); - - default: - throw new IllegalStateException(String.valueOf(methodId)); - } + NativeReflect reflect = new NativeReflect(); + reflect.setPrototype(ScriptableObject.getClassPrototype(scope, REFLECT_TAG)); + reflect.setParentScope(scope); + return reflect; } - private static Object js_apply(Context cx, Scriptable scope, Object[] args) { + private static Object apply(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (args.length < 3) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -161,7 +82,6 @@ private static Object js_apply(Context cx, Scriptable scope, Object[] args) { Scriptable callable = ScriptableObject.ensureScriptable(args[0]); - Scriptable thisObj = Undefined.SCRIPTABLE_UNDEFINED; if (args[1] instanceof Scriptable) { thisObj = (Scriptable) args[1]; } @@ -175,7 +95,7 @@ private static Object js_apply(Context cx, Scriptable scope, Object[] args) { true, cx, scope, callable, new Object[] {thisObj, argumentsList}); } - private static Scriptable js_construct(Context cx, Scriptable scope, Object[] args) { + private static Scriptable construct(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (args.length < 1) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -201,7 +121,7 @@ private static Scriptable js_construct(Context cx, Scriptable scope, Object[] ar return ctor.construct(cx, scope, callArgs); } - private static boolean js_defineProperty(Context cx, Object[] args) { + private static Object defineProperty(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (args.length < 3) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -221,20 +141,20 @@ private static boolean js_defineProperty(Context cx, Object[] args) { } } - private static boolean js_deleteProperty(Object[] args) { + private static Object deleteProperty(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); if (args.length > 1) { if (ScriptRuntime.isSymbol(args[1])) { return ScriptableObject.deleteProperty(target, (Symbol) args[1]); } - return ScriptableObject.deleteProperty(target, ScriptRuntime.toString(args[1])); } + return false; } - private static Object js_get(Object[] args) { + private static Object get(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); if (args.length > 1) { @@ -253,7 +173,7 @@ private static Object js_get(Object[] args) { return Undefined.SCRIPTABLE_UNDEFINED; } - private static Scriptable js_getOwnPropertyDescriptor(Context cx, Object[] args) { + private static Scriptable getOwnPropertyDescriptor(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); if (args.length > 1) { @@ -269,13 +189,13 @@ private static Scriptable js_getOwnPropertyDescriptor(Context cx, Object[] args) return Undefined.SCRIPTABLE_UNDEFINED; } - private static Scriptable js_getPrototypeOf(Object[] args) { + private static Scriptable getPrototypeOf(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); return target.getPrototype(); } - private static boolean js_has(Object[] args) { + private static Object has(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); if (args.length > 1) { @@ -288,12 +208,12 @@ private static boolean js_has(Object[] args) { return false; } - private static boolean js_isExtensible(Object[] args) { + private static Object isExtensible(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); return target.isExtensible(); } - private static Scriptable js_ownKeys(Context cx, Scriptable scope, Object[] args) { + private static Scriptable ownKeys(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); final List strings = new ArrayList<>(); @@ -314,14 +234,14 @@ private static Scriptable js_ownKeys(Context cx, Scriptable scope, Object[] args return cx.newArray(scope, keys); } - private static boolean js_preventExtensions(Object[] args) { + private static Object preventExtensions(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); target.preventExtensions(); return true; } - private static boolean js_set(Object[] args) { + private static Object set(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); if (args.length > 1) { @@ -340,7 +260,7 @@ private static boolean js_set(Object[] args) { return false; } - private static boolean js_setPrototypeOf(Object[] args) { + private static Object setPrototypeOf(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (args.length < 2) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -399,73 +319,15 @@ private static ScriptableObject checkTarget(Object[] args) { return ScriptableObject.ensureScriptableObject(args[0]); } - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "toSource": - id = Id_toSource; - break; - case "apply": - id = Id_apply; - break; - case "construct": - id = Id_construct; - break; - case "defineProperty": - id = Id_defineProperty; - break; - case "deleteProperty": - id = Id_deleteProperty; - break; - case "get": - id = Id_get; - break; - case "getOwnPropertyDescriptor": - id = Id_getOwnPropertyDescriptor; - break; - case "getPrototypeOf": - id = Id_getPrototypeOf; - break; - case "has": - id = Id_has; - break; - case "isExtensible": - id = Id_isExtensible; - break; - case "ownKeys": - id = Id_ownKeys; - break; - case "preventExtensions": - id = Id_preventExtensions; - break; - case "set": - id = Id_set; - break; - case "setPrototypeOf": - id = Id_setPrototypeOf; - break; - - default: - id = 0; - break; - } - return id; + private void defineProperty( + Scriptable scope, + String name, + int length, + Callable target, + int attributes, + int propertyAttributes) { + LambdaFunction f = new LambdaFunction(scope, name, length, target); + f.setStandardPropertyAttributes(propertyAttributes); + defineProperty(name, f, attributes); } - - private static final int Id_toSource = 1, - Id_apply = 2, - Id_construct = 3, - Id_defineProperty = 4, - Id_deleteProperty = 5, - Id_get = 6, - Id_getOwnPropertyDescriptor = 7, - Id_getPrototypeOf = 8, - Id_has = 9, - Id_isExtensible = 10, - Id_ownKeys = 11, - Id_preventExtensions = 12, - Id_set = 13, - Id_setPrototypeOf = 14, - LAST_METHOD_ID = Id_setPrototypeOf; -} +} \ No newline at end of file diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 63fb4c945f..f432beebc0 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -288,7 +288,7 @@ public static ScriptableObject initSafeStandardObjects( NativeBigInt.init(scope, sealed); NativeProxy.init(cx, scope, sealed); - NativeReflect.init(scope, sealed); + NativeReflect.init(cx, scope, sealed); } if (scope instanceof TopLevel) { @@ -2777,7 +2777,7 @@ public static Object applyOrCall( } /** @return true if the passed in Scriptable looks like an array */ - private static boolean isArrayLike(Scriptable obj) { + public static boolean isArrayLike(Scriptable obj) { return obj != null && (obj instanceof NativeArray || obj instanceof Arguments From bce5b2b65da681c28aa0166034ce4881b10d00a9 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Wed, 16 Aug 2023 19:01:51 +0200 Subject: [PATCH 09/24] built-ins/Proxy 120/306 (39.22%) --- .../AbstractEcmaObjectOperations.java | 46 +++++ src/org/mozilla/javascript/NativeProxy.java | 191 ++++++++++++++++-- .../javascript/tests/es6/NativeProxyTest.java | 70 ++++++- 3 files changed, 281 insertions(+), 26 deletions(-) diff --git a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java index b8529c2caf..6166bf1491 100644 --- a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java +++ b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java @@ -1,5 +1,9 @@ package org.mozilla.javascript; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Abstract Object Operations as defined by EcmaScript * @@ -232,4 +236,46 @@ static void put(Context cx, Scriptable o, int p, Object v, boolean isThrow) { base.put(p, o, v); } } + + /** + * CreateListFromArrayLike ( obj [ , elementTypes ] ) + * + *

https://262.ecma-international.org/12.0/#sec-createlistfromarraylike + */ + static List createListFromArrayLike(Context cx, Scriptable o, String[] elementTypes) { + if (elementTypes == null ) { + elementTypes = new String[] {"Undefined", "Null", "Boolean", "String", "Symbol", "Number", "BigInt", "Scriptable"}; + } + + ScriptableObject obj = ScriptableObject.ensureScriptableObject(o); + if (obj instanceof NativeArray) { + return Arrays.asList(((NativeArray) obj).toArray()); + } + + long len = lengthOfArrayLike(cx, obj); + List list = new ArrayList<>(); + long index = 0; + while (index < len) { + String indexName = ScriptRuntime.toString(index); + Object next = ScriptableObject.getProperty(obj, (int) index); + // ToDo use provided types + if (next instanceof NativeString + || ScriptRuntime.isSymbol(next)) { + list.add(next); + } + index++; + } + return list; + } + + /** + * LengthOfArrayLike ( obj ) + * + *

https://262.ecma-international.org/12.0/#sec-lengthofarraylike + */ + static long lengthOfArrayLike(Context cx, Scriptable o) { + Object value = ScriptableObject.getProperty(o, "length"); + long len = ScriptRuntime.toLength(new Object[] {value}, 0); + return len; + } } diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index 01b2df61ba..8c4c31e431 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -6,6 +6,10 @@ package org.mozilla.javascript; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * This class implements the Proxy object. * @@ -72,7 +76,6 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) { return obj; } }; - // constructor.setPrototypePropertyAttributes(DONTENUM | READONLY | PERMANENT); constructor.setPrototypeProperty(null); constructor.defineConstructorMethod( @@ -118,13 +121,45 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) { return ((Constructable) target).construct(cx, scope, args); } + /** + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "has"). + * 7. If trap is undefined, then + * a. Return ? target.[[HasProperty]](P). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). + * 9. If booleanTrapResult is false, then + * a. Let targetDesc be ? target.[[GetOwnProperty]](P). + * b. If targetDesc is not undefined, then + * i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + * ii. Let extensibleTarget be ? IsExtensible(target). + * iii. If extensibleTarget is false, throw a TypeError exception. + * 10. Return booleanTrapResult. + */ @Override public boolean has(String name, Scriptable start) { assertNotRevoked(); Callable trap = getTrap(TRAP_HAS); if (trap != null) { - return (boolean) callTrap(trap, new Object[] {target, name}); + + boolean booleanTrapResult = ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, name})); + if (!booleanTrapResult) { + ScriptableObject targetDesc = target.getOwnPropertyDescriptor(Context.getCurrentContext(), name); + if (targetDesc != null) { + if (Boolean.FALSE.equals(targetDesc.get("configurable"))) { + throw ScriptRuntime.typeError("proxy can't report an existing own property '\"attr\"' as non-existent on a non-extensible object"); + } + if (!target.isExtensible()) { + throw ScriptRuntime.typeError("proxy can't report an existing own property '\"attr\"' as non-existent on a non-extensible object"); + } + } + } + return booleanTrapResult; } return ScriptableObject.hasProperty(target, name); @@ -154,6 +189,44 @@ public boolean has(Symbol key, Scriptable start) { return ScriptableObject.hasProperty(target, key); } + /** + * see https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys + * + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Let trap be ? GetMethod(handler, "ownKeys"). + * 6. If trap is undefined, then + * a. Return ? target.[[OwnPropertyKeys]](). + * 7. Let trapResultArray be ? Call(trap, handler, « target »). + * 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String, Symbol »). + * 9. If trapResult contains any duplicate entries, throw a TypeError exception. + * 10. Let extensibleTarget be ? IsExtensible(target). + * 11. Let targetKeys be ? target.[[OwnPropertyKeys]](). + * 12. Assert: targetKeys is a List whose elements are only String and Symbol values. + * 13. Assert: targetKeys contains no duplicate entries. + * 14. Let targetConfigurableKeys be a new empty List. + * 15. Let targetNonconfigurableKeys be a new empty List. + * 16. For each element key of targetKeys, do + * a. Let desc be ? target.[[GetOwnProperty]](key). + * b. If desc is not undefined and desc.[[Configurable]] is false, then + * i. i. Append key as an element of targetNonconfigurableKeys. + * c. Else, + i. i. Append key as an element of targetConfigurableKeys. + * 17. If extensibleTarget is true and targetNonconfigurableKeys is empty, then + * a. Return trapResult. + * 18. Let uncheckedResultKeys be a List whose elements are the elements of trapResult. + * 19. For each element key of targetNonconfigurableKeys, do + * a. a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. + * b. Remove key from uncheckedResultKeys. + * 20. If extensibleTarget is true, return trapResult. + * 21. For each element key of targetConfigurableKeys, do + * a. a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. + * b. Remove key from uncheckedResultKeys. + * 22. If uncheckedResultKeys is not empty, throw a TypeError exception. + * 23. Return trapResult. + */ @Override public Object[] getIds() { assertNotRevoked(); @@ -161,10 +234,56 @@ public Object[] getIds() { Callable trap = getTrap(TRAP_OWN_KEYS); if (trap != null) { Object res = callTrap(trap, new Object[] {target}); - if (res instanceof NativeArray) { - return ((NativeArray) res).toArray(); + if (!(res instanceof Scriptable) || !ScriptRuntime.isArrayLike((Scriptable)res)) { + throw ScriptRuntime.typeError("ToDo"); + } + + Context cx = Context.getCurrentContext(); + List trapResult = AbstractEcmaObjectOperations.createListFromArrayLike(cx, (Scriptable) res, new String[] {"String", "Symbol"}); + + boolean extensibleTarget = target.isExtensible(); + Object[] targetKeys = target.getIds(); + + ArrayList targetConfigurableKeys = new ArrayList<>(); + ArrayList targetNonconfigurableKeys = new ArrayList<>(); + for (Object targetKey : targetKeys) { + ScriptableObject desc = target.getOwnPropertyDescriptor(cx, targetKey); + if (desc != null&& Boolean.FALSE.equals(desc.get("configurable"))) { + targetNonconfigurableKeys.add(targetKey); + } + else { + targetConfigurableKeys.add(targetKey); + } + } + + if (extensibleTarget && targetNonconfigurableKeys.size() == 0) { + return trapResult.toArray(); + } + + // ToDo Set? + List uncheckedResultKeys = Arrays.asList(trapResult); + for (Object key : targetNonconfigurableKeys) { + if (!uncheckedResultKeys.contains(key)) { + throw ScriptRuntime.typeError("ToDo"); + } + uncheckedResultKeys.remove(key); + } + if (extensibleTarget) { + return trapResult.toArray(); + } + + for (Object key : targetConfigurableKeys) { + if (!uncheckedResultKeys.contains(key)) { + throw ScriptRuntime.typeError("ToDo"); + } + uncheckedResultKeys.remove(key); } - return (Object[]) res; + + if (uncheckedResultKeys.size() > 0) { + throw ScriptRuntime.typeError("ToDo"); + } + + return trapResult.toArray(); } return target.getIds(); @@ -333,28 +452,66 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { target.defineOwnProperty(cx, id, desc); } + /** + * see https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-isextensible + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Let trap be ? GetMethod(handler, "isExtensible"). + * 6. If trap is undefined, then + * a. a. Return ? IsExtensible(target). + * 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)). + * 8. Let targetResult be ? IsExtensible(target). + * 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. + * 10. Return booleanTrapResult. + */ @Override public boolean isExtensible() { assertNotRevoked(); Callable trap = getTrap(TRAP_IS_EXTENSIBLE); - if (trap != null) { - callTrap(trap, new Object[] {target}); - } - - return target.isExtensible(); - } - + if (trap == null) { + return target.isExtensible(); + } + + boolean booleanTrapResult = ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target})); + boolean targetResult = target.isExtensible(); + + if (booleanTrapResult != targetResult) { + throw ScriptRuntime.typeError("IsExtensible trap has to return the same value as the target"); + } + return booleanTrapResult; + } + + /** + * see https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Let trap be ? GetMethod(handler, "preventExtensions"). + * 6. If trap is undefined, then + * a. Return ? target.[[PreventExtensions]](). + * 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)). + * 8. If booleanTrapResult is true, then + * a. Let extensibleTarget be ? IsExtensible(target). + * b. If extensibleTarget is true, throw a TypeError exception. + * 9. Return booleanTrapResult. + */ @Override public void preventExtensions() { assertNotRevoked(); Callable trap = getTrap(TRAP_PREVENT_EXTENSIONS); - if (trap != null) { - callTrap(trap, new Object[] {target}); + if (trap == null) { + target.preventExtensions(); + return; + } + callTrap(trap, new Object[] {target}); + if (target.isExtensible()) { + throw ScriptRuntime.typeError("target is not extensible"); } - - target.preventExtensions(); } @Override @@ -435,7 +592,7 @@ private static Object revocable(Context cx, Scriptable scope, Scriptable thisObj NativeObject revocable = (NativeObject) cx.newObject(scope); revocable.put("proxy", revocable, proxy); - revocable.put("revoke", revocable, new Revoker(proxy)); + revocable.put("revoke", revocable, new LambdaFunction(scope, "revoke", 0, new Revoker(proxy))); return revocable; } diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index c4c0224cac..bd016395e8 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -369,6 +369,7 @@ public void isExtensible() { + "var handler = {\n" + " isExtensible(target) {\n" + " res += ' a ' + (target == o);\n" + + " return Reflect.isExtensible(target);" + " },\n" + " preventExtensions(target) {\n" + " res += ' o ' + (target == o);\n" @@ -377,16 +378,17 @@ public void isExtensible() { + "var proxy1 = new Proxy(o, handler);\n" + "var x = Object.isExtensible(proxy1);\n" + "res += ' ' + x;\n" - + "x = Object.preventExtensions(proxy1);\n" - + "res += ' ' + x;\n" - + "x = Object.isExtensible(proxy1);\n" - + "res += ' ' + x;\n" - - + "var o2 = Object.seal({});\n" - + "var proxy2 = new Proxy(o2, handler);\n" - + "x = Object.isExtensible(proxy2);\n" +// + "x = Object.preventExtensions(proxy1);\n" +// + "res += ' ' + x;\n" +// + "x = Object.isExtensible(proxy1);\n" +// + "res += ' ' + x;\n" +// +// + "var o2 = Object.seal({});\n" +// + "var proxy2 = new Proxy(o2, handler);\n" +// + "x = Object.isExtensible(proxy2);\n" + "res += ' ' + x;\n"; - testString(" a true true o true [object Object] a true false a false false", js); +// testString(" a true true o true [object Object] a true false a false false", js); + testString(" a true true true", js); } @Test @@ -501,6 +503,56 @@ public void ownKeysWithoutHandlerNotEnumerable() { testString("", js); } + @Test + public void hasTargetNotExtensible() { + String js = + "var target = {};\n" + + + "var handler = {\n" + + " has: function(t, prop) {\n" + + " return 0;\n" + + " }\n" + + "};\n" + + + "var p = new Proxy(target, handler);\n" + + + "Object.defineProperty(target, 'attr', {\n" + + " configurable: true,\n" + + " extensible: false,\n" + + " value: 1\n" + + "});\n" + + + "Object.preventExtensions(target);\n" + + "try { 'attr' in p; } catch(e) { '' + e }\n"; + + testString("TypeError: proxy can't report an existing own property '\"attr\"' as non-existent on a non-extensible object", js); + } + + @Test + public void hasHandlerCallsIn() { + String js = + "var _handler, _target, _prop;\n" + + + "var target = {};\n" + + "var handler = {\n" + + " has: function(t, prop) {\n" + + " _handler = this;\n" + + " _target = t;\n" + + " _prop = prop;\n" +// + " return prop in t;\n" + + " return false;\n" + + " }\n" + + "};\n" + + "var p = new Proxy(target, handler);\r\n" + + + "'' + (_handler === handler)\n" + + "+ ' ' + (_target === target)" + + "+ ' ' + ('attr' === _prop)" + + "+ ' ' + ('attr' in p)"; + + testString("false false false false", js); + } + @Test public void hasWithoutHandler() { String js = From b51b7e8f2dddb8a4153c8006e4e0c3336c02d3c7 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Thu, 17 Aug 2023 17:16:57 +0200 Subject: [PATCH 10/24] built-ins/Proxy 93/306 (30.39%) --- .../AbstractEcmaObjectOperations.java | 25 +- src/org/mozilla/javascript/NativeProxy.java | 635 ++++++++++++++---- src/org/mozilla/javascript/NativeReflect.java | 104 ++- .../javascript/tests/es6/NativeProxyTest.java | 333 ++++----- .../tests/es6/NativeReflectTest.java | 51 +- 5 files changed, 755 insertions(+), 393 deletions(-) diff --git a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java index 6166bf1491..c01b97ad7c 100644 --- a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java +++ b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.Predicate; /** * Abstract Object Operations as defined by EcmaScript @@ -242,27 +243,29 @@ static void put(Context cx, Scriptable o, int p, Object v, boolean isThrow) { * *

https://262.ecma-international.org/12.0/#sec-createlistfromarraylike */ - static List createListFromArrayLike(Context cx, Scriptable o, String[] elementTypes) { - if (elementTypes == null ) { - elementTypes = new String[] {"Undefined", "Null", "Boolean", "String", "Symbol", "Number", "BigInt", "Scriptable"}; - } - + static List createListFromArrayLike( + Context cx, Scriptable o, Predicate elementTypesPredicate, String msg) { ScriptableObject obj = ScriptableObject.ensureScriptableObject(o); if (obj instanceof NativeArray) { - return Arrays.asList(((NativeArray) obj).toArray()); + Object[] arr = ((NativeArray) obj).toArray(); + for (Object next : arr) { + if (!elementTypesPredicate.test(next)) { + throw ScriptRuntime.typeError(msg); + } + } + return Arrays.asList(arr); } long len = lengthOfArrayLike(cx, obj); List list = new ArrayList<>(); long index = 0; while (index < len) { - String indexName = ScriptRuntime.toString(index); + // String indexName = ScriptRuntime.toString(index); Object next = ScriptableObject.getProperty(obj, (int) index); - // ToDo use provided types - if (next instanceof NativeString - || ScriptRuntime.isSymbol(next)) { - list.add(next); + if (!elementTypesPredicate.test(next)) { + throw ScriptRuntime.typeError(msg); } + list.add(next); index++; } return list; diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index 8c4c31e431..233eabb672 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -7,7 +7,7 @@ package org.mozilla.javascript; import java.util.ArrayList; -import java.util.Arrays; +import java.util.HashSet; import java.util.List; /** @@ -41,13 +41,12 @@ final class NativeProxy extends ScriptableObject implements Callable, Constructa private static final class Revoker implements Callable { private NativeProxy revocableProxy = null; - public Revoker(NativeProxy proxy){ + public Revoker(NativeProxy proxy) { revocableProxy = proxy; } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) { + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (revocableProxy != null) { revocableProxy.handler = null; revocableProxy.target = null; @@ -57,7 +56,6 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, } } - public static void init(Context cx, Scriptable scope, boolean sealed) { LambdaConstructor constructor = new LambdaConstructor( @@ -67,15 +65,16 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { LambdaConstructor.CONSTRUCTOR_NEW, NativeProxy::constructor) { - @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) { - NativeProxy obj = (NativeProxy) getTargetConstructor().construct(cx, scope, args); - // avoid getting trapped - obj.setPrototypeDirect(getClassPrototype()); - obj.setParentScope(scope); - return obj; - } - }; + @Override + public Scriptable construct(Context cx, Scriptable scope, Object[] args) { + NativeProxy obj = + (NativeProxy) getTargetConstructor().construct(cx, scope, args); + // avoid getting trapped + obj.setPrototypeDirect(getClassPrototype()); + obj.setParentScope(scope); + return obj; + } + }; constructor.setPrototypeProperty(null); constructor.defineConstructorMethod( @@ -93,8 +92,7 @@ private NativeProxy(ScriptableObject target, Scriptable handler) { if (target == null || !(target instanceof Callable)) { typeOf = super.getTypeOf(); - } - else { + } else { typeOf = target.getTypeOf(); } } @@ -105,8 +103,26 @@ public String getClassName() { return target.getClassName(); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget + */ @Override public Scriptable construct(Context cx, Scriptable scope, Object[] args) { + /* + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Assert: IsConstructor(target) is true. + * 6. Let trap be ? GetMethod(handler, "construct"). + * 7. If trap is undefined, then + * a. Return ? Construct(target, argumentsList, newTarget). + * 8. Let argArray be ! CreateArrayFromList(argumentsList). + * 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »). + * 10. If Type(newObj) is not Object, throw a TypeError exception. + * 11. Return newObj. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_CONSTRUCT); @@ -123,39 +139,44 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) { /** * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p - * 1. Assert: IsPropertyKey(P) is true. - * 2. Let handler be O.[[ProxyHandler]]. - * 3. If handler is null, throw a TypeError exception. - * 4. Assert: Type(handler) is Object. - * 5. Let target be O.[[ProxyTarget]]. - * 6. Let trap be ? GetMethod(handler, "has"). - * 7. If trap is undefined, then - * a. Return ? target.[[HasProperty]](P). - * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). - * 9. If booleanTrapResult is false, then - * a. Let targetDesc be ? target.[[GetOwnProperty]](P). - * b. If targetDesc is not undefined, then - * i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. - * ii. Let extensibleTarget be ? IsExtensible(target). - * iii. If extensibleTarget is false, throw a TypeError exception. - * 10. Return booleanTrapResult. */ @Override public boolean has(String name, Scriptable start) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "has"). + * 7. If trap is undefined, then + * a. Return ? target.[[HasProperty]](P). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). + * 9. If booleanTrapResult is false, then + * a. Let targetDesc be ? target.[[GetOwnProperty]](P). + * b. If targetDesc is not undefined, then + * i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + * ii. Let extensibleTarget be ? IsExtensible(target). + * iii. If extensibleTarget is false, throw a TypeError exception. + * 10. Return booleanTrapResult. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_HAS); if (trap != null) { - boolean booleanTrapResult = ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, name})); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, name})); if (!booleanTrapResult) { - ScriptableObject targetDesc = target.getOwnPropertyDescriptor(Context.getCurrentContext(), name); + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), name); if (targetDesc != null) { - if (Boolean.FALSE.equals(targetDesc.get("configurable"))) { - throw ScriptRuntime.typeError("proxy can't report an existing own property '\"attr\"' as non-existent on a non-extensible object"); - } - if (!target.isExtensible()) { - throw ScriptRuntime.typeError("proxy can't report an existing own property '\"attr\"' as non-existent on a non-extensible object"); + if (Boolean.FALSE.equals(targetDesc.get("configurable")) + || !target.isExtensible()) { + throw ScriptRuntime.typeError( + "proxy can't report an existing own property '" + + name + + "' as non-existent on a non-extensible object"); } } } @@ -165,93 +186,165 @@ public boolean has(String name, Scriptable start) { return ScriptableObject.hasProperty(target, name); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p + */ @Override public boolean has(int index, Scriptable start) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "has"). + * 7. If trap is undefined, then + * a. Return ? target.[[HasProperty]](P). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). + * 9. If booleanTrapResult is false, then + * a. Let targetDesc be ? target.[[GetOwnProperty]](P). + * b. If targetDesc is not undefined, then + * i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + * ii. Let extensibleTarget be ? IsExtensible(target). + * iii. If extensibleTarget is false, throw a TypeError exception. + * 10. Return booleanTrapResult. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_HAS); if (trap != null) { - return (boolean) callTrap(trap, new Object[] {target, index}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, index})); + if (!booleanTrapResult) { + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), index); + if (targetDesc != null) { + if (Boolean.FALSE.equals(targetDesc.get("configurable")) + || !target.isExtensible()) { + throw ScriptRuntime.typeError( + "proxy can't check an existing property ' + name + ' existance on an not configurable or not extensible object"); + } + } + } + + return booleanTrapResult; } return ScriptableObject.hasProperty(target, index); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p + */ @Override public boolean has(Symbol key, Scriptable start) { assertNotRevoked(); Callable trap = getTrap(TRAP_HAS); if (trap != null) { - return (boolean) callTrap(trap, new Object[] {target, key}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, key})); + if (!booleanTrapResult) { + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), key); + if (targetDesc != null) { + if (Boolean.FALSE.equals(targetDesc.get("configurable")) + || !target.isExtensible()) { + throw ScriptRuntime.typeError( + "proxy can't check an existing property ' + name + ' existance on an not configurable or not extensible object"); + } + } + } + + return booleanTrapResult; } return ScriptableObject.hasProperty(target, key); } /** - * see https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys - * - * 1. Let handler be O.[[ProxyHandler]]. - * 2. If handler is null, throw a TypeError exception. - * 3. Assert: Type(handler) is Object. - * 4. Let target be O.[[ProxyTarget]]. - * 5. Let trap be ? GetMethod(handler, "ownKeys"). - * 6. If trap is undefined, then - * a. Return ? target.[[OwnPropertyKeys]](). - * 7. Let trapResultArray be ? Call(trap, handler, « target »). - * 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String, Symbol »). - * 9. If trapResult contains any duplicate entries, throw a TypeError exception. - * 10. Let extensibleTarget be ? IsExtensible(target). - * 11. Let targetKeys be ? target.[[OwnPropertyKeys]](). - * 12. Assert: targetKeys is a List whose elements are only String and Symbol values. - * 13. Assert: targetKeys contains no duplicate entries. - * 14. Let targetConfigurableKeys be a new empty List. - * 15. Let targetNonconfigurableKeys be a new empty List. - * 16. For each element key of targetKeys, do - * a. Let desc be ? target.[[GetOwnProperty]](key). - * b. If desc is not undefined and desc.[[Configurable]] is false, then - * i. i. Append key as an element of targetNonconfigurableKeys. - * c. Else, - i. i. Append key as an element of targetConfigurableKeys. - * 17. If extensibleTarget is true and targetNonconfigurableKeys is empty, then - * a. Return trapResult. - * 18. Let uncheckedResultKeys be a List whose elements are the elements of trapResult. - * 19. For each element key of targetNonconfigurableKeys, do - * a. a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. - * b. Remove key from uncheckedResultKeys. - * 20. If extensibleTarget is true, return trapResult. - * 21. For each element key of targetConfigurableKeys, do - * a. a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. - * b. Remove key from uncheckedResultKeys. - * 22. If uncheckedResultKeys is not empty, throw a TypeError exception. - * 23. Return trapResult. + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys */ @Override - public Object[] getIds() { + Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { + /* + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Let trap be ? GetMethod(handler, "ownKeys"). + * 6. If trap is undefined, then + * a. Return ? target.[[OwnPropertyKeys]](). + * 7. Let trapResultArray be ? Call(trap, handler, « target »). + * 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String, Symbol »). + * 9. If trapResult contains any duplicate entries, throw a TypeError exception. + * 10. Let extensibleTarget be ? IsExtensible(target). + * 11. Let targetKeys be ? target.[[OwnPropertyKeys]](). + * 12. Assert: targetKeys is a List whose elements are only String and Symbol values. + * 13. Assert: targetKeys contains no duplicate entries. + * 14. Let targetConfigurableKeys be a new empty List. + * 15. Let targetNonconfigurableKeys be a new empty List. + * 16. For each element key of targetKeys, do + * a. Let desc be ? target.[[GetOwnProperty]](key). + * b. If desc is not undefined and desc.[[Configurable]] is false, then + * i. i. Append key as an element of targetNonconfigurableKeys. + * c. Else, + i. i. Append key as an element of targetConfigurableKeys. + * 17. If extensibleTarget is true and targetNonconfigurableKeys is empty, then + * a. Return trapResult. + * 18. Let uncheckedResultKeys be a List whose elements are the elements of trapResult. + * 19. For each element key of targetNonconfigurableKeys, do + * a. a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. + * b. Remove key from uncheckedResultKeys. + * 20. If extensibleTarget is true, return trapResult. + * 21. For each element key of targetConfigurableKeys, do + * a. a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. + * b. Remove key from uncheckedResultKeys. + * 22. If uncheckedResultKeys is not empty, throw a TypeError exception. + * 23. Return trapResult. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_OWN_KEYS); if (trap != null) { Object res = callTrap(trap, new Object[] {target}); - if (!(res instanceof Scriptable) || !ScriptRuntime.isArrayLike((Scriptable)res)) { - throw ScriptRuntime.typeError("ToDo"); + if (!(res instanceof Scriptable)) { + throw ScriptRuntime.typeError("ownKeys trap must be an object"); + } + if (!ScriptRuntime.isArrayLike((Scriptable) res)) { + throw ScriptRuntime.typeError("ownKeys trap must be an array like object"); } - Context cx = Context.getCurrentContext(); - List trapResult = AbstractEcmaObjectOperations.createListFromArrayLike(cx, (Scriptable) res, new String[] {"String", "Symbol"}); + Context cx = Context.getContext(); + + List trapResult = + AbstractEcmaObjectOperations.createListFromArrayLike( + cx, + (Scriptable) res, + (o) -> + o instanceof CharSequence + || o instanceof NativeString + || ScriptRuntime.isSymbol(o), + "proxy [[OwnPropertyKeys]] must return an array with only string and symbol elements"); boolean extensibleTarget = target.isExtensible(); - Object[] targetKeys = target.getIds(); + Object[] targetKeys = target.getIds(getNonEnumerable, getSymbols); + + HashSet uncheckedResultKeys = new HashSet(trapResult); + if (uncheckedResultKeys.size() != trapResult.size()) { + throw ScriptRuntime.typeError("ownKeys trap result must not contain duplicates"); + } ArrayList targetConfigurableKeys = new ArrayList<>(); ArrayList targetNonconfigurableKeys = new ArrayList<>(); for (Object targetKey : targetKeys) { ScriptableObject desc = target.getOwnPropertyDescriptor(cx, targetKey); - if (desc != null&& Boolean.FALSE.equals(desc.get("configurable"))) { + if (desc != null && Boolean.FALSE.equals(desc.get("configurable"))) { targetNonconfigurableKeys.add(targetKey); - } - else { + } else { targetConfigurableKeys.add(targetKey); } } @@ -260,11 +353,10 @@ public Object[] getIds() { return trapResult.toArray(); } - // ToDo Set? - List uncheckedResultKeys = Arrays.asList(trapResult); for (Object key : targetNonconfigurableKeys) { if (!uncheckedResultKeys.contains(key)) { - throw ScriptRuntime.typeError("ToDo"); + throw ScriptRuntime.typeError( + "proxy can't skip a non-configurable property " + key); } uncheckedResultKeys.remove(key); } @@ -274,26 +366,20 @@ public Object[] getIds() { for (Object key : targetConfigurableKeys) { if (!uncheckedResultKeys.contains(key)) { - throw ScriptRuntime.typeError("ToDo"); + throw ScriptRuntime.typeError( + "proxy can't skip a configurable property " + key); } uncheckedResultKeys.remove(key); } if (uncheckedResultKeys.size() > 0) { - throw ScriptRuntime.typeError("ToDo"); + throw ScriptRuntime.typeError("proxy can't skip properties"); } return trapResult.toArray(); } - return target.getIds(); - } - - @Override - public Object[] getAllIds() { - assertNotRevoked(); - - return target.getAllIds(); + return target.getIds(getNonEnumerable, getSymbols); } @Override @@ -378,55 +464,207 @@ public void put(Symbol key, Scriptable start, Object value) { symbolScriptableTarget.put(key, start, value); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-delete-p + */ @Override public void delete(String name) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "deleteProperty"). + * 7. If trap is undefined, then + * a. Return ? target.[[Delete]](P). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). + * 9. If booleanTrapResult is false, return false. + * 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 11. If targetDesc is undefined, return true. + * 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + * 13. Let extensibleTarget be ? IsExtensible(target). + * 14. If extensibleTarget is false, throw a TypeError exception. + * 15. Return true. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_DELETE_PROPERTY); if (trap != null) { - callTrap(trap, new Object[] {target, name}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, name})); + if (!booleanTrapResult) { + return; // false + } + + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), name); + if (targetDesc == null) { + return; // true + } + if (Boolean.FALSE.equals(targetDesc.get("configurable")) || !target.isExtensible()) { + throw ScriptRuntime.typeError( + "proxy can't delete an existing own property ' + name + ' on an not configurable or not extensible object"); + } + + return; // true } target.delete(name); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-delete-p + */ @Override public void delete(int index) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "deleteProperty"). + * 7. If trap is undefined, then + * a. Return ? target.[[Delete]](P). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). + * 9. If booleanTrapResult is false, return false. + * 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 11. If targetDesc is undefined, return true. + * 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + * 13. Let extensibleTarget be ? IsExtensible(target). + * 14. If extensibleTarget is false, throw a TypeError exception. + * 15. Return true. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_DELETE_PROPERTY); if (trap != null) { - callTrap(trap, new Object[] {target, index}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, index})); + if (!booleanTrapResult) { + return; // false + } + + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), index); + if (targetDesc == null) { + return; // true + } + if (Boolean.FALSE.equals(targetDesc.get("configurable")) || !target.isExtensible()) { + throw ScriptRuntime.typeError( + "proxy can't delete an existing own property ' + name + ' on an not configurable or not extensible object"); + } + + return; // true } target.delete(index); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-delete-p + */ @Override public void delete(Symbol key) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "deleteProperty"). + * 7. If trap is undefined, then + * a. Return ? target.[[Delete]](P). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). + * 9. If booleanTrapResult is false, return false. + * 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 11. If targetDesc is undefined, return true. + * 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + * 13. Let extensibleTarget be ? IsExtensible(target). + * 14. If extensibleTarget is false, throw a TypeError exception. + * 15. Return true. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_DELETE_PROPERTY); if (trap != null) { - callTrap(trap, new Object[] {target, key}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, key})); + if (!booleanTrapResult) { + return; // false + } + + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), key); + if (targetDesc == null) { + return; // true + } + if (Boolean.FALSE.equals(targetDesc.get("configurable")) || !target.isExtensible()) { + throw ScriptRuntime.typeError( + "proxy can't delete an existing own property ' + name + ' on an not configurable or not extensible object"); + } + + return; // true } SymbolScriptable symbolScriptableTarget = ensureSymbolScriptable(target); symbolScriptableTarget.delete(key); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p + */ @Override protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). + * 7. If trap is undefined, then + * a. Return ? target.[[GetOwnProperty]](P). + * 8. Let trapResultObj be ? Call(trap, handler, « target, P »). + * 9. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception. + * 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 11. If trapResultObj is undefined, then + * a. If targetDesc is undefined, return undefined. + * b. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + * c. Let extensibleTarget be ? IsExtensible(target). + * d. If extensibleTarget is false, throw a TypeError exception. + * e. Return undefined. + * 12. Let extensibleTarget be ? IsExtensible(target). + * 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj). + * 14. Call CompletePropertyDescriptor(resultDesc). + * 15. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc). + * 16. If valid is false, throw a TypeError exception. + * 17. If resultDesc.[[Configurable]] is false, then + * a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then + * i. Throw a TypeError exception. + * b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then + * i. If targetDesc.[[Writable]] is true, throw a TypeError exception. + * 18. Return resultDesc. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_GET_OWN_PROPERTY_DESCRIPTOR); if (trap != null) { - ScriptableObject proxiedDescriptor = (ScriptableObject) callTrap(trap, new Object[] {target, id}); + ScriptableObject proxiedDescriptor = + (ScriptableObject) callTrap(trap, new Object[] {target, id}); if (proxiedDescriptor != null) { Object value = ScriptableObject.getProperty(proxiedDescriptor, "value"); - int attributes = applyDescriptorToAttributeBitset(DONTENUM | READONLY | PERMANENT, proxiedDescriptor); + int attributes = + applyDescriptorToAttributeBitset( + DONTENUM | READONLY | PERMANENT, proxiedDescriptor); - ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, attributes); + ScriptableObject desc = + ScriptableObject.buildDataDescriptor(target, value, attributes); return desc; } // TODO @@ -440,8 +678,39 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { return target.getOwnPropertyDescriptor(cx, ScriptRuntime.toString(id)); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc + */ @Override public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "defineProperty"). + * 7. If trap is undefined, then + * a. Return ? target.[[DefineOwnProperty]](P, Desc). + * 8. Let descObj be FromPropertyDescriptor(Desc). + * 9. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)). + * 10. If booleanTrapResult is false, return false. + * 11. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 12. Let extensibleTarget be ? IsExtensible(target). + * 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then + * a. Let settingConfigFalse be true. + * 14. Else, let settingConfigFalse be false. + * 15. If targetDesc is undefined, then + * a. If extensibleTarget is false, throw a TypeError exception. + * b. If settingConfigFalse is true, throw a TypeError exception. + * 16. Else, + * a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception. + * b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception. + * c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then + * i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. + * 17. Return true. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_DEFINE_PROPERTY); @@ -453,21 +722,24 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { } /** - * see https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-isextensible - * 1. Let handler be O.[[ProxyHandler]]. - * 2. If handler is null, throw a TypeError exception. - * 3. Assert: Type(handler) is Object. - * 4. Let target be O.[[ProxyTarget]]. - * 5. Let trap be ? GetMethod(handler, "isExtensible"). - * 6. If trap is undefined, then - * a. a. Return ? IsExtensible(target). - * 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)). - * 8. Let targetResult be ? IsExtensible(target). - * 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. - * 10. Return booleanTrapResult. + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-isextensible */ @Override public boolean isExtensible() { + /* + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Let trap be ? GetMethod(handler, "isExtensible"). + * 6. If trap is undefined, then + * a. a. Return ? IsExtensible(target). + * 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)). + * 8. Let targetResult be ? IsExtensible(target). + * 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. + * 10. Return booleanTrapResult. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_IS_EXTENSIBLE); @@ -479,28 +751,32 @@ public boolean isExtensible() { boolean targetResult = target.isExtensible(); if (booleanTrapResult != targetResult) { - throw ScriptRuntime.typeError("IsExtensible trap has to return the same value as the target"); + throw ScriptRuntime.typeError( + "IsExtensible trap has to return the same value as the target"); } return booleanTrapResult; } /** - * see https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions - * 1. Let handler be O.[[ProxyHandler]]. - * 2. If handler is null, throw a TypeError exception. - * 3. Assert: Type(handler) is Object. - * 4. Let target be O.[[ProxyTarget]]. - * 5. Let trap be ? GetMethod(handler, "preventExtensions"). - * 6. If trap is undefined, then - * a. Return ? target.[[PreventExtensions]](). - * 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)). - * 8. If booleanTrapResult is true, then - * a. Let extensibleTarget be ? IsExtensible(target). - * b. If extensibleTarget is true, throw a TypeError exception. - * 9. Return booleanTrapResult. + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions */ @Override public void preventExtensions() { + /* + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Let trap be ? GetMethod(handler, "preventExtensions"). + * 6. If trap is undefined, then + * a. Return ? target.[[PreventExtensions]](). + * 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)). + * 8. If booleanTrapResult is true, then + * a. Let extensibleTarget be ? IsExtensible(target). + * b. If extensibleTarget is true, throw a TypeError exception. + * 9. Return booleanTrapResult. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_PREVENT_EXTENSIONS); @@ -519,13 +795,46 @@ public String getTypeOf() { return typeOf; } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof + */ @Override public Scriptable getPrototype() { + /* + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). + * 6. If trap is undefined, then + * a. Return ? target.[[GetPrototypeOf]](). + * 7. Let handlerProto be ? Call(trap, handler, « target »). + * 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception. + * 9. Let extensibleTarget be ? IsExtensible(target). + * 10. If extensibleTarget is true, return handlerProto. + * 11. Let targetProto be ? target.[[GetPrototypeOf]](). + * 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception. + * 13. Return handlerProto. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_GET_PROTOTYPE_OF); if (trap != null) { - return (ScriptableObject) callTrap(trap, new Object[] {target}); + Object handlerProto = callTrap(trap, new Object[] {target}); + + Scriptable handlerProtoScriptable = Undefined.SCRIPTABLE_UNDEFINED; + if (!Undefined.isUndefined(handlerProto)) { + handlerProtoScriptable = ensureScriptable(handlerProto); + } + if (target.isExtensible()) { + return handlerProtoScriptable; + } + if (handlerProto != target.getPrototype()) { + throw ScriptRuntime.typeError( + "getPrototypeOf trap has to return the original prototype"); + } + return handlerProtoScriptable; } return target.getPrototype(); @@ -535,21 +844,65 @@ private void setPrototypeDirect(Scriptable prototype) { super.setPrototype(prototype); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v + */ @Override public void setPrototype(Scriptable prototype) { + /* + * 1. Assert: Either Type(V) is Object or Type(V) is Null. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "setPrototypeOf"). + * 7. If trap is undefined, then + * a. Return ? target.[[SetPrototypeOf]](V). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, V »)). + * 9. If booleanTrapResult is false, return false. + * 10. Let extensibleTarget be ? IsExtensible(target). + * 11. If extensibleTarget is true, return true. + * 12. Let targetProto be ? target.[[SetPrototypeOf]](). + * 13. If SameValue(V, targetProto) is false, throw a TypeError exception. + * 14. Return true. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_SET_PROTOTYPE_OF); if (trap != null) { - callTrap(trap, new Object[] {target, prototype}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, prototype})); + if (!booleanTrapResult) { + return; // false + } + if (target.isExtensible()) { + return; // true + } + return; } target.setPrototype(prototype); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist + */ @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + /* + * 1. Let handler be O.[[ProxyHandler]]. + * 2. If handler is null, throw a TypeError exception. + * 3. Assert: Type(handler) is Object. + * 4. Let target be O.[[ProxyTarget]]. + * 5. Let trap be ? GetMethod(handler, "apply"). + * 6. If trap is undefined, then + * a. Return ? Call(target, thisArgument, argumentsList). + * 7. Let argArray be ! CreateArrayFromList(argumentsList). + * 8. Return ? Call(trap, handler, « target, thisArgument, argArray »). + */ assertNotRevoked(); Scriptable argumentsList = cx.newArray(scope, args); @@ -559,7 +912,8 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar return callTrap(trap, new Object[] {target, thisObj, argumentsList}); } - return ScriptRuntime.applyOrCall(true, cx, scope, target, new Object[] {thisObj, argumentsList}); + return ScriptRuntime.applyOrCall( + true, cx, scope, target, new Object[] {thisObj, argumentsList}); } private static NativeProxy constructor(Context cx, Scriptable scope, Object[] args) { @@ -570,11 +924,9 @@ private static NativeProxy constructor(Context cx, Scriptable scope, Object[] ar "2", Integer.toString(args.length)); } - Scriptable s = ScriptRuntime.toObject(cx, scope, args[0]); - ScriptableObject trgt = ensureScriptableObject(s); + ScriptableObject trgt = ensureScriptableObject(args[0]); - s = ScriptRuntime.toObject(cx, scope, args[1]); - ScriptableObject hndlr = ensureScriptableObject(s); + ScriptableObject hndlr = ensureScriptableObject(args[1]); NativeProxy proxy = new NativeProxy(trgt, hndlr); proxy.setPrototypeDirect(ScriptableObject.getClassPrototype(scope, PROXY_TAG)); @@ -583,7 +935,8 @@ private static NativeProxy constructor(Context cx, Scriptable scope, Object[] ar } // Proxy.revocable - private static Object revocable(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Object revocable( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (!ScriptRuntime.isObject(thisObj)) { throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(thisObj)); } @@ -592,7 +945,7 @@ private static Object revocable(Context cx, Scriptable scope, Scriptable thisObj NativeObject revocable = (NativeObject) cx.newObject(scope); revocable.put("proxy", revocable, proxy); - revocable.put("revoke", revocable, new LambdaFunction(scope, "revoke", 0, new Revoker(proxy))); + revocable.put("revoke", revocable, new LambdaFunction(scope, "", 0, new Revoker(proxy))); return revocable; } @@ -612,7 +965,7 @@ private Callable getTrap(String trapName) { } private Object callTrap(Callable trap, Object[] args) { - return trap.call(Context.getCurrentContext(), handler, handler, args); + return trap.call(Context.getContext(), handler, handler, args); } private void assertNotRevoked() { diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index ca12a275be..01b276f266 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -23,25 +23,70 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { NativeReflect reflect = new NativeReflect(); reflect.setPrototype(getObjectPrototype(scope)); reflect.setParentScope(scope); - if (sealed) { - reflect.sealObject(); - } - reflect.defineProperty(scope, "apply", 3, NativeReflect::apply, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "construct", 2, NativeReflect::construct, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "defineProperty", 3, NativeReflect::defineProperty, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "deleteProperty", 2, NativeReflect::deleteProperty, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty( + scope, "apply", 3, NativeReflect::apply, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty( + scope, "construct", 2, NativeReflect::construct, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty( + scope, + "defineProperty", + 3, + NativeReflect::defineProperty, + DONTENUM, + DONTENUM | READONLY); + reflect.defineProperty( + scope, + "deleteProperty", + 2, + NativeReflect::deleteProperty, + DONTENUM, + DONTENUM | READONLY); reflect.defineProperty(scope, "get", 2, NativeReflect::get, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "getOwnPropertyDescriptor", 2, NativeReflect::getOwnPropertyDescriptor, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "getPrototypeOf", 1, NativeReflect::getPrototypeOf, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty( + scope, + "getOwnPropertyDescriptor", + 2, + NativeReflect::getOwnPropertyDescriptor, + DONTENUM, + DONTENUM | READONLY); + reflect.defineProperty( + scope, + "getPrototypeOf", + 1, + NativeReflect::getPrototypeOf, + DONTENUM, + DONTENUM | READONLY); reflect.defineProperty(scope, "has", 2, NativeReflect::has, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "isExtensible", 1, NativeReflect::isExtensible, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "ownKeys", 1, NativeReflect::ownKeys, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "preventExtensions", 1, NativeReflect::preventExtensions, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty( + scope, + "isExtensible", + 1, + NativeReflect::isExtensible, + DONTENUM, + DONTENUM | READONLY); + reflect.defineProperty( + scope, "ownKeys", 1, NativeReflect::ownKeys, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty( + scope, + "preventExtensions", + 1, + NativeReflect::preventExtensions, + DONTENUM, + DONTENUM | READONLY); reflect.defineProperty(scope, "set", 3, NativeReflect::set, DONTENUM, DONTENUM | READONLY); - reflect.defineProperty(scope, "setPrototypeOf", 2, NativeReflect::setPrototypeOf, DONTENUM, DONTENUM | READONLY); + reflect.defineProperty( + scope, + "setPrototypeOf", + 2, + NativeReflect::setPrototypeOf, + DONTENUM, + DONTENUM | READONLY); ScriptableObject.defineProperty(scope, REFLECT_TAG, reflect, DONTENUM); + if (sealed) { + reflect.sealObject(); + } } private NativeReflect() {} @@ -95,7 +140,8 @@ private static Object apply(Context cx, Scriptable scope, Scriptable thisObj, Ob true, cx, scope, callable, new Object[] {thisObj, argumentsList}); } - private static Scriptable construct(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Scriptable construct( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (args.length < 1) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -104,11 +150,11 @@ private static Scriptable construct(Context cx, Scriptable scope, Scriptable thi Integer.toString(args.length)); } - if (!(args[0] instanceof Function)) { + if (!(args[0] instanceof Constructable)) { throw ScriptRuntime.typeErrorById("msg.not.ctor", ScriptRuntime.typeof(args[0])); } - Function ctor = (Function) args[0]; + Constructable ctor = (Constructable) args[0]; if (args.length < 2) { return ctor.construct(cx, scope, ScriptRuntime.emptyArgs); } @@ -121,7 +167,8 @@ private static Scriptable construct(Context cx, Scriptable scope, Scriptable thi return ctor.construct(cx, scope, callArgs); } - private static Object defineProperty(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Object defineProperty( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (args.length < 3) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -141,7 +188,8 @@ private static Object defineProperty(Context cx, Scriptable scope, Scriptable th } } - private static Object deleteProperty(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Object deleteProperty( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); if (args.length > 1) { @@ -173,7 +221,8 @@ private static Object get(Context cx, Scriptable scope, Scriptable thisObj, Obje return Undefined.SCRIPTABLE_UNDEFINED; } - private static Scriptable getOwnPropertyDescriptor(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Scriptable getOwnPropertyDescriptor( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); if (args.length > 1) { @@ -189,7 +238,8 @@ private static Scriptable getOwnPropertyDescriptor(Context cx, Scriptable scope, return Undefined.SCRIPTABLE_UNDEFINED; } - private static Scriptable getPrototypeOf(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Scriptable getPrototypeOf( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); return target.getPrototype(); @@ -208,12 +258,14 @@ private static Object has(Context cx, Scriptable scope, Scriptable thisObj, Obje return false; } - private static Object isExtensible(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Object isExtensible( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); return target.isExtensible(); } - private static Scriptable ownKeys(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Scriptable ownKeys( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); final List strings = new ArrayList<>(); @@ -234,7 +286,8 @@ private static Scriptable ownKeys(Context cx, Scriptable scope, Scriptable thisO return cx.newArray(scope, keys); } - private static Object preventExtensions(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Object preventExtensions( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); target.preventExtensions(); @@ -260,7 +313,8 @@ private static Object set(Context cx, Scriptable scope, Scriptable thisObj, Obje return false; } - private static Object setPrototypeOf(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Object setPrototypeOf( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (args.length < 2) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -330,4 +384,4 @@ private void defineProperty( f.setStandardPropertyAttributes(propertyAttributes); defineProperty(name, f, attributes); } -} \ No newline at end of file +} diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index bd016395e8..0cf4705488 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -15,21 +15,28 @@ public void testToString() { testString("[object Object]", "Object.prototype.toString.call(new Proxy({}, {}))"); testString("[object Array]", "Object.prototype.toString.call(new Proxy([], {}))"); - testString("[object Array]", "Object.prototype.toString.call(new Proxy(new Proxy([], {}), {}))"); + testString( + "[object Array]", + "Object.prototype.toString.call(new Proxy(new Proxy([], {}), {}))"); } @Test public void testToStringRevoke() { - String js = "var rev = Proxy.revocable(%s, {});\n" - + "rev.revoke();\n" - + "try {" - + " Object.prototype.toString.call(%s);\n" - + "} catch(e) {" - + " '' + e;" - + "}"; + String js = + "var rev = Proxy.revocable(%s, {});\n" + + "rev.revoke();\n" + + "try {" + + " Object.prototype.toString.call(%s);\n" + + "} catch(e) {" + + " '' + e;" + + "}"; - testString("TypeError: Illegal operation attempted on a revoked proxy", String.format(js, "{}", "rev.proxy")); - testString("TypeError: Illegal operation attempted on a revoked proxy", String.format(js, "[]", "rev.proxy")); + testString( + "TypeError: Illegal operation attempted on a revoked proxy", + String.format(js, "{}", "rev.proxy")); + testString( + "TypeError: Illegal operation attempted on a revoked proxy", + String.format(js, "[]", "rev.proxy")); } @Test @@ -37,30 +44,37 @@ public void prototype() { testString("false", "'' + Object.hasOwnProperty.call(Proxy, 'prototype')"); testString("2", "'' + Proxy.length"); - } @Test public void ctorMissingArgs() { - testString("TypeError: Proxy.ctor: At least 2 arguments required, but only 0 passed", "try { new Proxy() } catch(e) { '' + e }"); - testString("TypeError: Proxy.ctor: At least 2 arguments required, but only 1 passed", "try { new Proxy({}) } catch(e) { '' + e }"); + testString( + "TypeError: Proxy.ctor: At least 2 arguments required, but only 0 passed", + "try { new Proxy() } catch(e) { '' + e }"); + testString( + "TypeError: Proxy.ctor: At least 2 arguments required, but only 1 passed", + "try { new Proxy({}) } catch(e) { '' + e }"); - testString("TypeError: Cannot convert undefined to an object.", "try { new Proxy(undefined, {}) } catch(e) { '' + e }"); - testString("TypeError: Cannot convert null to an object.", "try { new Proxy(null, {}) } catch(e) { '' + e }"); + testString( + "TypeError: Expected argument of type object, but instead had type undefined", + "try { new Proxy(undefined, {}) } catch(e) { '' + e }"); + testString( + "TypeError: Expected argument of type object, but instead had type object", + "try { new Proxy(null, {}) } catch(e) { '' + e }"); } @Test public void ctorAsFunction() { - testString("TypeError: The constructor for Proxy may not be invoked as a function", "try { Proxy() } catch(e) { '' + e }"); + testString( + "TypeError: The constructor for Proxy may not be invoked as a function", + "try { Proxy() } catch(e) { '' + e }"); } @Test public void construct() { String js = "var _target, _handler, _args, _P;\n" - + "function Target() {}\n" - + "var handler = {\n" + " construct: function(t, args, newTarget) {\n" + " _handler = this;\n" @@ -70,9 +84,7 @@ public void construct() { + " return new t(args[0], args[1]);\n" + " }\n" + "};\n" - + "var P = new Proxy(Target, handler);\n" - + "new P(1, 4);\n" + "'' + (_handler === handler)\n" + "+ ' ' + (_target === Target)" @@ -88,7 +100,6 @@ public void apply() { "function sum(a, b) {\n" + " return a + b;\n" + "}\n" - + "var res = '';\n" + "var handler = {\n" + " apply: function (target, thisArg, argumentsList) {\n" @@ -96,7 +107,6 @@ public void apply() { + " return target(argumentsList[0], argumentsList[1]) * 7;\n" + " },\n" + "};\n" - + "var proxy1 = new Proxy(sum, handler);\n" + "var x = ' ' + proxy1(1, 2);\n" + "res + x"; @@ -119,12 +129,9 @@ public void applyParameters() { + " _args = args;\n" + " }\n" + "};\n" - + "var p = new Proxy(target, handler);\n" + "var context = {};\n" - + "p.call(context, 1, 4);\n" - + "'' + (_handler === handler)\n" + "+ ' ' + (_target === target)" + "+ ' ' + (_context === context)" @@ -138,7 +145,6 @@ public void applyTrapIsNull() { String js = "var calls = 0;\n" + "var _context;\n" - + "var target = new Proxy(function() {}, {\n" + " apply: function(_target, context, args) {\n" + " calls++;\n" @@ -146,14 +152,11 @@ public void applyTrapIsNull() { + " return args[0] + args[1];\n" + " }\n" + "})\n" - + "var p = new Proxy(target, {\n" + " apply: null\n" + "});\n" - + "var context = {};\n" + "var res = p.call(context, 1, 2);\n" - + "'' + calls\n" + "+ ' ' + (_context === context)" + "+ ' ' + res"; @@ -167,62 +170,12 @@ public void applyWithoutHandler() { "function sum(a, b) {\n" + " return a + b;\n" + "}\n" - + "var proxy1 = new Proxy(sum, {});\n" + "proxy1(1, 2);"; testDouble(3.0, js); } -// @Test -// public void construct() { -// String js = -// "var d = Reflect.construct(Date, [1776, 6, 4]);\n" -// + "'' + (d instanceof Date) + ' ' + d.getFullYear();"; -// testString("true 1776", js); -// } -// -// @Test -// public void constructNoConstructorNumber() { -// String js = "try {\n" -// + " Reflect.construct(function() {}, [], 1);\n" -// + "} catch(e) {\n" -// + " '' + e;\n" -// + "}"; -// testString("TypeError: \"number\" is not a constructor.", js); -// } -// -// @Test -// public void constructNoConstructorNull() { -// String js = "try {\n" -// + " Reflect.construct(function() {}, [], null);\n" -// + "} catch(e) {\n" -// + " '' + e;\n" -// + "}"; -// testString("TypeError: \"object\" is not a constructor.", js); -// } -// -// @Test -// public void constructNoConstructorObject() { -// String js = "try {\n" -// + " Reflect.construct(function() {}, [], {});\n" -// + "} catch(e) {\n" -// + " '' + e;\n" -// + "}"; -// testString("TypeError: \"object\" is not a constructor.", js); -// } -// -// @Test -// public void constructNoConstructorFunction() { -// String js = "try {\n" -// + " Reflect.construct(function() {}, [], Date.now);\n" -// + "} catch(e) {\n" -// + " '' + e;\n" -// + "}"; -// // testString("TypeError: \"object\" is not a constructor.", js); -// // found no way to check a function for constructor -// } - @Test public void defineProperty() { String js = @@ -263,7 +216,8 @@ public void definePropertyFreezedWithoutHandler() { + "} catch(e) {\n" + " '' + e;" + "}\n"; - testString("TypeError: Cannot add properties to this object because extensible is false.", js); + testString( + "TypeError: Cannot add properties to this object because extensible is false.", js); } @Test @@ -339,7 +293,9 @@ public void getOwnPropertyDescriptor() { + "+ ' ' + result.writable " + "+ ' ' + (result.get === fn) " + "+ ' ' + (result.set === undefined)"; - testString("undefined 7 [value,writable,enumerable,configurable] true true false false true", js); + testString( + "undefined 7 [value,writable,enumerable,configurable] true true false false true", + js); } @Test @@ -378,16 +334,7 @@ public void isExtensible() { + "var proxy1 = new Proxy(o, handler);\n" + "var x = Object.isExtensible(proxy1);\n" + "res += ' ' + x;\n" -// + "x = Object.preventExtensions(proxy1);\n" -// + "res += ' ' + x;\n" -// + "x = Object.isExtensible(proxy1);\n" -// + "res += ' ' + x;\n" -// -// + "var o2 = Object.seal({});\n" -// + "var proxy2 = new Proxy(o2, handler);\n" -// + "x = Object.isExtensible(proxy2);\n" + "res += ' ' + x;\n"; -// testString(" a true true o true [object Object] a true false a false false", js); testString(" a true true true", js); } @@ -399,7 +346,6 @@ public void isExtensibleWithoutHandler() { + "var result = '' + Object.isExtensible(o1) + '-' + Object.isExtensible(proxy1);\n" + "Object.preventExtensions(proxy1);\n" + "result += ' ' + Object.isExtensible(o1) + '-' + Object.isExtensible(proxy1);\n" - + "var o2 = Object.seal({});\n" + "var proxy2 = new Proxy(o2, {});\n" + "result += ' ' + Object.isExtensible(o2) + '-' + Object.isExtensible(proxy2);\n"; @@ -409,7 +355,8 @@ public void isExtensibleWithoutHandler() { @Test public void ownKeys() { - String js = "var o = { d: 42 };\n" + String js = + "var o = { d: 42 };\n" + "var res = '';\n" + "var handler = {\n" + " ownKeys(target) {\n" @@ -423,6 +370,33 @@ public void ownKeys() { testString("true d", js); } + @Test + public void ownKeysTrapUndefined() { + String js = + "var target = {\n" + + " foo: 1,\n" + + " bar: 2\n" + + "};\n" + + "var p = new Proxy(target, {});\n" + + "var keys = Object.getOwnPropertyNames(p);\n" + + "'' + keys[0] + ' ' + keys[1] + ' ' + keys.length"; + testString("foo bar 2", js); + } + + @Test + public void ownKeysArrayInTrapResult() { + String js = + "var p = new Proxy({}, {\n" + + " ownKeys: function() {\n" + + " return [ [] ];\n" + + " }\n" + + "});\n" + + "try { Object.keys(p); } catch(e) { '' + e }\n"; + testString( + "TypeError: proxy [[OwnPropertyKeys]] must return an array with only string and symbol elements", + js); + } + @Test public void ownKeysWithoutHandler() { String js = @@ -464,14 +438,14 @@ public void ownKeysWithoutHandler2() { @Test public void ownKeysWithoutHandlerEmptyObj() { - String js = "var proxy1 = new Proxy({}, {});\n" - + "'' + Object.keys(proxy1).length"; + String js = "var proxy1 = new Proxy({}, {});\n" + "'' + Object.keys(proxy1).length"; testString("0", js); } @Test public void ownKeysWithoutHandlerDeleteObj() { - String js = "var o = { d: 42 };\n" + String js = + "var o = { d: 42 };\n" + "delete o.d;\n" + "var proxy1 = new Proxy(o, {});\n" + "'' + Object.keys(proxy1).length"; @@ -480,15 +454,13 @@ public void ownKeysWithoutHandlerDeleteObj() { @Test public void ownKeysWithoutHandlerEmptyArray() { - String js = "var proxy1 = new Proxy([], {});\n" - + "'' + Object.keys(proxy1)"; + String js = "var proxy1 = new Proxy([], {});\n" + "'' + Object.keys(proxy1)"; testString("", js); } @Test public void ownKeysWithoutHandlerArray() { - String js = "var proxy1 = new Proxy([, , 2], {});\n" - + "'' + Object.keys(proxy1)"; + String js = "var proxy1 = new Proxy([, , 2], {});\n" + "'' + Object.keys(proxy1)"; testString("2", js); } @@ -507,44 +479,40 @@ public void ownKeysWithoutHandlerNotEnumerable() { public void hasTargetNotExtensible() { String js = "var target = {};\n" - + "var handler = {\n" + " has: function(t, prop) {\n" + " return 0;\n" + " }\n" + "};\n" - + "var p = new Proxy(target, handler);\n" - + "Object.defineProperty(target, 'attr', {\n" + " configurable: true,\n" + " extensible: false,\n" + " value: 1\n" + "});\n" - + "Object.preventExtensions(target);\n" + "try { 'attr' in p; } catch(e) { '' + e }\n"; - testString("TypeError: proxy can't report an existing own property '\"attr\"' as non-existent on a non-extensible object", js); + testString( + "TypeError: proxy can't report an existing own property 'attr' as non-existent on a non-extensible object", + js); } @Test public void hasHandlerCallsIn() { String js = "var _handler, _target, _prop;\n" - + "var target = {};\n" + "var handler = {\n" + " has: function(t, prop) {\n" + " _handler = this;\n" + " _target = t;\n" + " _prop = prop;\n" -// + " return prop in t;\n" + // + " return prop in t;\n" + " return false;\n" + " }\n" + "};\n" + "var p = new Proxy(target, handler);\r\n" - + "'' + (_handler === handler)\n" + "+ ' ' + (_target === target)" + "+ ' ' + ('attr' === _prop)" @@ -577,85 +545,56 @@ public void hasSymbolWithoutHandler() { testString("true false", js); } -// @Test -// public void getOwnPropertyDescriptorSymbol() { -// String js = -// "var s = Symbol('sym');\n" -// + "var o = {};\n" -// + "o[s] = 42;\n" -// + "var result = Reflect.getOwnPropertyDescriptor(o, s);\n" -// + "'' + result.value" -// + "+ ' ' + result.enumerable" -// + "+ ' ' + result.configurable" -// + "+ ' ' + result.writable"; -// testString("42 true true true", js); -// } -// -// @Test -// public void getOwnPropertyDescriptorUndefinedProperty() { -// String js = -// "var o = Object.create({p: 1});\n" -// + "var result = Reflect.getOwnPropertyDescriptor(o, 'p');\n" -// + "'' + (result === undefined)"; -// testString("true", js); -// } - @Test public void getPropertyByIntWithoutHandler() { - String js = "var a = ['zero', 'one'];" - + "var proxy1 = new Proxy(a, {});\n" - + "proxy1[1];"; + String js = "var a = ['zero', 'one'];" + "var proxy1 = new Proxy(a, {});\n" + "proxy1[1];"; testString("one", js); } - @Test - public void getProperty() { - String js = "var o = {};\n" - + "o.p1 = 'value 1';\n" - + "var proxy1 = new Proxy(o, { get: function(t, prop) {\n" - + " return t[prop] + '!';\n" - + " }});\n" - + "var result = ''\n;" - + "result += proxy1.p1;\n" - + "Object.defineProperty(o, 'p3', { get: function() { return 'foo'; } });\n" - + "result += ', ' + proxy1.p3;\n" - + "var o2 = Object.create({ p: 42 });\n" - + "var proxy2 = new Proxy(o2, {});\n" - + "result += ', ' + proxy2.p;\n" - + "result += ', ' + proxy2.u;\n"; + @Test + public void getProperty() { + String js = + "var o = {};\n" + + "o.p1 = 'value 1';\n" + + "var proxy1 = new Proxy(o, { get: function(t, prop) {\n" + + " return t[prop] + '!';\n" + + " }});\n" + + "var result = ''\n;" + + "result += proxy1.p1;\n" + + "Object.defineProperty(o, 'p3', { get: function() { return 'foo'; } });\n" + + "result += ', ' + proxy1.p3;\n" + + "var o2 = Object.create({ p: 42 });\n" + + "var proxy2 = new Proxy(o2, {});\n" + + "result += ', ' + proxy2.p;\n" + + "result += ', ' + proxy2.u;\n"; testString("value 1!, foo!, 42, undefined", js); } - @Test - public void getPropertyParameters() { - String js = "var _target, _handler, _prop, _receiver;\n" - + "var target = {\n" - + " attr: 1\n" - + "};\n" - - + "var handler = {\n" - + " get: function(t, prop, receiver) {\n" - + " _handler = this;\n" - + " _target = t;\n" - + " _prop = prop;\n" - + " _receiver = receiver;\n" - + " }\n" - + "};\n" - - + "var p = new Proxy(target, handler);\r\n" - - + "p.attr;\n" - - + "var res = '' + (_handler === handler)\n" - + "+ ' ' + (_target === target)" - + "+ ' ' + (_prop == 'attr')" - + "+ ' ' + (_receiver === p);" - - + "_prop = null;\n" - + "p['attr'];\n" - - + "res + ' ' + (_prop == 'attr')"; + @Test + public void getPropertyParameters() { + String js = + "var _target, _handler, _prop, _receiver;\n" + + "var target = {\n" + + " attr: 1\n" + + "};\n" + + "var handler = {\n" + + " get: function(t, prop, receiver) {\n" + + " _handler = this;\n" + + " _target = t;\n" + + " _prop = prop;\n" + + " _receiver = receiver;\n" + + " }\n" + + "};\n" + + "var p = new Proxy(target, handler);\r\n" + + "p.attr;\n" + + "var res = '' + (_handler === handler)\n" + + "+ ' ' + (_target === target)" + + "+ ' ' + (_prop == 'attr')" + + "+ ' ' + (_receiver === p);" + + "_prop = null;\n" + + "p['attr'];\n" + + "res + ' ' + (_prop == 'attr')"; testString("true true true true true", js); } @@ -684,14 +623,13 @@ public void getPropertyWithoutHandler() { public void getPrototypeOfNull() { String js = "var plainObjectTarget = new Proxy(Object.create(null), {});\n" - + "var plainObjectProxy = new Proxy(plainObjectTarget, {\n" - + " getPrototypeOf: null,\n" - + "});\n" - + "'' + Object.getPrototypeOf(plainObjectProxy);\n"; + + "var plainObjectProxy = new Proxy(plainObjectTarget, {\n" + + " getPrototypeOf: null,\n" + + "});\n" + + "'' + Object.getPrototypeOf(plainObjectProxy);\n"; testString("null", js); } - @Test public void setPrototypeOfWithoutHandler() { String js = @@ -747,21 +685,36 @@ public void typeof() { @Test public void typeofRevocable() { testString("object", "var rev = Proxy.revocable({}, {}); rev.revoke(); typeof rev.proxy"); - testString("function", "var rev = Proxy.revocable(function() {}, {}); rev.revoke(); typeof rev.proxy"); + testString( + "function", + "var rev = Proxy.revocable(function() {}, {}); rev.revoke(); typeof rev.proxy"); - String js = "var revocableTarget = Proxy.revocable(function() {}, {});\n" - + "revocableTarget.revoke();\n" - - + "var revocable = Proxy.revocable(revocableTarget.proxy, {});\n" - + "'' + typeof revocable.proxy;\n"; + String js = + "var revocableTarget = Proxy.revocable(function() {}, {});\n" + + "revocableTarget.revoke();\n" + + "var revocable = Proxy.revocable(revocableTarget.proxy, {});\n" + + "'' + typeof revocable.proxy;\n"; testString("function", js); } + @Test + public void revocableFunctionIsAnonymous() { + String js = + "var rev = Proxy.revocable({}, {}).revoke;\n" + + "var desc = Object.getOwnPropertyDescriptor(rev, 'name');\n" + + "'' + desc.name + ' ' + desc.value " + + "+ ' ' + desc.writable " + + "+ ' ' + desc.enumerable " + + "+ ' ' + desc.configurable"; + testString("undefined false false true", js); + } + @Test public void revocableGetPrototypeOf() { - testString("TypeError: Illegal operation attempted on a revoked proxy", + testString( + "TypeError: Illegal operation attempted on a revoked proxy", "var rev = Proxy.revocable({}, {}); rev.revoke(); " - + "try { Object.getPrototypeOf(rev.proxy); } catch(e) { '' + e }"); + + "try { Object.getPrototypeOf(rev.proxy); } catch(e) { '' + e }"); } private static void testString(String expected, String js) { diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java index b3cc0416ed..57eb645d9a 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java @@ -45,12 +45,7 @@ public void applyDetails() { @Test public void applyMissingArgs() { - String js = - "try {\n" - + " Reflect.apply();\n" - + "} catch(e) {\n" - + " '' + e;\n" - + "}"; + String js = "try {\n" + " Reflect.apply();\n" + "} catch(e) {\n" + " '' + e;\n" + "}"; testString( "TypeError: Reflect.apply: At least 3 arguments required, but only 0 passed", js); } @@ -88,41 +83,45 @@ public void construct() { @Test public void constructNoConstructorNumber() { - String js = "try {\n" - + " Reflect.construct(function() {}, [], 1);\n" - + "} catch(e) {\n" - + " '' + e;\n" - + "}"; + String js = + "try {\n" + + " Reflect.construct(function() {}, [], 1);\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; testString("TypeError: \"number\" is not a constructor.", js); } @Test public void constructNoConstructorNull() { - String js = "try {\n" - + " Reflect.construct(function() {}, [], null);\n" - + "} catch(e) {\n" - + " '' + e;\n" - + "}"; + String js = + "try {\n" + + " Reflect.construct(function() {}, [], null);\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; testString("TypeError: \"object\" is not a constructor.", js); } @Test public void constructNoConstructorObject() { - String js = "try {\n" - + " Reflect.construct(function() {}, [], {});\n" - + "} catch(e) {\n" - + " '' + e;\n" - + "}"; + String js = + "try {\n" + + " Reflect.construct(function() {}, [], {});\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; testString("TypeError: \"object\" is not a constructor.", js); } @Test public void constructNoConstructorFunction() { - String js = "try {\n" - + " Reflect.construct(function() {}, [], Date.now);\n" - + "} catch(e) {\n" - + " '' + e;\n" - + "}"; + String js = + "try {\n" + + " Reflect.construct(function() {}, [], Date.now);\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}"; // testString("TypeError: \"object\" is not a constructor.", js); // found no way to check a function for constructor } From f385a1fd977cba6ebb6d4070995a4ba174be5b7b Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Fri, 18 Aug 2023 10:22:02 +0200 Subject: [PATCH 11/24] getOwnPropertyDescriptor [built-ins/Proxy 88/306 (28.76%) / built-ins/Reflect 20/139 (14.39%)] --- src/org/mozilla/javascript/NativeProxy.java | 40 ++++++++++++++++--- .../mozilla/javascript/ScriptableObject.java | 2 +- .../javascript/tests/es6/NativeProxyTest.java | 13 ++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index 233eabb672..885a10e5b1 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -655,19 +655,47 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { Callable trap = getTrap(TRAP_GET_OWN_PROPERTY_DESCRIPTOR); if (trap != null) { - ScriptableObject proxiedDescriptor = - (ScriptableObject) callTrap(trap, new Object[] {target, id}); - if (proxiedDescriptor != null) { - Object value = ScriptableObject.getProperty(proxiedDescriptor, "value"); + Object trapResultObj = callTrap(trap, new Object[] {target, id}); + if (!Undefined.isUndefined(trapResultObj) + && !(trapResultObj instanceof Scriptable + && !ScriptRuntime.isSymbol(trapResultObj))) { + throw ScriptRuntime.typeError( + "getOwnPropertyDescriptor trap has to return undefined or an object"); + } + + ScriptableObject targetDesc; + if (ScriptRuntime.isSymbol(id)) { + targetDesc = target.getOwnPropertyDescriptor(cx, id); + } else { + targetDesc = target.getOwnPropertyDescriptor(cx, ScriptRuntime.toString(id)); + } + + if (Undefined.isUndefined(trapResultObj)) { + if (Undefined.isUndefined(targetDesc)) { + return null; + } + + if (Boolean.FALSE.equals(targetDesc.get("configurable")) + || !target.isExtensible()) { + throw ScriptRuntime.typeError( + "proxy can't report an existing own property '" + + id + + "' as non-existent on a non-extensible object"); + } + return null; + } + + Scriptable trapResult = (Scriptable) trapResultObj; + if (trapResultObj != null) { + Object value = ScriptableObject.getProperty(trapResult, "value"); int attributes = applyDescriptorToAttributeBitset( - DONTENUM | READONLY | PERMANENT, proxiedDescriptor); + DONTENUM | READONLY | PERMANENT, trapResult); ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, attributes); return desc; } - // TODO return null; } diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index f404aeaa1e..6812c7a399 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1779,7 +1779,7 @@ protected boolean sameValue(Object newValue, Object currentValue) { return ScriptRuntime.shallowEq(currentValue, newValue); } - protected int applyDescriptorToAttributeBitset(int attributes, ScriptableObject desc) { + protected int applyDescriptorToAttributeBitset(int attributes, Scriptable desc) { Object enumerable = getProperty(desc, "enumerable"); if (enumerable != NOT_FOUND) { attributes = diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index 0cf4705488..b3136a7840 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -298,6 +298,19 @@ public void getOwnPropertyDescriptor() { js); } + @Test + public void getOwnPropertyDescriptorResultUndefined() { + String js = + "var target = {attr: 1};\n" + + "var p = new Proxy(target, {\n" + + " getOwnPropertyDescriptor: function(t, prop) {\n" + + " return;\n" + + " }\n" + + " });\n" + + "'' + Object.getOwnPropertyDescriptor(p, 'attr');"; + testString("undefined", js); + } + @Test public void getOwnPropertyDescriptorWithoutHandler() { String js = From 33221556f0bb8d03678fa1e0c3e82a35af2df406 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Fri, 18 Aug 2023 12:23:44 +0200 Subject: [PATCH 12/24] more detailed get trap handling --- src/org/mozilla/javascript/NativeProxy.java | 216 +++++++++++++++++- .../javascript/tests/es6/NativeProxyTest.java | 5 +- 2 files changed, 213 insertions(+), 8 deletions(-) diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index 885a10e5b1..980030262a 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; /** * This class implements the Proxy object. @@ -382,36 +383,169 @@ Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { return target.getIds(getNonEnumerable, getSymbols); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver + */ @Override public Object get(String name, Scriptable start) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "get"). + * 7. If trap is undefined, then + * a. Return ? target.[[Get]](P, Receiver). + * 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »). + * 9. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then + * a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then + * i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. + * b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then + * i. If trapResult is not undefined, throw a TypeError exception. + * 11. Return trapResult. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_GET); if (trap != null) { - return callTrap(trap, new Object[] {target, name, this}); + Object trapResult = callTrap(trap, new Object[] {target, name, this}); + + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), name); + if (targetDesc != null + && !Undefined.isUndefined(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("configurable"))) { + if (ScriptableObject.isDataDescriptor(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("writable"))) { + if (!Objects.equals(trapResult, targetDesc.get("value"))) { + throw ScriptRuntime.typeError( + "proxy get has to return the same value as the plain call"); + } + } + if (ScriptableObject.isAccessorDescriptor(targetDesc) + && Undefined.isUndefined(targetDesc.get("get"))) { + if (!Undefined.isUndefined(trapResult)) { + throw ScriptRuntime.typeError( + "proxy get has to return the same value as the plain call"); + } + } + } + return trapResult; } return ScriptRuntime.getObjectProp(target, name, Context.getContext()); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver + */ @Override public Object get(int index, Scriptable start) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "get"). + * 7. If trap is undefined, then + * a. Return ? target.[[Get]](P, Receiver). + * 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »). + * 9. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then + * a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then + * i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. + * b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then + * i. If trapResult is not undefined, throw a TypeError exception. + * 11. Return trapResult. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_GET); if (trap != null) { - return callTrap(trap, new Object[] {target, index, this}); + Object trapResult = callTrap(trap, new Object[] {target, index, this}); + + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), index); + if (targetDesc != null + && !Undefined.isUndefined(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("configurable"))) { + if (ScriptableObject.isDataDescriptor(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("writable"))) { + if (!Objects.equals(trapResult, targetDesc.get("value"))) { + throw ScriptRuntime.typeError( + "proxy get has to return the same value as the plain call"); + } + } + if (ScriptableObject.isAccessorDescriptor(targetDesc) + && Undefined.isUndefined(targetDesc.get("get"))) { + if (!Undefined.isUndefined(trapResult)) { + throw ScriptRuntime.typeError( + "proxy get has to return the same value as the plain call"); + } + } + } + return trapResult; } + return ScriptRuntime.getObjectIndex(target, index, Context.getContext()); } + /** + * see + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver + */ @Override public Object get(Symbol key, Scriptable start) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "get"). + * 7. If trap is undefined, then + * a. Return ? target.[[Get]](P, Receiver). + * 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »). + * 9. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then + * a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then + * i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. + * b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then + * i. If trapResult is not undefined, throw a TypeError exception. + * 11. Return trapResult. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_GET); if (trap != null) { - return callTrap(trap, new Object[] {target, key, this}); + Object trapResult = callTrap(trap, new Object[] {target, key, this}); + + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), key); + if (targetDesc != null + && !Undefined.isUndefined(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("configurable"))) { + if (ScriptableObject.isDataDescriptor(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("writable"))) { + if (!Objects.equals(trapResult, targetDesc.get("value"))) { + throw ScriptRuntime.typeError( + "proxy get has to return the same value as the plain call"); + } + } + if (ScriptableObject.isAccessorDescriptor(targetDesc) + && Undefined.isUndefined(targetDesc.get("get"))) { + if (!Undefined.isUndefined(trapResult)) { + throw ScriptRuntime.typeError( + "proxy get has to return the same value as the plain call"); + } + } + } + return trapResult; } if (start == this) { @@ -421,8 +555,30 @@ public Object get(Symbol key, Scriptable start) { return symbolScriptableTarget.get(key, start); } + /** + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver + */ @Override public void put(String name, Scriptable start, Object value) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "set"). + * 7. If trap is undefined, then + * a. Return ? target.[[Set]](P, V, Receiver). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)). + * 9. If booleanTrapResult is false, return false. + * 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then + * a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then + * i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception. + * b. If IsAccessorDescriptor(targetDesc) is true, then + * i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. + * 12. Return true. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_SET); @@ -434,8 +590,30 @@ public void put(String name, Scriptable start, Object value) { ScriptableObject.putProperty(target, name, value); } + /** + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver + */ @Override public void put(int index, Scriptable start, Object value) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "set"). + * 7. If trap is undefined, then + * a. Return ? target.[[Set]](P, V, Receiver). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)). + * 9. If booleanTrapResult is false, return false. + * 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then + * a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then + * i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception. + * b. If IsAccessorDescriptor(targetDesc) is true, then + * i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. + * 12. Return true. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_SET); @@ -447,8 +625,30 @@ public void put(int index, Scriptable start, Object value) { ScriptableObject.putProperty(target, index, value); } + /** + * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver + */ @Override public void put(Symbol key, Scriptable start, Object value) { + /* + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let handler be O.[[ProxyHandler]]. + * 3. If handler is null, throw a TypeError exception. + * 4. Assert: Type(handler) is Object. + * 5. Let target be O.[[ProxyTarget]]. + * 6. Let trap be ? GetMethod(handler, "set"). + * 7. If trap is undefined, then + * a. Return ? target.[[Set]](P, V, Receiver). + * 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)). + * 9. If booleanTrapResult is false, return false. + * 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + * 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then + * a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then + * i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception. + * b. If IsAccessorDescriptor(targetDesc) is true, then + * i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. + * 12. Return true. + */ assertNotRevoked(); Callable trap = getTrap(TRAP_SET); @@ -852,9 +1052,15 @@ public Scriptable getPrototype() { Object handlerProto = callTrap(trap, new Object[] {target}); Scriptable handlerProtoScriptable = Undefined.SCRIPTABLE_UNDEFINED; - if (!Undefined.isUndefined(handlerProto)) { - handlerProtoScriptable = ensureScriptable(handlerProto); + if (handlerProtoScriptable == null + || Undefined.isUndefined(handlerProto) + || ScriptRuntime.isSymbol(handlerProto)) { + throw ScriptRuntime.typeErrorById( + "msg.arg.not.object", ScriptRuntime.typeof(handlerProto)); } + + handlerProtoScriptable = ensureScriptable(handlerProto); + if (target.isExtensible()) { return handlerProtoScriptable; } diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index b3136a7840..78e5e32ff9 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -521,11 +521,10 @@ public void hasHandlerCallsIn() { + " _handler = this;\n" + " _target = t;\n" + " _prop = prop;\n" - // + " return prop in t;\n" - + " return false;\n" + + " return prop in t;\n" + " }\n" + "};\n" - + "var p = new Proxy(target, handler);\r\n" + + "var p = new Proxy(target, handler);\n" + "'' + (_handler === handler)\n" + "+ ' ' + (_target === target)" + "+ ' ' + ('attr' === _prop)" From d794c8a7e774487ce9b7be6d627a812a7923c26d Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Thu, 24 Aug 2023 12:02:31 +0200 Subject: [PATCH 13/24] NativeProxy has to support revoking from a trap SymbolKeys are shallowEq to symbols isArray has to take care of proxies --- src/org/mozilla/javascript/NativeArray.java | 3 + src/org/mozilla/javascript/NativeProxy.java | 65 ++++++++++--------- src/org/mozilla/javascript/ScriptRuntime.java | 4 ++ .../javascript/tests/es6/NativeProxyTest.java | 4 +- 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 9096dec92e..23f68f25b7 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -2249,6 +2249,9 @@ private static boolean js_isArray(Object o) { if (!(o instanceof Scriptable)) { return false; } + if (o instanceof NativeProxy) { + return js_isArray(((NativeProxy)o).getTargetThrowIfRevoked()); + } return "Array".equals(((Scriptable) o).getClassName()); } diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index 980030262a..ad77702547 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -35,8 +35,8 @@ final class NativeProxy extends ScriptableObject implements Callable, Constructa private static final String TRAP_APPLY = "apply"; private static final String TRAP_CONSTRUCT = "construct"; - private ScriptableObject target; - private Scriptable handler; + private ScriptableObject targetObj; + private Scriptable handlerObj; private final String typeOf; private static final class Revoker implements Callable { @@ -49,8 +49,8 @@ public Revoker(NativeProxy proxy) { @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (revocableProxy != null) { - revocableProxy.handler = null; - revocableProxy.target = null; + revocableProxy.handlerObj = null; + revocableProxy.targetObj = null; revocableProxy = null; } return Undefined.instance; @@ -88,8 +88,8 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) { } private NativeProxy(ScriptableObject target, Scriptable handler) { - this.target = target; - this.handler = handler; + this.targetObj = target; + this.handlerObj = handler; if (target == null || !(target instanceof Callable)) { typeOf = super.getTypeOf(); @@ -100,7 +100,7 @@ private NativeProxy(ScriptableObject target, Scriptable handler) { @Override public String getClassName() { - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); return target.getClassName(); } @@ -124,7 +124,7 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) { * 10. If Type(newObj) is not Object, throw a TypeError exception. * 11. Return newObj. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_CONSTRUCT); if (trap != null) { @@ -161,7 +161,7 @@ public boolean has(String name, Scriptable start) { * iii. If extensibleTarget is false, throw a TypeError exception. * 10. Return booleanTrapResult. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_HAS); if (trap != null) { @@ -211,7 +211,7 @@ public boolean has(int index, Scriptable start) { * iii. If extensibleTarget is false, throw a TypeError exception. * 10. Return booleanTrapResult. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_HAS); if (trap != null) { @@ -241,7 +241,7 @@ public boolean has(int index, Scriptable start) { */ @Override public boolean has(Symbol key, Scriptable start) { - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_HAS); if (trap != null) { @@ -307,7 +307,7 @@ Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { * 22. If uncheckedResultKeys is not empty, throw a TypeError exception. * 23. Return trapResult. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_OWN_KEYS); if (trap != null) { @@ -407,7 +407,7 @@ public Object get(String name, Scriptable start) { * i. If trapResult is not undefined, throw a TypeError exception. * 11. Return trapResult. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_GET); if (trap != null) { @@ -463,7 +463,7 @@ public Object get(int index, Scriptable start) { * i. If trapResult is not undefined, throw a TypeError exception. * 11. Return trapResult. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_GET); if (trap != null) { @@ -519,7 +519,7 @@ public Object get(Symbol key, Scriptable start) { * i. If trapResult is not undefined, throw a TypeError exception. * 11. Return trapResult. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_GET); if (trap != null) { @@ -579,7 +579,7 @@ public void put(String name, Scriptable start, Object value) { * i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. * 12. Return true. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_SET); if (trap != null) { @@ -614,7 +614,7 @@ public void put(int index, Scriptable start, Object value) { * i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. * 12. Return true. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_SET); if (trap != null) { @@ -649,7 +649,7 @@ public void put(Symbol key, Scriptable start, Object value) { * i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. * 12. Return true. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_SET); if (trap != null) { @@ -688,7 +688,7 @@ public void delete(String name) { * 14. If extensibleTarget is false, throw a TypeError exception. * 15. Return true. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_DELETE_PROPERTY); if (trap != null) { @@ -738,7 +738,7 @@ public void delete(int index) { * 14. If extensibleTarget is false, throw a TypeError exception. * 15. Return true. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_DELETE_PROPERTY); if (trap != null) { @@ -788,7 +788,7 @@ public void delete(Symbol key) { * 14. If extensibleTarget is false, throw a TypeError exception. * 15. Return true. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_DELETE_PROPERTY); if (trap != null) { @@ -851,7 +851,7 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { * i. If targetDesc.[[Writable]] is true, throw a TypeError exception. * 18. Return resultDesc. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_GET_OWN_PROPERTY_DESCRIPTOR); if (trap != null) { @@ -939,7 +939,7 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { * i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. * 17. Return true. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_DEFINE_PROPERTY); if (trap != null) { @@ -968,7 +968,7 @@ public boolean isExtensible() { * 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. * 10. Return booleanTrapResult. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_IS_EXTENSIBLE); if (trap == null) { @@ -1005,7 +1005,7 @@ public void preventExtensions() { * b. If extensibleTarget is true, throw a TypeError exception. * 9. Return booleanTrapResult. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_PREVENT_EXTENSIONS); if (trap == null) { @@ -1045,7 +1045,7 @@ public Scriptable getPrototype() { * 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception. * 13. Return handlerProto. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_GET_PROTOTYPE_OF); if (trap != null) { @@ -1101,7 +1101,7 @@ public void setPrototype(Scriptable prototype) { * 13. If SameValue(V, targetProto) is false, throw a TypeError exception. * 14. Return true. */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Callable trap = getTrap(TRAP_SET_PROTOTYPE_OF); if (trap != null) { @@ -1137,7 +1137,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar * 7. Let argArray be ! CreateArrayFromList(argumentsList). * 8. Return ? Call(trap, handler, « target, thisArgument, argArray »). */ - assertNotRevoked(); + ScriptableObject target = getTargetThrowIfRevoked(); Scriptable argumentsList = cx.newArray(scope, args); @@ -1184,7 +1184,7 @@ private static Object revocable( } private Callable getTrap(String trapName) { - Object handlerProp = ScriptableObject.getProperty(handler, trapName); + Object handlerProp = ScriptableObject.getProperty(handlerObj, trapName); if (Scriptable.NOT_FOUND == handlerProp) { return null; } @@ -1199,12 +1199,13 @@ private Callable getTrap(String trapName) { } private Object callTrap(Callable trap, Object[] args) { - return trap.call(Context.getContext(), handler, handler, args); + return trap.call(Context.getContext(), handlerObj, handlerObj, args); } - private void assertNotRevoked() { - if (target == null) { + ScriptableObject getTargetThrowIfRevoked() { + if (targetObj == null) { throw ScriptRuntime.typeError("Illegal operation attempted on a revoked proxy"); } + return targetObj; } } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index f432beebc0..2f3a72baa0 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -3645,6 +3645,10 @@ public static boolean shallowEq(Object x, Object y) { if (y instanceof Boolean) { return x.equals(y); } + } else if (x instanceof SymbolKey) { + return x.equals(y); + } else if (y instanceof SymbolKey) { + return y.equals(x); } else if (x instanceof Scriptable) { if (x instanceof Wrapper && y instanceof Wrapper) { return ((Wrapper) x).unwrap() == ((Wrapper) y).unwrap(); diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index 78e5e32ff9..edcc7d7e93 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -27,8 +27,8 @@ public void testToStringRevoke() { + "rev.revoke();\n" + "try {" + " Object.prototype.toString.call(%s);\n" - + "} catch(e) {" - + " '' + e;" + + "} catch(e) {\n" + + " '' + e;\n" + "}"; testString( From 0f82fc0536d3dc215ddc52eaa8db188440423659 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Thu, 24 Aug 2023 19:18:18 +0200 Subject: [PATCH 14/24] implement more details for defineProperty fix missing return for put traps --- .../AbstractEcmaObjectOperations.java | 97 +++++++++++++++++++ src/org/mozilla/javascript/NativeProxy.java | 47 ++++++++- .../mozilla/javascript/ScriptableObject.java | 2 +- .../javascript/tests/es6/NativeProxyTest.java | 88 +++++++++++++++++ 4 files changed, 232 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java index c01b97ad7c..9776c0d287 100644 --- a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java +++ b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.function.Predicate; /** @@ -281,4 +282,100 @@ static long lengthOfArrayLike(Context cx, Scriptable o) { long len = ScriptRuntime.toLength(new Object[] {value}, 0); return len; } + + /** + * IsCompatiblePropertyDescriptor ( Extensible, Desc, Current ) + * + *

https://262.ecma-international.org/12.0/#sec-iscompatiblepropertydescriptor + */ + static boolean isCompatiblePropertyDescriptor(boolean extensible, ScriptableObject desc, ScriptableObject current) { + return validateAndApplyPropertyDescriptor(Undefined.SCRIPTABLE_UNDEFINED, Undefined.SCRIPTABLE_UNDEFINED, extensible, desc, current); + } + + /** + * ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current ) + * + *

https://262.ecma-international.org/12.0/#sec-validateandapplypropertydescriptor + */ + static boolean validateAndApplyPropertyDescriptor(Scriptable o, Scriptable p, boolean extensible, ScriptableObject desc, ScriptableObject current) { + if (Undefined.isUndefined(current)) { + if (!extensible) { + return false; + } + + if (ScriptableObject.isGenericDescriptor(desc) || ScriptableObject.isDataDescriptor(desc)) { + /* + i. i. If O is not undefined, create an own data property named P of object O whose [[Value]], [[Writable]], [[Enumerable]], and [[Configurable]] attribute values are described by Desc. + If the value of an attribute field of Desc is absent, the attribute of the newly created property is set to its default value. + */ + } + else { + /* + ii. ii. If O is not undefined, create an own accessor property named P of object O whose [[Get]], [[Set]], [[Enumerable]], and [[Configurable]] attribute values are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property is set to its default value. + */ + } + return true; + } + + if (desc.getIds().length == 0) { + return true; + } + + if (Boolean.FALSE.equals(current.get("configurable"))) { + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "configurable")) + && Boolean.TRUE.equals(desc.get("configurable"))) { + return false; + } + + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "enumerable")) + && !Objects.equals(desc.get("enumerable"), current.get("enumerable"))) { + return false; + } + } + + // if (!ScriptableObject.isGenericDescriptor(desc)) { + if (ScriptableObject.isGenericDescriptor(desc)) { + return true; + } + else if (!Objects.equals(ScriptableObject.isGenericDescriptor(current), ScriptableObject.isGenericDescriptor(desc))) { + if (Boolean.FALSE.equals(current.get("configurable"))) { + return false; + } + if (ScriptableObject.isDataDescriptor(current)) { + if (Boolean.FALSE.equals(current.get("configurable"))) { + // i. i. If O is not undefined, convert the property named P of object O from a data property to an accessor property. Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to their default values. + } + else { + // i. i. If O is not undefined, convert the property named P of object O from an accessor property to a data property. Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to their default values. + } + } + } + else if (ScriptableObject.isDataDescriptor(current) && ScriptableObject.isDataDescriptor(desc)) { + if (Boolean.FALSE.equals(current.get("configurable")) && Boolean.FALSE.equals(current.get("writable"))) { + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "writable")) + && Boolean.TRUE.equals(desc.get("writable"))) { + return false; + } + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "value")) + && !Objects.equals(desc.get("value"), current.get("value"))) { + return false; + } + return true; + } + } + else { + if (Boolean.FALSE.equals(current.get("configurable"))) { + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "set")) + && !Objects.equals(desc.get("set"), current.get("set"))) { + return false; + } + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "get")) + && !Objects.equals(desc.get("get"), current.get("get"))) { + return false; + } + return true; + } + } + return true; + } } diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index ad77702547..6ddb3d6da2 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -585,6 +585,8 @@ public void put(String name, Scriptable start, Object value) { if (trap != null) { ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); callTrap(trap, new Object[] {target, name, desc}); + + return; } ScriptableObject.putProperty(target, name, value); @@ -620,6 +622,8 @@ public void put(int index, Scriptable start, Object value) { if (trap != null) { ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); callTrap(trap, new Object[] {target, index, desc}); + + return; } ScriptableObject.putProperty(target, index, value); @@ -655,6 +659,8 @@ public void put(Symbol key, Scriptable start, Object value) { if (trap != null) { ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); callTrap(trap, new Object[] {target, key, desc}); + + return; } if (start == this) { @@ -943,7 +949,46 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { Callable trap = getTrap(TRAP_DEFINE_PROPERTY); if (trap != null) { - callTrap(trap, new Object[] {target, id, desc}); + boolean booleanTrapResult = ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, id, desc})); + if (!booleanTrapResult) { + return; + } + + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), id); + boolean extensibleTarget = target.isExtensible(); + + boolean settingConfigFalse = Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "configurable")) + && Boolean.FALSE.equals(desc.get("configurable")); + + if (targetDesc == null) { + if (!extensibleTarget || settingConfigFalse) { + throw ScriptRuntime.typeError( + "proxy can't define an incompatible property descriptor"); + } + } + else { + if(!AbstractEcmaObjectOperations.isCompatiblePropertyDescriptor(extensibleTarget, desc, targetDesc)) { + throw ScriptRuntime.typeError( + "proxy can't define an incompatible property descriptor"); + } + + if (settingConfigFalse && Boolean.TRUE.equals(targetDesc.get("configurable"))) { + throw ScriptRuntime.typeError( + "proxy can't define an incompatible property descriptor"); + } + + if (ScriptableObject.isDataDescriptor(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("configurable")) + && Boolean.TRUE.equals(targetDesc.get("writable"))) { + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "writable")) + && Boolean.FALSE.equals(desc.get("writable"))) { + throw ScriptRuntime.typeError( + "proxy can't define an incompatible property descriptor"); + } + } + } + return; } target.defineOwnProperty(cx, id, desc); diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 6812c7a399..45c9e1d50b 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1833,7 +1833,7 @@ protected static boolean isAccessorDescriptor(ScriptableObject desc) { * @param desc a property descriptor * @return true if this is a generic descriptor. */ - protected boolean isGenericDescriptor(ScriptableObject desc) { + protected static boolean isGenericDescriptor(ScriptableObject desc) { return !isDataDescriptor(desc) && !isAccessorDescriptor(desc); } diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index edcc7d7e93..ac6d5e0528 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -194,6 +194,94 @@ public void defineProperty() { testString("[object Object] p false undefined undefined", js); } + @Test + public void definePropertyTrapReturnsFalse() { + String js = + "var target = {};\n" + + "var p = new Proxy(target, {\n" + + " defineProperty: function(t, prop, desc) {\n" + + " return 0;\n" + + " }\n" + + "});\n" + + + "'' + Reflect.defineProperty(p, 'attr', {})" + + "+ ' ' + Object.getOwnPropertyDescriptor(target, 'attr')"; + testString("false undefined", js); + } + + @Test + public void definePropertyDescNotConfigurableAndTargetPropertyDescriptorConfigurableAndTrapResultIsTrue() { + String js = + "var target = {};\n" + + "var p = new Proxy(target, {\n" + + " defineProperty: function(t, prop, desc) {\n" + + " return true;\n" + + " }\n" + + "});\n" + + "Object.defineProperty(target, \"foo\", {\n" + + " value: 1,\n" + + " configurable: true\n" + + "});\n" + + "try {\n" + + " Object.defineProperty(p, \"foo\", {\n" + + " value: 1,\n" + + " configurable: false\n" + + " });\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}\n"; + testString("TypeError: proxy can't define an incompatible property descriptor", js); + } + + @Test + public void definePropertyDescAndTargetPropertyDescriptorNotCompatibleAndTrapResultIsTrue() { + String js = + "var target = {};\n" + + "var p = new Proxy(target, {\n" + + " defineProperty: function(t, prop, desc) {\n" + + " return true;\n" + + " }\n" + + "});\n" + + "Object.defineProperty(target, \"foo\", {\n" + + " value: 1\n" + + "});\n" + + + "try {\n" + + " Object.defineProperty(p, \"foo\", {\n" + + " value: 2\n" + + " });\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}\n"; + testString("TypeError: proxy can't define an incompatible property descriptor", js); + } + + @Test + public void definePropertyDescAndTargetPropertyDescriptorNotCompatibleAndTrapResultIsTrue2() { + String js = + "var target = Object.create(null);\n" + + "var p = new Proxy(target, {\n" + + " defineProperty: function() {\r\n" + + " return true;\r\n" + + " }\n" + + "});\n" + + + "Object.defineProperty(target, 'prop', {\n" + + " value: 1,\n" + + " configurable: false\n" + + "});\n" + + + "try {\n" + + " Object.defineProperty(p, 'prop', {\n" + + " value: 1,\n" + + " configurable: true\n" + + " });\n" + + "} catch(e) {\n" + + " '' + e;\n" + + "}\n"; + testString("TypeError: proxy can't define an incompatible property descriptor", js); + } + @Test public void definePropertyWithoutHandler() { String js = From a31a2106ef7024b311e56254b3dcd6f5cc57aea7 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Thu, 31 Aug 2023 20:25:47 +0200 Subject: [PATCH 15/24] construct now supports newTarget --- src/org/mozilla/javascript/NativeReflect.java | 53 ++++++++++++++++++- .../tests/es6/NativeReflectTest.java | 18 +++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index 01b276f266..62c2fb9731 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -140,8 +140,18 @@ private static Object apply(Context cx, Scriptable scope, Scriptable thisObj, Ob true, cx, scope, callable, new Object[] {thisObj, argumentsList}); } + /** + * see https://262.ecma-international.org/12.0/#sec-reflect.construct + */ private static Scriptable construct( Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + /* + * 1. If IsConstructor(target) is false, throw a TypeError exception. + * 2. If newTarget is not present, set newTarget to target. + * 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception. + * 4. Let args be ? CreateListFromArrayLike(argumentsList). + * 5. Return ? Construct(target, args, newTarget). + */ if (args.length < 1) { throw ScriptRuntime.typeErrorById( "msg.method.missing.parameter", @@ -159,12 +169,51 @@ private static Scriptable construct( return ctor.construct(cx, scope, ScriptRuntime.emptyArgs); } - if (args.length > 2 && !(args[2] instanceof Function)) { + if (args.length > 2 && !(args[2] instanceof Constructable)) { throw ScriptRuntime.typeErrorById("msg.not.ctor", ScriptRuntime.typeof(args[2])); } Object[] callArgs = ScriptRuntime.getApplyArguments(cx, args[1]); - return ctor.construct(cx, scope, callArgs); + + Object newTargetPrototype = null; + if (args.length > 2) { + Scriptable newTarget = ScriptableObject.ensureScriptable(args[2]); + + if (newTarget instanceof BaseFunction) { + newTargetPrototype = ((BaseFunction) newTarget).getPrototypeProperty(); + } else { + newTargetPrototype = newTarget.get("prototype", newTarget); + } + + if (!(newTargetPrototype instanceof Scriptable) + || ScriptRuntime.isSymbol(newTargetPrototype) + || Undefined.isUndefined(newTargetPrototype)) { + newTargetPrototype = null; + } + } + + // hack to set the right prototype before calling the ctor + if (ctor instanceof BaseFunction && newTargetPrototype != null) { + BaseFunction ctorBaseFunction = (BaseFunction) ctor; + Scriptable result = ctorBaseFunction.createObject(cx, scope); + if (result != null) { + result.setPrototype((Scriptable) newTargetPrototype); + + Object val = ctorBaseFunction.call(cx, scope, result, args); + if (val instanceof Scriptable) { + return (Scriptable) val; + } + + return result; + } + } + + Scriptable newScriptable = ctor.construct(cx, scope, callArgs); + if (newTargetPrototype != null) { + newScriptable.setPrototype((Scriptable) newTargetPrototype); + } + + return newScriptable; } private static Object defineProperty( diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java index 57eb645d9a..67dd58cfcd 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java @@ -81,6 +81,24 @@ public void construct() { testString("true 1776", js); } + @Test + public void constructNewTarget() { + String js = + "var o = {};\n" + + "var internPrototype;\n" + + + "function fn() {\n" + + " this.o = o;\n" + + " internPrototype = Object.getPrototypeOf(this);\n" + + "}\n" + + + "var result = Reflect.construct(fn, [], Array);\n" + + "'' + (Object.getPrototypeOf(result) === Array.prototype)" + + " + ' ' + (internPrototype === Array.prototype)" + + " + ' ' + (result.o === o)"; + testString("true true true", js); + } + @Test public void constructNoConstructorNumber() { String js = From e85eab25f5f32b11de09f4b4f64899e063db9d47 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Thu, 31 Aug 2023 20:31:54 +0200 Subject: [PATCH 16/24] current status of the impl --- testsrc/test262.properties | 293 +++++++++++++++++++------------------ 1 file changed, 154 insertions(+), 139 deletions(-) diff --git a/testsrc/test262.properties b/testsrc/test262.properties index d0e1048dcd..941d9c9339 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -1,6 +1,6 @@ # This is a configuration file for Test262SuiteTest.java. See ./README.md for more info about this file -built-ins/Array 177/2670 (6.63%) +built-ins/Array 161/2670 (6.03%) from/calling-from-valid-1-noStrict.js non-strict Spec pretty clearly says this should be undefined from/elements-deleted-after.js Checking to see if length changed, but spec says it should not from/iter-map-fn-this-non-strict.js non-strict Error propagation needs work in general @@ -12,15 +12,11 @@ built-ins/Array 177/2670 (6.63%) from/source-object-iterator-2.js Uses "get" syntax that's not implemented from/source-object-length-set-elem-prop-err.js from/source-object-length-set-elem-prop-non-writable.js - isArray/proxy.js {unsupported: [Proxy]} - isArray/proxy-revoked.js {unsupported: [Proxy]} length/define-own-prop-length-coercion-order.js length/define-own-prop-length-coercion-order-set.js length/define-own-prop-length-no-value-order.js length/define-own-prop-length-overflow-order.js of/proto-from-ctor-realm.js - of/return-abrupt-from-data-property-using-proxy.js {unsupported: [Proxy]} - prototype/concat/arg-length-exceeding-integer-limit.js {unsupported: [Proxy]} prototype/concat/Array.prototype.concat_large-typed-array.js new prototype/concat/Array.prototype.concat_non-array.js prototype/concat/Array.prototype.concat_small-typed-array.js @@ -28,8 +24,7 @@ built-ins/Array 177/2670 (6.63%) prototype/concat/create-ctor-poisoned.js prototype/concat/create-proto-from-ctor-realm-array.js {unsupported: [Symbol.species]} prototype/concat/create-proto-from-ctor-realm-non-array.js {unsupported: [Symbol.species]} - prototype/concat/create-proxy.js {unsupported: [Proxy, Symbol.species]} - prototype/concat/create-revoked-proxy.js {unsupported: [Proxy]} + prototype/concat/create-proxy.js {unsupported: [Symbol.species]} prototype/concat/create-species.js {unsupported: [Symbol.species]} prototype/concat/create-species-abrupt.js {unsupported: [Symbol.species]} prototype/concat/create-species-non-ctor.js {unsupported: [Symbol.species]} @@ -43,22 +38,17 @@ built-ins/Array 177/2670 (6.63%) prototype/concat/create-species-with-non-writable-property.js {unsupported: [Symbol.species]} prototype/concat/create-species-with-non-writable-property-spreadable.js {unsupported: [Symbol.species]} prototype/concat/is-concat-spreadable-get-order.js - prototype/concat/is-concat-spreadable-is-array-proxy-revoked.js {unsupported: [Proxy]} - prototype/concat/is-concat-spreadable-proxy.js {unsupported: [Proxy]} - prototype/concat/is-concat-spreadable-proxy-revoked.js {unsupported: [Proxy]} prototype/copyWithin/coerced-values-start-change-start.js prototype/copyWithin/coerced-values-start-change-target.js - prototype/copyWithin/return-abrupt-from-delete-proxy-target.js {unsupported: [Proxy]} + prototype/copyWithin/return-abrupt-from-delete-proxy-target.js prototype/copyWithin/return-abrupt-from-delete-target.js non-strict Not throwing properly on unwritable - prototype/copyWithin/return-abrupt-from-has-start.js {unsupported: [Proxy]} prototype/every/15.4.4.16-5-1-s.js non-strict prototype/filter/15.4.4.20-5-1-s.js non-strict prototype/filter/create-ctor-non-object.js prototype/filter/create-ctor-poisoned.js prototype/filter/create-proto-from-ctor-realm-array.js {unsupported: [Symbol.species]} prototype/filter/create-proto-from-ctor-realm-non-array.js {unsupported: [Symbol.species]} - prototype/filter/create-proxy.js {unsupported: [Proxy, Symbol.species]} - prototype/filter/create-revoked-proxy.js {unsupported: [Proxy]} + prototype/filter/create-proxy.js {unsupported: [Symbol.species]} prototype/filter/create-species.js {unsupported: [Symbol.species]} prototype/filter/create-species-abrupt.js {unsupported: [Symbol.species]} prototype/filter/create-species-non-ctor.js {unsupported: [Symbol.species]} @@ -88,25 +78,23 @@ built-ins/Array 177/2670 (6.63%) prototype/flat/target-array-with-non-configurable-property.js {unsupported: [Symbol.species]} prototype/flat/target-array-with-non-writable-property.js {unsupported: [Symbol.species]} prototype/forEach/15.4.4.18-5-1-s.js non-strict - prototype/includes/get-prop.js {unsupported: [Proxy]} - prototype/indexOf/calls-only-has-on-prototype-after-length-zeroed.js {unsupported: [Proxy]} + prototype/includes/get-prop.js + prototype/indexOf/calls-only-has-on-prototype-after-length-zeroed.js prototype/indexOf/length-zero-returns-minus-one.js - prototype/lastIndexOf/calls-only-has-on-prototype-after-length-zeroed.js {unsupported: [Proxy]} + prototype/lastIndexOf/calls-only-has-on-prototype-after-length-zeroed.js prototype/lastIndexOf/length-zero-returns-minus-one.js prototype/map/15.4.4.19-5-1-s.js non-strict prototype/map/create-ctor-non-object.js prototype/map/create-ctor-poisoned.js prototype/map/create-proto-from-ctor-realm-array.js {unsupported: [Symbol.species]} prototype/map/create-proto-from-ctor-realm-non-array.js {unsupported: [Symbol.species]} - prototype/map/create-proxy.js {unsupported: [Proxy, Symbol.species]} - prototype/map/create-revoked-proxy.js {unsupported: [Proxy]} + prototype/map/create-proxy.js {unsupported: [Symbol.species]} prototype/map/create-species.js {unsupported: [Symbol.species]} prototype/map/create-species-abrupt.js {unsupported: [Symbol.species]} prototype/map/create-species-non-ctor.js {unsupported: [Symbol.species]} prototype/map/create-species-null.js {unsupported: [Symbol.species]} prototype/map/create-species-poisoned.js {unsupported: [Symbol.species]} prototype/map/create-species-undef.js {unsupported: [Symbol.species]} - prototype/map/create-species-undef-invalid-len.js {unsupported: [Proxy]} prototype/map/target-array-non-extensible.js {unsupported: [Symbol.species]} prototype/map/target-array-with-non-configurable-property.js {unsupported: [Symbol.species]} prototype/map/target-array-with-non-writable-property.js {unsupported: [Symbol.species]} @@ -123,9 +111,7 @@ built-ins/Array 177/2670 (6.63%) prototype/slice/create-ctor-poisoned.js prototype/slice/create-proto-from-ctor-realm-array.js {unsupported: [Symbol.species]} prototype/slice/create-proto-from-ctor-realm-non-array.js {unsupported: [Symbol.species]} - prototype/slice/create-proxied-array-invalid-len.js {unsupported: [Proxy]} - prototype/slice/create-proxy.js {unsupported: [Proxy, Symbol.species]} - prototype/slice/create-revoked-proxy.js {unsupported: [Proxy]} + prototype/slice/create-proxy.js {unsupported: [Symbol.species]} prototype/slice/create-species.js {unsupported: [Symbol.species]} prototype/slice/create-species-abrupt.js {unsupported: [Symbol.species]} prototype/slice/create-species-neg-zero.js {unsupported: [Symbol.species]} @@ -133,7 +119,6 @@ built-ins/Array 177/2670 (6.63%) prototype/slice/create-species-null.js {unsupported: [Symbol.species]} prototype/slice/create-species-poisoned.js {unsupported: [Symbol.species]} prototype/slice/create-species-undef.js {unsupported: [Symbol.species]} - prototype/slice/length-exceeding-integer-limit-proxied-array.js prototype/slice/target-array-non-extensible.js {unsupported: [Symbol.species]} prototype/slice/target-array-with-non-configurable-property.js {unsupported: [Symbol.species]} prototype/slice/target-array-with-non-writable-property.js {unsupported: [Symbol.species]} @@ -146,8 +131,8 @@ built-ins/Array 177/2670 (6.63%) prototype/splice/create-ctor-poisoned.js prototype/splice/create-proto-from-ctor-realm-array.js {unsupported: [Symbol.species]} prototype/splice/create-proto-from-ctor-realm-non-array.js {unsupported: [Symbol.species]} - prototype/splice/create-proxy.js {unsupported: [Proxy, Symbol.species]} - prototype/splice/create-revoked-proxy.js {unsupported: [Proxy]} + prototype/splice/create-proxy.js {unsupported: [Symbol.species]} + prototype/splice/create-revoked-proxy.js prototype/splice/create-species.js {unsupported: [Symbol.species]} prototype/splice/create-species-abrupt.js {unsupported: [Symbol.species]} prototype/splice/create-species-length-exceeding-integer-limit.js {unsupported: [Symbol.species]} @@ -156,8 +141,7 @@ built-ins/Array 177/2670 (6.63%) prototype/splice/create-species-null.js {unsupported: [Symbol.species]} prototype/splice/create-species-poisoned.js {unsupported: [Symbol.species]} prototype/splice/create-species-undef.js {unsupported: [Symbol.species]} - prototype/splice/create-species-undef-invalid-len.js {unsupported: [Proxy]} - prototype/splice/property-traps-order-with-species.js {unsupported: [Proxy, Symbol.species]} + prototype/splice/property-traps-order-with-species.js {unsupported: [Symbol.species]} prototype/splice/S15.4.4.12_A6.1_T2.js incorrect length handling prototype/splice/S15.4.4.12_A6.1_T3.js non-strict prototype/splice/set_length_no_args.js @@ -229,7 +213,7 @@ built-ins/BigInt 14/68 (20.59%) built-ins/Boolean 1/49 (2.04%) proto-from-ctor-realm.js -built-ins/DataView 165/455 (36.26%) +built-ins/DataView 164/455 (36.04%) prototype/buffer/detached-buffer.js prototype/buffer/invoked-as-accessor.js prototype/buffer/length.js @@ -373,7 +357,6 @@ built-ins/DataView 165/455 (36.26%) custom-proto-access-throws.js custom-proto-access-throws-sab.js {unsupported: [SharedArrayBuffer]} custom-proto-if-not-object-fallbacks-to-default-prototype-sab.js {unsupported: [SharedArrayBuffer]} - custom-proto-if-object-is-used.js custom-proto-if-object-is-used-sab.js {unsupported: [SharedArrayBuffer]} defined-bytelength-and-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} defined-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} @@ -396,7 +379,7 @@ built-ins/DataView 165/455 (36.26%) toindex-bytelength-sab.js {unsupported: [SharedArrayBuffer]} toindex-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} -built-ins/Date 39/707 (5.52%) +built-ins/Date 38/707 (5.37%) prototype/setFullYear/15.9.5.40_1.js prototype/Symbol.toPrimitive/hint-default-first-invalid.js prototype/Symbol.toPrimitive/hint-default-first-non-callable.js @@ -424,7 +407,6 @@ built-ins/Date 39/707 (5.52%) proto-from-ctor-realm-one.js proto-from-ctor-realm-two.js proto-from-ctor-realm-zero.js - subclassing.js value-get-symbol-to-prim-err.js value-symbol-to-prim-err.js value-symbol-to-prim-invocation.js @@ -467,7 +449,7 @@ built-ins/eval 3/9 (33.33%) name.js private-identifiers-not-empty.js {unsupported: [class-fields-private]} -built-ins/Function 186/505 (36.83%) +built-ins/Function 185/505 (36.63%) internals/Call 2/2 (100.0%) internals/Construct 6/6 (100.0%) length/S15.3.5.1_A1_T3.js strict @@ -528,7 +510,7 @@ built-ins/Function 186/505 (36.83%) prototype/Symbol.hasInstance/this-val-bound-target.js prototype/Symbol.hasInstance/this-val-not-callable.js prototype/Symbol.hasInstance/this-val-poisoned-prototype.js - prototype/Symbol.hasInstance/value-get-prototype-of-err.js {unsupported: [Proxy]} + prototype/Symbol.hasInstance/value-get-prototype-of-err.js prototype/Symbol.hasInstance/value-negative.js prototype/Symbol.hasInstance/value-non-obj.js prototype/Symbol.hasInstance/value-positive.js @@ -583,17 +565,16 @@ built-ins/Function 186/505 (36.83%) prototype/toString/private-method-class-statement.js prototype/toString/private-static-method-class-expression.js prototype/toString/private-static-method-class-statement.js - prototype/toString/proxy-arrow-function.js {unsupported: [Proxy]} - prototype/toString/proxy-async-function.js {unsupported: [Proxy, async-functions]} - prototype/toString/proxy-async-generator-function.js {unsupported: [Proxy, async-iteration]} - prototype/toString/proxy-async-generator-method-definition.js {unsupported: [Proxy, async-iteration]} - prototype/toString/proxy-async-method-definition.js {unsupported: [Proxy, async-functions]} - prototype/toString/proxy-bound-function.js {unsupported: [Proxy]} - prototype/toString/proxy-class.js {unsupported: [Proxy, class]} - prototype/toString/proxy-function-expression.js {unsupported: [Proxy]} - prototype/toString/proxy-generator-function.js {unsupported: [Proxy]} - prototype/toString/proxy-method-definition.js {unsupported: [Proxy]} - prototype/toString/proxy-non-callable-throws.js {unsupported: [Proxy]} + prototype/toString/proxy-arrow-function.js + prototype/toString/proxy-async-function.js {unsupported: [async-functions]} + prototype/toString/proxy-async-generator-function.js {unsupported: [async-iteration]} + prototype/toString/proxy-async-generator-method-definition.js {unsupported: [async-iteration]} + prototype/toString/proxy-async-method-definition.js {unsupported: [async-functions]} + prototype/toString/proxy-bound-function.js + prototype/toString/proxy-class.js {unsupported: [class]} + prototype/toString/proxy-function-expression.js + prototype/toString/proxy-generator-function.js + prototype/toString/proxy-method-definition.js prototype/toString/setter-class-expression.js prototype/toString/setter-class-expression-static.js prototype/toString/setter-class-statement.js @@ -694,40 +675,32 @@ built-ins/isNaN 8/16 (50.0%) ~built-ins/IteratorPrototype -built-ins/JSON 36/140 (25.71%) +built-ins/JSON 28/140 (20.0%) parse/builtin.js - parse/revived-proxy.js {unsupported: [Proxy]} - parse/revived-proxy-revoked.js {unsupported: [Proxy]} - parse/reviver-array-define-prop-err.js {unsupported: [Proxy]} - parse/reviver-array-delete-err.js {unsupported: [Proxy]} + parse/revived-proxy.js + parse/reviver-array-define-prop-err.js parse/reviver-array-get-prop-from-prototype.js - parse/reviver-array-length-coerce-err.js {unsupported: [Proxy]} - parse/reviver-array-length-get-err.js {unsupported: [Proxy]} + parse/reviver-array-length-coerce-err.js + parse/reviver-array-length-get-err.js parse/reviver-call-order.js - parse/reviver-object-define-prop-err.js {unsupported: [Proxy]} - parse/reviver-object-delete-err.js {unsupported: [Proxy]} + parse/reviver-object-define-prop-err.js parse/reviver-object-get-prop-from-prototype.js parse/reviver-object-non-configurable-prop-create.js parse/reviver-object-non-configurable-prop-delete.js strict - parse/reviver-object-own-keys-err.js {unsupported: [Proxy]} parse/text-negative-zero.js stringify/builtin.js - stringify/replacer-array-abrupt.js {unsupported: [Proxy]} + stringify/replacer-array-abrupt.js stringify/replacer-array-number.js - stringify/replacer-array-proxy.js {unsupported: [Proxy]} - stringify/replacer-array-proxy-revoked.js {unsupported: [Proxy]} - stringify/replacer-array-proxy-revoked-realm.js {unsupported: [Proxy]} - stringify/replacer-array-wrong-type.js {unsupported: [Proxy]} + stringify/replacer-array-proxy.js + stringify/replacer-array-wrong-type.js stringify/replacer-function-arguments.js stringify/replacer-function-object-deleted-property.js stringify/replacer-function-result.js - stringify/value-array-abrupt.js {unsupported: [Proxy]} - stringify/value-array-proxy.js {unsupported: [Proxy]} - stringify/value-array-proxy-revoked.js {unsupported: [Proxy]} + stringify/value-array-abrupt.js + stringify/value-array-proxy.js stringify/value-bigint-cross-realm.js stringify/value-bigint-tojson-receiver.js - stringify/value-object-proxy.js {unsupported: [Proxy]} - stringify/value-object-proxy-revoked.js {unsupported: [Proxy]} + stringify/value-object-proxy.js stringify/value-string-escape-ascii.js stringify/value-string-escape-unicode.js @@ -770,12 +743,10 @@ built-ins/Number 9/283 (3.18%) S9.3.1_A3_T1_U180E.js {unsupported: [u180e]} S9.3.1_A3_T2_U180E.js {unsupported: [u180e]} -built-ins/Object 122/3150 (3.87%) - assign/source-own-prop-desc-missing.js {unsupported: [Proxy]} - assign/source-own-prop-error.js {unsupported: [Proxy]} - assign/source-own-prop-keys-error.js {unsupported: [Proxy]} +built-ins/Object 102/3150 (3.24%) + assign/source-own-prop-error.js assign/strings-and-symbol-order.js - assign/strings-and-symbol-order-proxy.js {unsupported: [Proxy]} + assign/strings-and-symbol-order-proxy.js defineProperties/15.2.3.7-6-a-112.js non-strict defineProperties/15.2.3.7-6-a-113.js non-strict defineProperties/15.2.3.7-6-a-118.js @@ -792,7 +763,7 @@ built-ins/Object 122/3150 (3.87%) defineProperties/15.2.3.7-6-a-184.js defineProperties/15.2.3.7-6-a-185.js defineProperties/15.2.3.7-6-a-282.js - defineProperties/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} + defineProperties/proxy-no-ownkeys-returned-keys-order.js defineProperty/15.2.3.6-4-116.js non-strict defineProperty/15.2.3.6-4-117.js non-strict defineProperty/15.2.3.6-4-122.js @@ -811,11 +782,9 @@ built-ins/Object 122/3150 (3.87%) defineProperty/15.2.3.6-4-293-3.js non-strict defineProperty/15.2.3.6-4-293-4.js strict defineProperty/15.2.3.6-4-336.js - entries/observable-operations.js {unsupported: [Proxy]} + entries/observable-operations.js entries/order-after-define-property.js - freeze/abrupt-completion.js {unsupported: [Proxy]} - freeze/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} - freeze/throws-when-false.js + freeze/proxy-no-ownkeys-returned-keys-order.js fromEntries/evaluation-order.js fromEntries/iterator-closed-for-null-entry.js fromEntries/iterator-closed-for-string-entry.js @@ -828,40 +797,30 @@ built-ins/Object 122/3150 (3.87%) fromEntries/iterator-not-closed-for-uncallable-next.js fromEntries/to-property-key.js fromEntries/uses-keys-not-iterator.js - getOwnPropertyDescriptors/observable-operations.js {unsupported: [Proxy]} getOwnPropertyDescriptors/order-after-define-property.js - getOwnPropertyDescriptors/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} - getOwnPropertyDescriptors/proxy-undefined-descriptor.js {unsupported: [Proxy]} + getOwnPropertyDescriptors/proxy-no-ownkeys-returned-keys-order.js + getOwnPropertyDescriptors/proxy-undefined-descriptor.js getOwnPropertyDescriptor/15.2.3.3-4-212.js getOwnPropertyDescriptor/15.2.3.3-4-213.js getOwnPropertyDescriptor/15.2.3.3-4-214.js getOwnPropertyDescriptor/15.2.3.3-4-215.js getOwnPropertyDescriptor/15.2.3.3-4-250.js getOwnPropertyNames/order-after-define-property.js - getOwnPropertyNames/proxy-invariant-absent-not-configurable-symbol-key.js {unsupported: [Proxy]} - getOwnPropertyNames/proxy-invariant-duplicate-symbol-entry.js {unsupported: [Proxy]} - getOwnPropertyNames/proxy-invariant-not-extensible-absent-symbol-key.js {unsupported: [Proxy]} - getOwnPropertyNames/proxy-invariant-not-extensible-extra-symbol-key.js {unsupported: [Proxy]} - getOwnPropertySymbols/proxy-invariant-absent-not-configurable-string-key.js {unsupported: [Proxy]} - getOwnPropertySymbols/proxy-invariant-duplicate-string-entry.js {unsupported: [Proxy]} - getOwnPropertySymbols/proxy-invariant-not-extensible-absent-string-key.js {unsupported: [Proxy]} - getOwnPropertySymbols/proxy-invariant-not-extensible-extra-string-key.js {unsupported: [Proxy]} + getOwnPropertyNames/proxy-invariant-absent-not-configurable-symbol-key.js + getOwnPropertyNames/proxy-invariant-not-extensible-absent-symbol-key.js internals/DefineOwnProperty/consistent-value-regexp-dollar1.js - isFrozen/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} - isSealed/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} - keys/order-after-define-property.js {unsupported: [Proxy]} - keys/property-traps-order-with-proxied-array.js {unsupported: [Proxy]} + isFrozen/proxy-no-ownkeys-returned-keys-order.js + isSealed/proxy-no-ownkeys-returned-keys-order.js + keys/order-after-define-property.js + keys/property-traps-order-with-proxied-array.js keys/proxy-keys.js - keys/proxy-non-enumerable-prop-invariant-1.js {unsupported: [Proxy]} - keys/proxy-non-enumerable-prop-invariant-2.js {unsupported: [Proxy]} - keys/proxy-non-enumerable-prop-invariant-3.js {unsupported: [Proxy]} - preventExtensions/abrupt-completion.js {unsupported: [Proxy]} - preventExtensions/throws-when-false.js + keys/proxy-non-enumerable-prop-invariant-1.js + keys/proxy-non-enumerable-prop-invariant-2.js + keys/proxy-non-enumerable-prop-invariant-3.js prototype/hasOwnProperty/symbol_property_toPrimitive.js prototype/hasOwnProperty/symbol_property_toString.js prototype/hasOwnProperty/symbol_property_valueOf.js prototype/hasOwnProperty/topropertykey_before_toobject.js - prototype/isPrototypeOf/arg-is-proxy.js {unsupported: [Proxy]} prototype/isPrototypeOf/builtin.js prototype/isPrototypeOf/null-this-and-primitive-arg-returns-false.js prototype/isPrototypeOf/undefined-this-and-primitive-arg-returns-false.js @@ -871,25 +830,19 @@ built-ins/Object 122/3150 (3.87%) prototype/toLocaleString/primitive_this_value.js strict prototype/toLocaleString/primitive_this_value_getter.js strict prototype/toString/get-symbol-tag-err.js - prototype/toString/proxy-array.js {unsupported: [Proxy]} - prototype/toString/proxy-function.js {unsupported: [Proxy, async-functions]} - prototype/toString/proxy-function-async.js {unsupported: [Proxy, async-functions]} - prototype/toString/proxy-revoked.js {unsupported: [Proxy]} - prototype/toString/proxy-revoked-during-get-call.js {unsupported: [Proxy]} + prototype/toString/proxy-function.js {unsupported: [async-functions]} + prototype/toString/proxy-function-async.js {unsupported: [async-functions]} prototype/toString/symbol-tag-non-str-bigint.js prototype/toString/symbol-tag-non-str-builtin.js - prototype/toString/symbol-tag-non-str-proxy-function.js {unsupported: [Proxy, async-functions]} + prototype/toString/symbol-tag-non-str-proxy-function.js {unsupported: [async-functions]} prototype/toString/symbol-tag-override-bigint.js prototype/toString/symbol-tag-override-instances.js prototype/toString/symbol-tag-override-primitives.js prototype/toString/symbol-tag-str.js prototype/valueOf/S15.2.4.4_A14.js prototype/valueOf/S15.2.4.4_A15.js - seal/abrupt-completion.js {unsupported: [Proxy]} - seal/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} - seal/throws-when-false.js - setPrototypeOf/set-error.js {unsupported: [Proxy]} - values/observable-operations.js {unsupported: [Proxy]} + seal/proxy-no-ownkeys-returned-keys-order.js + values/observable-operations.js values/order-after-define-property.js proto-from-ctor-realm.js subclass-object-arg.js {unsupported: [class]} @@ -904,7 +857,7 @@ built-ins/parseInt 3/60 (5.0%) S15.1.2.2_A2_T10_U180E.js {unsupported: [u180e]} S15.1.2.2_A9.2.js -built-ins/Promise 404/599 (67.45%) +built-ins/Promise 403/599 (67.28%) allSettled/capability-resolve-throws-reject.js {unsupported: [async]} allSettled/ctx-ctor.js {unsupported: [class]} allSettled/does-not-invoke-array-setters.js {unsupported: [async]} @@ -1129,7 +1082,6 @@ built-ins/Promise 404/599 (67.45%) prototype/finally/subclass-resolve-count.js {unsupported: [async]} prototype/finally/subclass-species-constructor-reject-count.js prototype/finally/subclass-species-constructor-resolve-count.js - prototype/finally/this-value-proxy.js prototype/then/capability-executor-called-twice.js {unsupported: [class]} prototype/then/capability-executor-not-callable.js {unsupported: [class]} prototype/then/ctor-access-count.js {unsupported: [async]} @@ -1306,26 +1258,96 @@ built-ins/Promise 404/599 (67.45%) resolve-thenable-deferred.js {unsupported: [async]} resolve-thenable-immed.js {unsupported: [async]} -~built-ins/Proxy - -built-ins/Reflect 28/139 (20.14%) +built-ins/Proxy 79/306 (25.82%) + construct/arguments-realm.js + construct/call-parameters.js + construct/call-parameters-new-target.js + construct/trap-is-missing-target-is-proxy.js {unsupported: [class]} + construct/trap-is-null.js + construct/trap-is-null-target-is-proxy.js {unsupported: [class]} + construct/trap-is-undefined.js + construct/trap-is-undefined-no-property.js + construct/trap-is-undefined-proto-from-newtarget-realm.js + construct/trap-is-undefined-target-is-proxy.js {unsupported: [class]} + defineProperty/desc-realm.js + defineProperty/targetdesc-not-configurable-writable-desc-not-writable.js + defineProperty/targetdesc-undefined-target-is-not-extensible-realm.js non-strict + defineProperty/trap-is-missing-target-is-proxy.js + defineProperty/trap-is-undefined-target-is-proxy.js + defineProperty/trap-return-is-false.js + deleteProperty/boolean-trap-result-boolean-false.js + deleteProperty/return-false-not-strict.js non-strict + deleteProperty/return-false-strict.js strict + deleteProperty/targetdesc-is-configurable-target-is-not-extensible.js + deleteProperty/trap-is-missing-target-is-proxy.js + deleteProperty/trap-is-null-target-is-proxy.js + deleteProperty/trap-is-undefined-strict.js strict + deleteProperty/trap-is-undefined-target-is-proxy.js + getOwnPropertyDescriptor/result-is-undefined-targetdesc-is-undefined.js + getOwnPropertyDescriptor/resultdesc-is-invalid-descriptor.js + getOwnPropertyDescriptor/resultdesc-is-not-configurable-not-writable-targetdesc-is-writable.js + getOwnPropertyDescriptor/resultdesc-is-not-configurable-targetdesc-is-configurable.js + getOwnPropertyDescriptor/resultdesc-is-not-configurable-targetdesc-is-undefined.js + getOwnPropertyDescriptor/trap-is-missing-target-is-proxy.js + getOwnPropertyDescriptor/trap-is-null-target-is-proxy.js + getOwnPropertyDescriptor/trap-is-undefined.js + getOwnPropertyDescriptor/trap-is-undefined-target-is-proxy.js + get/accessor-get-is-undefined-throws.js + get/trap-is-null-target-is-proxy.js + get/trap-is-undefined-receiver.js + has/call-in-prototype.js + has/call-in-prototype-index.js + has/call-with.js non-strict + has/return-false-target-not-extensible-using-with.js non-strict + has/return-false-target-prop-exists-using-with.js non-strict + has/return-false-targetdesc-not-configurable-using-with.js non-strict + has/return-is-abrupt-with.js non-strict + has/return-true-target-prop-exists-using-with.js non-strict + has/trap-is-missing-target-is-proxy.js + has/trap-is-not-callable-using-with.js non-strict + has/trap-is-null-target-is-proxy.js + ownKeys/trap-is-undefined-target-is-proxy.js + preventExtensions/return-false.js + preventExtensions/trap-is-undefined-target-is-proxy.js {unsupported: [module]} + revocable/builtin.js + revocable/revocation-function-nonconstructor.js + setPrototypeOf/internals-call-order.js + setPrototypeOf/not-extensible-target-not-same-target-prototype.js + setPrototypeOf/return-abrupt-from-target-getprototypeof.js + setPrototypeOf/toboolean-trap-result-false.js + setPrototypeOf/toboolean-trap-result-true-target-is-extensible.js + setPrototypeOf/trap-is-missing-target-is-proxy.js + setPrototypeOf/trap-is-null-target-is-proxy.js + set/boolean-trap-result-is-false-boolean-return-false.js + set/boolean-trap-result-is-false-null-return-false.js + set/boolean-trap-result-is-false-number-return-false.js + set/boolean-trap-result-is-false-string-return-false.js + set/boolean-trap-result-is-false-undefined-return-false.js + set/call-parameters.js + set/call-parameters-prototype.js + set/call-parameters-prototype-index.js + set/target-property-is-accessor-not-configurable-set-is-undefined.js + set/target-property-is-not-configurable-not-writable-not-equal-to-v.js + set/trap-is-missing-receiver-multiple-calls.js + set/trap-is-missing-receiver-multiple-calls-index.js + set/trap-is-missing-target-is-proxy.js + set/trap-is-null-receiver.js + set/trap-is-null-target-is-proxy.js + set/trap-is-undefined-target-is-proxy.js + create-handler-not-object-throw-symbol.js + create-target-not-object-throw-symbol.js + get-fn-realm.js + get-fn-realm-recursive.js + +built-ins/Reflect 19/139 (13.67%) construct/newtarget-is-not-constructor-throws.js - construct/return-with-newtarget-argument.js defineProperty/return-abrupt-from-property-key.js - defineProperty/return-abrupt-from-result.js {unsupported: [Proxy]} - deleteProperty/return-abrupt-from-result.js {unsupported: [Proxy]} + deleteProperty/return-abrupt-from-result.js deleteProperty/return-boolean.js strict - getOwnPropertyDescriptor/return-abrupt-from-result.js {unsupported: [Proxy]} - getPrototypeOf/return-abrupt-from-result.js {unsupported: [Proxy]} get/return-value-from-receiver.js - has/return-abrupt-from-result.js {unsupported: [Proxy]} - isExtensible/return-abrupt-from-result.js {unsupported: [Proxy]} ownKeys/order-after-define-property.js - ownKeys/return-abrupt-from-result.js {unsupported: [Proxy]} ownKeys/return-on-corresponding-order-large-index.js {unsupported: [computed-property-names]} - preventExtensions/return-abrupt-from-result.js {unsupported: [Proxy]} - preventExtensions/return-boolean-from-proxy-object.js {unsupported: [Proxy]} - setPrototypeOf/return-abrupt-from-result.js {unsupported: [Proxy]} + preventExtensions/return-boolean-from-proxy-object.js set/call-prototype-property-set.js set/creates-a-data-descriptor.js set/different-property-descriptors.js @@ -2243,7 +2265,7 @@ built-ins/TypedArray 992/1070 (92.71%) name.js prototype.js -built-ins/TypedArrayConstructors 518/684 (75.73%) +built-ins/TypedArrayConstructors 513/684 (75.0%) BigInt64Array/prototype 4/4 (100.0%) BigInt64Array 7/7 (100.0%) BigUint64Array/prototype 4/4 (100.0%) @@ -2283,7 +2305,6 @@ built-ins/TypedArrayConstructors 518/684 (75.73%) ctors/buffer-arg/toindex-bytelength-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/toindex-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/typedarray-backed-by-sharedarraybuffer.js {unsupported: [SharedArrayBuffer]} - ctors/buffer-arg/use-custom-proto-if-object.js ctors/buffer-arg/use-custom-proto-if-object-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/use-default-proto-if-custom-proto-is-not-object-sab.js {unsupported: [SharedArrayBuffer]} ctors/length-arg/custom-proto-access-throws.js @@ -2292,10 +2313,8 @@ built-ins/TypedArrayConstructors 518/684 (75.73%) ctors/length-arg/is-symbol-throws.js ctors/length-arg/proto-from-ctor-realm.js ctors/length-arg/toindex-length.js - ctors/length-arg/use-custom-proto-if-object.js ctors/no-args/custom-proto-access-throws.js ctors/no-args/proto-from-ctor-realm.js - ctors/no-args/use-custom-proto-if-object.js ctors/object-arg/as-generator-iterable-returns.js ctors/object-arg/custom-proto-access-throws.js ctors/object-arg/iterating-throws.js @@ -2313,7 +2332,6 @@ built-ins/TypedArrayConstructors 518/684 (75.73%) ctors/object-arg/throws-setting-obj-to-primitive-typeerror.js ctors/object-arg/throws-setting-property.js ctors/object-arg/throws-setting-symbol-property.js - ctors/object-arg/use-custom-proto-if-object.js ctors/typedarray-arg/custom-proto-access-throws.js ctors/typedarray-arg/detached-when-species-retrieved-different-type.js {unsupported: [Symbol.species]} ctors/typedarray-arg/detached-when-species-retrieved-same-type.js {unsupported: [Symbol.species]} @@ -2337,7 +2355,6 @@ built-ins/TypedArrayConstructors 518/684 (75.73%) ctors/typedarray-arg/same-ctor-buffer-ctor-species-undefined.js {unsupported: [Symbol.species]} ctors/typedarray-arg/same-ctor-buffer-ctor-value-not-obj-throws.js ctors/typedarray-arg/src-typedarray-big-throws.js - ctors/typedarray-arg/use-custom-proto-if-object.js Float32Array/prototype/not-typedarray-object.js Float32Array/prototype/proto.js Float64Array/prototype/not-typedarray-object.js @@ -2413,7 +2430,7 @@ built-ins/TypedArrayConstructors 518/684 (75.73%) internals/Get/key-is-out-of-bounds.js internals/Get/key-is-symbol.js internals/HasProperty/BigInt 15/15 (100.0%) - internals/HasProperty/abrupt-from-ordinary-has-parent-hasproperty.js {unsupported: [Proxy]} + internals/HasProperty/abrupt-from-ordinary-has-parent-hasproperty.js internals/HasProperty/detached-buffer.js internals/HasProperty/detached-buffer-key-is-not-number.js internals/HasProperty/detached-buffer-key-is-symbol.js @@ -4532,7 +4549,7 @@ language/expressions/object 841/1081 (77.8%) dstr/meth-obj-ptrn-rest-getter.js {unsupported: [object-rest]} dstr/meth-obj-ptrn-rest-skip-non-enumerable.js {unsupported: [object-rest]} dstr/meth-obj-ptrn-rest-val-obj.js {unsupported: [object-rest]} - dstr/object-rest-proxy-ownkeys-returned-keys-order.js {unsupported: [Proxy, object-rest]} + dstr/object-rest-proxy-ownkeys-returned-keys-order.js {unsupported: [object-rest]} method-definition/async-await-as-binding-identifier.js {unsupported: [async-functions]} method-definition/async-await-as-binding-identifier-escaped.js {unsupported: [async-functions]} method-definition/async-await-as-identifier-reference.js {unsupported: [async-functions]} @@ -4806,9 +4823,9 @@ language/expressions/object 841/1081 (77.8%) let-non-strict-syntax.js non-strict literal-property-name-bigint.js {unsupported: [class]} method.js - object-spread-proxy-ownkeys-returned-keys-order.js {unsupported: [Proxy]} + object-spread-proxy-ownkeys-returned-keys-order.js prop-def-id-eval-error.js non-strict - prop-def-id-eval-error-2.js {unsupported: [Proxy]} + prop-def-id-eval-error-2.js non-strict prop-dup-data-data.js strict prop-dup-data-set.js strict prop-dup-get-data.js strict @@ -4924,8 +4941,7 @@ language/expressions/template-literal 2/57 (3.51%) language/expressions/this 0/6 (0.0%) -language/expressions/typeof 1/16 (6.25%) - proxy.js {unsupported: [Proxy]} +language/expressions/typeof 0/16 (0.0%) language/expressions/unary-minus 1/14 (7.14%) bigint-non-primitive.js @@ -5531,7 +5547,7 @@ language/statements/for-in 39/114 (34.21%) scope-head-lex-open.js scope-head-var-none.js non-strict -language/statements/for-of 471/725 (64.97%) +language/statements/for-of 470/725 (64.83%) dstr/array-elem-init-assignment.js dstr/array-elem-init-evaluation.js dstr/array-elem-init-fn-name-arrow.js @@ -5979,7 +5995,6 @@ language/statements/for-of 471/725 (64.97%) head-var-bound-names-let.js non-strict head-var-init.js head-var-no-expr.js - iterator-as-proxy.js {unsupported: [Proxy]} iterator-close-non-object.js iterator-close-non-throw-get-method-abrupt.js iterator-close-non-throw-get-method-is-null.js @@ -6344,8 +6359,8 @@ language/types 9/113 (7.96%) number/S8.5_A4_T1.js number/S8.5_A4_T2.js non-strict reference/get-value-prop-base-primitive-realm.js - reference/put-value-prop-base-primitive.js {unsupported: [Proxy]} - reference/put-value-prop-base-primitive-realm.js {unsupported: [Proxy]} + reference/put-value-prop-base-primitive.js + reference/put-value-prop-base-primitive-realm.js undefined/S8.1_A3_T1.js undefined/S8.1_A3_T2.js non-strict From a51eeb0a423545b34607eba6301fea08847a30f5 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Thu, 31 Aug 2023 20:38:44 +0200 Subject: [PATCH 17/24] spotless --- .../AbstractEcmaObjectOperations.java | 53 ++++++++++++------- src/org/mozilla/javascript/NativeArray.java | 2 +- src/org/mozilla/javascript/NativeProxy.java | 17 +++--- src/org/mozilla/javascript/NativeReflect.java | 4 +- .../javascript/tests/es6/NativeProxyTest.java | 21 ++++---- .../tests/es6/NativeReflectTest.java | 2 - 6 files changed, 55 insertions(+), 44 deletions(-) diff --git a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java index 9776c0d287..a73b4a96a6 100644 --- a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java +++ b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java @@ -288,28 +288,39 @@ static long lengthOfArrayLike(Context cx, Scriptable o) { * *

https://262.ecma-international.org/12.0/#sec-iscompatiblepropertydescriptor */ - static boolean isCompatiblePropertyDescriptor(boolean extensible, ScriptableObject desc, ScriptableObject current) { - return validateAndApplyPropertyDescriptor(Undefined.SCRIPTABLE_UNDEFINED, Undefined.SCRIPTABLE_UNDEFINED, extensible, desc, current); + static boolean isCompatiblePropertyDescriptor( + boolean extensible, ScriptableObject desc, ScriptableObject current) { + return validateAndApplyPropertyDescriptor( + Undefined.SCRIPTABLE_UNDEFINED, + Undefined.SCRIPTABLE_UNDEFINED, + extensible, + desc, + current); } /** - * ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current ) + * ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current ) * *

https://262.ecma-international.org/12.0/#sec-validateandapplypropertydescriptor */ - static boolean validateAndApplyPropertyDescriptor(Scriptable o, Scriptable p, boolean extensible, ScriptableObject desc, ScriptableObject current) { + static boolean validateAndApplyPropertyDescriptor( + Scriptable o, + Scriptable p, + boolean extensible, + ScriptableObject desc, + ScriptableObject current) { if (Undefined.isUndefined(current)) { if (!extensible) { return false; } - if (ScriptableObject.isGenericDescriptor(desc) || ScriptableObject.isDataDescriptor(desc)) { + if (ScriptableObject.isGenericDescriptor(desc) + || ScriptableObject.isDataDescriptor(desc)) { /* i. i. If O is not undefined, create an own data property named P of object O whose [[Value]], [[Writable]], [[Enumerable]], and [[Configurable]] attribute values are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property is set to its default value. */ - } - else { + } else { /* ii. ii. If O is not undefined, create an own accessor property named P of object O whose [[Get]], [[Set]], [[Enumerable]], and [[Configurable]] attribute values are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property is set to its default value. */ @@ -336,22 +347,29 @@ static boolean validateAndApplyPropertyDescriptor(Scriptable o, Scriptable p, bo // if (!ScriptableObject.isGenericDescriptor(desc)) { if (ScriptableObject.isGenericDescriptor(desc)) { return true; - } - else if (!Objects.equals(ScriptableObject.isGenericDescriptor(current), ScriptableObject.isGenericDescriptor(desc))) { + } else if (!Objects.equals( + ScriptableObject.isGenericDescriptor(current), + ScriptableObject.isGenericDescriptor(desc))) { if (Boolean.FALSE.equals(current.get("configurable"))) { return false; } if (ScriptableObject.isDataDescriptor(current)) { if (Boolean.FALSE.equals(current.get("configurable"))) { - // i. i. If O is not undefined, convert the property named P of object O from a data property to an accessor property. Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to their default values. - } - else { - // i. i. If O is not undefined, convert the property named P of object O from an accessor property to a data property. Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to their default values. + // i. i. If O is not undefined, convert the property named P of object O from a + // data property to an accessor property. Preserve the existing values of the + // converted property's [[Configurable]] and [[Enumerable]] attributes and set + // the rest of the property's attributes to their default values. + } else { + // i. i. If O is not undefined, convert the property named P of object O from an + // accessor property to a data property. Preserve the existing values of the + // converted property's [[Configurable]] and [[Enumerable]] attributes and set + // the rest of the property's attributes to their default values. } } - } - else if (ScriptableObject.isDataDescriptor(current) && ScriptableObject.isDataDescriptor(desc)) { - if (Boolean.FALSE.equals(current.get("configurable")) && Boolean.FALSE.equals(current.get("writable"))) { + } else if (ScriptableObject.isDataDescriptor(current) + && ScriptableObject.isDataDescriptor(desc)) { + if (Boolean.FALSE.equals(current.get("configurable")) + && Boolean.FALSE.equals(current.get("writable"))) { if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "writable")) && Boolean.TRUE.equals(desc.get("writable"))) { return false; @@ -362,8 +380,7 @@ else if (ScriptableObject.isDataDescriptor(current) && ScriptableObject.isDataDe } return true; } - } - else { + } else { if (Boolean.FALSE.equals(current.get("configurable"))) { if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "set")) && !Objects.equals(desc.get("set"), current.get("set"))) { diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 23f68f25b7..e6b65c2194 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -2250,7 +2250,7 @@ private static boolean js_isArray(Object o) { return false; } if (o instanceof NativeProxy) { - return js_isArray(((NativeProxy)o).getTargetThrowIfRevoked()); + return js_isArray(((NativeProxy) o).getTargetThrowIfRevoked()); } return "Array".equals(((Scriptable) o).getClassName()); } diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index 6ddb3d6da2..29174ae013 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -949,26 +949,27 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { Callable trap = getTrap(TRAP_DEFINE_PROPERTY); if (trap != null) { - boolean booleanTrapResult = ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, id, desc})); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, id, desc})); if (!booleanTrapResult) { return; } - ScriptableObject targetDesc = - target.getOwnPropertyDescriptor(Context.getContext(), id); + ScriptableObject targetDesc = target.getOwnPropertyDescriptor(Context.getContext(), id); boolean extensibleTarget = target.isExtensible(); - boolean settingConfigFalse = Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "configurable")) - && Boolean.FALSE.equals(desc.get("configurable")); + boolean settingConfigFalse = + Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "configurable")) + && Boolean.FALSE.equals(desc.get("configurable")); if (targetDesc == null) { if (!extensibleTarget || settingConfigFalse) { throw ScriptRuntime.typeError( "proxy can't define an incompatible property descriptor"); } - } - else { - if(!AbstractEcmaObjectOperations.isCompatiblePropertyDescriptor(extensibleTarget, desc, targetDesc)) { + } else { + if (!AbstractEcmaObjectOperations.isCompatiblePropertyDescriptor( + extensibleTarget, desc, targetDesc)) { throw ScriptRuntime.typeError( "proxy can't define an incompatible property descriptor"); } diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index 62c2fb9731..ce4c276a1d 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -140,9 +140,7 @@ private static Object apply(Context cx, Scriptable scope, Scriptable thisObj, Ob true, cx, scope, callable, new Object[] {thisObj, argumentsList}); } - /** - * see https://262.ecma-international.org/12.0/#sec-reflect.construct - */ + /** see https://262.ecma-international.org/12.0/#sec-reflect.construct */ private static Scriptable construct( Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { /* diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index ac6d5e0528..32026f4d5b 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -198,19 +198,19 @@ public void defineProperty() { public void definePropertyTrapReturnsFalse() { String js = "var target = {};\n" - + "var p = new Proxy(target, {\n" - + " defineProperty: function(t, prop, desc) {\n" - + " return 0;\n" - + " }\n" - + "});\n" - - + "'' + Reflect.defineProperty(p, 'attr', {})" - + "+ ' ' + Object.getOwnPropertyDescriptor(target, 'attr')"; + + "var p = new Proxy(target, {\n" + + " defineProperty: function(t, prop, desc) {\n" + + " return 0;\n" + + " }\n" + + "});\n" + + "'' + Reflect.defineProperty(p, 'attr', {})" + + "+ ' ' + Object.getOwnPropertyDescriptor(target, 'attr')"; testString("false undefined", js); } @Test - public void definePropertyDescNotConfigurableAndTargetPropertyDescriptorConfigurableAndTrapResultIsTrue() { + public void + definePropertyDescNotConfigurableAndTargetPropertyDescriptorConfigurableAndTrapResultIsTrue() { String js = "var target = {};\n" + "var p = new Proxy(target, {\n" @@ -245,7 +245,6 @@ public void definePropertyDescAndTargetPropertyDescriptorNotCompatibleAndTrapRes + "Object.defineProperty(target, \"foo\", {\n" + " value: 1\n" + "});\n" - + "try {\n" + " Object.defineProperty(p, \"foo\", {\n" + " value: 2\n" @@ -265,12 +264,10 @@ public void definePropertyDescAndTargetPropertyDescriptorNotCompatibleAndTrapRes + " return true;\r\n" + " }\n" + "});\n" - + "Object.defineProperty(target, 'prop', {\n" + " value: 1,\n" + " configurable: false\n" + "});\n" - + "try {\n" + " Object.defineProperty(p, 'prop', {\n" + " value: 1,\n" diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java index 67dd58cfcd..5651d80853 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java @@ -86,12 +86,10 @@ public void constructNewTarget() { String js = "var o = {};\n" + "var internPrototype;\n" - + "function fn() {\n" + " this.o = o;\n" + " internPrototype = Object.getPrototypeOf(this);\n" + "}\n" - + "var result = Reflect.construct(fn, [], Array);\n" + "'' + (Object.getPrototypeOf(result) === Array.prototype)" + " + ' ' + (internPrototype === Array.prototype)" From c8f06098f6c5f4e21ced242600b2d6b18f7b03e6 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Fri, 1 Sep 2023 08:36:26 +0200 Subject: [PATCH 18/24] remove leftover from lambda migration --- src/org/mozilla/javascript/NativeReflect.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index ce4c276a1d..d52d9ea244 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -96,26 +96,6 @@ public String getClassName() { return "Reflect"; } - private static NativeReflect constructor(Context cx, Scriptable scope, Object[] args) { - if (args.length < 2) { - throw ScriptRuntime.typeErrorById( - "msg.method.missing.parameter", - "Proxy.ctor", - "2", - Integer.toString(args.length)); - } - Scriptable s = ScriptRuntime.toObject(cx, scope, args[0]); - ScriptableObject trgt = ensureScriptableObject(s); - - s = ScriptRuntime.toObject(cx, scope, args[1]); - ScriptableObject hndlr = ensureScriptableObject(s); - - NativeReflect reflect = new NativeReflect(); - reflect.setPrototype(ScriptableObject.getClassPrototype(scope, REFLECT_TAG)); - reflect.setParentScope(scope); - return reflect; - } - private static Object apply(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (args.length < 3) { throw ScriptRuntime.typeErrorById( From 298cbcc2f3602c39732b7777c77bab4bb7d3acc1 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Fri, 1 Sep 2023 08:42:31 +0200 Subject: [PATCH 19/24] defineOwnProperty() now return a boolean - THIS BREAKS backward compatibility --- src/org/mozilla/javascript/Arguments.java | 13 +++++++------ src/org/mozilla/javascript/IdScriptableObject.java | 8 ++++---- src/org/mozilla/javascript/NativeArray.java | 3 ++- src/org/mozilla/javascript/NativeProxy.java | 8 ++++---- src/org/mozilla/javascript/NativeReflect.java | 3 +-- src/org/mozilla/javascript/ScriptableObject.java | 8 +++++--- testsrc/test262.properties | 3 +-- 7 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index a8bc9250f6..462646beb1 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -350,33 +350,34 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { } @Override - protected void defineOwnProperty( + protected boolean defineOwnProperty( Context cx, Object id, ScriptableObject desc, boolean checkValid) { super.defineOwnProperty(cx, id, desc, checkValid); if (ScriptRuntime.isSymbol(id)) { - return; + return true; } double d = ScriptRuntime.toNumber(id); int index = (int) d; - if (d != index) return; + if (d != index) return true; Object value = arg(index); - if (value == NOT_FOUND) return; + if (value == NOT_FOUND) return true; if (isAccessorDescriptor(desc)) { removeArg(index); - return; + return true; } Object newValue = getProperty(desc, "value"); - if (newValue == NOT_FOUND) return; + if (newValue == NOT_FOUND) return true; replaceArg(index, newValue); if (isFalse(getProperty(desc, "writable"))) { removeArg(index); } + return true; } // ECMAScript2015 diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index ba46b01940..3fc374c73e 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -852,7 +852,7 @@ private IdFunctionObject newIdFunction( } @Override - protected void defineOwnProperty( + protected boolean defineOwnProperty( Context cx, Object key, ScriptableObject desc, boolean checkValid) { if (key instanceof String) { String name = (String) key; @@ -874,7 +874,7 @@ protected void defineOwnProperty( } } setAttributes(name, applyDescriptorToAttributeBitset(attr, desc)); - return; + return true; } } if (prototypeValues != null) { @@ -904,12 +904,12 @@ protected void defineOwnProperty( super.delete(name); } - return; + return true; } } } } - super.defineOwnProperty(cx, key, desc, checkValid); + return super.defineOwnProperty(cx, key, desc, checkValid); } @Override diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index e6b65c2194..0213bed11b 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -701,7 +701,7 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { } @Override - protected void defineOwnProperty( + protected boolean defineOwnProperty( Context cx, Object id, ScriptableObject desc, boolean checkValid) { long index = toArrayIndex(id); if (index >= length) { @@ -732,6 +732,7 @@ protected void defineOwnProperty( lengthAttr = getAttributes("length"); // Update cached attributes value for length property } + return true; } /** See ECMA 15.4.1,2 */ diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index 29174ae013..acbe68fc88 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -917,7 +917,7 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc */ @Override - public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { + public boolean defineOwnProperty(Context cx, Object id, ScriptableObject desc) { /* * 1. Assert: IsPropertyKey(P) is true. * 2. Let handler be O.[[ProxyHandler]]. @@ -952,7 +952,7 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { boolean booleanTrapResult = ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, id, desc})); if (!booleanTrapResult) { - return; + return false; } ScriptableObject targetDesc = target.getOwnPropertyDescriptor(Context.getContext(), id); @@ -989,10 +989,10 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { } } } - return; + return true; } - target.defineOwnProperty(cx, id, desc); + return target.defineOwnProperty(cx, id, desc); } /** diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index d52d9ea244..514053fb79 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -208,8 +208,7 @@ private static Object defineProperty( ScriptableObject desc = ScriptableObject.ensureScriptableObject(args[2]); try { - target.defineOwnProperty(cx, args[1], desc); - return true; + return target.defineOwnProperty(cx, args[1], desc); } catch (EcmaError e) { return false; } diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 45c9e1d50b..2540b95255 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1566,9 +1566,9 @@ public void defineOwnProperties(Context cx, ScriptableObject props) { * @param id the name/index of the property * @param desc the new property descriptor, as described in 8.6.1 */ - public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { + public boolean defineOwnProperty(Context cx, Object id, ScriptableObject desc) { checkPropertyDefinition(desc); - defineOwnProperty(cx, id, desc, true); + return defineOwnProperty(cx, id, desc, true); } /** @@ -1581,7 +1581,7 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { * @param desc the new property descriptor, as described in 8.6.1 * @param checkValid whether to perform validity checks */ - protected void defineOwnProperty( + protected boolean defineOwnProperty( Context cx, Object id, ScriptableObject desc, boolean checkValid) { Object key = null; @@ -1650,6 +1650,8 @@ protected void defineOwnProperty( } slot.setAttributes(attributes); } + + return true; } /** diff --git a/testsrc/test262.properties b/testsrc/test262.properties index 941d9c9339..3501b845fa 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -1258,7 +1258,7 @@ built-ins/Promise 403/599 (67.28%) resolve-thenable-deferred.js {unsupported: [async]} resolve-thenable-immed.js {unsupported: [async]} -built-ins/Proxy 79/306 (25.82%) +built-ins/Proxy 78/306 (25.49%) construct/arguments-realm.js construct/call-parameters.js construct/call-parameters-new-target.js @@ -1274,7 +1274,6 @@ built-ins/Proxy 79/306 (25.82%) defineProperty/targetdesc-undefined-target-is-not-extensible-realm.js non-strict defineProperty/trap-is-missing-target-is-proxy.js defineProperty/trap-is-undefined-target-is-proxy.js - defineProperty/trap-return-is-false.js deleteProperty/boolean-trap-result-boolean-false.js deleteProperty/return-false-not-strict.js non-strict deleteProperty/return-false-strict.js strict From 22b5f411ace66910e6aa778f21f9e2cb20d02a6d Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Sat, 2 Sep 2023 18:11:56 +0200 Subject: [PATCH 20/24] preventExtensions has to return a boolean (incompatible change) --- .../AbstractEcmaObjectOperations.java | 5 ++-- src/org/mozilla/javascript/NativeObject.java | 21 ++++++++++++---- src/org/mozilla/javascript/NativeProxy.java | 17 +++++++------ src/org/mozilla/javascript/NativeReflect.java | 3 +-- .../mozilla/javascript/ScriptableObject.java | 3 ++- .../javascript/tests/es6/NativeProxyTest.java | 24 +++++++++++++++++++ testsrc/test262.properties | 6 ++--- 7 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java index a73b4a96a6..0e787d9f4c 100644 --- a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java +++ b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java @@ -130,8 +130,9 @@ static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) { */ ScriptableObject obj = ScriptableObject.ensureScriptableObject(o); - // TODO check .preventExtensions() return value once implemented and act accordingly to spec - obj.preventExtensions(); + if (!obj.preventExtensions()) { + return false; + } for (Object key : obj.getIds(true, true)) { ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, key); diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 6408e53a55..a896dc99d4 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -564,7 +564,10 @@ public Object execIdCall( } ScriptableObject obj = ensureScriptableObject(arg); - obj.preventExtensions(); + boolean status = obj.preventExtensions(); + if (!status) { + throw ScriptRuntime.typeError("Object.preventExtensions is not allowed"); + } return obj; } case ConstructorId_defineProperties: @@ -622,8 +625,12 @@ public Object execIdCall( return arg; } - AbstractEcmaObjectOperations.setIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); + boolean status = + AbstractEcmaObjectOperations.setIntegrityLevel( + cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); + if (!status) { + throw ScriptRuntime.typeError("Object is not sealable"); + } return arg; } @@ -635,8 +642,12 @@ public Object execIdCall( return arg; } - AbstractEcmaObjectOperations.setIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); + boolean status = + AbstractEcmaObjectOperations.setIntegrityLevel( + cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); + if (!status) { + throw ScriptRuntime.typeError("Object is not freezable"); + } return arg; } diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index acbe68fc88..a6d671fc46 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -1022,9 +1022,7 @@ public boolean isExtensible() { } boolean booleanTrapResult = ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target})); - boolean targetResult = target.isExtensible(); - - if (booleanTrapResult != targetResult) { + if (booleanTrapResult != target.isExtensible()) { throw ScriptRuntime.typeError( "IsExtensible trap has to return the same value as the target"); } @@ -1036,7 +1034,7 @@ public boolean isExtensible() { * https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions */ @Override - public void preventExtensions() { + public boolean preventExtensions() { /* * 1. Let handler be O.[[ProxyHandler]]. * 2. If handler is null, throw a TypeError exception. @@ -1055,13 +1053,14 @@ public void preventExtensions() { Callable trap = getTrap(TRAP_PREVENT_EXTENSIONS); if (trap == null) { - target.preventExtensions(); - return; + return target.preventExtensions(); } - callTrap(trap, new Object[] {target}); - if (target.isExtensible()) { - throw ScriptRuntime.typeError("target is not extensible"); + boolean booleanTrapResult = ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target})); + if (booleanTrapResult && target.isExtensible()) { + throw ScriptRuntime.typeError("target is extensible but trap returned true"); } + + return booleanTrapResult; } @Override diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index 514053fb79..280f1d31c0 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -316,8 +316,7 @@ private static Object preventExtensions( Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { ScriptableObject target = checkTarget(args); - target.preventExtensions(); - return true; + return target.preventExtensions(); } private static Object set(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 2540b95255..d9ac07ee3b 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1966,8 +1966,9 @@ public boolean isExtensible() { return isExtensible; } - public void preventExtensions() { + public boolean preventExtensions() { isExtensible = false; + return true; } /** diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index 32026f4d5b..0a93fccff2 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -451,6 +451,30 @@ public void isExtensibleWithoutHandler() { testString("true-true false-false false-false", js); } + @Test + public void preventExtensionsTrapReturnsNoBoolean() { + String js = + "var target = {};\n" + + "var p = new Proxy({}, {\n" + + " preventExtensions: function(t) {\n" + + " return 0;\n" + + " }\n" + + "});\n" + + "var res = '' + Reflect.preventExtensions(p);\n" + + "Object.preventExtensions(target);\n" + + "res += ' ' + Reflect.preventExtensions(p);\n"; + testString("false false", js); + } + + @Test + public void preventExtensionsTrapIsUndefined() { + String js = + "var target = {};\n" + + "var p = new Proxy(target, {});\n" + + "'' + Reflect.preventExtensions(p);"; + testString("true", js); + } + @Test public void ownKeys() { String js = diff --git a/testsrc/test262.properties b/testsrc/test262.properties index 3501b845fa..de5281a91a 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -1258,7 +1258,7 @@ built-ins/Promise 403/599 (67.28%) resolve-thenable-deferred.js {unsupported: [async]} resolve-thenable-immed.js {unsupported: [async]} -built-ins/Proxy 78/306 (25.49%) +built-ins/Proxy 77/306 (25.16%) construct/arguments-realm.js construct/call-parameters.js construct/call-parameters-new-target.js @@ -1306,7 +1306,6 @@ built-ins/Proxy 78/306 (25.49%) has/trap-is-not-callable-using-with.js non-strict has/trap-is-null-target-is-proxy.js ownKeys/trap-is-undefined-target-is-proxy.js - preventExtensions/return-false.js preventExtensions/trap-is-undefined-target-is-proxy.js {unsupported: [module]} revocable/builtin.js revocable/revocation-function-nonconstructor.js @@ -1338,7 +1337,7 @@ built-ins/Proxy 78/306 (25.49%) get-fn-realm.js get-fn-realm-recursive.js -built-ins/Reflect 19/139 (13.67%) +built-ins/Reflect 18/139 (12.95%) construct/newtarget-is-not-constructor-throws.js defineProperty/return-abrupt-from-property-key.js deleteProperty/return-abrupt-from-result.js @@ -1346,7 +1345,6 @@ built-ins/Reflect 19/139 (13.67%) get/return-value-from-receiver.js ownKeys/order-after-define-property.js ownKeys/return-on-corresponding-order-large-index.js {unsupported: [computed-property-names]} - preventExtensions/return-boolean-from-proxy-object.js set/call-prototype-property-set.js set/creates-a-data-descriptor.js set/different-property-descriptors.js From bde73f987298a580a206bc0df60ff5434e0eaaf9 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Sat, 2 Sep 2023 19:29:35 +0200 Subject: [PATCH 21/24] one more get fix --- src/org/mozilla/javascript/NativeProxy.java | 3 +- .../javascript/tests/es6/NativeProxyTest.java | 36 +++++++++++++++++++ testsrc/test262.properties | 6 ++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index a6d671fc46..be869900ec 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -467,7 +467,8 @@ public Object get(int index, Scriptable start) { Callable trap = getTrap(TRAP_GET); if (trap != null) { - Object trapResult = callTrap(trap, new Object[] {target, index, this}); + Object trapResult = + callTrap(trap, new Object[] {target, ScriptRuntime.toString(index), this}); ScriptableObject targetDesc = target.getOwnPropertyDescriptor(Context.getContext(), index); diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java index 0a93fccff2..99bee0a519 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java @@ -666,6 +666,42 @@ public void hasSymbolWithoutHandler() { testString("true false", js); } + @Test + public void getTrapIsNullTargetIsProxy() { + String js = + "var stringTarget = new Proxy(new String('str'), {});\n" + + "var stringProxy = new Proxy(stringTarget, {\n" + + " get: null,\n" + + "});\n" + + "'' + stringProxy.length" + + " + ' ' + stringProxy[0]" + + " + ' ' + stringProxy[4];"; + testString("3 s undefined", js); + } + + @Test + public void getTrapIsNullTargetIsProxy2() { + String js = + "var sym = Symbol();\n" + + "var target = new Proxy({}, {\n" + + " get: function(_target, key) {\n" + + " switch (key) {\n" + + " case sym: return 1;\n" + + " case \"10\": return 2;\n" + + " case \"foo\": return 3;\n" + + " }\n" + + " },\n" + + "});\n" + + "var proxy = new Proxy(target, {\n" + + " get: null,\n" + + "});\n" + + "'' + proxy[sym]" + + " + ' ' + proxy[10]" + + " + ' ' + Object.create(proxy).foo" + + " + ' ' + proxy.bar;"; + testString("1 2 3 undefined", js); + } + @Test public void getPropertyByIntWithoutHandler() { String js = "var a = ['zero', 'one'];" + "var proxy1 = new Proxy(a, {});\n" + "proxy1[1];"; diff --git a/testsrc/test262.properties b/testsrc/test262.properties index de5281a91a..79150d4a1c 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -1,6 +1,6 @@ # This is a configuration file for Test262SuiteTest.java. See ./README.md for more info about this file -built-ins/Array 161/2670 (6.03%) +built-ins/Array 160/2670 (5.99%) from/calling-from-valid-1-noStrict.js non-strict Spec pretty clearly says this should be undefined from/elements-deleted-after.js Checking to see if length changed, but spec says it should not from/iter-map-fn-this-non-strict.js non-strict Error propagation needs work in general @@ -78,7 +78,6 @@ built-ins/Array 161/2670 (6.03%) prototype/flat/target-array-with-non-configurable-property.js {unsupported: [Symbol.species]} prototype/flat/target-array-with-non-writable-property.js {unsupported: [Symbol.species]} prototype/forEach/15.4.4.18-5-1-s.js non-strict - prototype/includes/get-prop.js prototype/indexOf/calls-only-has-on-prototype-after-length-zeroed.js prototype/indexOf/length-zero-returns-minus-one.js prototype/lastIndexOf/calls-only-has-on-prototype-after-length-zeroed.js @@ -1258,7 +1257,7 @@ built-ins/Promise 403/599 (67.28%) resolve-thenable-deferred.js {unsupported: [async]} resolve-thenable-immed.js {unsupported: [async]} -built-ins/Proxy 77/306 (25.16%) +built-ins/Proxy 76/306 (24.84%) construct/arguments-realm.js construct/call-parameters.js construct/call-parameters-new-target.js @@ -1292,7 +1291,6 @@ built-ins/Proxy 77/306 (25.16%) getOwnPropertyDescriptor/trap-is-undefined.js getOwnPropertyDescriptor/trap-is-undefined-target-is-proxy.js get/accessor-get-is-undefined-throws.js - get/trap-is-null-target-is-proxy.js get/trap-is-undefined-receiver.js has/call-in-prototype.js has/call-in-prototype-index.js From 9d66995109c5bbcc3bca047626a3dc0b284bbf37 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Sun, 3 Sep 2023 11:03:25 +0200 Subject: [PATCH 22/24] next minor step --- src/org/mozilla/javascript/NativeProxy.java | 84 +++++++++++++++++-- src/org/mozilla/javascript/NativeReflect.java | 2 + testsrc/test262.properties | 10 +-- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/org/mozilla/javascript/NativeProxy.java b/src/org/mozilla/javascript/NativeProxy.java index be869900ec..39f3f3a4e8 100644 --- a/src/org/mozilla/javascript/NativeProxy.java +++ b/src/org/mozilla/javascript/NativeProxy.java @@ -216,7 +216,8 @@ public boolean has(int index, Scriptable start) { Callable trap = getTrap(TRAP_HAS); if (trap != null) { boolean booleanTrapResult = - ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, index})); + ScriptRuntime.toBoolean( + callTrap(trap, new Object[] {target, ScriptRuntime.toString(index)})); if (!booleanTrapResult) { ScriptableObject targetDesc = target.getOwnPropertyDescriptor(Context.getContext(), index); @@ -585,9 +586,30 @@ public void put(String name, Scriptable start, Object value) { Callable trap = getTrap(TRAP_SET); if (trap != null) { ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); - callTrap(trap, new Object[] {target, name, desc}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, name, desc})); + if (!booleanTrapResult) { + return; // false + } - return; + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), name); + if (targetDesc != null + && !Undefined.isUndefined(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("configurable"))) { + if (ScriptableObject.isDataDescriptor(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("writable"))) { + if (!Objects.equals(value, targetDesc.get("value"))) { + throw ScriptRuntime.typeError( + "proxy set has to use the same value as the plain call"); + } + } + if (ScriptableObject.isAccessorDescriptor(targetDesc) + && Undefined.isUndefined(targetDesc.get("set"))) { + throw ScriptRuntime.typeError("proxy set has to be available"); + } + } + return; // true } ScriptableObject.putProperty(target, name, value); @@ -622,9 +644,33 @@ public void put(int index, Scriptable start, Object value) { Callable trap = getTrap(TRAP_SET); if (trap != null) { ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); - callTrap(trap, new Object[] {target, index, desc}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean( + callTrap( + trap, + new Object[] {target, ScriptRuntime.toString(index), desc})); + if (!booleanTrapResult) { + return; // false + } - return; + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), index); + if (targetDesc != null + && !Undefined.isUndefined(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("configurable"))) { + if (ScriptableObject.isDataDescriptor(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("writable"))) { + if (!Objects.equals(value, targetDesc.get("value"))) { + throw ScriptRuntime.typeError( + "proxy set has to use the same value as the plain call"); + } + } + if (ScriptableObject.isAccessorDescriptor(targetDesc) + && Undefined.isUndefined(targetDesc.get("set"))) { + throw ScriptRuntime.typeError("proxy set has to be available"); + } + } + return; // true } ScriptableObject.putProperty(target, index, value); @@ -659,9 +705,30 @@ public void put(Symbol key, Scriptable start, Object value) { Callable trap = getTrap(TRAP_SET); if (trap != null) { ScriptableObject desc = ScriptableObject.buildDataDescriptor(target, value, EMPTY); - callTrap(trap, new Object[] {target, key, desc}); + boolean booleanTrapResult = + ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, key, desc})); + if (!booleanTrapResult) { + return; // false + } - return; + ScriptableObject targetDesc = + target.getOwnPropertyDescriptor(Context.getContext(), key); + if (targetDesc != null + && !Undefined.isUndefined(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("configurable"))) { + if (ScriptableObject.isDataDescriptor(targetDesc) + && Boolean.FALSE.equals(targetDesc.get("writable"))) { + if (!Objects.equals(value, targetDesc.get("value"))) { + throw ScriptRuntime.typeError( + "proxy set has to use the same value as the plain call"); + } + } + if (ScriptableObject.isAccessorDescriptor(targetDesc) + && Undefined.isUndefined(targetDesc.get("set"))) { + throw ScriptRuntime.typeError("proxy set has to be available"); + } + } + return; // true } if (start == this) { @@ -750,7 +817,8 @@ public void delete(int index) { Callable trap = getTrap(TRAP_DELETE_PROPERTY); if (trap != null) { boolean booleanTrapResult = - ScriptRuntime.toBoolean(callTrap(trap, new Object[] {target, index})); + ScriptRuntime.toBoolean( + callTrap(trap, new Object[] {target, ScriptRuntime.toString(index)})); if (!booleanTrapResult) { return; // false } diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index 280f1d31c0..ada3b48467 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -83,6 +83,8 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { DONTENUM, DONTENUM | READONLY); + reflect.defineProperty(SymbolKey.TO_STRING_TAG, REFLECT_TAG, DONTENUM | READONLY); + ScriptableObject.defineProperty(scope, REFLECT_TAG, reflect, DONTENUM); if (sealed) { reflect.sealObject(); diff --git a/testsrc/test262.properties b/testsrc/test262.properties index 79150d4a1c..0a2d96d228 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -1,6 +1,6 @@ # This is a configuration file for Test262SuiteTest.java. See ./README.md for more info about this file -built-ins/Array 160/2670 (5.99%) +built-ins/Array 159/2670 (5.96%) from/calling-from-valid-1-noStrict.js non-strict Spec pretty clearly says this should be undefined from/elements-deleted-after.js Checking to see if length changed, but spec says it should not from/iter-map-fn-this-non-strict.js non-strict Error propagation needs work in general @@ -40,7 +40,6 @@ built-ins/Array 160/2670 (5.99%) prototype/concat/is-concat-spreadable-get-order.js prototype/copyWithin/coerced-values-start-change-start.js prototype/copyWithin/coerced-values-start-change-target.js - prototype/copyWithin/return-abrupt-from-delete-proxy-target.js prototype/copyWithin/return-abrupt-from-delete-target.js non-strict Not throwing properly on unwritable prototype/every/15.4.4.16-5-1-s.js non-strict prototype/filter/15.4.4.20-5-1-s.js non-strict @@ -1257,7 +1256,7 @@ built-ins/Promise 403/599 (67.28%) resolve-thenable-deferred.js {unsupported: [async]} resolve-thenable-immed.js {unsupported: [async]} -built-ins/Proxy 76/306 (24.84%) +built-ins/Proxy 74/306 (24.18%) construct/arguments-realm.js construct/call-parameters.js construct/call-parameters-new-target.js @@ -1302,7 +1301,6 @@ built-ins/Proxy 76/306 (24.84%) has/return-true-target-prop-exists-using-with.js non-strict has/trap-is-missing-target-is-proxy.js has/trap-is-not-callable-using-with.js non-strict - has/trap-is-null-target-is-proxy.js ownKeys/trap-is-undefined-target-is-proxy.js preventExtensions/trap-is-undefined-target-is-proxy.js {unsupported: [module]} revocable/builtin.js @@ -1323,7 +1321,6 @@ built-ins/Proxy 76/306 (24.84%) set/call-parameters-prototype.js set/call-parameters-prototype-index.js set/target-property-is-accessor-not-configurable-set-is-undefined.js - set/target-property-is-not-configurable-not-writable-not-equal-to-v.js set/trap-is-missing-receiver-multiple-calls.js set/trap-is-missing-receiver-multiple-calls-index.js set/trap-is-missing-target-is-proxy.js @@ -1335,7 +1332,7 @@ built-ins/Proxy 76/306 (24.84%) get-fn-realm.js get-fn-realm-recursive.js -built-ins/Reflect 18/139 (12.95%) +built-ins/Reflect 17/139 (12.23%) construct/newtarget-is-not-constructor-throws.js defineProperty/return-abrupt-from-property-key.js deleteProperty/return-abrupt-from-result.js @@ -1353,7 +1350,6 @@ built-ins/Reflect 18/139 (12.95%) set/set-value-on-accessor-descriptor-with-receiver.js set/set-value-on-data-descriptor.js set/symbol-property.js - Symbol.toStringTag.js built-ins/RegExp 897/1464 (61.27%) CharacterClassEscapes 24/24 (100.0%) From c48cfa5348641bbf2b9e095414ca556d0d02894a Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Sat, 23 Sep 2023 18:27:53 +0200 Subject: [PATCH 23/24] fix handling of primitive values in Reflect.apply (see https://github.com/HtmlUnit/htmlunit/issues/641) --- src/org/mozilla/javascript/NativeReflect.java | 2 ++ .../tests/es6/NativeReflectTest.java | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index ada3b48467..d7f1a05618 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -111,6 +111,8 @@ private static Object apply(Context cx, Scriptable scope, Scriptable thisObj, Ob if (args[1] instanceof Scriptable) { thisObj = (Scriptable) args[1]; + } else if (ScriptRuntime.isPrimitive(args[1])) { + thisObj = cx.newObject(scope, "Object", new Object[] {args[1]}); } if (ScriptRuntime.isSymbol(args[2])) { diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java index 5651d80853..bc853bb331 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeReflectTest.java @@ -17,6 +17,29 @@ public void testToString() { @Test public void apply() { testDouble(1.0, "Reflect.apply(Math.floor, undefined, [1.75])"); + testString( + "hello", + "Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111])"); + testInt(4, "Reflect.apply(RegExp.prototype.exec, /ab/, ['confabulation']).index"); + testString("i", "Reflect.apply(''.charAt, 'ponies', [3])"); + } + + @Test + public void applyString() { + testString("foo", "Reflect.apply(String.prototype.toString, 'foo', [])"); + testString("oo", "Reflect.apply(String.prototype.substring, 'foo', [1])"); + } + + @Test + public void applyNumber() { + testString("1.234567e+3", "Reflect.apply(Number.prototype.toExponential, 1234.567, [])"); + testString("1.23e+3", "Reflect.apply(Number.prototype.toExponential, 1234.567, [2])"); + } + + @Test + public void applyBoolean() { + testString("true", "Reflect.apply(Boolean.prototype.toString, true, [])"); + testString("false", "Reflect.apply(Boolean.prototype.toString, false, [])"); } @Test @@ -425,4 +448,17 @@ private static void testDouble(double expected, String js) { return null; }); } + + private static void testInt(int expected, String js) { + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + final Scriptable scope = cx.initStandardObjects(); + + Object result = cx.evaluateString(scope, js, "test", 1, null); + assertEquals(expected, ((Integer) result).intValue()); + + return null; + }); + } } From 11aabc58ea3e983fa8281dcf0867961704e88ff5 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Sun, 24 Sep 2023 09:16:45 +0200 Subject: [PATCH 24/24] fix rebase --- src/org/mozilla/javascript/NativeReflect.java | 12 ------------ testsrc/test262.properties | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/org/mozilla/javascript/NativeReflect.java b/src/org/mozilla/javascript/NativeReflect.java index d7f1a05618..f8d342b352 100644 --- a/src/org/mozilla/javascript/NativeReflect.java +++ b/src/org/mozilla/javascript/NativeReflect.java @@ -401,16 +401,4 @@ private static ScriptableObject checkTarget(Object[] args) { } return ScriptableObject.ensureScriptableObject(args[0]); } - - private void defineProperty( - Scriptable scope, - String name, - int length, - Callable target, - int attributes, - int propertyAttributes) { - LambdaFunction f = new LambdaFunction(scope, name, length, target); - f.setStandardPropertyAttributes(propertyAttributes); - defineProperty(name, f, attributes); - } } diff --git a/testsrc/test262.properties b/testsrc/test262.properties index 0a2d96d228..d49ba6976f 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -673,7 +673,7 @@ built-ins/isNaN 8/16 (50.0%) ~built-ins/IteratorPrototype -built-ins/JSON 28/140 (20.0%) +built-ins/JSON 27/140 (19.29%) parse/builtin.js parse/revived-proxy.js parse/reviver-array-define-prop-err.js