Bug 611388 - |const| should be block scoped and require an initializer. (r=shu)

This commit is contained in:
Eric Faust 2014-10-30 17:27:03 -07:00
Родитель a49ab6595d
Коммит 5f99a47be9
48 изменённых файлов: 312 добавлений и 204 удалений

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

@ -54,12 +54,23 @@ const debug = false;
var expectedConsoleMessages = [];
var expectLoggedMessages = null;
try {
const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].
getService(Components.interfaces.nsIRDFService);
const ContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"].
getService(Components.interfaces.nsIRDFContainerUtils);
} catch(ex) { }
function get_RDF() {
try {
return Components.classes["@mozilla.org/rdf/rdf-service;1"].
getService(Components.interfaces.nsIRDFService);
} catch (ex) { }
}
function get_ContainerUtils()
{
try {
return Components.classes["@mozilla.org/rdf/container-utils;1"].
getService(Components.interfaces.nsIRDFContainerUtils);
} catch(ex) { }
}
const RDF = get_RDF();
const ContainerUtils = get_ContainerUtils();
var xmlDoc;

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

@ -1240,7 +1240,7 @@ LookupAliasedName(BytecodeEmitter *bce, HandleScript script, PropertyName *name,
if (freeVariables[i].isHoistedUse() && bindingIndex >= lexicalBegin) {
MOZ_ASSERT(pn);
MOZ_ASSERT(pn->isUsed());
pn->pn_dflags |= PND_LET;
pn->pn_dflags |= PND_LEXICAL;
}
break;
@ -1289,7 +1289,7 @@ AssignHops(BytecodeEmitter *bce, ParseNode *pn, unsigned src, ScopeCoordinate *d
static inline MaybeCheckLexical
NodeNeedsCheckLexical(ParseNode *pn)
{
return pn->isHoistedLetUse() ? CheckLexical : DontCheckLexical;
return pn->isHoistedLexicalUse() ? CheckLexical : DontCheckLexical;
}
static bool
@ -1461,6 +1461,7 @@ BytecodeEmitter::isAliasedName(ParseNode *pn)
switch (dn->kind()) {
case Definition::LET:
case Definition::CONST:
/*
* There are two ways to alias a let variable: nested functions and
* dynamic scope operations. (This is overly conservative since the
@ -1485,7 +1486,7 @@ BytecodeEmitter::isAliasedName(ParseNode *pn)
*/
return script->formalIsAliased(pn->pn_cookie.slot());
case Definition::VAR:
case Definition::CONST:
case Definition::GLOBALCONST:
MOZ_ASSERT_IF(sc->allLocalsAliased(), script->varIsAliased(pn->pn_cookie.slot()));
return script->varIsAliased(pn->pn_cookie.slot());
case Definition::PLACEHOLDER:
@ -1777,6 +1778,7 @@ BindNameToSlotHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
break;
case Definition::VAR:
case Definition::GLOBALCONST:
case Definition::CONST:
case Definition::LET:
switch (op) {
@ -2077,7 +2079,7 @@ CheckSideEffects(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool
}
}
if (pn->isHoistedLetUse()) {
if (pn->isHoistedLexicalUse()) {
// Hoisted uses of lexical bindings throw on access.
*answer = true;
}
@ -4656,7 +4658,7 @@ EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
ParseNode *varList = pnLet->pn_left;
MOZ_ASSERT(varList->isArity(PN_LIST));
ParseNode *letBody = pnLet->pn_right;
MOZ_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE));
MOZ_ASSERT(letBody->isLexical() && letBody->isKind(PNK_LEXICALSCOPE));
int letHeadDepth = bce->stackDepth;
@ -4746,7 +4748,7 @@ static bool
EmitForInOrOfVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool *letDecl)
{
*letDecl = pn->isKind(PNK_LEXICALSCOPE);
MOZ_ASSERT_IF(*letDecl, pn->isLet());
MOZ_ASSERT_IF(*letDecl, pn->isLexical());
// If the left part is 'var x', emit code to define x if necessary using a
// prolog opcode, but do not emit a pop. If it is 'let x', EnterBlockScope
@ -6699,7 +6701,7 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
// Assign the destructuring arguments before defining any functions,
// see bug 419662.
MOZ_ASSERT(pnchild->isKind(PNK_SEMI));
MOZ_ASSERT(pnchild->pn_kid->isKind(PNK_VAR) || pnchild->pn_kid->isKind(PNK_CONST));
MOZ_ASSERT(pnchild->pn_kid->isKind(PNK_VAR) || pnchild->pn_kid->isKind(PNK_GLOBALCONST));
if (!EmitTree(cx, bce, pnchild))
return false;
pnchild = pnchild->pn_next;
@ -6832,7 +6834,7 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
break;
case PNK_VAR:
case PNK_CONST:
case PNK_GLOBALCONST:
if (!EmitVariables(cx, bce, pn, InitializeVars))
return false;
break;
@ -6995,6 +6997,8 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
break;
case PNK_LET:
case PNK_CONST:
MOZ_ASSERT_IF(pn->isKind(PNK_CONST), !pn->isArity(PN_BINARY));
ok = pn->isArity(PN_BINARY)
? EmitLet(cx, bce, pn)
: EmitVariables(cx, bce, pn, InitializeVars);

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

@ -667,8 +667,8 @@ class FullParseHandler
uint16_t firstDominatingLexicalSlot)
{
MOZ_ASSERT(pn->isUsed());
if (dn->isLet() && dn->pn_cookie.slot() < firstDominatingLexicalSlot)
pn->pn_dflags |= PND_LET;
if (dn->isLexical() && dn->pn_cookie.slot() < firstDominatingLexicalSlot)
pn->pn_dflags |= PND_LEXICAL;
}
static uintptr_t definitionToBits(Definition *dn) {

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

@ -308,7 +308,7 @@ const char *
Definition::kindString(Kind kind)
{
static const char * const table[] = {
"", js_var_str, js_const_str, js_let_str, js_function_str, "argument", "unknown"
"", js_var_str, js_const_str, js_const_str, js_let_str, "argument", js_function_str, "unknown"
};
MOZ_ASSERT(unsigned(kind) <= unsigned(ARG));
@ -505,7 +505,7 @@ Parser<FullParseHandler>::cloneLeftHandSide(ParseNode *opn)
if (opn->isDefn()) {
/* We copied some definition-specific state into pn. Clear it out. */
pn->pn_cookie.makeFree();
pn->pn_dflags &= ~(PND_LET | PND_BOUND);
pn->pn_dflags &= ~(PND_LEXICAL | PND_BOUND);
pn->setDefn(false);
handler.linkUseToDef(pn, (Definition *) opn);

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

@ -114,6 +114,7 @@ class UpvarCookie
F(CONTINUE) \
F(VAR) \
F(CONST) \
F(GLOBALCONST) \
F(WITH) \
F(RETURN) \
F(NEW) \
@ -682,7 +683,8 @@ class ParseNode
Definition *resolve();
/* PN_CODE and PN_NAME pn_dflags bits. */
#define PND_LET 0x01 /* let (block-scoped) binding or use of a hoisted let */
#define PND_LEXICAL 0x01 /* lexical (block-scoped) binding or use of a hoisted
let or const */
#define PND_CONST 0x02 /* const binding (orthogonal to let) */
#define PND_ASSIGNED 0x04 /* set if ever LHS of assignment */
#define PND_PLACEHOLDER 0x08 /* placeholder definition for lexdep */
@ -764,7 +766,7 @@ class ParseNode
inline bool test(unsigned flag) const;
bool isLet() const { return test(PND_LET) && !isUsed(); }
bool isLexical() const { return test(PND_LEXICAL) && !isUsed(); }
bool isConst() const { return test(PND_CONST); }
bool isPlaceholder() const { return test(PND_PLACEHOLDER); }
bool isDeoptimized() const { return test(PND_DEOPTIMIZED); }
@ -772,7 +774,7 @@ class ParseNode
bool isClosed() const { return test(PND_CLOSED); }
bool isBound() const { return test(PND_BOUND); }
bool isImplicitArguments() const { return test(PND_IMPLICITARGUMENTS); }
bool isHoistedLetUse() const { return test(PND_LET) && isUsed(); }
bool isHoistedLexicalUse() const { return test(PND_LEXICAL) && isUsed(); }
/* True if pn is a parsenode representing a literal constant. */
bool isLiteral() const {
@ -1414,7 +1416,7 @@ struct Definition : public ParseNode
return pn_cookie.isFree();
}
enum Kind { MISSING = 0, VAR, CONST, LET, ARG, NAMED_LAMBDA, PLACEHOLDER };
enum Kind { MISSING = 0, VAR, GLOBALCONST, CONST, LET, ARG, NAMED_LAMBDA, PLACEHOLDER };
bool canHaveInitializer() { return int(kind()) <= int(ARG); }
@ -1433,10 +1435,10 @@ struct Definition : public ParseNode
return PLACEHOLDER;
if (isOp(JSOP_GETARG))
return ARG;
if (isLexical())
return isConst() ? CONST : LET;
if (isConst())
return CONST;
if (isLet())
return LET;
return GLOBALCONST;
return VAR;
}
};

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

@ -122,7 +122,7 @@ MarkUsesAsHoistedLexical(ParseNode *pn)
// Distinguish hoisted uses as a different JSOp for easier compilation.
while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
MOZ_ASSERT(pnu->isUsed());
pnu->pn_dflags |= PND_LET;
pnu->pn_dflags |= PND_LEXICAL;
pnup = &pnu->pn_link;
}
}
@ -137,7 +137,7 @@ ParseContext<FullParseHandler>::define(TokenStream &ts,
MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
Definition *prevDef = nullptr;
if (kind == Definition::LET)
if (kind == Definition::LET || kind == Definition::CONST)
prevDef = decls_.lookupFirst(name);
else
MOZ_ASSERT(!decls_.lookupFirst(name));
@ -148,7 +148,8 @@ ParseContext<FullParseHandler>::define(TokenStream &ts,
if (prevDef) {
ParseNode **pnup = &prevDef->dn_uses;
ParseNode *pnu;
unsigned start = (kind == Definition::LET) ? pn->pn_blockid : bodyid;
unsigned start = (kind == Definition::LET || kind == Definition::CONST) ? pn->pn_blockid
: bodyid;
while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
MOZ_ASSERT(pnu->pn_blockid >= bodyid);
@ -170,7 +171,7 @@ ParseContext<FullParseHandler>::define(TokenStream &ts,
pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED;
}
MOZ_ASSERT_IF(kind != Definition::LET, !lexdeps->lookup(name));
MOZ_ASSERT_IF(kind != Definition::LET && kind != Definition::CONST, !lexdeps->lookup(name));
pn->setDefn(true);
pn->pn_dflags &= ~PND_PLACEHOLDER;
if (kind == Definition::CONST)
@ -197,7 +198,7 @@ ParseContext<FullParseHandler>::define(TokenStream &ts,
return false;
break;
case Definition::CONST:
case Definition::GLOBALCONST:
case Definition::VAR:
if (sc->isFunctionBox()) {
dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETLOCAL : JSOP_GETLOCAL);
@ -215,8 +216,9 @@ ParseContext<FullParseHandler>::define(TokenStream &ts,
break;
case Definition::LET:
case Definition::CONST:
dn->setOp(JSOP_INITLEXICAL);
dn->pn_dflags |= (PND_LET | PND_BOUND);
dn->pn_dflags |= (PND_LEXICAL | PND_BOUND);
MOZ_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */
if (atBodyLevel()) {
if (!bodyLevelLexicals_.append(dn))
@ -314,7 +316,8 @@ template <typename ParseHandler>
void
ParseContext<ParseHandler>::popLetDecl(JSAtom *atom)
{
MOZ_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET);
MOZ_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET ||
ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::CONST);
decls_.remove(atom);
}
@ -338,6 +341,7 @@ AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec
kind = Binding::VARIABLE;
break;
case Definition::CONST:
case Definition::GLOBALCONST:
kind = Binding::CONSTANT;
break;
case Definition::ARG:
@ -1175,6 +1179,7 @@ struct BindData
JSOp op; /* prolog bytecode or nop */
Binder binder; /* binder, discriminates u */
bool isConst; /* const binding? */
struct LetData {
explicit LetData(ExclusiveContext *cx) : blockObj(cx) {}
@ -1183,18 +1188,21 @@ struct BindData
unsigned overflow;
} let;
void initLet(VarContext varContext, StaticBlockObject *blockObj, unsigned overflow) {
void initLexical(VarContext varContext, StaticBlockObject *blockObj, unsigned overflow,
bool isConst = false) {
this->pn = ParseHandler::null();
this->op = JSOP_INITLEXICAL;
this->binder = Parser<ParseHandler>::bindLet;
this->isConst = isConst;
this->binder = Parser<ParseHandler>::bindLexical;
this->let.varContext = varContext;
this->let.blockObj = blockObj;
this->let.overflow = overflow;
}
void initVarOrConst(JSOp op) {
void initVarOrGlobalConst(JSOp op) {
this->op = op;
this->binder = Parser<ParseHandler>::bindVarOrConst;
this->isConst = op == JSOP_DEFCONST;
this->binder = Parser<ParseHandler>::bindVarOrGlobalConst;
}
};
@ -1295,7 +1303,7 @@ static bool
IsNonDominatingInScopedSwitch(ParseContext<FullParseHandler> *pc, HandleAtom name,
Definition *dn)
{
MOZ_ASSERT(dn->isLet());
MOZ_ASSERT(dn->isLexical());
StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr);
if (stmt && stmt->type == STMT_SWITCH)
return dn->pn_cookie.slot() < stmt->firstDominatingLexicalInCase;
@ -1304,9 +1312,9 @@ IsNonDominatingInScopedSwitch(ParseContext<FullParseHandler> *pc, HandleAtom nam
static void
AssociateUsesWithOuterDefinition(ParseNode *pnu, Definition *dn, Definition *outer_dn,
bool markUsesAsLet)
bool markUsesAsLexical)
{
uint32_t dflags = markUsesAsLet ? PND_LET : 0;
uint32_t dflags = markUsesAsLexical ? PND_LEXICAL : 0;
while (true) {
pnu->pn_lexdef = outer_dn;
pnu->pn_dflags |= dflags;
@ -1418,10 +1426,10 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHan
// from another case in a switch, those uses also need to
// be marked as needing dead zone checks.
RootedAtom name(context, atom);
bool markUsesAsLet = outer_dn->isLet() &&
(bodyLevelHoistedUse ||
IsNonDominatingInScopedSwitch(outerpc, name, outer_dn));
AssociateUsesWithOuterDefinition(pnu, dn, outer_dn, markUsesAsLet);
bool markUsesAsLexical = outer_dn->isLexical() &&
(bodyLevelHoistedUse ||
IsNonDominatingInScopedSwitch(outerpc, name, outer_dn));
AssociateUsesWithOuterDefinition(pnu, dn, outer_dn, markUsesAsLexical);
}
outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
@ -1770,7 +1778,8 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
MOZ_ASSERT(!dn->isUsed());
MOZ_ASSERT(dn->isDefn());
bool throwRedeclarationError = dn->kind() == Definition::CONST ||
bool throwRedeclarationError = dn->kind() == Definition::GLOBALCONST ||
dn->kind() == Definition::CONST ||
dn->kind() == Definition::LET;
if (options().extraWarningsOption || throwRedeclarationError) {
JSAutoByteString name;
@ -1999,7 +2008,10 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
* function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
*/
if (DefinitionNode dn = pc->decls().lookupFirst(funName)) {
if (dn == Definition::CONST) {
if (dn == Definition::GLOBALCONST ||
dn == Definition::CONST ||
dn == Definition::LET)
{
JSAutoByteString name;
if (!AtomToPrintableString(context, funName, &name) ||
!report(ParseError, false, null(), JSMSG_REDECLARED_VAR,
@ -2853,7 +2865,7 @@ Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
template <typename ParseHandler>
bool
Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, HandlePropertyName name)
Parser<ParseHandler>::reportRedeclaration(Node pn, Definition::Kind redeclKind, HandlePropertyName name)
{
JSAutoByteString printable;
if (!AtomToPrintableString(context, name, &printable))
@ -2863,14 +2875,18 @@ Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, HandlePropertyN
if (stmt && stmt->type == STMT_CATCH) {
report(ParseError, false, pn, JSMSG_REDECLARED_CATCH_IDENTIFIER, printable.ptr());
} else {
report(ParseError, false, pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable",
printable.ptr());
if (redeclKind == Definition::ARG) {
report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, printable.ptr());
} else {
report(ParseError, false, pn, JSMSG_REDECLARED_VAR, Definition::kindString(redeclKind),
printable.ptr());
}
}
return false;
}
/*
* Define a let-variable in a block, let-expression, or comprehension scope. pc
* Define a lexical binding in a block, let-expression, or comprehension scope. pc
* must already be in such a scope.
*
* Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
@ -2880,8 +2896,8 @@ Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, HandlePropertyN
*/
template <>
/* static */ bool
Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data,
HandlePropertyName name, Parser<FullParseHandler> *parser)
Parser<FullParseHandler>::bindLexical(BindData<FullParseHandler> *data,
HandlePropertyName name, Parser<FullParseHandler> *parser)
{
ParseContext<FullParseHandler> *pc = parser->pc;
ParseNode *pn = data->pn;
@ -2918,25 +2934,34 @@ Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data,
if (!pn->pn_cookie.set(parser->tokenStream, pc->staticLevel, index))
return false;
Definition *dn = pc->decls().lookupFirst(name);
Definition::Kind bindingKind = data->isConst ? Definition::CONST : Definition::LET;
/*
* For bindings that are hoisted to the beginning of the block/function,
* define() right now. Otherwise, delay define until PushLetScope.
*/
if (data->let.varContext == HoistVars) {
Definition *dn = pc->decls().lookupFirst(name);
if (dn && dn->pn_blockid == pc->blockid())
return parser->reportRedeclaration(pn, dn->isConst(), name);
if (!pc->define(parser->tokenStream, name, pn, Definition::LET))
return parser->reportRedeclaration(pn, dn->kind(), name);
if (!pc->define(parser->tokenStream, name, pn, bindingKind))
return false;
}
if (blockObj) {
bool redeclared;
RootedId id(cx, NameToId(name));
RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, index, &redeclared));
RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id,
data->isConst, index, &redeclared));
if (!shape) {
if (redeclared)
parser->reportRedeclaration(pn, false, name);
if (redeclared) {
// The only way to be redeclared without a previous definition is if we're in a
// comma separated list in a DontHoistVars block, so a let block of for header. In
// that case, we must be redeclaring the same type of definition as we're trying to
// make.
Definition::Kind dnKind = dn ? dn->kind() : bindingKind;
parser->reportRedeclaration(pn, dnKind, name);
}
return false;
}
@ -2954,8 +2979,8 @@ Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data,
template <>
/* static */ bool
Parser<SyntaxParseHandler>::bindLet(BindData<SyntaxParseHandler> *data,
HandlePropertyName name, Parser<SyntaxParseHandler> *parser)
Parser<SyntaxParseHandler>::bindLexical(BindData<SyntaxParseHandler> *data,
HandlePropertyName name, Parser<SyntaxParseHandler> *parser)
{
if (!parser->checkStrictBinding(name, data->pn))
return false;
@ -3097,8 +3122,8 @@ OuterLet(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, HandleAtom atom)
template <typename ParseHandler>
/* static */ bool
Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser)
Parser<ParseHandler>::bindVarOrGlobalConst(BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser)
{
ExclusiveContext *cx = parser->context;
ParseContext<ParseHandler> *pc = parser->pc;
@ -3138,7 +3163,7 @@ Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data,
if (defs.empty()) {
return pc->define(parser->tokenStream, name, pn,
isConstDecl ? Definition::CONST : Definition::VAR);
isConstDecl ? Definition::GLOBALCONST : Definition::VAR);
}
/*
@ -3165,6 +3190,7 @@ Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data,
bool inCatchBody = (stmt && stmt->type == STMT_CATCH);
bool error = (isConstDecl ||
dn_kind == Definition::CONST ||
dn_kind == Definition::GLOBALCONST ||
(dn_kind == Definition::LET &&
(!inCatchBody || OuterLet(pc, stmt, name))));
@ -3511,7 +3537,7 @@ Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInf
if (!pn)
return null();
pn->pn_dflags |= PND_LET;
pn->pn_dflags |= PND_LEXICAL;
/* Populate the new scope with decls found in the head with updated blockid. */
if (!ForEachLetDef(tokenStream, pc, blockObj, AddLetDecl(stmt->blockid)))
@ -3698,10 +3724,10 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
* The four options here are:
* - PNK_VAR: We're parsing var declarations.
* - PNK_CONST: We're parsing const declarations.
* - PNK_GLOBALCONST: We're parsing const declarations at toplevel (see bug 589119).
* - PNK_LET: We are parsing a let declaration.
* - PNK_CALL: We are parsing the head of a let block.
*/
MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_CALL);
MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_GLOBALCONST);
/*
* The simple flag is set if the declaration has the form 'var x', with
@ -3709,7 +3735,11 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
*/
MOZ_ASSERT_IF(psimple, *psimple);
JSOp op = kind == PNK_LET ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST;
JSOp op = JSOP_NOP;
if (kind == PNK_VAR)
op = JSOP_DEFVAR;
else if (kind == PNK_GLOBALCONST)
op = JSOP_DEFCONST;
Node pn = handler.newList(kind, null(), op);
if (!pn)
@ -3721,10 +3751,12 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
* this code will change soon.
*/
BindData<ParseHandler> data(context);
if (kind == PNK_LET)
data.initLet(varContext, blockObj, JSMSG_TOO_MANY_LOCALS);
else
data.initVarOrConst(op);
if (kind == PNK_VAR || kind == PNK_GLOBALCONST) {
data.initVarOrGlobalConst(op);
} else {
data.initLexical(varContext, blockObj, JSMSG_TOO_MANY_LOCALS,
/* isConst = */ kind == PNK_CONST);
}
bool first = true;
Node pn2;
@ -3757,7 +3789,8 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
// See comment below for bindBeforeInitializer in the code that
// handles the non-destructuring case.
bool bindBeforeInitializer = kind != PNK_LET || parsingForInOrOfInit;
bool bindBeforeInitializer = (kind != PNK_LET && kind != PNK_CONST) ||
parsingForInOrOfInit;
if (bindBeforeInitializer && !checkDestructuring(&data, pn2))
return null();
@ -3794,10 +3827,10 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
}
RootedPropertyName name(context, tokenStream.currentName());
pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_GLOBALCONST, varContext);
if (!pn2)
return null();
if (data.op == JSOP_DEFCONST)
if (data.isConst)
handler.setFlag(pn2, PND_CONST);
data.pn = pn2;
@ -3818,7 +3851,7 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
// If we are not parsing a let declaration, bind the name
// now. Otherwise we must wait until after parsing the initializing
// assignment.
bool bindBeforeInitializer = kind != PNK_LET;
bool bindBeforeInitializer = kind != PNK_LET && kind != PNK_CONST;
if (bindBeforeInitializer && !data.binder(&data, name, this))
return null();
@ -3832,6 +3865,11 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
if (!handler.finishInitializerAssignment(pn2, init, data.op))
return null();
} else {
if (data.isConst && !pc->parsingForInit) {
report(ParseError, false, null(), JSMSG_BAD_CONST_DECL);
return null();
}
if (!data.binder(&data, name, this))
return null();
}
@ -3849,7 +3887,7 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
template <>
ParseNode *
Parser<FullParseHandler>::letDeclaration()
Parser<FullParseHandler>::lexicalDeclaration(bool isConst)
{
handler.disableSyntaxParser();
@ -3869,7 +3907,8 @@ Parser<FullParseHandler>::letDeclaration()
*/
StmtInfoPC *stmt = pc->topStmt;
if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
report(ParseError, false, null(), JSMSG_LET_DECL_NOT_IN_BLOCK);
report(ParseError, false, null(), JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
isConst ? "const" : "let");
return null();
}
@ -3888,9 +3927,10 @@ Parser<FullParseHandler>::letDeclaration()
* conflicting slots. Forbid top-level let declarations to
* prevent such conflicts from ever occurring.
*/
bool globalLet = !pc->sc->isFunctionBox() && stmt == pc->topScopeStmt;
if (options().selfHostingMode && globalLet) {
report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LET);
bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->topScopeStmt;
if (options().selfHostingMode && isGlobal) {
report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL,
isConst ? "'const'" : "'let'");
return null();
}
@ -3908,7 +3948,12 @@ Parser<FullParseHandler>::letDeclaration()
* FIXME global-level lets are still considered vars until
* other bugs are fixed.
*/
pn = variables(globalLet ? PNK_VAR : PNK_LET);
ParseNodeKind kind = PNK_LET;
if (isGlobal)
kind = isConst ? PNK_GLOBALCONST : PNK_VAR;
else if (isConst)
kind = PNK_CONST;
pn = variables(kind);
if (!pn)
return null();
pn->pn_xflags |= PNX_POPVAR;
@ -3968,7 +4013,8 @@ Parser<FullParseHandler>::letDeclaration()
pc->blockNode = pn1;
}
pn = variables(PNK_LET, nullptr, &pc->staticScope->as<StaticBlockObject>(), HoistVars);
pn = variables(isConst ? PNK_CONST : PNK_LET, nullptr,
&pc->staticScope->as<StaticBlockObject>(), HoistVars);
if (!pn)
return null();
pn->pn_xflags = PNX_POPVAR;
@ -3979,7 +4025,7 @@ Parser<FullParseHandler>::letDeclaration()
template <>
SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::letDeclaration()
Parser<SyntaxParseHandler>::lexicalDeclaration(bool)
{
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure;
@ -4000,7 +4046,7 @@ Parser<FullParseHandler>::letStatement()
pn = letBlock(LetStatement);
MOZ_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
} else {
pn = letDeclaration();
pn = lexicalDeclaration(/* isConst = */ false);
}
return pn;
}
@ -4257,8 +4303,7 @@ Parser<ParseHandler>::exportDeclaration()
break;
case TOK_VAR:
case TOK_CONST:
kid = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
kid = variables(PNK_VAR);
if (!kid)
return null();
kid->pn_xflags = PNX_POPVAR;
@ -4274,7 +4319,8 @@ Parser<ParseHandler>::exportDeclaration()
// and fall through.
tokenStream.ungetToken();
case TOK_LET:
kid = letDeclaration();
case TOK_CONST:
kid = lexicalDeclaration(tt == TOK_CONST);
if (!kid)
return null();
break;
@ -4418,7 +4464,7 @@ Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion versi
if (isForDecl) {
if (pn1->pn_count > 1)
return false;
if (pn1->isOp(JSOP_DEFCONST))
if (pn1->isKind(PNK_CONST))
return false;
// In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning.
@ -4456,6 +4502,22 @@ Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion versi
}
}
template <>
bool
Parser<FullParseHandler>::checkForHeadConstInitializers(ParseNode *pn1)
{
if (!pn1->isKind(PNK_CONST))
return true;
for (ParseNode *assign = pn1->pn_head; assign; assign = assign->pn_next) {
MOZ_ASSERT(assign->isKind(PNK_ASSIGN) || assign->isKind(PNK_NAME));
if (assign->isKind(PNK_NAME) && !assign->isAssigned())
return false;
// PNK_ASSIGN nodes (destructuring assignment) are always assignments.
}
return true;
}
template <>
ParseNode *
Parser<FullParseHandler>::forStatement()
@ -4515,11 +4577,11 @@ Parser<FullParseHandler>::forStatement()
* clause of an ordinary for loop.
*/
pc->parsingForInit = true;
if (tt == TOK_VAR || tt == TOK_CONST) {
if (tt == TOK_VAR) {
isForDecl = true;
tokenStream.consumeKnownToken(tt);
pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
} else if (tt == TOK_LET) {
pn1 = variables(PNK_VAR);
} else if (tt == TOK_LET || tt == TOK_CONST) {
handler.disableSyntaxParser();
tokenStream.consumeKnownToken(tt);
if (!tokenStream.peekToken(&tt))
@ -4531,7 +4593,8 @@ Parser<FullParseHandler>::forStatement()
blockObj = StaticBlockObject::create(context);
if (!blockObj)
return null();
pn1 = variables(PNK_LET, nullptr, blockObj, DontHoistVars);
pn1 = variables(tt == TOK_CONST ? PNK_CONST: PNK_LET, nullptr, blockObj,
DontHoistVars);
}
} else {
pn1 = expr();
@ -4723,8 +4786,14 @@ Parser<FullParseHandler>::forStatement()
if (blockObj) {
/*
* Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
* to induce the correct scoping for A.
* to induce the correct scoping for A. Ensure here that the previously
* unchecked assignment mandate for const declarations holds.
*/
if (!checkForHeadConstInitializers(pn1)) {
report(ParseError, false, nullptr, JSMSG_BAD_CONST_DECL);
return null();
}
forLetImpliedBlock = pushLetScope(blockObj, &letStmt);
if (!forLetImpliedBlock)
return null();
@ -4849,7 +4918,7 @@ Parser<SyntaxParseHandler>::forStatement()
if (tt == TOK_VAR) {
isForDecl = true;
tokenStream.consumeKnownToken(tt);
lhsNode = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST, &simpleForDecl);
lhsNode = variables(PNK_VAR, &simpleForDecl);
}
else if (tt == TOK_CONST || tt == TOK_LET) {
JS_ALWAYS_FALSE(abortIfSyntaxParser());
@ -5518,8 +5587,8 @@ Parser<ParseHandler>::tryStatement()
* scoped, not a property of a new Object instance. This is
* an intentional change that anticipates ECMA Ed. 4.
*/
data.initLet(HoistVars, &pc->staticScope->template as<StaticBlockObject>(),
JSMSG_TOO_MANY_CATCH_VARS);
data.initLexical(HoistVars, &pc->staticScope->template as<StaticBlockObject>(),
JSMSG_TOO_MANY_CATCH_VARS);
MOZ_ASSERT(data.let.blockObj);
if (!tokenStream.getToken(&tt))
@ -5646,9 +5715,10 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
case TOK_CONST:
if (!abortIfSyntaxParser())
return null();
// FALL THROUGH
return lexicalDeclaration(/* isConst = */ true);
case TOK_VAR: {
Node pn = variables(tt == TOK_CONST ? PNK_CONST : PNK_VAR);
Node pn = variables(PNK_VAR);
if (!pn)
return null();
@ -6625,7 +6695,8 @@ Parser<FullParseHandler>::legacyComprehensionTail(ParseNode *bodyExpr, unsigned
return null();
MOZ_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object);
data.initLet(HoistVars, &pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG);
data.initLexical(HoistVars, &pc->staticScope->as<StaticBlockObject>(),
JSMSG_ARRAY_INIT_TOO_BIG);
while (true) {
/*
@ -7068,7 +7139,7 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
if (!blockObj)
return null();
data.initLet(DontHoistVars, blockObj, JSMSG_TOO_MANY_LOCALS);
data.initLexical(DontHoistVars, blockObj, JSMSG_TOO_MANY_LOCALS);
Node lhs = newName(name);
if (!lhs)
return null();

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

@ -542,7 +542,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
Node tryStatement();
Node debuggerStatement();
Node letDeclaration();
Node lexicalDeclaration(bool isConst);
Node letStatement();
Node importDeclaration();
Node exportDeclaration();
@ -634,6 +634,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach,
ParseNodeKind headKind);
bool checkForHeadConstInitializers(Node pn1);
bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder);
bool checkStrictAssignment(Node lhs);
bool checkStrictBinding(PropertyName *name, Node pn);
@ -667,16 +668,16 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
HandlePropertyName name, Parser<ParseHandler> *parser);
static bool
bindLet(BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser);
bindLexical(BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser);
static bool
bindVarOrConst(BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser);
bindVarOrGlobalConst(BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser);
static Node null() { return ParseHandler::null(); }
bool reportRedeclaration(Node pn, bool isConst, HandlePropertyName name);
bool reportRedeclaration(Node pn, Definition::Kind redeclKind, HandlePropertyName name);
bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);

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

@ -2,7 +2,6 @@ load(libdir + "asm.js");
load(libdir + "asserts.js");
assertAsmTypeFail(USE_ASM + "var i; function f(){} return f");
assertAsmTypeFail(USE_ASM + "const i; function f(){} return f");
assertEq(asmLink(asmCompile(USE_ASM + "var i=0; function f(){} return f"))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + "const i=0; function f(){} return f"))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + "var i=42; function f(){ return i|0 } return f"))(), 42);

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

@ -9,5 +9,5 @@
for each (y in [])
)
)
{const functional}
{const functional=undefined}
})()

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

@ -1,5 +1,5 @@
// Binary: cache/js-dbg-64-fe91973cc783-linux
// Flags:
//
const x;[x]='';
const [x]='';
for(;[] && false;){}

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

@ -1 +1 @@
Function("with([])const x=0")()
Function("with([]){const x=0}")()

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

@ -0,0 +1,5 @@
// |jit-test| error: TypeError
{
const x = 0;
function x() { }
}

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

@ -0,0 +1,3 @@
// |jit-test| error: TypeError
const x = 0;
function x() { }

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

@ -0,0 +1,5 @@
// |jit-test| error: TypeError
{
let x;
function x() { }
}

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

@ -9,15 +9,4 @@ function assertThrowsReferenceError(f) {
}
assertThrowsReferenceError(function () { delete x; let x; });
// FIXME do this unconditionally once bug 611388 lands.
function constIsLexical() {
try {
(function () { z++; const z; })();
return false;
} catch (e) {
return true;
}
}
if (constIsLexical())
assertThrowsReferenceError(function () { delete x; const x; });
assertThrowsReferenceError(function () { delete x; const x = undefined; });

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

@ -10,15 +10,4 @@ function assertThrowsReferenceError(f) {
// TDZ is effectful, don't optimize out x.
assertThrowsReferenceError(function () { x; let x; });
// FIXME do this unconditionally once bug 611388 lands.
function constIsLexical() {
try {
(function () { z++; const z; })();
return false;
} catch (e) {
return true;
}
}
if (constIsLexical())
assertThrowsReferenceError(function () { x; const x; });
assertThrowsReferenceError(function () { x; const x = undefined; });

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

@ -235,10 +235,10 @@ test("const x = 1 @");
test("const x = 1 + @");
test("const x = 1 + 2 @");
test("const x = 1 + 2, @");
test("const x = 1 + 2, y @");
test("const x = 1 + 2, y, @");
test("const x = 1 + 2, y, z @");
test("const x = 1 + 2, y, z; @");
test("const x = 1 + 2, y = 0@");
test("const x = 1 + 2, y = 0, @");
test("const x = 1 + 2, y = 0, z = 0 @");
test("const x = 1 + 2, y = 0, z = 0; @");
test("const [ @");
test("const [ x @");

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

@ -1,4 +1,4 @@
with ({b:1}) {
const [ b ] = [];
assertEq(b, undefined);
}
assertEq(b, undefined);

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

@ -1,3 +1,5 @@
// |jit-test| error: ReferenceError
var g = newGlobal();
var dbg = new g.Debugger(this);

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

@ -110,7 +110,7 @@ MSG_DEF(JSMSG_BAD_PROTOTYPE, 1, JSEXN_TYPEERR, "'prototype' property o
MSG_DEF(JSMSG_IN_NOT_OBJECT, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}")
MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 0, JSEXN_RANGEERR, "too many constructor arguments")
MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 0, JSEXN_RANGEERR, "too many function arguments")
MSG_DEF(JSMSG_UNINITIALIZED_LEXICAL, 1, JSEXN_REFERENCEERR, "can't access let declaration `{0}' before initialization")
MSG_DEF(JSMSG_UNINITIALIZED_LEXICAL, 1, JSEXN_REFERENCEERR, "can't access lexical declaration `{0}' before initialization")
// Date
MSG_DEF(JSMSG_INVALID_DATE, 0, JSEXN_RANGEERR, "invalid date")
@ -181,6 +181,7 @@ MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as'
MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration")
MSG_DEF(JSMSG_BAD_CONTINUE, 0, JSEXN_SYNTAXERR, "continue must be inside loop")
MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 0, JSEXN_REFERENCEERR, "invalid destructuring assignment operator")
MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET, 0, JSEXN_SYNTAXERR, "invalid destructuring target")
@ -244,7 +245,7 @@ MSG_DEF(JSMSG_INVALID_FOR_OF_INIT, 0, JSEXN_SYNTAXERR, "for-of loop variable
MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for")
MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found")
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK, 0, JSEXN_SYNTAXERR, "let declaration not directly within block")
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
@ -285,7 +286,7 @@ MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_TYPEERR, "redeclaration of i
MSG_DEF(JSMSG_REDECLARED_PARAM, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}")
MSG_DEF(JSMSG_RESERVED_ID, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier")
MSG_DEF(JSMSG_REST_WITH_DEFAULT, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LET,0, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level 'let' declarations")
MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, 1, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level {0} declarations")
MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition")
MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")

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

@ -1732,7 +1732,7 @@ class ASTSerializer
bool sourceElement(ParseNode *pn, MutableHandleValue dst);
bool declaration(ParseNode *pn, MutableHandleValue dst);
bool variableDeclaration(ParseNode *pn, bool let, MutableHandleValue dst);
bool variableDeclaration(ParseNode *pn, bool lexical, MutableHandleValue dst);
bool variableDeclarator(ParseNode *pn, MutableHandleValue dst);
bool let(ParseNode *pn, bool expr, MutableHandleValue dst);
bool importDeclaration(ParseNode *pn, MutableHandleValue dst);
@ -2001,6 +2001,7 @@ ASTSerializer::declaration(ParseNode *pn, MutableHandleValue dst)
{
MOZ_ASSERT(pn->isKind(PNK_FUNCTION) ||
pn->isKind(PNK_VAR) ||
pn->isKind(PNK_GLOBALCONST) ||
pn->isKind(PNK_LET) ||
pn->isKind(PNK_CONST));
@ -2009,21 +2010,28 @@ ASTSerializer::declaration(ParseNode *pn, MutableHandleValue dst)
return function(pn, AST_FUNC_DECL, dst);
case PNK_VAR:
case PNK_CONST:
case PNK_GLOBALCONST:
return variableDeclaration(pn, false, dst);
default:
MOZ_ASSERT(pn->isKind(PNK_LET));
MOZ_ASSERT(pn->isKind(PNK_LET) || pn->isKind(PNK_CONST));
return variableDeclaration(pn, true, dst);
}
}
bool
ASTSerializer::variableDeclaration(ParseNode *pn, bool let, MutableHandleValue dst)
ASTSerializer::variableDeclaration(ParseNode *pn, bool lexical, MutableHandleValue dst)
{
MOZ_ASSERT(let ? pn->isKind(PNK_LET) : (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)));
MOZ_ASSERT_IF(lexical, pn->isKind(PNK_LET) || pn->isKind(PNK_CONST));
MOZ_ASSERT_IF(!lexical, pn->isKind(PNK_VAR) || pn->isKind(PNK_GLOBALCONST));
VarDeclKind kind = let ? VARDECL_LET : pn->isKind(PNK_VAR) ? VARDECL_VAR : VARDECL_CONST;
VarDeclKind kind = VARDECL_ERR;
// Treat both the toplevel const binding (secretly var-like) and the lexical const
// the same way
if (lexical)
kind = pn->isKind(PNK_LET) ? VARDECL_LET : VARDECL_CONST;
else
kind = pn->isKind(PNK_VAR) ? VARDECL_VAR : VARDECL_CONST;
NodeVector dtors(cx);
if (!dtors.reserve(pn->pn_count))
@ -2166,6 +2174,7 @@ ASTSerializer::exportDeclaration(ParseNode *pn, MutableHandleValue dst)
case PNK_VAR:
case PNK_CONST:
case PNK_GLOBALCONST:
case PNK_LET:
if (!variableDeclaration(kid, kind == PNK_LET, &decl))
return false;
@ -2308,7 +2317,7 @@ ASTSerializer::forInit(ParseNode *pn, MutableHandleValue dst)
return true;
}
return (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))
return (pn->isKind(PNK_VAR) || pn->isKind(PNK_GLOBALCONST))
? variableDeclaration(pn, false, dst)
: expression(pn, dst);
}
@ -2341,10 +2350,11 @@ ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst)
switch (pn->getKind()) {
case PNK_FUNCTION:
case PNK_VAR:
case PNK_CONST:
case PNK_GLOBALCONST:
return declaration(pn, dst);
case PNK_LET:
case PNK_CONST:
return pn->isArity(PN_BINARY)
? let(pn, false, dst)
: declaration(pn, dst);

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

@ -129,11 +129,11 @@ Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle
if (bi->aliased()) {
// Per ES6, lexical bindings cannot be accessed until
// initialized. Remember the first aliased slot that is a
// body-level let, so that they may be initialized to sentinel
// body-level lexical, so that they may be initialized to sentinel
// magic values.
if (numBodyLevelLexicals > 0 &&
nslots < aliasedBodyLevelLexicalBegin &&
bi->kind() == Binding::VARIABLE &&
bi.isBodyLevelLexical() &&
bi.localIndex() >= numVars)
{
aliasedBodyLevelLexicalBegin = nslots;

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

@ -1740,6 +1740,11 @@ class BindingIter
MOZ_ASSERT(i_ >= bindings_->numArgs());
return i_ - bindings_->numArgs();
}
bool isBodyLevelLexical() const {
MOZ_ASSERT(!done());
const Binding &binding = **this;
return binding.kind() != Binding::ARGUMENT;
}
const Binding &operator*() const { MOZ_ASSERT(!done()); return bindings_->bindingArray()[i_]; }
const Binding *operator->() const { MOZ_ASSERT(!done()); return &bindings_->bindingArray()[i_]; }

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

@ -5,7 +5,7 @@
*/
try {
evalcx("Object.freeze(this); eval('const q;')");
evalcx("Object.freeze(this); eval('const q = undefined;')");
} catch (e) {
assertEq(e.message, "({lazy:false}) is not extensible");
}

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

@ -28,7 +28,7 @@ function test()
for (var i = 0; i < limit; i++)
{
eval('const pv' + i + ';');
eval('const pv' + i + ' = undefined;');
}
reportCompare(expect, actual, summary);

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

@ -21,7 +21,7 @@ var limit = 2 << 16;
for (var i = 0; i < limit; i++)
{
eval('const pv' + i + ';');
eval('const pv' + i + ' = undefined;');
}
reportCompare(expect, actual, summary);

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

@ -14,7 +14,7 @@ printStatus (summary);
jit(true);
const c; (function() { for (var j=0;j<5;++j) { c = 1; } })();
const c = undefined; (function() { for (var j=0;j<5;++j) { c = 1; } })();
jit(false);

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

@ -19,8 +19,8 @@ function test()
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
(function() { for each(x in y) { } const x; });
(function() { for each(x in y) { } const x = undefined; });
reportCompare(expect, actual, summary);

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

@ -20,7 +20,6 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
expect = /TypeError: redeclaration of const b/;
try
{
eval('(function() { let(x = 1) { const b = 2 }; let b = 3; })');
@ -30,7 +29,7 @@ function test()
actual = ex + '';
}
reportMatch(expect, actual, summary);
reportCompare(expect, actual, summary);
exitFunc ('test');
}

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

@ -47,7 +47,7 @@ function test()
try
{
actual = 'No Error';
(function () { f(x = yield); const x; });
(function () { f(x = yield); const x = undefined; });
}
catch(ex)
{

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

@ -46,7 +46,7 @@ function test()
// =====
for(
const NaN;
const NaN = undefined;
this.__defineSetter__("x4", function(){});
(eval("", (p={})))) let ({} = (((x ))(function ([]) {})), x1) y;

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

@ -25,7 +25,7 @@ function test()
expect = 'TypeError: redeclaration of formal parameter e';
try
{
eval('(function (e) { var e; const e; });');
eval('(function (e) { var e; const e = undefined; });');
}
catch(ex)
{

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

@ -22,7 +22,7 @@ function test()
// ------- Comment #101 From Gary Kwong [:nth10sd]
uneval(function(){with({functional: []}){x5, y = this;const y }});
uneval(function(){with({functional: []}){x5, y = this;const y = undefined }});
// Assertion failure: strcmp(rval, with_cookie) == 0, at ../jsopcode.cpp:2567
reportCompare(expect, actual, summary);

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

@ -32,9 +32,9 @@ function test()
function f() {
"" + (function(){
for( ; [function(){}] ; x = 0)
with({x: ""})
with({x: ""}) {
const x = []
});
}});
}
f();

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

@ -22,11 +22,11 @@ function test()
// ------- Comment #112 From Jesse Ruderman
expect = 'TypeError: q is not a function';
expect = 'ReferenceError: can\'t access lexical declaration `q\' before initialization';
try
{
q = new Function("(function() { q(3); })(); const q;"); q();
q = new Function("(function() { q(3); })(); const q = undefined;"); q();
}
catch(ex)
{

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

@ -28,7 +28,7 @@ function test()
try
{
eval('x; function x(){}; const x;');
eval('x; function x(){}; const x = undefined;');
}
catch(ex)
{
@ -46,7 +46,7 @@ function test()
// =====
try
{
(function(){(yield []) (function(){with({}){x} }); const x;})();
(function(){(yield []) (function(){with({}){x} }); const x = undefined;})();
}
catch(ex)
{
@ -81,7 +81,7 @@ function test()
// =====
try
{
eval('(function(){{for(c in (function (){ for(x in (x1))window} )()) {const x;} }})();');
eval('(function(){{for(c in (function (){ for(x in (x1))window} )()) {const x = undefined;} }})();');
}
catch(ex)
{

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

@ -22,7 +22,7 @@ function test()
printStatus (summary);
// crash [@ js_Interpret]
(eval("(function(){ this.watch(\"x\", function () { new function ()y } ); const y });"))();
(eval("(function(){ this.watch(\"x\", function () { new function ()y } ); const y = undefined });"))();
x = NaN;
reportCompare(expect, actual, summary + ': 2');

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

@ -21,7 +21,7 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
expect = 'TypeError: redeclaration of variable e';
expect = 'TypeError: redeclaration of var e';
try
{
eval('{ var e = 3; let e = ""; } print(typeof e);');

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

@ -24,7 +24,7 @@ function test()
expect = 'SyntaxError: invalid for/in left-hand side';
try
{
eval('const x; for (x in []);');
eval('const x = undefined; for (x in []);');
}
catch(ex)
{

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

@ -21,7 +21,8 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
with({x: (x -= 0)}){([]); const x }
let x;
with({x: (x -= 0)}){([]); const x = undefined; }
reportCompare(expect, actual, summary);

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

@ -26,19 +26,19 @@ assertEq(testLenientAndStrict('let x,arguments;',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('const eval;',
assertEq(testLenientAndStrict('const eval = undefined;',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('const x,eval;',
assertEq(testLenientAndStrict('const x = undefined,eval = undefined;',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('const arguments;',
assertEq(testLenientAndStrict('const arguments = undefined;',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('const x,arguments;',
assertEq(testLenientAndStrict('const x = undefined,arguments = undefined;',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);

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

@ -30,7 +30,7 @@ function assertRedeclarationErrorThrown(expression)
}
}
assertRedeclarationErrorThrown("try {} catch(e) { const e; }");
assertRedeclarationErrorThrown("try {} catch(e) { const e = undefined; }");
assertRedeclarationErrorThrown("try {} catch(e) { let e; }");
if (typeof reportCompare === "function")

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

@ -686,6 +686,10 @@ testParamPatternCombinations(function(n) ("[a" + n + ", ..." + "b" + n + "]"),
function testVarPatternCombinations(makePattSrc, makePattPatt) {
var pattSrcs = makePatternCombinations(function(n) ("x" + n), makePattSrc);
var pattPatts = makePatternCombinations(function(n) ({ id: ident("x" + n), init: null }), makePattPatt);
// It's illegal to have uninitialized const declarations, so we need a
// separate set of patterns and sources.
var constSrcs = makePatternCombinations(function(n) ("x" + n + " = undefined"), makePattSrc);
var constPatts = makePatternCombinations(function(n) ({ id: ident("x" + n), init: ident("undefined") }), makePattPatt);
for (var i = 0; i < pattSrcs.length; i++) {
// variable declarations in blocks
@ -695,15 +699,15 @@ function testVarPatternCombinations(makePattSrc, makePattPatt) {
assertLocalDecl("let " + pattSrcs[i].join(",") + ";", letDecl(pattPatts[i]));
assertBlockDecl("let " + pattSrcs[i].join(",") + ";", letDecl(pattPatts[i]));
assertDecl("const " + pattSrcs[i].join(",") + ";", constDecl(pattPatts[i]));
assertDecl("const " + constSrcs[i].join(",") + ";", constDecl(constPatts[i]));
// variable declarations in for-loop heads
assertStmt("for (var " + pattSrcs[i].join(",") + "; foo; bar);",
forStmt(varDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt));
assertStmt("for (let " + pattSrcs[i].join(",") + "; foo; bar);",
letStmt(pattPatts[i], forStmt(null, ident("foo"), ident("bar"), emptyStmt)));
assertStmt("for (const " + pattSrcs[i].join(",") + "; foo; bar);",
forStmt(constDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt));
assertStmt("for (const " + constSrcs[i].join(",") + "; foo; bar);",
letStmt(constPatts[i], forStmt(null, ident("foo"), ident("bar"), emptyStmt)));
}
}
@ -1075,7 +1079,7 @@ assertGlobalStmt("for (;;) continue", forStmt(null, null, null, 15), { continueS
assertBlockDecl("var x", "var", { variableDeclaration: function(kind) kind });
assertBlockDecl("let x", "let", { variableDeclaration: function(kind) kind });
assertBlockDecl("const x", "const", { variableDeclaration: function(kind) kind });
assertBlockDecl("const x = undefined", "const", { variableDeclaration: function(kind) kind });
assertBlockDecl("function f() { }", "function", { functionDeclaration: function() "function" });
assertGlobalExpr("(x,y,z)", 1, { sequenceExpression: function() 1 });

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

@ -694,7 +694,7 @@ StaticBlockObject::create(ExclusiveContext *cx)
/* static */ Shape *
StaticBlockObject::addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id,
unsigned index, bool *redeclared)
bool constant, unsigned index, bool *redeclared)
{
MOZ_ASSERT(JSID_IS_ATOM(id));
MOZ_ASSERT(index < LOCAL_INDEX_LIMIT);
@ -713,11 +713,13 @@ StaticBlockObject::addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block
* block's shape later.
*/
uint32_t slot = JSSLOT_FREE(&BlockObject::class_) + index;
uint32_t readonly = constant ? JSPROP_READONLY : 0;
uint32_t propFlags = readonly | JSPROP_ENUMERATE | JSPROP_PERMANENT;
return NativeObject::addPropertyInternal<SequentialExecution>(cx, block, id,
/* getter = */ nullptr,
/* setter = */ nullptr,
slot,
JSPROP_ENUMERATE | JSPROP_PERMANENT,
propFlags,
/* attrs = */ 0,
spp,
/* allowDictionary = */ false);
@ -785,18 +787,20 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope,
? AtomToId(atom)
: INT_TO_JSID(i));
uint32_t propFlags;
if (!xdr->codeUint32(&propFlags))
return false;
bool readonly = !!(propFlags & 1);
bool redeclared;
if (!StaticBlockObject::addVar(cx, obj, id, i, &redeclared)) {
if (!StaticBlockObject::addVar(cx, obj, id, readonly, i, &redeclared)) {
MOZ_ASSERT(!redeclared);
return false;
}
uint32_t aliased;
if (!xdr->codeUint32(&aliased))
return false;
MOZ_ASSERT(aliased == 0 || aliased == 1);
obj->setAliased(i, !!aliased);
bool aliased = !!(propFlags >> 1);
obj->setAliased(i, aliased);
}
} else {
AutoShapeVector shapes(cx);
@ -823,8 +827,10 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope,
if (!XDRAtom(xdr, &atom))
return false;
uint32_t aliased = obj->isAliased(i);
if (!xdr->codeUint32(&aliased))
bool aliased = obj->isAliased(i);
bool readonly = !shape->writable();
uint32_t propFlags = (aliased << 1) | readonly;
if (!xdr->codeUint32(&propFlags))
return false;
}
}
@ -862,7 +868,7 @@ CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<Static
unsigned i = srcBlock->shapeToIndex(**p);
bool redeclared;
if (!StaticBlockObject::addVar(cx, clone, id, i, &redeclared)) {
if (!StaticBlockObject::addVar(cx, clone, id, !(*p)->writable(), i, &redeclared)) {
MOZ_ASSERT(!redeclared);
return nullptr;
}

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

@ -569,7 +569,7 @@ class StaticBlockObject : public BlockObject
static const unsigned LOCAL_INDEX_LIMIT = JS_BIT(16);
static Shape *addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id,
unsigned index, bool *redeclared);
bool constant, unsigned index, bool *redeclared);
};
class ClonedBlockObject : public BlockObject

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

@ -34,7 +34,7 @@ namespace js {
* Nightly) and without (all others). FIXME: Bug 1066322 - Enable ES6 symbols
* in all builds.
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 190;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 192;
static_assert(XDR_BYTECODE_VERSION_SUBTRAHEND % 2 == 0, "see the comment above");
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - (XDR_BYTECODE_VERSION_SUBTRAHEND

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

@ -19,16 +19,18 @@
// Boilerplate used to be able to import this module both from the main
// thread and from worker threads.
// Since const is lexically scoped, hoist the
// conditionally-useful definition ourselves.
const Cu = typeof Components != "undefined" ? Components.utils : undefined;
const Ci = typeof Components != "undefined" ? Components.interfaces : undefined;
const Cc = typeof Components != "undefined" ? Components.classes : undefined;
if (typeof Components != "undefined") {
// Global definition of |exports|, to keep everybody happy.
// In non-main thread, |exports| is provided by the module
// loader.
this.exports = {};
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cc = Components.classes;
Cu.import("resource://gre/modules/Services.jsm", this);
}

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

@ -410,9 +410,8 @@ function shutdownPlaces(aKeepAliveConnection)
}
const FILENAME_BOOKMARKS_HTML = "bookmarks.html";
let (backup_date = new Date().toLocaleFormat("%Y-%m-%d")) {
const FILENAME_BOOKMARKS_JSON = "bookmarks-" + backup_date + ".json";
}
const FILENAME_BOOKMARKS_JSON = "bookmarks-" +
(new Date().toLocaleFormat("%Y-%m-%d")) + ".json";
/**
* Creates a bookmarks.html file in the profile folder from a given source file.