Skip to content

Commit

Permalink
base implementation of proxy and reflect support
Browse files Browse the repository at this point in the history
  • Loading branch information
rbri committed Oct 11, 2024
1 parent 66622e5 commit 51d32bd
Show file tree
Hide file tree
Showing 15 changed files with 4,091 additions and 715 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.mozilla.javascript;

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

/**
* Abstract Object Operations as defined by EcmaScript
Expand Down Expand Up @@ -134,8 +137,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 @@ -303,4 +307,191 @@ static Map<Object, List<Object>> groupBy(

return groups;
}

/**
* 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(
Context cx, boolean extensible, ScriptableObject desc, ScriptableObject current) {
return validateAndApplyPropertyDescriptor(
cx,
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(
Context cx,
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)) {
return true;
}

if (ScriptableObject.isDataDescriptor(current) != ScriptableObject.isDataDescriptor(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;
}

/**
* IsConstructor ( argument )
*
* <p>https://262.ecma-international.org/12.0/#sec-isconstructor
*/
static boolean isConstructor(Context cx, Object argument) {
/*
The abstract operation IsConstructor takes argument argument (an ECMAScript language value).
It determines if argument is a function object with a [[Construct]] internal method.
It performs the following steps when called:
1. If Type(argument) is not Object, return false.
2. If argument has a [[Construct]] internal method, return true.
3. Return false.
*/

// Found no good way to implement this based on the spec.
// Therefor I did this as first step - this only supports Lambda based method declarations.
// see #1376 for more
if (argument instanceof LambdaConstructor) {
return true;
}
if (argument instanceof LambdaFunction) {
return false;
}

return argument instanceof Constructable;
}
}
13 changes: 7 additions & 6 deletions rhino/src/main/java/org/mozilla/javascript/Arguments.java
Original file line number Diff line number Diff line change
Expand Up @@ -359,33 +359,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
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 CharSequence) {
String name = key.toString();
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 rhino/src/main/java/org/mozilla/javascript/Interpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2054,7 +2054,7 @@ 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);
Expand Down Expand Up @@ -3305,7 +3305,7 @@ private static CallFrame processThrowable(
// Continuation jump is almost done: capturedFrame
// points to the call to the function that captured
// continuation, so clone capturedFrame and
// emulate return that function with the supplied result
// emulate return that function with the suplied result
frame = cjump.capturedFrame.cloneFrozen();
setCallResult(frame, cjump.result, cjump.resultDbl);
// restart the execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ public LambdaConstructor(
this.flags = CONSTRUCTOR_DEFAULT;
}

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 rhino/src/main/java/org/mozilla/javascript/NativeArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,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 @@ -877,6 +877,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 @@ -2200,6 +2201,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: 15 additions & 6 deletions rhino/src/main/java/org/mozilla/javascript/NativeObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,9 @@ public Object execIdCall(
}

ScriptableObject obj = ensureScriptableObject(arg);
obj.preventExtensions();
if (!obj.preventExtensions()) {
throw ScriptRuntime.typeError("Object.preventExtensions is not allowed");
}
return obj;
}
case ConstructorId_defineProperties:
Expand Down Expand Up @@ -626,9 +628,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;
}
case ConstructorId_freeze:
Expand All @@ -639,8 +644,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

0 comments on commit 51d32bd

Please sign in to comment.