Skip to content

Commit 4778f31

Browse files
authored
Refactor proxies to be callable objects only if their initial target is callable
* Refactor `NativeProxy` so only proxies of callables are callable. * Fix checking of proxy functions being constructors.
1 parent f3a9949 commit 4778f31

3 files changed

Lines changed: 100 additions & 84 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,10 @@ The abstract operation IsConstructor takes argument argument (an ECMAScript lang
531531
if (argument instanceof LambdaFunction) {
532532
return false;
533533
}
534+
if (argument instanceof NativeProxy.NativeProxyFunction) {
535+
var f = ((NativeProxy) argument).getTargetThrowIfRevoked();
536+
return isConstructor(cx, f);
537+
}
534538

535539
return argument instanceof Constructable;
536540
}

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

Lines changed: 93 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* @author Ronald Brill
1818
*/
19-
final class NativeProxy extends ScriptableObject implements Function {
19+
class NativeProxy extends ScriptableObject {
2020
private static final long serialVersionUID = 6676871870513494844L;
2121

2222
private static final String PROXY_TAG = "Proxy";
@@ -102,41 +102,6 @@ public String getClassName() {
102102
return target.getClassName();
103103
}
104104

105-
/**
106-
* see <a
107-
* href="https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget">10.5.13
108-
* [[Construct]] (argumentsList, newTarget)</a>
109-
*/
110-
@Override
111-
public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
112-
/*
113-
* 1. Let handler be O.[[ProxyHandler]].
114-
* 2. If handler is null, throw a TypeError exception.
115-
* 3. Assert: Type(handler) is Object.
116-
* 4. Let target be O.[[ProxyTarget]].
117-
* 5. Assert: IsConstructor(target) is true.
118-
* 6. Let trap be ? GetMethod(handler, "construct").
119-
* 7. If trap is undefined, then
120-
* a. Return ? Construct(target, argumentsList, newTarget).
121-
* 8. Let argArray be ! CreateArrayFromList(argumentsList).
122-
* 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
123-
* 10. If Type(newObj) is not Object, throw a TypeError exception.
124-
* 11. Return newObj.
125-
*/
126-
ScriptableObject target = getTargetThrowIfRevoked();
127-
128-
Function trap = getTrap(TRAP_CONSTRUCT);
129-
if (trap != null) {
130-
Object result = callTrap(trap, new Object[] {target, args, this});
131-
if (!(result instanceof Scriptable) || ScriptRuntime.isSymbol(result)) {
132-
throw ScriptRuntime.typeError("Constructor trap has to return a scriptable.");
133-
}
134-
return (ScriptableObject) result;
135-
}
136-
137-
return ((Constructable) target).construct(cx, scope, args);
138-
}
139-
140105
/**
141106
* <a
142107
* href="https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p">10.5.7
@@ -1259,47 +1224,6 @@ public void setPrototype(Scriptable prototype) {
12591224
target.setPrototype(prototype);
12601225
}
12611226

1262-
/**
1263-
* see <a
1264-
* href="https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist">10.5.12
1265-
* [[Call]] (thisArgument, argumentsList)</a>
1266-
*/
1267-
@Override
1268-
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
1269-
/*
1270-
* 1. Let handler be O.[[ProxyHandler]].
1271-
* 2. If handler is null, throw a TypeError exception.
1272-
* 3. Assert: Type(handler) is Object.
1273-
* 4. Let target be O.[[ProxyTarget]].
1274-
* 5. Let trap be ? GetMethod(handler, "apply").
1275-
* 6. If trap is undefined, then
1276-
* a. Return ? Call(target, thisArgument, argumentsList).
1277-
* 7. Let argArray be ! CreateArrayFromList(argumentsList).
1278-
* 8. Return ? Call(trap, handler, « target, thisArgument, argArray »).
1279-
*/
1280-
ScriptableObject target = getTargetThrowIfRevoked();
1281-
1282-
Scriptable argumentsList = cx.newArray(scope, args);
1283-
1284-
Function trap = getTrap(TRAP_APPLY);
1285-
if (trap != null) {
1286-
return callTrap(trap, new Object[] {target, thisObj, argumentsList});
1287-
}
1288-
1289-
return ScriptRuntime.applyOrCall(
1290-
true, cx, scope, target, new Object[] {thisObj, argumentsList});
1291-
}
1292-
1293-
@Override
1294-
public Scriptable getDeclarationScope() {
1295-
ScriptableObject target = getTargetThrowIfRevoked();
1296-
if (target instanceof Function) {
1297-
return ((Function) target).getDeclarationScope();
1298-
}
1299-
Kit.codeBug();
1300-
return null;
1301-
}
1302-
13031227
private static NativeProxy constructor(Context cx, Scriptable scope, Object[] args) {
13041228
if (args.length < 2) {
13051229
throw ScriptRuntime.typeErrorById(
@@ -1311,7 +1235,13 @@ private static NativeProxy constructor(Context cx, Scriptable scope, Object[] ar
13111235
ScriptableObject target = ensureScriptableObjectButNotSymbol(args[0]);
13121236
ScriptableObject handler = ensureScriptableObjectButNotSymbol(args[1]);
13131237

1314-
NativeProxy proxy = new NativeProxy(target, handler);
1238+
NativeProxy proxy;
1239+
if (target instanceof Function) {
1240+
proxy = new NativeProxyFunction(target, handler);
1241+
} else {
1242+
proxy = new NativeProxy(target, handler);
1243+
}
1244+
13151245
proxy.setPrototypeDirect(ScriptableObject.getClassPrototype(scope, PROXY_TAG));
13161246
proxy.setParentScope(scope);
13171247
return proxy;
@@ -1332,7 +1262,7 @@ private static Object revocable(
13321262
return revocable;
13331263
}
13341264

1335-
private Function getTrap(String trapName) {
1265+
protected final Function getTrap(String trapName) {
13361266
Object handlerProp = ScriptableObject.getProperty(handlerObj, trapName);
13371267
if (Scriptable.NOT_FOUND == handlerProp) {
13381268
return null;
@@ -1347,7 +1277,7 @@ private Function getTrap(String trapName) {
13471277
return (Function) handlerProp;
13481278
}
13491279

1350-
private Object callTrap(Function trap, Object[] args) {
1280+
protected final Object callTrap(Function trap, Object[] args) {
13511281
return trap.call(Context.getContext(), trap.getDeclarationScope(), handlerObj, args);
13521282
}
13531283

@@ -1357,4 +1287,87 @@ ScriptableObject getTargetThrowIfRevoked() {
13571287
}
13581288
return targetObj;
13591289
}
1290+
1291+
static class NativeProxyFunction extends NativeProxy implements Function {
1292+
1293+
NativeProxyFunction(ScriptableObject target, Scriptable handler) {
1294+
super(target, handler);
1295+
}
1296+
1297+
/**
1298+
* see <a href=
1299+
* "https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget">10.5.13
1300+
* [[Construct]] (argumentsList, newTarget)</a>
1301+
*/
1302+
@Override
1303+
public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
1304+
/*
1305+
* 1. Let handler be O.[[ProxyHandler]].
1306+
* 2. If handler is null, throw a TypeError exception.
1307+
* 3. Assert: Type(handler) is Object.
1308+
* 4. Let target be O.[[ProxyTarget]].
1309+
* 5. Assert: IsConstructor(target) is true.
1310+
* 6. Let trap be ? GetMethod(handler, "construct").
1311+
* 7. If trap is undefined, then
1312+
* a. Return ? Construct(target, argumentsList, newTarget).
1313+
* 8. Let argArray be ! CreateArrayFromList(argumentsList).
1314+
* 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
1315+
* 10. If Type(newObj) is not Object, throw a TypeError exception.
1316+
* 11. Return newObj.
1317+
*/
1318+
ScriptableObject target = getTargetThrowIfRevoked();
1319+
1320+
Function trap = getTrap(TRAP_CONSTRUCT);
1321+
if (trap != null) {
1322+
Object result = callTrap(trap, new Object[] {target, args, this});
1323+
if (!(result instanceof Scriptable) || ScriptRuntime.isSymbol(result)) {
1324+
throw ScriptRuntime.typeError("Constructor trap has to return a scriptable.");
1325+
}
1326+
return (ScriptableObject) result;
1327+
}
1328+
1329+
return ((Constructable) target).construct(cx, scope, args);
1330+
}
1331+
1332+
/**
1333+
* see <a href=
1334+
* "https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist">10.5.12
1335+
* [[Call]] (thisArgument, argumentsList)</a>
1336+
*/
1337+
@Override
1338+
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
1339+
/*
1340+
* 1. Let handler be O.[[ProxyHandler]].
1341+
* 2. If handler is null, throw a TypeError exception.
1342+
* 3. Assert: Type(handler) is Object.
1343+
* 4. Let target be O.[[ProxyTarget]].
1344+
* 5. Let trap be ? GetMethod(handler, "apply").
1345+
* 6. If trap is undefined, then
1346+
* a. Return ? Call(target, thisArgument, argumentsList).
1347+
* 7. Let argArray be ! CreateArrayFromList(argumentsList).
1348+
* 8. Return ? Call(trap, handler, « target, thisArgument, argArray »).
1349+
*/
1350+
ScriptableObject target = getTargetThrowIfRevoked();
1351+
1352+
Scriptable argumentsList = cx.newArray(scope, args);
1353+
1354+
Function trap = getTrap(TRAP_APPLY);
1355+
if (trap != null) {
1356+
return callTrap(trap, new Object[] {target, thisObj, argumentsList});
1357+
}
1358+
1359+
return ScriptRuntime.applyOrCall(
1360+
true, cx, scope, target, new Object[] {thisObj, argumentsList});
1361+
}
1362+
1363+
@Override
1364+
public Scriptable getDeclarationScope() {
1365+
ScriptableObject target = getTargetThrowIfRevoked();
1366+
if (target instanceof Function) {
1367+
return ((Function) target).getDeclarationScope();
1368+
}
1369+
Kit.codeBug();
1370+
return null;
1371+
}
1372+
}
13601373
}

tests/testsrc/test262.properties

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,14 +1466,14 @@ built-ins/JSON 42/165 (25.45%)
14661466
rawJSON 10/10 (100.0%)
14671467
stringify/replacer-array-abrupt.js
14681468
stringify/replacer-array-proxy.js
1469-
stringify/replacer-array-wrong-type.js
1469+
stringify/replacer-array-proxy-revoked.js
1470+
stringify/replacer-array-proxy-revoked-realm.js
14701471
stringify/replacer-function-arguments.js
14711472
stringify/replacer-function-object-deleted-property.js
14721473
stringify/replacer-function-result.js
14731474
stringify/value-array-abrupt.js
14741475
stringify/value-array-proxy.js
14751476
stringify/value-bigint-tojson-receiver.js
1476-
stringify/value-object-proxy.js
14771477

14781478
built-ins/Map 35/204 (17.16%)
14791479
prototype/getOrInsertComputed 19/19 (100.0%)
@@ -2014,7 +2014,7 @@ built-ins/Promise 383/639 (59.94%)
20142014
resolve-thenable-deferred.js {unsupported: [async]}
20152015
resolve-thenable-immed.js {unsupported: [async]}
20162016

2017-
built-ins/Proxy 66/311 (21.22%)
2017+
built-ins/Proxy 65/311 (20.9%)
20182018
construct/arguments-realm.js
20192019
construct/call-parameters.js
20202020
construct/call-parameters-new-target.js
@@ -2078,7 +2078,6 @@ built-ins/Proxy 66/311 (21.22%)
20782078
set/trap-is-null-receiver.js
20792079
set/trap-is-null-target-is-proxy.js
20802080
set/trap-is-undefined-target-is-proxy.js
2081-
create-target-is-not-a-constructor.js
20822081
get-fn-realm.js
20832082
get-fn-realm-recursive.js
20842083

0 commit comments

Comments
 (0)