Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implementation of Reflect & Proxy support #1431

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
32c76f9
first implementation of NativeReflect
rbri Jun 6, 2023
2af111f
fix broken backward compatibility (use the same approach as for all t…
rbri Jun 7, 2023
6f2e843
fix typos
rbri Jun 7, 2023
a14836c
working on proxy support (wip)
rbri Aug 13, 2023
a778d4b
apply implemented
rbri Aug 14, 2023
11e7ea1
built-ins/Proxy 152/306 (49.67%)
rbri Aug 14, 2023
ccb2bda
built-ins/Proxy 134/306 (43.79%)
rbri Aug 15, 2023
18f010a
convert to lambda
rbri Aug 16, 2023
bc02187
built-ins/Proxy 120/306 (39.22%)
rbri Aug 16, 2023
42f8dfe
built-ins/Proxy 93/306 (30.39%)
rbri Aug 17, 2023
166f81c
getOwnPropertyDescriptor [built-ins/Proxy 88/306 (28.76%) / built-ins…
rbri Aug 18, 2023
8e0cab8
more detailed get trap handling
rbri Aug 18, 2023
7c16e1b
NativeProxy has to support revoking from a trap
rbri Aug 24, 2023
bbd5e5d
implement more details for defineProperty
rbri Aug 24, 2023
c1ff1fd
construct now supports newTarget
rbri Aug 31, 2023
1d8d99d
current status of the impl
rbri Aug 31, 2023
9b2993f
spotless
rbri Aug 31, 2023
db5443e
remove leftover from lambda migration
rbri Sep 1, 2023
f2caa7c
defineOwnProperty() now return a boolean - THIS BREAKS backward compa…
rbri Sep 1, 2023
89fa899
preventExtensions has to return a boolean (incompatible change)
rbri Sep 2, 2023
cd353f1
one more get fix
rbri Sep 2, 2023
f12c678
next minor step
rbri Sep 3, 2023
4f4944a
fix handling of primitive values in Reflect.apply (see https://github…
rbri Sep 23, 2023
976af2c
fix rebase
rbri Sep 24, 2023
7288d22
fix rebase
rbri Nov 5, 2023
8518a8f
some more test are fixed with the latest rebase
rbri Nov 10, 2023
70bfa44
some fixes based on https://github.com/HtmlUnit/htmlunit-rhino-fork/p…
rbri Dec 27, 2023
144e93c
spotless
rbri Dec 27, 2023
2c87299
spotbugs
rbri Dec 27, 2023
b51da8c
add test for ctor args fix
rbri Dec 30, 2023
36d7615
set-callTrap should be called with the value, not the descriptor of v…
rbri Apr 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 166 additions & 2 deletions src/org/mozilla/javascript/AbstractEcmaObjectOperations.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package org.mozilla.javascript;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;

/**
* Abstract Object Operations as defined by EcmaScript
*
Expand Down Expand Up @@ -124,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);
Expand Down Expand Up @@ -232,4 +239,161 @@ static void put(Context cx, Scriptable o, int p, Object v, boolean isThrow) {
base.put(p, o, v);
}
}

/**
* CreateListFromArrayLike ( obj [ , elementTypes ] )
*
* <p>https://262.ecma-international.org/12.0/#sec-createlistfromarraylike
*/
static List<Object> createListFromArrayLike(
Context cx, Scriptable o, Predicate<Object> elementTypesPredicate, String msg) {
ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
if (obj instanceof NativeArray) {
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<Object> list = new ArrayList<>();
long index = 0;
while (index < len) {
// String indexName = ScriptRuntime.toString(index);
Object next = ScriptableObject.getProperty(obj, (int) index);
if (!elementTypesPredicate.test(next)) {
throw ScriptRuntime.typeError(msg);
}
list.add(next);
index++;
}
return list;
}

/**
* LengthOfArrayLike ( obj )
*
* <p>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;
}

/**
* IsCompatiblePropertyDescriptor ( Extensible, Desc, Current )
*
* <p>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 )
*
* <p>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;
}
}
13 changes: 7 additions & 6 deletions src/org/mozilla/javascript/Arguments.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions src/org/mozilla/javascript/IdScriptableObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -875,7 +875,7 @@ protected void defineOwnProperty(
}
attr = applyDescriptorToAttributeBitset(attr, desc);
setAttributes(name, attr);
return;
return true;
}
}
if (prototypeValues != null) {
Expand Down Expand Up @@ -905,12 +905,12 @@ protected void defineOwnProperty(
super.delete(name);
}

return;
return true;
}
}
}
}
super.defineOwnProperty(cx, key, desc, checkValid);
return super.defineOwnProperty(cx, key, desc, checkValid);
}

@Override
Expand Down
4 changes: 2 additions & 2 deletions src/org/mozilla/javascript/Interpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/org/mozilla/javascript/LambdaConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 5 additions & 1 deletion src/org/mozilla/javascript/NativeArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -2249,6 +2250,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());
}

Expand Down
21 changes: 16 additions & 5 deletions src/org/mozilla/javascript/NativeObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
Loading