Skip to content

Commit 7a58552

Browse files
committed
base implementation of proxy and reflect support
1 parent 2fcc422 commit 7a58552

15 files changed

+4055
-785
lines changed

rhino/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java

Lines changed: 193 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package org.mozilla.javascript;
22

33
import java.util.ArrayList;
4+
import java.util.Arrays;
45
import java.util.LinkedHashMap;
56
import java.util.List;
67
import java.util.Map;
8+
import java.util.Objects;
9+
import java.util.function.Predicate;
710

811
/**
912
* Abstract Object Operations as defined by EcmaScript
@@ -134,8 +137,9 @@ static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) {
134137
*/
135138
ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
136139

137-
// TODO check .preventExtensions() return value once implemented and act accordingly to spec
138-
obj.preventExtensions();
140+
if (!obj.preventExtensions()) {
141+
return false;
142+
}
139143

140144
for (Object key : obj.getIds(true, true)) {
141145
ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, key);
@@ -303,4 +307,191 @@ static Map<Object, List<Object>> groupBy(
303307

304308
return groups;
305309
}
310+
311+
/**
312+
* CreateListFromArrayLike ( obj [ , elementTypes ] )
313+
*
314+
* <p>https://262.ecma-international.org/12.0/#sec-createlistfromarraylike
315+
*/
316+
static List<Object> createListFromArrayLike(
317+
Context cx, Scriptable o, Predicate<Object> elementTypesPredicate, String msg) {
318+
ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
319+
if (obj instanceof NativeArray) {
320+
Object[] arr = ((NativeArray) obj).toArray();
321+
for (Object next : arr) {
322+
if (!elementTypesPredicate.test(next)) {
323+
throw ScriptRuntime.typeError(msg);
324+
}
325+
}
326+
return Arrays.asList(arr);
327+
}
328+
329+
long len = lengthOfArrayLike(cx, obj);
330+
List<Object> list = new ArrayList<>();
331+
long index = 0;
332+
while (index < len) {
333+
// String indexName = ScriptRuntime.toString(index);
334+
Object next = ScriptableObject.getProperty(obj, (int) index);
335+
if (!elementTypesPredicate.test(next)) {
336+
throw ScriptRuntime.typeError(msg);
337+
}
338+
list.add(next);
339+
index++;
340+
}
341+
return list;
342+
}
343+
344+
/**
345+
* LengthOfArrayLike ( obj )
346+
*
347+
* <p>https://262.ecma-international.org/12.0/#sec-lengthofarraylike
348+
*/
349+
static long lengthOfArrayLike(Context cx, Scriptable o) {
350+
Object value = ScriptableObject.getProperty(o, "length");
351+
long len = ScriptRuntime.toLength(new Object[] {value}, 0);
352+
return len;
353+
}
354+
355+
/**
356+
* IsCompatiblePropertyDescriptor ( Extensible, Desc, Current )
357+
*
358+
* <p>https://262.ecma-international.org/12.0/#sec-iscompatiblepropertydescriptor
359+
*/
360+
static boolean isCompatiblePropertyDescriptor(
361+
Context cx, boolean extensible, ScriptableObject desc, ScriptableObject current) {
362+
return validateAndApplyPropertyDescriptor(
363+
cx,
364+
Undefined.SCRIPTABLE_UNDEFINED,
365+
Undefined.SCRIPTABLE_UNDEFINED,
366+
extensible,
367+
desc,
368+
current);
369+
}
370+
371+
/**
372+
* ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current )
373+
*
374+
* <p>https://262.ecma-international.org/12.0/#sec-validateandapplypropertydescriptor
375+
*/
376+
static boolean validateAndApplyPropertyDescriptor(
377+
Context cx,
378+
Scriptable o,
379+
Scriptable p,
380+
boolean extensible,
381+
ScriptableObject desc,
382+
ScriptableObject current) {
383+
if (Undefined.isUndefined(current)) {
384+
if (!extensible) {
385+
return false;
386+
}
387+
388+
if (ScriptableObject.isGenericDescriptor(desc)
389+
|| ScriptableObject.isDataDescriptor(desc)) {
390+
/*
391+
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.
392+
If the value of an attribute field of Desc is absent, the attribute of the newly created property is set to its default value.
393+
*/
394+
} else {
395+
/*
396+
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.
397+
*/
398+
}
399+
return true;
400+
}
401+
402+
if (desc.getIds().length == 0) {
403+
return true;
404+
}
405+
406+
if (Boolean.FALSE.equals(current.get("configurable"))) {
407+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "configurable"))
408+
&& Boolean.TRUE.equals(desc.get("configurable"))) {
409+
return false;
410+
}
411+
412+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "enumerable"))
413+
&& !Objects.equals(desc.get("enumerable"), current.get("enumerable"))) {
414+
return false;
415+
}
416+
}
417+
418+
if (ScriptableObject.isGenericDescriptor(desc)) {
419+
return true;
420+
}
421+
422+
if (ScriptableObject.isDataDescriptor(current) != ScriptableObject.isDataDescriptor(desc)) {
423+
if (Boolean.FALSE.equals(current.get("configurable"))) {
424+
return false;
425+
}
426+
if (ScriptableObject.isDataDescriptor(current)) {
427+
if (Boolean.FALSE.equals(current.get("configurable"))) {
428+
// i. i. If O is not undefined, convert the property named P of object O from a
429+
// data property to an accessor property. Preserve the existing values of the
430+
// converted property's [[Configurable]] and [[Enumerable]] attributes and set
431+
// the rest of the property's attributes to their default values.
432+
} else {
433+
// i. i. If O is not undefined, convert the property named P of object O from an
434+
// accessor property to a data property. Preserve the existing values of the
435+
// converted property's [[Configurable]] and [[Enumerable]] attributes and set
436+
// the rest of the property's attributes to their default values.
437+
}
438+
}
439+
} else if (ScriptableObject.isDataDescriptor(current)
440+
&& ScriptableObject.isDataDescriptor(desc)) {
441+
if (Boolean.FALSE.equals(current.get("configurable"))
442+
&& Boolean.FALSE.equals(current.get("writable"))) {
443+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "writable"))
444+
&& Boolean.TRUE.equals(desc.get("writable"))) {
445+
return false;
446+
}
447+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "value"))
448+
&& !Objects.equals(desc.get("value"), current.get("value"))) {
449+
return false;
450+
}
451+
return true;
452+
}
453+
} else {
454+
if (Boolean.FALSE.equals(current.get("configurable"))) {
455+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "set"))
456+
&& !Objects.equals(desc.get("set"), current.get("set"))) {
457+
return false;
458+
}
459+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "get"))
460+
&& !Objects.equals(desc.get("get"), current.get("get"))) {
461+
return false;
462+
}
463+
return true;
464+
}
465+
}
466+
return true;
467+
}
468+
469+
/**
470+
* IsConstructor ( argument )
471+
*
472+
* <p>https://262.ecma-international.org/12.0/#sec-isconstructor
473+
*/
474+
static boolean isConstructor(Context cx, Object argument) {
475+
/*
476+
The abstract operation IsConstructor takes argument argument (an ECMAScript language value).
477+
It determines if argument is a function object with a [[Construct]] internal method.
478+
It performs the following steps when called:
479+
480+
1. If Type(argument) is not Object, return false.
481+
2. If argument has a [[Construct]] internal method, return true.
482+
3. Return false.
483+
*/
484+
485+
// Found no good way to implement this based on the spec.
486+
// Therefor I did this as first step - this only supports Lambda based method declarations.
487+
// see #1376 for more
488+
if (argument instanceof LambdaConstructor) {
489+
return true;
490+
}
491+
if (argument instanceof LambdaFunction) {
492+
return false;
493+
}
494+
495+
return argument instanceof Constructable;
496+
}
306497
}

