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

Proxy and Reflect implementation #1637

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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 @@ -2199,6 +2200,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
22 changes: 16 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,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 @@ -626,9 +629,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 +645,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
Loading