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

first implementation of Reflect & Proxy support #1332

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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 @@ -874,7 +874,7 @@ protected void defineOwnProperty(
}
}
setAttributes(name, applyDescriptorToAttributeBitset(attr, desc));
return;
return true;
}
}
if (prototypeValues != null) {
Expand Down Expand Up @@ -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
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