rhino/src/main/java/org/mozilla/javascript/Arguments.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -359,33 +359,34 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
359359
}
360360

361361
@Override
362-
protected void defineOwnProperty(
362+
protected boolean defineOwnProperty(
363363
Context cx, Object id, ScriptableObject desc, boolean checkValid) {
364364
super.defineOwnProperty(cx, id, desc, checkValid);
365365
if (ScriptRuntime.isSymbol(id)) {
366-
return;
366+
return true;
367367
}
368368

369369
double d = ScriptRuntime.toNumber(id);
370370
int index = (int) d;
371-
if (d != index) return;
371+
if (d != index) return true;
372372

373373
Object value = arg(index);
374-
if (value == NOT_FOUND) return;
374+
if (value == NOT_FOUND) return true;
375375

376376
if (isAccessorDescriptor(desc)) {
377377
removeArg(index);
378-
return;
378+
return true;
379379
}
380380

381381
Object newValue = getProperty(desc, "value");
382-
if (newValue == NOT_FOUND) return;
382+
if (newValue == NOT_FOUND) return true;
383383

384384
replaceArg(index, newValue);
385385

386386
if (isFalse(getProperty(desc, "writable"))) {
387387
removeArg(index);
388388
}
389+
return true;
389390
}
390391

391392
// ECMAScript2015

rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ private IdFunctionObject newIdFunction(
856856
}
857857

858858
@Override
859-
protected void defineOwnProperty(
859+
protected boolean defineOwnProperty(
860860
Context cx, Object key, ScriptableObject desc, boolean checkValid) {
861861
if (key instanceof CharSequence) {
862862
String name = key.toString();
@@ -879,7 +879,7 @@ protected void defineOwnProperty(
879879
}
880880
attr = applyDescriptorToAttributeBitset(attr, desc);
881881
setAttributes(name, attr);
882-
return;
882+
return true;
883883
}
884884
}
885885
if (prototypeValues != null) {
@@ -909,12 +909,12 @@ protected void defineOwnProperty(
909909
super.delete(name);
910910
}
911911

912-
return;
912+
return true;
913913
}
914914
}
915915
}
916916
}
917-
super.defineOwnProperty(cx, key, desc, checkValid);
917+
return super.defineOwnProperty(cx, key, desc, checkValid);
918918
}
919919

