зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1169731 - [[Call]] on a class constructor should throw. (r=jandem)
This commit is contained in:
Родитель
3a830f0dcf
Коммит
ec18bf7a14
|
@ -108,6 +108,7 @@ namespace JS {
|
|||
_(CantInlineNoBaseline) \
|
||||
_(CantInlineLazy) \
|
||||
_(CantInlineNotConstructor) \
|
||||
_(CantInlineClassConstructor) \
|
||||
_(CantInlineDisabledIon) \
|
||||
_(CantInlineTooManyArgs) \
|
||||
_(CantInlineRecursive) \
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
load(libdir + "class.js");
|
||||
|
||||
var test = `
|
||||
|
||||
function test(fun) {
|
||||
fun();
|
||||
}
|
||||
|
||||
// Generate a CallAnyScripted stub in test()
|
||||
for (let i = 0; i < 20; i++) {
|
||||
test(function() { /* wheeee */ });
|
||||
}
|
||||
|
||||
class foo {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
// Compile foo()
|
||||
for (let i = 0; i < 11; i++)
|
||||
new foo();
|
||||
|
||||
try {
|
||||
test(foo);
|
||||
throw new Error("Invoking a class constructor without new must throw");
|
||||
} catch (e if e instanceof TypeError) { }
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
|
@ -9560,6 +9560,10 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
|||
if (constructing && !fun->isConstructor())
|
||||
return true;
|
||||
|
||||
// Likewise, if the callee is a class constructor, we have to throw.
|
||||
if (!constructing && fun->isClassConstructor())
|
||||
return true;
|
||||
|
||||
if (!fun->hasJITCode()) {
|
||||
// Don't treat this as an unoptimizable case, as we'll add a stub
|
||||
// when the callee becomes hot.
|
||||
|
@ -10418,10 +10422,13 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
|
|||
// Ensure the object is a function.
|
||||
masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
|
||||
&failure);
|
||||
if (isConstructing_)
|
||||
if (isConstructing_) {
|
||||
masm.branchIfNotInterpretedConstructor(callee, regs.getAny(), &failure);
|
||||
else
|
||||
} else {
|
||||
masm.branchIfFunctionHasNoScript(callee, &failure);
|
||||
masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, callee,
|
||||
regs.getAny(), &failure);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the JSScript.
|
||||
|
|
|
@ -3028,10 +3028,12 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call)
|
|||
|
||||
// Guard that calleereg is an interpreted function with a JSScript.
|
||||
// If we are constructing, also ensure the callee is a constructor.
|
||||
if (call->mir()->isConstructing())
|
||||
if (call->mir()->isConstructing()) {
|
||||
masm.branchIfNotInterpretedConstructor(calleereg, nargsreg, &invoke);
|
||||
else
|
||||
} else {
|
||||
masm.branchIfFunctionHasNoScript(calleereg, &invoke);
|
||||
masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, calleereg, objreg, &invoke);
|
||||
}
|
||||
|
||||
// Knowing that calleereg is a non-native function, load the JSScript.
|
||||
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
|
||||
|
@ -3125,6 +3127,7 @@ CodeGenerator::visitCallKnown(LCallKnown* call)
|
|||
|
||||
// Native single targets are handled by LCallNative.
|
||||
MOZ_ASSERT(!target->isNative());
|
||||
MOZ_ASSERT_IF(target->isClassConstructor(), call->isConstructing());
|
||||
// Missing arguments must have been explicitly appended by the IonBuilder.
|
||||
DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
|
||||
MOZ_ASSERT(target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack);
|
||||
|
|
|
@ -438,6 +438,11 @@ IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
|
|||
return DontInline(inlineScript, "Callee is not a constructor");
|
||||
}
|
||||
|
||||
if (!callInfo.constructing() && target->isClassConstructor()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineClassConstructor);
|
||||
return DontInline(inlineScript, "Not constructing class constructor");
|
||||
}
|
||||
|
||||
AnalysisMode analysisMode = info().analysisMode();
|
||||
if (!CanIonCompile(inlineScript, analysisMode)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
|
||||
|
|
|
@ -456,7 +456,7 @@ LIRGenerator::visitCall(MCall* call)
|
|||
MOZ_ASSERT(ok, "How can we not have four temp registers?");
|
||||
lir = new(alloc()) LCallDOMNative(tempFixed(cxReg), tempFixed(objReg),
|
||||
tempFixed(privReg), tempFixed(argsReg));
|
||||
} else if (target) {
|
||||
} else if (target && !(target->isClassConstructor() && !call->isConstructing())) {
|
||||
// Call known functions.
|
||||
if (target->isNative()) {
|
||||
Register cxReg, numReg, vpReg, tmpReg;
|
||||
|
|
|
@ -941,6 +941,15 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
branchTestClassIsProxy(proxy, scratch, label);
|
||||
}
|
||||
|
||||
void branchFunctionKind(Condition cond, JSFunction::FunctionKind kind, Register fun,
|
||||
Register scratch, Label* label)
|
||||
{
|
||||
Address flags(fun, JSFunction::offsetOfFlags());
|
||||
load32(flags, scratch);
|
||||
and32(Imm32(JSFunction::FUNCTION_KIND_MASK), scratch);
|
||||
branch32(cond, scratch, Imm32(kind << JSFunction::FUNCTION_KIND_SHIFT), label);
|
||||
}
|
||||
|
||||
public:
|
||||
#ifndef JS_CODEGEN_ARM64
|
||||
// StackPointer manipulation functions.
|
||||
|
|
|
@ -102,6 +102,7 @@ MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: i
|
|||
MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
|
||||
MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
|
||||
MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
|
||||
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
|
||||
|
||||
// JSON
|
||||
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Class constructors don't have a [[Call]]
|
||||
var test = `
|
||||
|
||||
class Foo {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
assertThrowsInstanceOf(Foo, TypeError);
|
||||
|
||||
class Bar extends Foo {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
assertThrowsInstanceOf(Bar, TypeError);
|
||||
|
||||
assertThrowsInstanceOf(class { constructor() { } }, TypeError);
|
||||
assertThrowsInstanceOf(class extends Foo { constructor() { } }, TypeError);
|
||||
|
||||
assertThrowsInstanceOf(class foo { constructor() { } }, TypeError);
|
||||
assertThrowsInstanceOf(class foo extends Foo { constructor() { } }, TypeError);
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -44,7 +44,7 @@ for (let a of [testClass,
|
|||
assertEq(aConstDesc.writable, true);
|
||||
assertEq(aConstDesc.configurable, true);
|
||||
assertEq(aConstDesc.enumerable, false);
|
||||
aConstDesc.value();
|
||||
new aConstDesc.value();
|
||||
assertEq(constructorCalled, true);
|
||||
|
||||
// __proto__ is just an identifier for classes. No prototype changes are made.
|
||||
|
|
|
@ -699,6 +699,11 @@ js::Invoke(JSContext* cx, CallArgs args, MaybeConstruct construct)
|
|||
|
||||
/* Invoke native functions. */
|
||||
JSFunction* fun = &args.callee().as<JSFunction>();
|
||||
if (construct != CONSTRUCT && fun->isClassConstructor()) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CALL_CLASS_CONSTRUCTOR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fun->isNative()) {
|
||||
MOZ_ASSERT_IF(construct, !fun->isConstructor());
|
||||
return CallJSNative(cx, fun->native(), args);
|
||||
|
@ -2947,7 +2952,9 @@ CASE(JSOP_FUNCALL)
|
|||
bool isFunction = IsFunctionObject(args.calleev(), &maybeFun);
|
||||
|
||||
/* Don't bother trying to fast-path calls to scripted non-constructors. */
|
||||
if (!isFunction || !maybeFun->isInterpreted() || !maybeFun->isConstructor()) {
|
||||
if (!isFunction || !maybeFun->isInterpreted() || !maybeFun->isConstructor() ||
|
||||
(!construct && maybeFun->isClassConstructor()))
|
||||
{
|
||||
if (construct) {
|
||||
if (!InvokeConstructor(cx, args))
|
||||
goto error;
|
||||
|
|
|
@ -29,11 +29,11 @@ namespace js {
|
|||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 292;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 293;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 401,
|
||||
static_assert(JSErr_Limit == 402,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
|
Загрузка…
Ссылка в новой задаче