Skip to content

Commit 8ca0db7

Browse files
committed
base implementation of proxy and reflect support
1 parent 53e49ac commit 8ca0db7

15 files changed

+4060
-713
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
@@ -852,7 +852,7 @@ private IdFunctionObject newIdFunction(
852852
}
853853

854854
@Override
855-
protected void defineOwnProperty(
855+
protected boolean defineOwnProperty(
856856
Context cx, Object key, ScriptableObject desc, boolean checkValid) {
857857
if (key instanceof CharSequence) {
858858
String name = key.toString();
@@ -875,7 +875,7 @@ protected void defineOwnProperty(
875875
}
876876
attr = applyDescriptorToAttributeBitset(attr, desc);
877877
setAttributes(name, attr);
878-
return;
878+
return true;
879879
}
880880
}
881881
if (prototypeValues != null) {
@@ -905,12 +905,12 @@ protected void defineOwnProperty(
905905
super.delete(name);
906906
}
907907

908-
return;
908+
return true;
909909
}
910910
}
911911
}
912912
}
913-
super.defineOwnProperty(cx, key, desc, checkValid);
913+
return super.defineOwnProperty(cx, key, desc, checkValid);
914914
}
915915

916916
@Override

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,7 +2054,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl
20542054
continue StateLoop;
20552055
}
20562056
}
2057-
if (!(lhs instanceof Function)) {
2057+
if (!(lhs instanceof Constructable)) {
20582058
if (lhs == DBL_MRK)
20592059
lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
20602060
throw ScriptRuntime.notFunctionError(lhs);
@@ -3305,7 +3305,7 @@ private static CallFrame processThrowable(
33053305
// Continuation jump is almost done: capturedFrame
33063306
// points to the call to the function that captured
33073307
// continuation, so clone capturedFrame and
3308-
// emulate return that function with the supplied result
3308+
// emulate return that function with the suplied result
33093309
frame = cjump.capturedFrame.cloneFrozen();
33103310
setCallResult(frame, cjump.result, cjump.resultDbl);
33113311
// 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)