920920
@Override

rhino/src/main/java/org/mozilla/javascript/Interpreter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,7 +2228,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl
22282228
continue StateLoop;
22292229
}
22302230
}
2231-
if (!(lhs instanceof Function)) {
2231+
if (!(lhs instanceof Constructable)) {
22322232
if (lhs == DBL_MRK)
22332233
lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
22342234
throw ScriptRuntime.notFunctionError(lhs);
@@ -3559,7 +3559,7 @@ private static CallFrame processThrowable(
35593559
// Continuation jump is almost done: capturedFrame
35603560
// points to the call to the function that captured
35613561
// continuation, so clone capturedFrame and
3562-
// emulate return that function with the supplied result
3562+
// emulate return that function with the suplied result
35633563
frame = cjump.capturedFrame.cloneFrozen();
35643564
setCallResult(frame, cjump.result, cjump.resultDbl);
35653565
// restart the execution

rhino/src/main/java/org/mozilla/javascript/LambdaConstructor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ public LambdaConstructor(
104104
this.flags = CONSTRUCTOR_DEFAULT;
105105
}
106106

107+
protected Constructable getTargetConstructor() {
108+
return targetConstructor;
109+
}
110+
107111
@Override
108112
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
109113
if ((flags & CONSTRUCTOR_FUNCTION) == 0) {

rhino/src/main/java/org/mozilla/javascript/NativeArray.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
846846
}
847847

848848
@Override
849-
protected void defineOwnProperty(
849+
protected boolean defineOwnProperty(
850850
Context cx, Object id, ScriptableObject desc, boolean checkValid) {
851851
long index = toArrayIndex(id);
852852
if (index >= length) {
@@ -877,6 +877,7 @@ protected void defineOwnProperty(
877877
lengthAttr =
878878
getAttributes("length"); // Update cached attributes value for length property
879879
}
880+
return true;
880881
}
881882

882883
/** See ECMA 15.4.1,2 */
@@ -2200,6 +2201,9 @@ private static boolean js_isArray(Object o) {
22002201
if (!(o instanceof Scriptable)) {
22012202
return false;
22022203
}
2204+
if (o instanceof NativeProxy) {
2205+
return js_isArray(((NativeProxy) o).getTargetThrowIfRevoked());
2206+
}
22032207
return "Array".equals(((Scriptable) o).getClassName());
22042208
}
22052209

rhino/src/main/java/org/mozilla/javascript/NativeObject.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,9 @@ public Object execIdCall(
568568
}
569569

570570
ScriptableObject obj = ensureScriptableObject(arg);
571-
obj.preventExtensions();
571+
if (!obj.preventExtensions()) {
572+
throw ScriptRuntime.typeError("Object.preventExtensions is not allowed");
573+
}
572574
return obj;
573575
}
574576
case ConstructorId_defineProperties:
@@ -626,9 +628,12 @@ public Object execIdCall(
626628
return arg;
627629
}
628630

629-
AbstractEcmaObjectOperations.setIntegrityLevel(
630-
cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED);
631-
631+
boolean status =
632+
AbstractEcmaObjectOperations.setIntegrityLevel(
633+
cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED);
634+
if (!status) {
635+
throw ScriptRuntime.typeError("Object is not sealable");
636+
}
632637
return arg;
633638
}
634639
case ConstructorId_freeze:
@@ -639,8 +644,12 @@ public Object execIdCall(
639644
return arg;
640645
}
641646

642-
AbstractEcmaObjectOperations.setIntegrityLevel(
643-
cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN);
647+
boolean status =
648+
AbstractEcmaObjectOperations.setIntegrityLevel(
649+
cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN);
650+
if (!status) {
651+
throw ScriptRuntime.typeError("Object is not freezable");
652+
}
644653

645654
return arg;
646655
}

0 commit comments

Comments
 (0)