Bug 1169736 - Temporarily disallow eval and arrow functions inside derived class constructors. (r=jorendorff)

This commit is contained in:
Eric Faust 2015-06-17 14:38:23 -07:00
Родитель ec18bf7a14
Коммит 9b8c4983bf
14 изменённых файлов: 118 добавлений и 15 удалений

Просмотреть файл

@ -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 "