Skip to content

Commit 8ca38bf

Browse files
authored
further improve 'arguments' support
* Use the existing ScriptRuntime.typeErrorThrower(cx) instead of having a separate ThrowTypeError in Arguments. This also fixes the setup of the ThrowTypeError returned by ScriptRuntime.typeErrorThrower(cx). * test cases as reference and for checking backward compatibility All test passing with the code base before the first 'argument' changes (commit 3f4e1a7) * migrate Arguments to lambda and fix two more cases * arguments accessed from the function itself are readonly
1 parent e6bbaed commit 8ca38bf

File tree

6 files changed

+1378
-186
lines changed

6 files changed

+1378
-186
lines changed

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

Lines changed: 77 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,25 @@
1414
* @see org.mozilla.javascript.NativeCall
1515
* @author Norris Boyd
1616
*/
17-
final class Arguments extends IdScriptableObject {
17+
class Arguments extends ScriptableObject {
1818
private static final long serialVersionUID = 4275508002492040609L;
1919

2020
private static final String CLASS_NAME = "Arguments";
2121

2222
// Fields to hold caller, callee and length properties,
2323
// where NOT_FOUND value tags deleted properties.
24-
// In addition if callerObj == NULL_VALUE, it tags null for scripts, as
25-
// initial callerObj == null means access to caller arguments available
26-
// only in JS <= 1.3 scripts
27-
private Object callerObj;
24+
// In addition, the 'caller' arguments is only available in JS <= 1.3 scripts
2825
private Object calleeObj;
2926
private Object lengthObj;
3027

31-
private int callerAttr = DONTENUM;
32-
private int calleeAttr = DONTENUM;
33-
private int lengthAttr = DONTENUM;
34-
3528
private NativeCall activation;
3629

3730
// Initially args holds activation.getOriginalArgs(), but any modification
3831
// of its elements triggers creation of a copy. If its element holds NOT_FOUND,
3932
// it indicates deleted index, in which case super class is queried.
4033
private Object[] args;
4134

42-
public Arguments(NativeCall activation) {
35+
public Arguments(NativeCall activation, Context cx) {
4336
this.activation = activation;
4437

4538
Scriptable parent = activation.getParentScope();
@@ -52,19 +45,13 @@ public Arguments(NativeCall activation) {
5245
JSFunction f = activation.function;
5346
calleeObj = f;
5447

55-
int version = f.getLanguageVersion();
56-
if (version <= Context.VERSION_1_3 && version != Context.VERSION_DEFAULT) {
57-
callerObj = null;
58-
} else {
59-
callerObj = NOT_FOUND;
60-
}
61-
6248
defineProperty(
6349
SymbolKey.ITERATOR,
6450
TopLevel.getBuiltinPrototype(
6551
ScriptableObject.getTopLevelScope(parent), TopLevel.Builtins.Array)
6652
.get("values", parent),
6753
ScriptableObject.DONTENUM);
54+
defineProperty("length", lengthObj, ScriptableObject.DONTENUM);
6855

6956
if (activation.isStrict) {
7057
// ECMAScript2015
@@ -75,14 +62,28 @@ public Arguments(NativeCall activation) {
7562
// 9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]:
7663
// %ThrowTypeError%,
7764
// [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
78-
setGetterOrSetter("caller", 0, new ThrowTypeError(parent, "caller"), true);
79-
setGetterOrSetter("caller", 0, new ThrowTypeError(parent, "caller"), false);
80-
setGetterOrSetter("callee", 0, new ThrowTypeError(parent, "callee"), true);
81-
setGetterOrSetter("callee", 0, new ThrowTypeError(parent, "callee"), false);
82-
setAttributes("caller", DONTENUM | PERMANENT);
83-
setAttributes("callee", DONTENUM | PERMANENT);
84-
callerObj = null;
65+
BaseFunction typeErrorThrower = ScriptRuntime.typeErrorThrower(cx);
66+
int version = cx.getLanguageVersion();
67+
if (version <= Context.VERSION_1_8) {
68+
setGetterOrSetter("caller", 0, typeErrorThrower, true);
69+
setGetterOrSetter("caller", 0, typeErrorThrower, false);
70+
setGetterOrSetter("callee", 0, typeErrorThrower, true);
71+
setGetterOrSetter("callee", 0, typeErrorThrower, false);
72+
setAttributes("caller", DONTENUM | PERMANENT);
73+
setAttributes("callee", DONTENUM | PERMANENT);
74+
} else {
75+
setGetterOrSetter("callee", 0, typeErrorThrower, true);
76+
setGetterOrSetter("callee", 0, typeErrorThrower, false);
77+
setAttributes("callee", DONTENUM | PERMANENT);
78+
}
8579
calleeObj = null;
80+
} else {
81+
defineProperty("callee", calleeObj, ScriptableObject.DONTENUM);
82+
83+
int version = cx.getLanguageVersion();
84+
if (version <= Context.VERSION_1_3 && version != Context.VERSION_DEFAULT) {
85+
defineProperty("caller", (Object) null, ScriptableObject.DONTENUM);
86+
}
8687
}
8788
}
8889

@@ -196,6 +197,11 @@ public void put(String name, Scriptable start, Object value) {
196197
super.put(name, start, value);
197198
}
198199

200+
@Override
201+
public void put(Symbol key, Scriptable start, Object value) {
202+
super.put(key, start, value);
203+
}
204+
199205
@Override
200206
public void delete(int index) {
201207
if (0 <= index && index < args.length) {
@@ -204,128 +210,6 @@ public void delete(int index) {
204210
super.delete(index);
205211
}
206212

207-
private static final int Id_callee = 1,
208-
Id_length = 2,
209-
Id_caller = 3,
210-
MAX_INSTANCE_ID = Id_caller;
211-
212-
@Override
213-
protected int getMaxInstanceId() {
214-
return MAX_INSTANCE_ID;
215-
}
216-
217-
@Override
218-
protected int findInstanceIdInfo(String s) {
219-
int id;
220-
switch (s) {
221-
case "callee":
222-
id = Id_callee;
223-
break;
224-
case "length":
225-
id = Id_length;
226-
break;
227-
case "caller":
228-
id = Id_caller;
229-
break;
230-
default:
231-
id = 0;
232-
break;
233-
}
234-
Context cx = Context.getContext();
235-
if (cx.isStrictMode()) {
236-
if (id == Id_callee || id == Id_caller) {
237-
return super.findInstanceIdInfo(s);
238-
}
239-
}
240-
241-
if (id == 0) return super.findInstanceIdInfo(s);
242-
243-
int attr;
244-
switch (id) {
245-
case Id_callee:
246-
attr = calleeAttr;
247-
break;
248-
case Id_caller:
249-
attr = callerAttr;
250-
break;
251-
case Id_length:
252-
attr = lengthAttr;
253-
break;
254-
default:
255-
throw new IllegalStateException();
256-
}
257-
return instanceIdInfo(attr, id);
258-
}
259-
260-
@Override
261-
protected String getInstanceIdName(int id) {
262-
switch (id) {
263-
case Id_callee:
264-
return "callee";
265-
case Id_length:
266-
return "length";
267-
case Id_caller:
268-
return "caller";
269-
}
270-
return null;
271-
}
272-
273-
@Override
274-
protected Object getInstanceIdValue(int id) {
275-
switch (id) {
276-
case Id_callee:
277-
return calleeObj;
278-
case Id_length:
279-
return lengthObj;
280-
case Id_caller:
281-
{
282-
Object value = callerObj;
283-
if (value == UniqueTag.NULL_VALUE) {
284-
value = null;
285-
} else if (value == null) {
286-
NativeCall caller = activation.parentActivationCall;
287-
if (caller != null) {
288-
value = caller.get("arguments", caller);
289-
}
290-
}
291-
return value;
292-
}
293-
}
294-
return super.getInstanceIdValue(id);
295-
}
296-
297-
@Override
298-
protected void setInstanceIdValue(int id, Object value) {
299-
switch (id) {
300-
case Id_callee:
301-
calleeObj = value;
302-
return;
303-
case Id_length:
304-
lengthObj = value;
305-
return;
306-
case Id_caller:
307-
callerObj = (value != null) ? value : UniqueTag.NULL_VALUE;
308-
return;
309-
}
310-
super.setInstanceIdValue(id, value);
311-
}
312-
313-
@Override
314-
protected void setInstanceIdAttributes(int id, int attr) {
315-
switch (id) {
316-
case Id_callee:
317-
calleeAttr = attr;
318-
return;
319-
case Id_length:
320-
lengthAttr = attr;
321-
return;
322-
case Id_caller:
323-
callerAttr = attr;
324-
return;
325-
}
326-
super.setInstanceIdAttributes(id, attr);
327-
}
328-
329213
@Override
330214
Object[] getIds(CompoundOperationMap map, boolean getNonEnumerable, boolean getSymbols) {
331215
Object[] ids = super.getIds(map, getNonEnumerable, getSymbols);
@@ -428,28 +312,60 @@ protected boolean defineOwnProperty(
428312
return true;
429313
}
430314

431-
private static final class ThrowTypeError extends BaseFunction {
432-
private static final long serialVersionUID = -744615873947395749L;
433-
private String propertyName;
315+
static final class ReadonlyArguments extends Arguments {
316+
private boolean initialized;
434317

435-
ThrowTypeError(Scriptable scope, String propertyName) {
436-
this.propertyName = propertyName;
437-
setPrototype(ScriptableObject.getFunctionPrototype(scope));
318+
public ReadonlyArguments(Arguments arguments, Context cx) {
319+
super(arguments.activation, cx);
320+
initialized = true;
321+
}
438322

439-
setAttributes("length", DONTENUM | PERMANENT | READONLY);
440-
setAttributes("name", DONTENUM | PERMANENT | READONLY);
323+
@Override
324+
public void put(int index, Scriptable start, Object value) {
325+
if (initialized) {
326+
return;
327+
}
328+
super.put(index, start, value);
329+
}
441330

442-
setAttributes("arity", DONTENUM);
443-
delete("arity");
444-
setAttributes("arguments", DONTENUM);
445-
delete("arguments");
331+
@Override
332+
public void put(String name, Scriptable start, Object value) {
333+
if (initialized) {
334+
return;
335+
}
336+
super.put(name, start, value);
337+
}
338+
339+
@Override
340+
public void put(Symbol key, Scriptable start, Object value) {
341+
if (initialized) {
342+
return;
343+
}
344+
super.put(key, start, value);
345+
}
346+
347+
@Override
348+
public void delete(int index) {
349+
if (initialized) {
350+
return;
351+
}
352+
super.delete(index);
353+
}
446354

447-
preventExtensions();
355+
@Override
356+
public void delete(String name) {
357+
if (initialized) {
358+
return;
359+
}
360+
super.delete(name);
448361
}
449362

450363
@Override
451-
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
452-
throw ScriptRuntime.typeErrorById("msg.arguments.not.access.strict", propertyName);
364+
public void delete(Symbol key) {
365+
if (initialized) {
366+
return;
367+
}
368+
super.delete(key);
453369
}
454370
}
455371
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,15 @@ private Object getArguments() {
713713
}
714714
Context cx = Context.getContext();
715715
NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this);
716-
return (activation == null) ? null : activation.get("arguments", activation);
716+
// return (activation == null) ? null : activation.get("arguments", activation);
717+
if (activation == null) {
718+
return null;
719+
}
720+
Object arguments = activation.get("arguments", activation);
721+
if (arguments instanceof Arguments && cx.getLanguageVersion() >= Context.VERSION_ES6) {
722+
return new Arguments.ReadonlyArguments((Arguments) arguments, cx);
723+
}
724+
return arguments;
717725
}
718726

719727
private static Scriptable jsConstructor(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ static void init(Scriptable scope, boolean sealed) {
7979
// initialize "arguments" property but only if it was not overridden by
8080
// the parameter with the same name
8181
if (!super.has("arguments", this) && !isArrow) {
82-
arguments = new Arguments(this);
82+
arguments = new Arguments(this, cx);
8383
defineProperty("arguments", arguments, PERMANENT);
8484
}
8585
}

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

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,28 +68,43 @@ public static BaseFunction typeErrorThrower() {
6868
/** Returns representation of the [[ThrowTypeError]] object. See ECMA 5 spec, 13.2.3 */
6969
public static BaseFunction typeErrorThrower(Context cx) {
7070
if (cx.typeErrorThrower == null) {
71-
BaseFunction thrower =
72-
new BaseFunction() {
73-
private static final long serialVersionUID = -5891740962154902286L;
74-
75-
@Override
76-
public Object call(
77-
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
78-
throw typeErrorById("msg.op.not.allowed");
79-
}
80-
81-
@Override
82-
public int getLength() {
83-
return 0;
84-
}
85-
};
86-
ScriptRuntime.setFunctionProtoAndParent(thrower, cx, cx.topCallScope, false);
87-
thrower.preventExtensions();
71+
BaseFunction thrower = new ThrowTypeError(cx.topCallScope);
8872
cx.typeErrorThrower = thrower;
8973
}
9074
return cx.typeErrorThrower;
9175
}
9276

77+
private static final class ThrowTypeError extends BaseFunction {
78+
private static final long serialVersionUID = -5891740962154902286L;
79+
80+
ThrowTypeError(Scriptable scope) {
81+
setPrototype(ScriptableObject.getFunctionPrototype(scope));
82+
83+
setAttributes("length", DONTENUM | PERMANENT | READONLY);
84+
setAttributes("name", DONTENUM | PERMANENT | READONLY);
85+
86+
// delete arity and arguments (without further checking)
87+
getMap().compute(this, "arity", 0, ThrowTypeError::removeWithoutChecking);
88+
getMap().compute(this, "arguments", 0, ThrowTypeError::removeWithoutChecking);
89+
90+
preventExtensions();
91+
}
92+
93+
private static Slot removeWithoutChecking(
94+
Object key,
95+
int index,
96+
Slot slot,
97+
CompoundOperationMap compoundOp,
98+
SlotMapOwner owner) {
99+
return null;
100+
}
101+
102+
@Override
103+
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
104+
throw typeErrorById("msg.op.not.allowed");
105+
}
106+
}
107+
93108
public static Object concat(Object lhs, Object rhs) {
94109
String rhsString = ScriptRuntime.toString(rhs);
95110
String lhsString = ScriptRuntime.toString(lhs);

0 commit comments

Comments
 (0)