зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1169736 - Temporarily disallow eval and arrow functions inside derived class constructors. (r=jorendorff)
This commit is contained in:
Родитель
ec18bf7a14
Коммит
9b8c4983bf
|
@ -250,6 +250,12 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
|
|||
return false;
|
||||
}
|
||||
|
||||
if (evalType == DIRECT_EVAL && caller.script()->isDerivedClassConstructor()) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DISABLED_DERIVED_CLASS,
|
||||
"direct eval");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ES5 15.1.2.1 step 1.
|
||||
if (args.length() < 1) {
|
||||
args.rval().setUndefined();
|
||||
|
|
|
@ -637,6 +637,10 @@ class FullParseHandler
|
|||
void addFunctionArgument(ParseNode* pn, ParseNode* argpn) {
|
||||
pn->pn_body->append(argpn);
|
||||
}
|
||||
void setDerivedClassConstructor(ParseNode* pn) {
|
||||
MOZ_ASSERT(pn->isKind(PNK_FUNCTION));
|
||||
pn->pn_funbox->setDerivedClassConstructor();
|
||||
}
|
||||
|
||||
ParseNode* newLexicalScope(ObjectBox* blockBox) {
|
||||
return new_<LexicalScopeNode>(blockBox, pos());
|
||||
|
|
|
@ -1699,6 +1699,7 @@ enum FunctionSyntaxKind
|
|||
Arrow,
|
||||
Method,
|
||||
ClassConstructor,
|
||||
DerivedClassConstructor,
|
||||
Getter,
|
||||
Setter
|
||||
};
|
||||
|
|
|
@ -1210,6 +1210,7 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
|||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||
break;
|
||||
case ClassConstructor:
|
||||
case DerivedClassConstructor:
|
||||
flags = JSFunction::INTERPRETED_CLASS_CONSTRUCTOR;
|
||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||
break;
|
||||
|
@ -2243,6 +2244,8 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox* funbo
|
|||
lazy->setGeneratorKind(funbox->generatorKind());
|
||||
if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
|
||||
lazy->setUsesArgumentsApplyAndThis();
|
||||
if (funbox->isDerivedClassConstructor())
|
||||
lazy->setIsDerivedClassConstructor();
|
||||
PropagateTransitiveParseFlags(funbox, lazy);
|
||||
|
||||
fun->initLazyScript(lazy);
|
||||
|
@ -2264,6 +2267,9 @@ Parser<FullParseHandler>::functionArgsAndBody(InHandling inHandling, ParseNode*
|
|||
if (!funbox)
|
||||
return false;
|
||||
|
||||
if (kind == DerivedClassConstructor)
|
||||
funbox->setDerivedClassConstructor();
|
||||
|
||||
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
|
||||
|
||||
// Try a syntax parse for this inner function.
|
||||
|
@ -2426,6 +2432,9 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned st
|
|||
return null();
|
||||
funbox->length = fun->nargs() - fun->hasRest();
|
||||
|
||||
if (fun->lazyScript()->isDerivedClassConstructor())
|
||||
funbox->setDerivedClassConstructor();
|
||||
|
||||
Directives newDirectives = directives;
|
||||
ParseContext<FullParseHandler> funpc(this, /* parent = */ nullptr, pn, funbox,
|
||||
&newDirectives, staticLevel, /* bodyid = */ 0,
|
||||
|
@ -5960,7 +5969,8 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
|
|||
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
|
||||
|
||||
ParseNode* classMethods = propertyList(yieldHandling, ClassBody);
|
||||
ParseNode* classMethods = propertyList(yieldHandling,
|
||||
hasHeritage ? DerivedClassBody : ClassBody);
|
||||
if (!classMethods)
|
||||
return null();
|
||||
|
||||
|
@ -6537,6 +6547,11 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
|
|||
if (!tokenStream.peekToken(&ignored))
|
||||
return null();
|
||||
|
||||
if (pc->sc->isFunctionBox() && pc->sc->asFunctionBox()->isDerivedClassConstructor()) {
|
||||
report(ParseError, false, null(), JSMSG_DISABLED_DERIVED_CLASS, "arrow functions");
|
||||
return null();
|
||||
}
|
||||
|
||||
return functionDef(inHandling, yieldHandling, nullptr, Arrow, NotGenerator);
|
||||
}
|
||||
|
||||
|
@ -8307,7 +8322,7 @@ template <typename ParseHandler>
|
|||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::newPropertyListNode(PropListType type)
|
||||
{
|
||||
if (type == ClassBody)
|
||||
if (IsClassBody(type))
|
||||
return handler.newClassMethodList(pos().begin);
|
||||
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
|
@ -8335,7 +8350,7 @@ Parser<ParseHandler>::propertyList(YieldHandling yieldHandling, PropListType typ
|
|||
break;
|
||||
|
||||
bool isStatic = false;
|
||||
if (type == ClassBody) {
|
||||
if (IsClassBody(type)) {
|
||||
if (ltok == TOK_SEMI)
|
||||
continue;
|
||||
|
||||
|
@ -8475,7 +8490,7 @@ Parser<ParseHandler>::propertyList(YieldHandling yieldHandling, PropListType typ
|
|||
}
|
||||
}
|
||||
|
||||
if (type == ClassBody) {
|
||||
if (IsClassBody(type)) {
|
||||
if (!isStatic && atom == context->names().constructor) {
|
||||
if (isGenerator || op != JSOP_INITPROP) {
|
||||
report(ParseError, false, propname, JSMSG_BAD_METHOD_DEF);
|
||||
|
@ -8499,7 +8514,7 @@ Parser<ParseHandler>::propertyList(YieldHandling yieldHandling, PropListType typ
|
|||
return null();
|
||||
|
||||
if (tt == TOK_COLON) {
|
||||
if (type == ClassBody) {
|
||||
if (IsClassBody(type)) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
|
||||
return null();
|
||||
}
|
||||
|
@ -8541,7 +8556,7 @@ Parser<ParseHandler>::propertyList(YieldHandling yieldHandling, PropListType typ
|
|||
* Support, e.g., |var {x, y} = o| as destructuring shorthand
|
||||
* for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
|
||||
*/
|
||||
if (type == ClassBody) {
|
||||
if (IsClassBody(type)) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
|
||||
return null();
|
||||
}
|
||||
|
@ -8563,7 +8578,9 @@ Parser<ParseHandler>::propertyList(YieldHandling yieldHandling, PropListType typ
|
|||
} else if (tt == TOK_LP) {
|
||||
tokenStream.ungetToken();
|
||||
if (!methodDefinition(yieldHandling, type, propList, propname,
|
||||
isConstructor ? ClassConstructor : Method,
|
||||
isConstructor ? type == DerivedClassBody ? DerivedClassConstructor
|
||||
: ClassConstructor
|
||||
: Method,
|
||||
isGenerator ? StarGenerator : NotGenerator, isStatic, op))
|
||||
{
|
||||
return null();
|
||||
|
@ -8595,7 +8612,7 @@ Parser<ParseHandler>::propertyList(YieldHandling yieldHandling, PropListType typ
|
|||
}
|
||||
|
||||
// Default constructors not yet implemented. See bug 1105463
|
||||
if (type == ClassBody && !seenConstructor) {
|
||||
if (IsClassBody(type) && !seenConstructor) {
|
||||
report(ParseError, false, null(), JSMSG_NO_CLASS_CONSTRUCTOR);
|
||||
return null();
|
||||
}
|
||||
|
@ -8610,7 +8627,8 @@ Parser<ParseHandler>::methodDefinition(YieldHandling yieldHandling, PropListType
|
|||
Node propList, Node propname, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind, bool isStatic, JSOp op)
|
||||
{
|
||||
MOZ_ASSERT(kind == Method || kind == ClassConstructor || kind == Getter || kind == Setter);
|
||||
MOZ_ASSERT(kind == Method || kind == ClassConstructor || kind == DerivedClassConstructor ||
|
||||
kind == Getter || kind == Setter);
|
||||
/* NB: Getter function in { get x(){} } is unnamed. */
|
||||
RootedPropertyName funName(context);
|
||||
if ((kind == Method || kind == ClassConstructor) && tokenStream.isCurrentTokenType(TOK_NAME))
|
||||
|
@ -8622,7 +8640,7 @@ Parser<ParseHandler>::methodDefinition(YieldHandling yieldHandling, PropListType
|
|||
if (!fn)
|
||||
return false;
|
||||
|
||||
if (listType == ClassBody)
|
||||
if (IsClassBody(listType))
|
||||
return handler.addClassMethodDefinition(propList, propname, fn, op, isStatic);
|
||||
|
||||
MOZ_ASSERT(listType == ObjectLiteral);
|
||||
|
|
|
@ -323,7 +323,13 @@ struct BindData;
|
|||
class CompExprTransplanter;
|
||||
|
||||
enum VarContext { HoistVars, DontHoistVars };
|
||||
enum PropListType { ObjectLiteral, ClassBody };
|
||||
enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody };
|
||||
|
||||
inline bool
|
||||
IsClassBody(PropListType type)
|
||||
{
|
||||
return type == ClassBody || type == DerivedClassBody;
|
||||
}
|
||||
|
||||
// Specify a value for an ES6 grammar parametrization. We have no enum for
|
||||
// [Return] because its behavior is exactly equivalent to checking whether
|
||||
|
|
|
@ -127,6 +127,7 @@ class FunctionContextFlags
|
|||
bool definitelyNeedsArgsObj:1;
|
||||
|
||||
bool needsHomeObject:1;
|
||||
bool isDerivedClassConstructor:1;
|
||||
|
||||
public:
|
||||
FunctionContextFlags()
|
||||
|
@ -135,7 +136,8 @@ class FunctionContextFlags
|
|||
needsDeclEnvObject(false),
|
||||
argumentsHasLocalBinding(false),
|
||||
definitelyNeedsArgsObj(false),
|
||||
needsHomeObject(false)
|
||||
needsHomeObject(false),
|
||||
isDerivedClassConstructor(false)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -333,6 +335,7 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
|
||||
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
|
||||
bool needsHomeObject() const { return funCxFlags.needsHomeObject; }
|
||||
bool isDerivedClassConstructor() const { return funCxFlags.isDerivedClassConstructor; }
|
||||
|
||||
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
|
||||
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
|
||||
|
@ -342,6 +345,8 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
funCxFlags.definitelyNeedsArgsObj = true; }
|
||||
void setNeedsHomeObject() { MOZ_ASSERT(function()->allowSuperProperty());
|
||||
funCxFlags.needsHomeObject = true; }
|
||||
void setDerivedClassConstructor() { MOZ_ASSERT(function()->isClassConstructor());
|
||||
funCxFlags.isDerivedClassConstructor = true; }
|
||||
|
||||
bool hasDefaults() const {
|
||||
return length != function()->nargs() - function()->hasRest();
|
||||
|
|
|
@ -329,6 +329,7 @@ class SyntaxParseHandler
|
|||
void setEndPosition(Node pn, Node oth) {}
|
||||
void setEndPosition(Node pn, uint32_t end) {}
|
||||
|
||||
void setDerivedClassConstructor(Node pn) {}
|
||||
|
||||
void setPosition(Node pn, const TokenPos& pos) {}
|
||||
TokenPos getPosition(Node pn) {
|
||||
|
|
|
@ -103,6 +103,7 @@ MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can'
|
|||
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|")
|
||||
MSG_DEF(JSMSG_DISABLED_DERIVED_CLASS, 1, JSEXN_INTERNALERR, "{0} temporarily disallowed in derived class constructors")
|
||||
|
||||
// JSON
|
||||
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
|
||||
|
|
|
@ -2651,6 +2651,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
|
|||
script->funHasExtensibleScope_ = funbox ? funbox->hasExtensibleScope() : false;
|
||||
script->funNeedsDeclEnvObject_ = funbox ? funbox->needsDeclEnvObject() : false;
|
||||
script->needsHomeObject_ = funbox ? funbox->needsHomeObject() : false;
|
||||
script->isDerivedClassConstructor_ = funbox ? funbox->isDerivedClassConstructor() : false;
|
||||
script->hasSingletons_ = bce->hasSingletons;
|
||||
|
||||
if (funbox) {
|
||||
|
|
|
@ -1025,6 +1025,8 @@ class JSScript : public js::gc::TenuredCell
|
|||
|
||||
bool needsHomeObject_:1;
|
||||
|
||||
bool isDerivedClassConstructor_:1;
|
||||
|
||||
// Add padding so JSScript is gc::Cell aligned. Make padding protected
|
||||
// instead of private to suppress -Wunused-private-field compiler warnings.
|
||||
protected:
|
||||
|
@ -1297,6 +1299,9 @@ class JSScript : public js::gc::TenuredCell
|
|||
return needsHomeObject_;
|
||||
}
|
||||
|
||||
bool isDerivedClassConstructor() const {
|
||||
return isDerivedClassConstructor_;
|
||||
}
|
||||
|
||||
/*
|
||||
* As an optimization, even when argsHasLocalBinding, the function prologue
|
||||
|
@ -1940,7 +1945,7 @@ class LazyScript : public gc::TenuredCell
|
|||
uint32_t version : 8;
|
||||
|
||||
uint32_t numFreeVariables : 24;
|
||||
uint32_t numInnerFunctions : 22;
|
||||
uint32_t numInnerFunctions : 21;
|
||||
|
||||
uint32_t generatorKindBits : 2;
|
||||
|
||||
|
@ -1953,6 +1958,7 @@ class LazyScript : public gc::TenuredCell
|
|||
uint32_t usesArgumentsApplyAndThis : 1;
|
||||
uint32_t hasBeenCloned : 1;
|
||||
uint32_t treatAsRunOnce : 1;
|
||||
uint32_t isDerivedClassConstructor : 1;
|
||||
};
|
||||
|
||||
union {
|
||||
|
@ -2124,6 +2130,13 @@ class LazyScript : public gc::TenuredCell
|
|||
p_.treatAsRunOnce = true;
|
||||
}
|
||||
|
||||
bool isDerivedClassConstructor() const {
|
||||
return p_.isDerivedClassConstructor;
|
||||
}
|
||||
void setIsDerivedClassConstructor() {
|
||||
p_.isDerivedClassConstructor = true;
|
||||
}
|
||||
|
||||
const char* filename() const {
|
||||
return scriptSource()->filename();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
|
||||
var test = `
|
||||
|
||||
class base {
|
||||
constructor() {
|
||||
eval('');
|
||||
(()=>0)();
|
||||
}
|
||||
}
|
||||
|
||||
class derived extends base {
|
||||
constructor() {
|
||||
eval('');
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure eval and arrows are still valid in non-derived constructors.
|
||||
new base();
|
||||
|
||||
// Eval throws in derived class constructors, in both class expressions and
|
||||
// statements.
|
||||
assertThrowsInstanceOf((() => new derived()), InternalError);
|
||||
assertThrowsInstanceOf((() => new class extends base { constructor() { eval('') } }()), InternalError);
|
||||
|
||||
var g = newGlobal();
|
||||
var dbg = Debugger(g);
|
||||
dbg.onDebuggerStatement = function(frame) { assertThrowsInstanceOf(()=>frame.eval(''), InternalError); };
|
||||
g.eval("new class foo extends null { constructor() { debugger; } }()");
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -118,6 +118,9 @@ function testClasses() {
|
|||
// Currently, we do not allow default constructors
|
||||
assertClassError("class NAME { }", TypeError);
|
||||
|
||||
// For now, disallow arrow functions in derived class constructors
|
||||
assertClassError("class NAME extends null { constructor() { (() => 0); }", TypeError);
|
||||
|
||||
// It is an error to have two methods named constructor, but not other
|
||||
// names, regardless if one is an accessor or a generator or static.
|
||||
assertClassError("class NAME { constructor() { } constructor(a) { } }", SyntaxError);
|
||||
|
|
|
@ -6302,6 +6302,13 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code
|
|||
MOZ_ASSERT_IF(iter, !scope);
|
||||
MOZ_ASSERT_IF(!iter, scope && scope->is<GlobalObject>());
|
||||
|
||||
if (iter && iter->script()->isDerivedClassConstructor()) {
|
||||
MOZ_ASSERT(iter->isFunctionFrame() && iter->calleeTemplate()->isClassConstructor());
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DISABLED_DERIVED_CLASS,
|
||||
"debugger eval");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check the first argument, the eval code string. */
|
||||
if (!code.isString()) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
|
||||
|
|
|
@ -29,11 +29,11 @@ namespace js {
|
|||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 293;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 294;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 402,
|
||||
static_assert(JSErr_Limit == 403,
|
||||
"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 "
|
||||
|
|
Загрузка…
Ссылка в новой задаче