Skip to content

Commit f2e8a75

Browse files
committed
base implementation of proxy and reflect support
1 parent b08de52 commit f2e8a75

15 files changed

+4085
-727
lines changed

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

Lines changed: 163 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,161 @@ 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+
boolean extensible, ScriptableObject desc, ScriptableObject current) {
362+
return validateAndApplyPropertyDescriptor(
363+
Undefined.SCRIPTABLE_UNDEFINED,
364+
Undefined.SCRIPTABLE_UNDEFINED,
365+
extensible,
366+
desc,
367+
current);
368+
}
369+
370+
/**
371+
* ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current )
372+
*
373+
* <p>https://262.ecma-international.org/12.0/#sec-validateandapplypropertydescriptor
374+
*/
375+
static boolean validateAndApplyPropertyDescriptor(
376+
Scriptable o,
377+
Scriptable p,
378+
boolean extensible,
379+
ScriptableObject desc,
380+
ScriptableObject current) {
381+
if (Undefined.isUndefined(current)) {
382+
if (!extensible) {
383+
return false;
384+
}
385+
386+
if (ScriptableObject.isGenericDescriptor(desc)
387+
|| ScriptableObject.isDataDescriptor(desc)) {
388+
/*
389+
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.
390+
If the value of an attribute field of Desc is absent, the attribute of the newly created property is set to its default value.
391+
*/
392+
} else {
393+
/*
394+
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.
395+
*/
396+
}
397+
return true;
398+
}
399+
400+
if (desc.getIds().length == 0) {
401+
return true;
402+
}
403+
404+
if (Boolean.FALSE.equals(current.get("configurable"))) {
405+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "configurable"))
406+
&& Boolean.TRUE.equals(desc.get("configurable"))) {
407+
return false;
408+
}
409+
410+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "enumerable"))
411+
&& !Objects.equals(desc.get("enumerable"), current.get("enumerable"))) {
412+
return false;
413+
}
414+
}
415+
416+
// if (!ScriptableObject.isGenericDescriptor(desc)) {
417+
if (ScriptableObject.isGenericDescriptor(desc)) {
418+
return true;
419+
} else if (!Objects.equals(
420+
ScriptableObject.isGenericDescriptor(current),
421+
ScriptableObject.isGenericDescriptor(desc))) {
422+
if (Boolean.FALSE.equals(current.get("configurable"))) {
423+
return false;
424+
}
425+
if (ScriptableObject.isDataDescriptor(current)) {
426+
if (Boolean.FALSE.equals(current.get("configurable"))) {
427+
// i. i. If O is not undefined, convert the property named P of object O from a
428+
// data property to an accessor property. Preserve the existing values of the
429+
// converted property's [[Configurable]] and [[Enumerable]] attributes and set
430+
// the rest of the property's attributes to their default values.
431+
} else {
432+
// i. i. If O is not undefined, convert the property named P of object O from an
433+
// accessor property to a data property. Preserve the existing values of the
434+
// converted property's [[Configurable]] and [[Enumerable]] attributes and set
435+
// the rest of the property's attributes to their default values.
436+
}
437+
}
438+
} else if (ScriptableObject.isDataDescriptor(current)
439+
&& ScriptableObject.isDataDescriptor(desc)) {
440+
if (Boolean.FALSE.equals(current.get("configurable"))
441+
&& Boolean.FALSE.equals(current.get("writable"))) {
442+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "writable"))
443+
&& Boolean.TRUE.equals(desc.get("writable"))) {
444+
return false;
445+
}
446+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "value"))
447+
&& !Objects.equals(desc.get("value"), current.get("value"))) {
448+
return false;
449+
}
450+
return true;
451+
}
452+
} else {
453+
if (Boolean.FALSE.equals(current.get("configurable"))) {
454+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "set"))
455+
&& !Objects.equals(desc.get("set"), current.get("set"))) {
456+
return false;
457+
}
458+
if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "get"))
459+
&& !Objects.equals(desc.get("get"), current.get("get"))) {
460+
return false;
461+
}
462+
return true;
463+
}
464+
}
465+
return true;
466+
}
306467
}

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

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

355355
@Override
356-
protected void defineOwnProperty(
356+
protected boolean defineOwnProperty(
357357
Context cx, Object id, ScriptableObject desc, boolean checkValid) {
358358
super.defineOwnProperty(cx, id, desc, checkValid);
359359
if (ScriptRuntime.isSymbol(id)) {
360-
return;
360+
return true;
361361
}
362362

363363
double d = ScriptRuntime.toNumber(id);
364364
int index = (int) d;
365-
if (d != index) return;
365+
if (d != index) return true;
366366

367367
Object value = arg(index);
368-
if (value == NOT_FOUND) return;
368+
if (value == NOT_FOUND) return true;
369369

370370
if (isAccessorDescriptor(desc)) {
371371
removeArg(index);
372-
return;
372+
return true;
373373
}
374374

375375
Object newValue = getProperty(desc, "value");
376-
if (newValue == NOT_FOUND) return;
376+
if (newValue == NOT_FOUND) return true;
377377

378378
replaceArg(index, newValue);
379379

380380
if (isFalse(getProperty(desc, "writable"))) {
381381
removeArg(index);
382382
}
383+
return true;
383384
}
384385

385386
// 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
@@ -2030,12 +2030,12 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl
20302030
continue StateLoop;
20312031
}
20322032
}
2033-
if (!(lhs instanceof Function)) {
2033+
if (!(lhs instanceof Constructable)) {
20342034
if (lhs == DBL_MRK)
20352035
lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
20362036
throw ScriptRuntime.notFunctionError(lhs);
20372037
}
2038-
Function fun = (Function) lhs;
2038+
Constructable fun = (Constructable) lhs;
20392039

20402040
if (fun instanceof IdFunctionObject) {
20412041
IdFunctionObject ifun = (IdFunctionObject) fun;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ public LambdaConstructor(
6666
this.flags = flags;
6767
}
6868

69+
protected Constructable getTargetConstructor() {
70+
return targetConstructor;
71+
}
72+
6973
@Override
7074
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
7175
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 */
@@ -2198,6 +2199,9 @@ private static boolean js_isArray(Object o) {
21982199
if (!(o instanceof Scriptable)) {
21992200
return false;
22002201
}
2202+
if (o instanceof NativeProxy) {
2203+
return js_isArray(((NativeProxy) o).getTargetThrowIfRevoked());
2204+
}
22012205
return "Array".equals(((Scriptable) o).getClassName());
22022206
}
22032207

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

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

570570
ScriptableObject obj = ensureScriptableObject(arg);
571-
obj.preventExtensions();
571+
boolean status = obj.preventExtensions();
572+
if (!status) {
573+
throw ScriptRuntime.typeError("Object.preventExtensions is not allowed");
574+
}
572575
return obj;
573576
}
574577
case ConstructorId_defineProperties:
@@ -626,9 +629,12 @@ public Object execIdCall(
626629
return arg;
627630
}
628631

629-
AbstractEcmaObjectOperations.setIntegrityLevel(
630-
cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED);
631-
632+
boolean status =
633+
AbstractEcmaObjectOperations.setIntegrityLevel(
634+
cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED);
635+
if (!status) {
636+
throw ScriptRuntime.typeError("Object is not sealable");
637+
}
632638
return arg;
633639
}
634640
case ConstructorId_freeze:
@@ -639,8 +645,12 @@ public Object execIdCall(
639645
return arg;
640646
}
641647

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

645655
return arg;
646656
}

0 commit comments

Comments
 (0)