зеркало из https://github.com/mozilla/rhino.git
Extend LambdaConstructor for more flexibility
This lets us create constructors that behave differently when invoked using "new" than when called directly. The native "Date" class is an example. The default behavior, which uses the same behavior in either case but can automatically throw an exception if only one form is supported, still works.
This commit is contained in:
Родитель
5c8707faa0
Коммит
5c413d16c8
|
@ -66,12 +66,30 @@ public class LambdaConstructor extends LambdaFunction {
|
|||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new constructor that may be called using new or as a function, and exhibits
|
||||
* different behavior for each.
|
||||
*/
|
||||
public LambdaConstructor(
|
||||
Scriptable scope,
|
||||
String name,
|
||||
int length,
|
||||
Callable target,
|
||||
Constructable targetConstructor) {
|
||||
super(scope, name, length, target);
|
||||
this.targetConstructor = targetConstructor;
|
||||
this.flags = CONSTRUCTOR_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
|
||||
if ((flags & CONSTRUCTOR_FUNCTION) == 0) {
|
||||
throw ScriptRuntime.typeErrorById("msg.constructor.no.function", getFunctionName());
|
||||
}
|
||||
return targetConstructor.construct(cx, scope, args);
|
||||
if (target == null) {
|
||||
return targetConstructor.construct(cx, scope, args);
|
||||
}
|
||||
return target.call(cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,7 @@ public class LambdaFunction extends BaseFunction {
|
|||
private static final long serialVersionUID = -8388132362854748293L;
|
||||
|
||||
// The target is expected to be a lambda -- lambdas should not be serialized.
|
||||
private final transient Callable target;
|
||||
protected final transient Callable target;
|
||||
private final String name;
|
||||
private final int length;
|
||||
|
||||
|
|
|
@ -202,6 +202,22 @@ public class LambdaFunctionTest {
|
|||
+ "assertThrows(() => { new noNewFunc(); }, TypeError)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lambdaSpecialConstructorCallConstructor() {
|
||||
// This class has different functions for the constructor when invoked
|
||||
// by "new" and when invoked as a function
|
||||
SpecialConstructorClass.init(cx, root);
|
||||
// Invoke the function via "new" and ensure that the constructor functionality is executed
|
||||
eval("let o = new SpecialConstructorClass('foo');\n" + "assertEquals('foo', o.value);\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lambdaSpecialConstructorCallFunction() {
|
||||
SpecialConstructorClass.init(cx, root);
|
||||
// Invoke the function directly and ensure that the function functionality is executed
|
||||
eval("let v = SpecialConstructorClass('foo');\n" + "assertEquals('You passed foo', v);\n");
|
||||
}
|
||||
|
||||
private static class TestClass extends ScriptableObject {
|
||||
|
||||
private String instanceVal;
|
||||
|
@ -285,4 +301,53 @@ public class LambdaFunctionTest {
|
|||
return "Hello, " + ScriptRuntime.toString(args[0]) + '!';
|
||||
}
|
||||
}
|
||||
|
||||
private static class SpecialConstructorClass extends ScriptableObject {
|
||||
private String value;
|
||||
|
||||
public static void init(Context cx, Scriptable scope) {
|
||||
LambdaConstructor constructor =
|
||||
new LambdaConstructor(
|
||||
scope,
|
||||
"SpecialConstructorClass",
|
||||
1,
|
||||
(Context lcx, Scriptable s, Scriptable thisObj, Object[] args) -> {
|
||||
String arg = "";
|
||||
if (args.length > 0) {
|
||||
arg = ScriptRuntime.toString(args[0]);
|
||||
}
|
||||
return "You passed " + arg;
|
||||
},
|
||||
(Context lcx, Scriptable s, Object[] args) -> {
|
||||
SpecialConstructorClass tc = new SpecialConstructorClass();
|
||||
if (args.length > 0) {
|
||||
tc.value = ScriptRuntime.toString(args[0]);
|
||||
}
|
||||
return tc;
|
||||
});
|
||||
constructor.definePrototypeProperty(
|
||||
cx,
|
||||
"value",
|
||||
(Scriptable s) -> {
|
||||
SpecialConstructorClass thisObj =
|
||||
LambdaConstructor.convertThisObject(
|
||||
s, SpecialConstructorClass.class);
|
||||
return thisObj.value;
|
||||
},
|
||||
(Scriptable s, Object newVal) -> {
|
||||
SpecialConstructorClass thisObj =
|
||||
LambdaConstructor.convertThisObject(
|
||||
s, SpecialConstructorClass.class);
|
||||
thisObj.value = ScriptRuntime.toString(newVal);
|
||||
},
|
||||
0);
|
||||
ScriptableObject.defineProperty(
|
||||
scope, "SpecialConstructorClass", constructor, PERMANENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "SpecialConstructorClass";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче