Skip to content

Commit fe235a9

Browse files
committed
NativeReflect: fix Reflect.construct() with LambdaConstructor
1 parent f5e23b9 commit fe235a9

3 files changed

Lines changed: 37 additions & 2 deletions

File tree

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,17 @@ protected Constructable getTargetConstructor() {
127127
return targetConstructor;
128128
}
129129

130+
public boolean isCallable() {
131+
return (flags & CONSTRUCTOR_FUNCTION) != 0;
132+
}
133+
134+
public boolean isConstructable() {
135+
return (flags & CONSTRUCTOR_NEW) != 0;
136+
}
137+
130138
@Override
131139
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
132-
if ((flags & CONSTRUCTOR_FUNCTION) == 0) {
140+
if (!isCallable()) {
133141
throw ScriptRuntime.typeErrorById("msg.constructor.no.function", getFunctionName());
134142
}
135143
scope = getDeclarationScope();
@@ -141,7 +149,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar
141149

142150
@Override
143151
public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
144-
if ((flags & CONSTRUCTOR_NEW) == 0) {
152+
if (!isConstructable()) {
145153
throw ScriptRuntime.typeErrorById("msg.no.new", getFunctionName());
146154
}
147155
return fireConstructor(cx, getDeclarationScope(), args);

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* This class implements the Reflect object.
1515
*
1616
* @author Ronald Brill
17+
* @author Lai Quang Duong
1718
*/
1819
final class NativeReflect extends ScriptableObject {
1920
private static final long serialVersionUID = 2920773905356325445L;
@@ -143,6 +144,15 @@ private static Scriptable construct(
143144
if (result != null) {
144145
result.setPrototype((Scriptable) newTargetPrototype);
145146

147+
// LambdaConstructor could be non-callable (requires "new") or
148+
// non-constructable (no "new"). Check its flag to decide.
149+
if (ctorBaseFunction instanceof LambdaConstructor
150+
&& ((LambdaConstructor) ctorBaseFunction).isConstructable()) {
151+
Scriptable newScriptable = ctorBaseFunction.construct(cx, scope, callArgs);
152+
newScriptable.setPrototype((Scriptable) newTargetPrototype);
153+
return newScriptable;
154+
}
155+
146156
Object val = ctorBaseFunction.call(cx, scope, result, callArgs);
147157
if (val instanceof Scriptable) {
148158
return (Scriptable) val;

tests/src/test/java/org/mozilla/javascript/tests/es6/NativeReflectTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,4 +460,21 @@ public void setPrototypeOfSame() {
460460
+ "+ ' ' + Reflect.setPrototypeOf(o3, proto)";
461461
Utils.assertWithAllModes_ES6("true true true", js);
462462
}
463+
464+
@Test
465+
public void constructSubclassBuiltin() {
466+
String js =
467+
"function CustomSet() {\n"
468+
+ " return Reflect.construct(Set, arguments, this.constructor);\n"
469+
+ "}\n"
470+
+ "CustomSet.prototype = Object.create(Set.prototype);\n"
471+
+ "CustomSet.prototype.constructor = CustomSet;\n"
472+
+ "var set = new CustomSet([1, 2, 3]);\n"
473+
+ "set.add(4);\n"
474+
+ "'' + Array.from(set)"
475+
+ " + ' ' + (set instanceof CustomSet)"
476+
+ " + ' ' + (set instanceof Set)"
477+
+ " + ' ' + (Object.getPrototypeOf(set) === CustomSet.prototype)";
478+
Utils.assertWithAllModes_ES6("1,2,3,4 true true true", js);
479+
}
463480
}

0 commit comments

Comments
 (0)