Bug 930414 - Check for duplicate module exports r=shu

This commit is contained in:
Jon Coppeard 2015-09-04 14:14:43 +01:00
parent 071566ef1a
commit 725f28c7b5
7 changed files with 81 additions and 6 deletions

View File

@ -1172,6 +1172,7 @@ ObjectBox::trace(JSTracer* trc)
} else if (box->isModuleBox()) {
ModuleBox* modulebox = box->asModuleBox();
modulebox->bindings.trace(trc);
modulebox->exportNames.trace(trc);
}
box = box->traceLink;
}

View File

@ -697,7 +697,8 @@ ModuleBox::ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObjec
ParseContext<ParseHandler>* outerpc)
: ObjectBox(module, traceListHead),
SharedContext(cx, Directives(true), false),
bindings()
bindings(),
exportNames(cx)
{}
template <typename ParseHandler>
@ -4553,7 +4554,7 @@ Parser<ParseHandler>::importDeclaration()
{
MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
if (!pc->atModuleLevel()) {
report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
return null();
}
@ -4649,13 +4650,39 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
ClassContext classContext,
DefaultHandling defaultHandling);
template<>
bool
Parser<FullParseHandler>::addExportName(JSAtom* exportName)
{
TraceableVector<JSAtom*>& exportNames = pc->sc->asModuleBox()->exportNames;
for (JSAtom* name : exportNames) {
if (name == exportName) {
JSAutoByteString str;
if (AtomToPrintableString(context, exportName, &str))
report(ParseError, false, null(), JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
return false;
}
}
return exportNames.append(exportName);
}
template<>
bool
Parser<SyntaxParseHandler>::addExportName(JSAtom* exportName)
{
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure;
}
template<>
ParseNode*
Parser<FullParseHandler>::exportDeclaration()
{
MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
if (!pc->atModuleLevel()) {
report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
return null();
}
@ -4705,6 +4732,9 @@ Parser<FullParseHandler>::exportDeclaration()
if (!exportName)
return null();
if (!addExportName(exportName->pn_atom))
return null();
Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
if (!exportSpec)
return null();
@ -4756,13 +4786,22 @@ Parser<FullParseHandler>::exportDeclaration()
kid = functionStmt(YieldIsKeyword, NameRequired);
if (!kid)
return null();
if (!addExportName(kid->pn_funbox->function()->atom()))
return null();
break;
case TOK_CLASS:
case TOK_CLASS: {
kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired);
if (!kid)
return null();
const ClassNode& cls = kid->as<ClassNode>();
MOZ_ASSERT(cls.names());
if (!addExportName(cls.names()->innerBinding()->pn_atom))
return null();
break;
}
case TOK_VAR:
kid = variables(YieldIsName, PNK_VAR, NotInForInit);
@ -4773,6 +4812,15 @@ Parser<FullParseHandler>::exportDeclaration()
kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr;
if (!kid)
return null();
MOZ_ASSERT(kid->isArity(PN_LIST));
for (ParseNode* var = kid->pn_head; var; var = var->pn_next) {
if (var->isKind(PNK_ASSIGN))
var = var->pn_left;
MOZ_ASSERT(var->isKind(PNK_NAME));
if (!addExportName(var->pn_atom))
return null();
}
break;
case TOK_DEFAULT: {
@ -4798,6 +4846,9 @@ Parser<FullParseHandler>::exportDeclaration()
if (!kid)
return null();
if (!addExportName(context->names().default_))
return null();
return handler.newExportDefaultDeclaration(kid, TokenPos(begin, pos().end));
}

View File

@ -739,6 +739,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
TokenKind tt, unsigned msg);
bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
bool addExportName(JSAtom* exportName);
enum ClassContext { ClassStatement, ClassExpression };
Node classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling);

View File

@ -381,6 +381,7 @@ class ModuleBox : public ObjectBox, public SharedContext
{
public:
Bindings bindings;
TraceableVector<JSAtom*> exportNames;
template <typename ParseHandler>
ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,

View File

@ -0,0 +1,20 @@
// Test errors due to duplicate exports
load(libdir + "asserts.js");
function testSyntaxError(source) {
assertThrowsInstanceOf(function () {
parseModule(source);
}, SyntaxError);
}
testSyntaxError("export var v; export var v;");
testSyntaxError("export var x, y, z; export var y;");
testSyntaxError("export default 1; export default 2;");
testSyntaxError("export var default; export default 1;");
testSyntaxError("export var default; export default function() {};");
testSyntaxError("export var default; export default function foo() {};");
testSyntaxError("export var default; export default export class { constructor() {} };");
testSyntaxError("export var default; export default export class foo { constructor() {} };");
testSyntaxError("var v; export {v}; export {v};");
testSyntaxError("var v, x; export {v}; export {x as v};");

View File

@ -244,6 +244,7 @@ MSG_DEF(JSMSG_DEPRECATED_LET_BLOCK, 0, JSEXN_NONE, "JavaScript 1.7's let bl
MSG_DEF(JSMSG_DEPRECATED_FOR_EACH, 0, JSEXN_NONE, "JavaScript 1.6's for-each-in loops are deprecated; consider using ES6 for-of instead")
MSG_DEF(JSMSG_DEPRECATED_OCTAL, 0, JSEXN_SYNTAXERR, "octal literals and octal escape sequences are deprecated")
MSG_DEF(JSMSG_DEPRECATED_PRAGMA, 1, JSEXN_NONE, "Using //@ to indicate {0} pragmas is deprecated. Use //# instead")
MSG_DEF(JSMSG_DUPLICATE_EXPORT_NAME, 1, JSEXN_SYNTAXERR, "duplicate export name '{0}'")
MSG_DEF(JSMSG_DUPLICATE_FORMAL, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}")
MSG_DEF(JSMSG_DUPLICATE_LABEL, 0, JSEXN_SYNTAXERR, "duplicate label")
MSG_DEF(JSMSG_DUPLICATE_PROPERTY, 1, JSEXN_SYNTAXERR, "property name {0} appears more than once in object literal")

View File

@ -29,11 +29,11 @@ namespace js {
*
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 305;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 306;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
static_assert(JSErr_Limit == 406,
static_assert(JSErr_Limit == 407,
"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 "