author | Luke Wagner <luke@mozilla.com> |
Mon, 06 May 2013 18:27:51 -0700 | |
changeset 131032 | b7c57f0f04272bc9a64546b18b85b498cb8f056f |
parent 131031 | 4ca9a6bd8f64d8d8892362ef2e1cebde209907eb |
child 131033 | 65463f2f7ba9f4a1bc45631ea875081a7547593d |
push id | 27653 |
push user | lwagner@mozilla.com |
push date | Tue, 07 May 2013 01:32:02 +0000 |
treeherder | mozilla-inbound@b7c57f0f0427 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | terrence |
bugs | 854602 |
milestone | 23.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/ion/AsmJS.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit-test/lib/asm.js | file | annotate | diff | comparison | revisions | |
js/src/js.msg | file | annotate | diff | comparison | revisions |
--- a/js/src/ion/AsmJS.cpp +++ b/js/src/ion/AsmJS.cpp @@ -404,16 +404,31 @@ class Type case Intish: return MIRType_Int32; case Void: return MIRType_None; } JS_NOT_REACHED("Invalid Type"); return MIRType_None; } + + const char *toChars() const { + switch (which_) { + case Double: return "double"; + case Doublish: return "doublish"; + case Fixnum: return "fixnum"; + case Int: return "int"; + case Signed: return "signed"; + case Unsigned: return "unsigned"; + case Intish: return "intish"; + case Void: return "void"; + } + JS_NOT_REACHED("Invalid Type"); + return ""; + } }; // Represents the subset of Type that can be used as the return type of a // function. class RetType { public: enum Which { @@ -1069,17 +1084,17 @@ class ModuleCompiler FuncVector functions_; FuncPtrTableVector funcPtrTables_; ExitMap exits_; MathNameMap standardLibraryMathNames_; GlobalAccessVector globalAccesses_; Label stackOverflowLabel_; Label operationCallbackLabel_; - const char * errorString_; + char * errorString_; ParseNode * errorNode_; TokenStream & tokenStream_; DebugOnly<int> currentPass_; bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltin builtin) { JSAtom *atom = Atomize(cx_, name, strlen(name)); if (!atom) @@ -1100,19 +1115,22 @@ class ModuleCompiler globalAccesses_(cx), errorString_(NULL), errorNode_(NULL), tokenStream_(ts), currentPass_(1) {} ~ModuleCompiler() { - if (errorString_) - tokenStream_.reportAsmJSError(errorNode_->pn_pos.begin, JSMSG_USE_ASM_TYPE_FAIL, + if (errorString_) { + tokenStream_.reportAsmJSError(errorNode_->pn_pos.begin, + JSMSG_USE_ASM_TYPE_FAIL, errorString_); + JS_smprintf_free(errorString_); + } // Avoid spurious Label assertions on compilation failure. if (!stackOverflowLabel_.bound()) stackOverflowLabel_.bind(0); if (!operationCallbackLabel_.bound()) operationCallbackLabel_.bind(0); } @@ -1145,23 +1163,48 @@ class ModuleCompiler module_ = cx_->new_<AsmJSModule>(cx_); if (!module_) return false; return true; } - bool fail(const char *str, ParseNode *pn) { + bool fail(ParseNode *pn, const char *str) { JS_ASSERT(!errorString_); JS_ASSERT(!errorNode_); JS_ASSERT(str); JS_ASSERT(pn); - errorString_ = str; + errorNode_ = pn; + errorString_ = strdup(str); + return false; + } + + bool failfVA(ParseNode *pn, const char *fmt, va_list ap) { + JS_ASSERT(!errorString_); + JS_ASSERT(!errorNode_); + JS_ASSERT(fmt); + JS_ASSERT(pn); errorNode_ = pn; + errorString_ = JS_vsmprintf(fmt, ap); + return false; + } + + bool failf(ParseNode *pn, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + failfVA(pn, fmt, ap); + va_end(ap); + return false; + } + + bool failName(ParseNode *pn, const char *fmt, PropertyName *name) { + JSAutoByteString bytes(cx_, name); + if (bytes.ptr()) + failf(pn, fmt, bytes.ptr()); return false; } /*************************************************** Read-only interface */ JSContext *cx() const { return cx_; } MacroAssembler &masm() { return masm_; } Label &stackOverflowLabel() { return stackOverflowLabel_; } @@ -1547,22 +1590,35 @@ class FunctionCompiler curBlock_->add(ins); curBlock_->initSlot(info().localSlot(local.slot), ins); } } return true; } - bool fail(const char *str, ParseNode *pn) + bool fail(ParseNode *pn, const char *str) + { + return m_.fail(pn, str); + } + + bool failf(ParseNode *pn, const char *fmt, ...) { - m_.fail(str, pn); + va_list ap; + va_start(ap, fmt); + m_.failfVA(pn, fmt, ap); + va_end(ap); return false; } + bool failName(ParseNode *pn, const char *fmt, PropertyName *name) + { + return m_.failName(pn, fmt, name); + } + ~FunctionCompiler() { if (!m().hasError() && !cx()->isExceptionPending()) { JS_ASSERT(loopStack_.empty()); JS_ASSERT(unlabeledBreaks_.empty()); JS_ASSERT(unlabeledContinues_.empty()); JS_ASSERT(labeledBreaks_.empty()); JS_ASSERT(labeledContinues_.empty()); @@ -2377,58 +2433,58 @@ NewAsmJSModuleObject(JSContext *cx, Scop /*****************************************************************************/ // asm.js type-checking and code-generation algorithm static bool CheckIdentifier(ModuleCompiler &m, PropertyName *name, ParseNode *nameNode) { if (name == m.cx()->names().arguments || name == m.cx()->names().eval) - return m.fail("disallowed asm.js parameter name", nameNode); + return m.failName(nameNode, "'%s' is not an allowed identifier", name); return true; } static bool CheckModuleLevelName(ModuleCompiler &m, PropertyName *name, ParseNode *nameNode) { if (!CheckIdentifier(m, name, nameNode)) return false; if (name == m.moduleFunctionName() || name == m.module().globalArgumentName() || name == m.module().importArgumentName() || name == m.module().bufferArgumentName() || m.lookupGlobal(name)) { - return m.fail("Duplicate names not allowed", nameNode); + return m.failName(nameNode, "duplicate name '%s' not allowed", name); } return true; } static bool CheckFunctionHead(ModuleCompiler &m, ParseNode *fn, ParseNode **stmtIter) { if (FunctionObject(fn)->hasRest()) - return m.fail("rest args not allowed in asm.js", fn); + return m.fail(fn, "rest args not allowed"); if (!FunctionHasStatementList(fn)) - return m.fail("expression closures not allowed in asm.js", fn); + return m.fail(fn, "expression closures not allowed"); *stmtIter = ListHead(FunctionStatementList(fn)); return true; } static bool CheckArgument(ModuleCompiler &m, ParseNode *arg, PropertyName **name) { if (!IsDefinition(arg)) - return m.fail("overlapping argument names not allowed", arg); + return m.failName(arg, "duplicate argument name '%s' not allowed", arg->name()); if (MaybeDefinitionInitializer(arg)) - return m.fail("default arguments not allowed", arg); + return m.fail(arg, "default arguments not allowed"); if (!CheckIdentifier(m, arg->name(), arg)) return false; *name = arg->name(); return true; } @@ -2448,17 +2504,17 @@ static bool CheckModuleArguments(ModuleCompiler &m, ParseNode *fn) { unsigned numFormals; ParseNode *arg1 = FunctionArgsList(fn, &numFormals); ParseNode *arg2 = arg1 ? NextNode(arg1) : NULL; ParseNode *arg3 = arg2 ? NextNode(arg2) : NULL; if (numFormals > 3) - return m.fail("asm.js modules takes at most 3 argument.", fn); + return m.fail(fn, "asm.js modules takes at most 3 argument"); PropertyName *arg1Name = NULL; if (numFormals >= 1 && !CheckModuleArgument(m, arg1, &arg1Name)) return false; m.initGlobalArgumentName(arg1Name); PropertyName *arg2Name = NULL; if (numFormals >= 2 && !CheckModuleArgument(m, arg2, &arg2Name)) @@ -2474,24 +2530,24 @@ CheckModuleArguments(ModuleCompiler &m, } static bool SkipUseAsmDirective(ModuleCompiler &m, ParseNode **stmtIter) { ParseNode *firstStatement = *stmtIter; if (!IsExpressionStatement(firstStatement)) - return m.fail("No funny stuff before the 'use asm' directive", firstStatement); + return m.fail(firstStatement, "unsupported statement before 'use asm' directive"); ParseNode *expr = ExpressionStatementExpr(firstStatement); if (!expr || !expr->isKind(PNK_STRING)) - return m.fail("No funny stuff before the 'use asm' directive", firstStatement); + return m.fail(firstStatement, "unsupported statement before 'use asm' directive"); if (StringAtom(expr) != m.cx()->names().useAsm) - return m.fail("asm.js precludes other directives", firstStatement); + return m.fail(firstStatement, "\"use asm\" precludes other directives"); *stmtIter = NextNode(firstStatement); return true; } static bool CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode) { @@ -2502,95 +2558,101 @@ CheckGlobalVariableInitConstant(ModuleCo case NumLit::NegativeInt: case NumLit::BigUnsigned: type = VarType::Int; break; case NumLit::Double: type = VarType::Double; break; case NumLit::OutOfRangeInt: - return m.fail("Global initializer is out of representable integer range", initNode); + return m.fail(initNode, "global initializer is out of representable integer range"); } return m.addGlobalVarInitConstant(varName, type, literal.value()); } static bool CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion, ParseNode **coercedExpr = NULL) { switch (coercionNode->getKind()) { case PNK_BITOR: { ParseNode *rhs = BinaryRight(coercionNode); if (!IsNumericLiteral(rhs)) - return m.fail("Must use |0 for argument/return coercion.", rhs); + return m.fail(rhs, "must use |0 for argument/return coercion"); NumLit rhsLiteral = ExtractNumericLiteral(rhs); if (rhsLiteral.which() != NumLit::Fixnum || rhsLiteral.toInt32() != 0) - return m.fail("Must use |0 for argument/return coercion.", rhs); + return m.fail(rhs, "must use |0 for argument/return coercion"); *coercion = AsmJS_ToInt32; if (coercedExpr) *coercedExpr = BinaryLeft(coercionNode); return true; } case PNK_POS: { *coercion = AsmJS_ToNumber; if (coercedExpr) *coercedExpr = UnaryKid(coercionNode); return true; } default:; } - return m.fail("in coercion expression, the expression must be of the form +x or x|0", coercionNode); + return m.fail(coercionNode, "in coercion expression, the expression must be of the form +x or x|0"); } static bool CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode) { AsmJSCoercion coercion; ParseNode *coercedExpr; if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr)) return false; if (!coercedExpr->isKind(PNK_DOT)) - return m.fail("Bad global variable import expression", coercedExpr); + return m.failName(coercedExpr, "invalid import expression for global '%s'", varName); ParseNode *base = DotBase(coercedExpr); PropertyName *field = DotMember(coercedExpr); - if (!IsUseOfName(base, m.module().importArgumentName())) - return m.fail("Expecting c.y where c is the import parameter", coercedExpr); + PropertyName *importName = m.module().importArgumentName(); + if (!importName) + return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter"); + if (!IsUseOfName(base, importName)) + return m.failName(coercedExpr, "base of import expression must be '%s'", importName); return m.addGlobalVarImport(varName, field, coercion); } static bool CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr, bool first) { ParseNode *ctorExpr = ListHead(newExpr); if (!ctorExpr->isKind(PNK_DOT)) - return m.fail("Only valid 'new' import is 'new global.XYZArray(buf)'", ctorExpr); + return m.fail(ctorExpr, "only valid 'new' import is 'new global.*Array(buf)'"); ParseNode *base = DotBase(ctorExpr); PropertyName *field = DotMember(ctorExpr); - if (!IsUseOfName(base, m.module().globalArgumentName())) - return m.fail("Expecting global.y", base); + PropertyName *globalName = m.module().globalArgumentName(); + if (!globalName) + return m.fail(base, "cannot create array view without an asm.js global parameter"); + if (!IsUseOfName(base, globalName)) + return m.failName(base, "expecting '%s.*Array", globalName); ParseNode *bufArg = NextNode(ctorExpr); - if (!bufArg) - return m.fail("Constructor needs an argument", ctorExpr); - - if (NextNode(bufArg) != NULL) - return m.fail("Only one argument may be passed to a typed array constructor", bufArg); - - if (!IsUseOfName(bufArg, m.module().bufferArgumentName())) - return m.fail("Argument to typed array constructor must be ArrayBuffer name", bufArg); + if (!bufArg || NextNode(bufArg) != NULL) + return m.fail(ctorExpr, "array view constructor takes exactly one argument"); + + PropertyName *bufferName = m.module().bufferArgumentName(); + if (!bufferName) + return m.fail(bufArg, "cannot create array view without an asm.js heap parameter"); + if (!IsUseOfName(bufArg, bufferName)) + return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName); JSAtomState &names = m.cx()->names(); ArrayBufferView::ViewType type; if (field == names.Int8Array) type = ArrayBufferView::TYPE_INT8; else if (field == names.Uint8Array) type = ArrayBufferView::TYPE_UINT8; else if (field == names.Int16Array) @@ -2601,81 +2663,80 @@ CheckNewArrayView(ModuleCompiler &m, Pro type = ArrayBufferView::TYPE_INT32; else if (field == names.Uint32Array) type = ArrayBufferView::TYPE_UINT32; else if (field == names.Float32Array) type = ArrayBufferView::TYPE_FLOAT32; else if (field == names.Float64Array) type = ArrayBufferView::TYPE_FLOAT64; else - return m.fail("could not match typed array name", ctorExpr); + return m.fail(ctorExpr, "could not match typed array name"); return m.addArrayView(varName, type, field); } static bool CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode) { ParseNode *base = DotBase(initNode); PropertyName *field = DotMember(initNode); if (base->isKind(PNK_DOT)) { ParseNode *global = DotBase(base); PropertyName *math = DotMember(base); if (!IsUseOfName(global, m.module().globalArgumentName()) || math != m.cx()->names().Math) - return m.fail("Expecting global.Math", base); + return m.fail(base, "expecting global.Math"); AsmJSMathBuiltin mathBuiltin; if (!m.lookupStandardLibraryMathName(field, &mathBuiltin)) - return m.fail("Does not match a standard Math builtin", initNode); + return m.failName(initNode, "'%s' is not a standard Math builtin", field); return m.addMathBuiltin(varName, mathBuiltin, field); } if (IsUseOfName(base, m.module().globalArgumentName())) { if (field == m.cx()->names().NaN) return m.addGlobalConstant(varName, js_NaN, field); if (field == m.cx()->names().Infinity) return m.addGlobalConstant(varName, js_PositiveInfinity, field); - return m.fail("Does not match a standard global constant", initNode); + return m.failName(initNode, "'%s' is not a standard global constant", field); } if (IsUseOfName(base, m.module().importArgumentName())) return m.addFFI(varName, field); - return m.fail("Expecting c.y where c is either the global or import parameter", initNode); + return m.fail(initNode, "expecting c.y where c is either the global or foreign parameter"); } static bool CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool first) { if (!IsDefinition(var)) - return m.fail("Import variable names must be unique", var); + return m.fail(var, "import variable names must be unique"); if (!CheckModuleLevelName(m, var->name(), var)) return false; ParseNode *initNode = MaybeDefinitionInitializer(var); if (!initNode) - return m.fail("Module import needs initializer", var); + return m.fail(var, "module import needs initializer"); if (IsNumericLiteral(initNode)) return CheckGlobalVariableInitConstant(m, var->name(), initNode); if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS)) return CheckGlobalVariableInitImport(m, var->name(), initNode); if (initNode->isKind(PNK_NEW)) return CheckNewArrayView(m, var->name(), initNode, first); if (initNode->isKind(PNK_DOT)) return CheckGlobalDotImport(m, var->name(), initNode); - return m.fail("Unsupported import expression", initNode); - + return m.fail(initNode, "unsupported import expression"); } static bool CheckModuleGlobals(ModuleCompiler &m, ParseNode **stmtIter) { ParseNode *stmt = SkipEmptyStatements(*stmtIter); bool first = true; @@ -2688,42 +2749,46 @@ CheckModuleGlobals(ModuleCompiler &m, Pa } } *stmtIter = stmt; return true; } static bool +ArgFail(ModuleCompiler &m, PropertyName *argName, ParseNode *stmt) +{ + return m.failName(stmt, "expecting argument type declaration for '%s' of the " + "form 'arg = arg|0' or 'arg = +arg'", argName); +} + +static bool CheckArgumentType(ModuleCompiler &m, ParseNode *fn, PropertyName *argName, ParseNode *stmt, VarType *type) { - if (!stmt) - return m.fail("Missing parameter type declaration statements", fn); - - if (!IsExpressionStatement(stmt)) - return m.fail("Expecting expression statement type of the form 'arg = coercion;'", stmt); + if (!stmt || !IsExpressionStatement(stmt)) + return ArgFail(m, argName, stmt ? stmt : fn); ParseNode *initNode = ExpressionStatementExpr(stmt); if (!initNode || !initNode->isKind(PNK_ASSIGN)) - return m.fail("Expecting expression statement type of the form 'arg = coercion;'", stmt); + return ArgFail(m, argName, stmt); ParseNode *argNode = BinaryLeft(initNode); ParseNode *coercionNode = BinaryRight(initNode); if (!IsUseOfName(argNode, argName)) - return m.fail("left-hand side of 'arg = expr;' must be the name of an argument.", argNode); + return ArgFail(m, argName, stmt); ParseNode *coercedExpr; AsmJSCoercion coercion; if (!CheckTypeAnnotation(m, coercionNode, &coercion, &coercedExpr)) return false; if (!IsUseOfName(coercedExpr, argName)) - return m.fail("For argument type declaration, need 'x = coercion(x)'", coercedExpr); + return ArgFail(m, argName, stmt); *type = VarType(coercion); return true; } static bool CheckArguments(ModuleCompiler &m, ParseNode *fn, MIRTypeVector *argTypes, ParseNode **stmtIter) { @@ -2737,17 +2802,17 @@ CheckArguments(ModuleCompiler &m, ParseN return false; for (unsigned i = 0; i < numFormals; i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) { PropertyName *argName; if (!CheckArgument(m, argpn, &argName)) return false; if (dupSet.has(argName)) - return m.fail("asm.js arguments must have distinct names", argpn); + return m.failName(argpn, "duplicate argument name '%s' not allowed", argName); if (!dupSet.putNew(argName)) return false; VarType argType; if (!CheckArgumentType(m, fn, argName, stmt, &argType)) return false; if (!argTypes->append(argType.toMIRType())) @@ -2768,17 +2833,17 @@ CheckReturnType(ModuleCompiler &m, Parse } ParseNode *coercionNode = UnaryKid(stmt); if (IsNumericLiteral(coercionNode)) { switch (ExtractNumericLiteral(coercionNode).which()) { case NumLit::BigUnsigned: case NumLit::OutOfRangeInt: - return m.fail("Returned literal is out of integer range", coercionNode); + return m.fail(coercionNode, "returned literal is out of integer range"); case NumLit::Fixnum: case NumLit::NegativeInt: *returnType = RetType::Signed; break; case NumLit::Double: *returnType = RetType::Double; break; } @@ -2826,17 +2891,17 @@ CheckFunctionSignatures(ModuleCompiler & ParseNode *fn = SkipEmptyStatements(*stmtIter); for (; fn && fn->isKind(PNK_FUNCTION); fn = NextNonEmptyStatement(fn)) { if (!CheckFunctionSignature(m, fn)) return false; } if (fn && fn->isKind(PNK_NOP)) - return m.fail("duplicate function names are not allowed", fn); + return m.fail(fn, "duplicate function names are not allowed"); *stmtIter = fn; return true; } static bool SameSignature(const ModuleCompiler::Func &a, const ModuleCompiler::Func &b) { @@ -2848,47 +2913,47 @@ SameSignature(const ModuleCompiler::Func } return true; } static bool CheckFuncPtrTable(ModuleCompiler &m, ParseNode *var) { if (!IsDefinition(var)) - return m.fail("Function-pointer table name must be unique", var); + return m.fail(var, "function-pointer table name must be unique"); PropertyName *name = var->name(); if (!CheckModuleLevelName(m, name, var)) return false; ParseNode *arrayLiteral = MaybeDefinitionInitializer(var); if (!arrayLiteral || !arrayLiteral->isKind(PNK_ARRAY)) - return m.fail("Function-pointer table's initializer must be an array literal", var); + return m.fail(var, "function-pointer table's initializer must be an array literal"); unsigned length = ListLength(arrayLiteral); if (!IsPowerOfTwo(length)) - return m.fail("Function-pointer table's length must be a power of 2", arrayLiteral); + return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length); ModuleCompiler::FuncPtrVector funcPtrs(m.cx()); const ModuleCompiler::Func *firstFunction = NULL; for (ParseNode *elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) { if (!elem->isKind(PNK_NAME)) - return m.fail("Function-pointer table's elements must be names of functions", elem); + return m.fail(elem, "function-pointer table's elements must be names of functions"); PropertyName *funcName = elem->name(); const ModuleCompiler::Func *func = m.lookupFunction(funcName); if (!func) - return m.fail("Function-pointer table's elements must be names of functions", elem); + return m.fail(elem, "function-pointer table's elements must be names of functions"); if (firstFunction) { if (!SameSignature(*firstFunction, *func)) - return m.fail("All functions in table must have same signature", elem); + return m.fail(elem, "all functions in table must have same signature"); } else { firstFunction = func; } if (!funcPtrs.append(func)) return false; } @@ -2910,67 +2975,67 @@ CheckFuncPtrTables(ModuleCompiler &m, Pa *stmtIter = stmt; return true; } static bool CheckModuleExportFunction(ModuleCompiler &m, ParseNode *returnExpr) { if (!returnExpr->isKind(PNK_NAME)) - return m.fail("an asm.js export statement must be of the form 'return name'", returnExpr); + return m.fail(returnExpr, "export statement must be of the form 'return name'"); PropertyName *funcName = returnExpr->name(); const ModuleCompiler::Func *func = m.lookupFunction(funcName); if (!func) - return m.fail("exported function name not found", returnExpr); + return m.failName(returnExpr, "exported function name '%s' not found", funcName); return m.addExportedFunction(func, /* maybeFieldName = */ NULL); } static bool CheckModuleExportObject(ModuleCompiler &m, ParseNode *object) { JS_ASSERT(object->isKind(PNK_OBJECT)); for (ParseNode *pn = ListHead(object); pn; pn = NextNode(pn)) { if (!IsNormalObjectField(m.cx(), pn)) - return m.fail("Only normal object properties may be used in the export object literal", pn); + return m.fail(pn, "only normal object properties may be used in the export object literal"); PropertyName *fieldName = ObjectNormalFieldName(m.cx(), pn); ParseNode *initNode = ObjectFieldInitializer(pn); if (!initNode->isKind(PNK_NAME)) - return m.fail("Initializer of exported object literal must be name of function", initNode); + return m.fail(initNode, "initializer of exported object literal must be name of function"); PropertyName *funcName = initNode->name(); const ModuleCompiler::Func *func = m.lookupFunction(funcName); if (!func) - return m.fail("exported function name not found", initNode); + return m.failName(initNode, "exported function name '%s' not found", funcName); if (!m.addExportedFunction(func, fieldName)) return false; } return true; } static bool CheckModuleExports(ModuleCompiler &m, ParseNode *fn, ParseNode **stmtIter) { ParseNode *returnNode = SkipEmptyStatements(*stmtIter); if (!returnNode || !returnNode->isKind(PNK_RETURN)) - return m.fail("asm.js must end with a return export statement", fn); + return m.fail(fn, "asm.js module must end with a return export statement"); ParseNode *returnExpr = UnaryKid(returnNode); if (!returnExpr) - return m.fail("an asm.js export statement must return something", returnNode); + return m.fail(returnNode, "export statement must return something"); if (returnExpr->isKind(PNK_OBJECT)) { if (!CheckModuleExportObject(m, returnExpr)) return false; } else { if (!CheckModuleExportFunction(m, returnExpr)) return false; } @@ -2990,17 +3055,17 @@ CheckNumericLiteral(FunctionCompiler &f, switch (literal.which()) { case NumLit::Fixnum: case NumLit::NegativeInt: case NumLit::BigUnsigned: case NumLit::Double: break; case NumLit::OutOfRangeInt: - return f.fail("Numeric literal out of representable integer range", num); + return f.fail(num, "numeric literal out of representable integer range"); } *type = literal.type(); *def = f.constant(literal.value()); return true; } static bool @@ -3024,73 +3089,76 @@ CheckVarRef(FunctionCompiler &f, ParseNo *def = f.loadGlobalVar(*global); *type = global->varType().toType(); break; case ModuleCompiler::Global::Function: case ModuleCompiler::Global::FFI: case ModuleCompiler::Global::MathBuiltin: case ModuleCompiler::Global::FuncPtrTable: case ModuleCompiler::Global::ArrayView: - return f.fail("Global may not be accessed by ordinary expressions", varRef); + return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name); } return true; } - return f.fail("Name not found in scope", varRef); + return f.failName(varRef, "'%s' not found in local or asm.js module scope", name); } static bool CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType *viewType, MDefinition **def) { ParseNode *viewName = ElemBase(elem); ParseNode *indexExpr = ElemIndex(elem); if (!viewName->isKind(PNK_NAME)) - return f.fail("Left-hand side of x[y] must be a name", viewName); + return f.fail(viewName, "base of array access must be a typed array view name"); const ModuleCompiler::Global *global = f.lookupGlobal(viewName->name()); if (!global || global->which() != ModuleCompiler::Global::ArrayView) - return f.fail("Left-hand side of x[y] must be typed array view name", viewName); + return f.fail(viewName, "base of array access must be a typed array view name"); *viewType = global->viewType(); uint32_t pointer; if (IsLiteralUint32(indexExpr, &pointer)) { pointer <<= TypedArrayShift(*viewType); *def = f.constant(Int32Value(pointer)); return true; } MDefinition *pointerDef; if (indexExpr->isKind(PNK_RSH)) { ParseNode *shiftNode = BinaryRight(indexExpr); ParseNode *pointerNode = BinaryLeft(indexExpr); uint32_t shift; - if (!IsLiteralUint32(shiftNode, &shift) || shift != TypedArrayShift(*viewType)) - return f.fail("The shift amount must be a constant matching the array " - "element size", shiftNode); + if (!IsLiteralUint32(shiftNode, &shift)) + return f.failf(shiftNode, "shift amount must be constant"); + + unsigned requiredShift = TypedArrayShift(*viewType); + if (shift != requiredShift) + return f.failf(shiftNode, "shift amount must be %u", requiredShift); Type pointerType; if (!CheckExpr(f, pointerNode, Use::ToInt32, &pointerDef, &pointerType)) return false; if (!pointerType.isIntish()) - return f.fail("Pointer input must be intish", pointerNode); + return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars()); } else { if (TypedArrayShift(*viewType) != 0) - return f.fail("The shift amount is 0 so this must be a Int8/Uint8 array", indexExpr); + return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access"); Type pointerType; if (!CheckExpr(f, indexExpr, Use::ToInt32, &pointerDef, &pointerType)) return false; if (!pointerType.isInt()) - return f.fail("Pointer input must be int", indexExpr); + return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars()); } // Mask off the low bits to account for clearing effect of a right shift // followed by the left shift implicit in the array access. E.g., H32[i>>2] // loses the low two bits. int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1); *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask))); return true; @@ -3120,21 +3188,21 @@ CheckStoreArray(FunctionCompiler &f, Par MDefinition *rhsDef; Type rhsType; if (!CheckExpr(f, rhs, Use::NoCoercion, &rhsDef, &rhsType)) return false; switch (TypedArrayStoreType(viewType)) { case ArrayStore_Intish: if (!rhsType.isIntish()) - return f.fail("Right-hand side of store must be intish", lhs); + return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars()); break; case ArrayStore_Double: if (rhsType != Type::Double) - return f.fail("Right-hand side of store must be double", lhs); + return f.failf(lhs, "%s is not double", rhsType.toChars()); break; } f.storeHeap(viewType, pointerDef, rhsDef); *def = rhsDef; *type = rhsType; return true; @@ -3146,27 +3214,31 @@ CheckAssignName(FunctionCompiler &f, Par Rooted<PropertyName *> name(f.cx(), lhs->name()); MDefinition *rhsDef; Type rhsType; if (!CheckExpr(f, rhs, Use::NoCoercion, &rhsDef, &rhsType)) return false; if (const FunctionCompiler::Local *lhsVar = f.lookupLocal(name)) { - if (!(rhsType <= lhsVar->type)) - return f.fail("Right-hand side of assignment must be subtype of left-hand side", lhs); + if (!(rhsType <= lhsVar->type)) { + return f.failf(lhs, "%s is not a subtype of %s", + rhsType.toChars(), lhsVar->type.toType().toChars()); + } f.assign(*lhsVar, rhsDef); } else if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) { if (global->which() != ModuleCompiler::Global::Variable) - return f.fail("Only global variables are mutable, not FFI functions etc", lhs); - if (!(rhsType <= global->varType())) - return f.fail("Right-hand side of assignment must be subtype of left-hand side", lhs); + return f.failName(lhs, "'%s' is not a mutable variable", name); + if (!(rhsType <= global->varType())) { + return f.failf(lhs, "%s is not a subtype of %s", + rhsType.toChars(), global->varType().toType().toChars()); + } f.storeGlobalVar(*global, rhsDef); } else { - return f.fail("Variable name in left-hand side of assignment not found", lhs); + return f.failName(lhs, "'%s' not found in local or asm.js module scope", name); } *def = rhsDef; *type = rhsType; return true; } static bool @@ -3177,51 +3249,53 @@ CheckAssign(FunctionCompiler &f, ParseNo ParseNode *rhs = BinaryRight(assign); if (lhs->getKind() == PNK_ELEM) return CheckStoreArray(f, lhs, rhs, def, type); if (lhs->getKind() == PNK_NAME) return CheckAssignName(f, lhs, rhs, def, type); - return f.fail("Left-hand side of assignment must be a variable or heap", assign); + return f.fail(assign, "left-hand side of assignment must be a variable or array access"); } static bool CheckMathIMul(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type) { if (CallArgListLength(call) != 2) - return f.fail("Math.imul must be passed 2 arguments", call); + return f.fail(call, "Math.imul must be passed 2 arguments"); ParseNode *lhs = CallArgList(call); ParseNode *rhs = NextNode(lhs); MDefinition *lhsDef; Type lhsType; if (!CheckExpr(f, lhs, Use::ToInt32, &lhsDef, &lhsType)) return false; MDefinition *rhsDef; Type rhsType; if (!CheckExpr(f, rhs, Use::ToInt32, &rhsDef, &rhsType)) return false; - if (!lhsType.isIntish() || !rhsType.isIntish()) - return f.fail("Math.imul calls must be passed 2 intish arguments", call); + if (!lhsType.isIntish()) + return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars()); + if (!rhsType.isIntish()) + return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars()); *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer); *type = Type::Signed; return true; } static bool CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type) { if (CallArgListLength(call) != 1) - return f.fail("Math.abs must be passed 1 argument", call); + return f.fail(call, "Math.abs must be passed 1 argument"); ParseNode *arg = CallArgList(call); MDefinition *argDef; Type argType; if (!CheckExpr(f, arg, Use::ToNumber, &argDef, &argType)) return false; @@ -3232,33 +3306,33 @@ CheckMathAbs(FunctionCompiler &f, ParseN } if (argType.isDoublish()) { *def = f.unary<MAbs>(argDef, MIRType_Double); *type = Type::Double; return true; } - return f.fail("Math.abs must be passed either a signed or doublish argument", call); + return f.failf(call, "%s is not a subtype of signed or doublish", argType.toChars()); } static bool CheckCallArgs(FunctionCompiler &f, ParseNode *callNode, Use use, FunctionCompiler::Args *args) { f.startCallArgs(args); ParseNode *argNode = CallArgList(callNode); for (unsigned i = 0; i < CallArgListLength(callNode); i++, argNode = NextNode(argNode)) { MDefinition *argDef; Type argType; if (!CheckExpr(f, argNode, use, &argDef, &argType)) return false; if (argType.isVoid()) - return f.fail("Void cannot be passed as an argument", argNode); + return f.fail(argNode, "void is not a valid argument type"); if (!f.passArg(argDef, argType, args)) return false; } f.finishCallArgs(args); return true; } @@ -3267,22 +3341,28 @@ static bool CheckInternalCall(FunctionCompiler &f, ParseNode *callNode, const ModuleCompiler::Func &callee, Use use, MDefinition **def, Type *type) { FunctionCompiler::Args args(f); if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args)) return false; - if (args.length() != callee.numArgs()) - return f.fail("Wrong number of arguments", callNode); + if (args.length() != callee.numArgs()) { + return f.failf(callNode, "%u arguments passed to function taking %u", + args.length(), callee.numArgs()); + } for (unsigned i = 0; i < args.length(); i++) { - if (!(args.type(i) <= callee.argType(i))) - return f.fail("actual arg type is not subtype of formal arg type", callNode); + Type actual = args.type(i); + VarType formal = callee.argType(i); + if (!(actual <= formal)) { + return f.failf(callNode, "argument %u: %s is not a subtype of %s", + i, actual.toChars(), formal.toType().toChars()); + } } if (!f.internalCall(callee, args, def)) return false; // Note: the eventual goal for this code is to perform single-pass type // checking. A single pass means that we may not have seen the callee's definition // when we encounter a callsite. To address this, asm.js requires call @@ -3295,92 +3375,105 @@ CheckInternalCall(FunctionCompiler &f, P // of 'void', 'intish', 'doublish'). // - later checking that use.toReturnType was conservative, i.e., that the // declared return type was a subtype of use.toReturnType. // For now, we check both conditions here. With a single-pass type checking // algorithm, we'd accumulate all the uses for a given callee name // (detecting inconsistencies between uses) and check this meet-over-uses // against the declared return type when the definition was encountered. - if (!(callee.returnType() <= use)) - return f.fail("return type of callee incompatible with use", callNode); + RetType declared = callee.returnType(); + if (!(declared <= use)) { + return f.failf(callNode, "%s is not a subtype of %s", + declared.toType().toChars(), use.toReturnType().toChars()); + } *type = use.toReturnType(); return true; } static bool CheckFuncPtrCall(FunctionCompiler &f, ParseNode *callNode, Use use, MDefinition **def, Type *type) { ParseNode *callee = CallCallee(callNode); ParseNode *elemBase = ElemBase(callee); ParseNode *indexExpr = ElemIndex(callee); if (!elemBase->isKind(PNK_NAME)) - return f.fail("Expecting name (of function-pointer array)", elemBase); + return f.fail(elemBase, "expecting name of function-pointer array"); const ModuleCompiler::FuncPtrTable *table = f.m().lookupFuncPtrTable(elemBase->name()); if (!table) - return f.fail("Expecting name of function-pointer array", elemBase); + return f.fail(elemBase, "expecting name of function-pointer array"); if (!indexExpr->isKind(PNK_BITAND)) - return f.fail("Function-pointer table index expression needs & mask", indexExpr); + return f.fail(indexExpr, "function-pointer table index expression needs & mask"); ParseNode *indexNode = BinaryLeft(indexExpr); ParseNode *maskNode = BinaryRight(indexExpr); uint32_t mask; if (!IsLiteralUint32(maskNode, &mask) || mask != table->mask()) - return f.fail("Function-pointer table index mask must be the table length minus 1", maskNode); + return f.failf(maskNode, "function-pointer table index mask value must be %u", table->mask()); MDefinition *indexDef; Type indexType; if (!CheckExpr(f, indexNode, Use::ToInt32, &indexDef, &indexType)) return false; if (!indexType.isIntish()) - return f.fail("Function-pointer table index expression must be intish", indexNode); + return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars()); FunctionCompiler::Args args(f); if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args)) return false; - if (args.length() != table->sig().numArgs()) - return f.fail("Wrong number of arguments", callNode); + if (args.length() != table->sig().numArgs()) { + return f.failf(callNode, "%u arguments passed to function taking %u", + args.length(), table->sig().numArgs()); + } for (unsigned i = 0; i < args.length(); i++) { - if (!(args.type(i) <= table->sig().argType(i))) - return f.fail("actual arg type is not subtype of formal arg type", callNode); + Type actual = args.type(i); + VarType formal = table->sig().argType(i); + if (!(actual <= formal)) { + return f.failf(callNode, "argument %u: %s is not a subtype of %s", + i, actual.toChars(), formal.toType().toChars()); + } } if (!f.funcPtrCall(*table, indexDef, args, def)) return false; - if (!(table->sig().returnType() <= use)) - return f.fail("return type of callee incompatible with use", callNode); + RetType declared = table->sig().returnType(); + if (!(declared <= use)) { + return f.failf(callNode, "%s is not a subtype of %s", + declared.toType().toChars(), use.toReturnType().toChars()); + } *type = use.toReturnType(); return true; } static bool CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, Use use, MDefinition **def, Type *type) { FunctionCompiler::Args args(f); if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args)) return false; MIRTypeVector argMIRTypes(f.cx()); for (unsigned i = 0; i < args.length(); i++) { - if (!args.type(i).isExtern()) - return f.fail("args to FFI call must be <: extern", callNode); - if (!argMIRTypes.append(args.type(i).toMIRType())) + Type argType = args.type(i); + if (!argType.isExtern()) + return f.failf(callNode, "%s is not a subtype of extern", argType.toChars()); + if (!argMIRTypes.append(argType.toMIRType())) return false; } unsigned exitIndex; if (!f.m().addExit(ffiIndex, CallCallee(callNode)->name(), Move(argMIRTypes), use, &exitIndex)) return false; if (!f.ffiCall(exitIndex, args, use.toMIRType(), def)) @@ -3426,22 +3519,24 @@ CheckMathBuiltinCall(FunctionCompiler &f case AsmJSMathBuiltin_atan2: arity = 2; callee = BinaryMathFunCast(ecmaAtan2); break; } FunctionCompiler::Args args(f); if (!CheckCallArgs(f, callNode, Use::ToNumber, &args)) return false; - if (args.length() != arity) - return f.fail("Math builtin call passed wrong number of argument", callNode); + if (args.length() != arity) { + return f.failf(callNode, "Math builtin call passed %u arguments, expected %u", + args.length(), arity); + } for (unsigned i = 0; i < args.length(); i++) { if (!args.type(i).isDoublish()) - return f.fail("Builtin calls must be passed 1 doublish argument", callNode); + return f.failf(callNode, "%s is not a subtype of doublish", args.type(i).toChars()); } if (!f.builtinCall(callee, args, MIRType_Double, def)) return false; *type = Type::Double; return true; } @@ -3450,35 +3545,35 @@ static bool CheckCall(FunctionCompiler &f, ParseNode *call, Use use, MDefinition **def, Type *type) { ParseNode *callee = CallCallee(call); if (callee->isKind(PNK_ELEM)) return CheckFuncPtrCall(f, call, use, def, type); if (!callee->isKind(PNK_NAME)) - return f.fail("Unexpected callee expression type", callee); + return f.fail(callee, "unexpected callee expression type"); if (const ModuleCompiler::Global *global = f.lookupGlobal(callee->name())) { switch (global->which()) { case ModuleCompiler::Global::Function: return CheckInternalCall(f, call, f.m().function(global->funcIndex()), use, def, type); case ModuleCompiler::Global::FFI: return CheckFFICall(f, call, global->ffiIndex(), use, def, type); case ModuleCompiler::Global::MathBuiltin: return CheckMathBuiltinCall(f, call, global->mathBuiltin(), def, type); case ModuleCompiler::Global::Constant: case ModuleCompiler::Global::Variable: case ModuleCompiler::Global::FuncPtrTable: case ModuleCompiler::Global::ArrayView: - return f.fail("Global is not callable function", callee); + return f.failName(callee, "'%s' is not callable function", callee->name()); } } - return f.fail("Call target not found in global scope", callee); + return f.failName(callee, "'%s' not found in local or asm.js module scope", callee->name()); } static bool CheckPos(FunctionCompiler &f, ParseNode *pos, MDefinition **def, Type *type) { JS_ASSERT(pos->isKind(PNK_POS)); ParseNode *operand = UnaryKid(pos); @@ -3489,17 +3584,17 @@ CheckPos(FunctionCompiler &f, ParseNode if (operandType.isSigned()) *def = f.unary<MToDouble>(operandDef); else if (operandType.isUnsigned()) *def = f.unary<MAsmJSUnsignedToDouble>(operandDef); else if (operandType.isDoublish()) *def = operandDef; else - return f.fail("Operand to unary + must be signed, unsigned or doubleish", operand); + return f.failf(operand, "%s is not a subtype of signed, unsigned or doublish", operandType.toChars()); *type = Type::Double; return true; } static bool CheckNot(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) { @@ -3507,17 +3602,17 @@ CheckNot(FunctionCompiler &f, ParseNode ParseNode *operand = UnaryKid(expr); MDefinition *operandDef; Type operandType; if (!CheckExpr(f, operand, Use::NoCoercion, &operandDef, &operandType)) return false; if (!operandType.isInt()) - return f.fail("Operand to ! must be int", operand); + return f.failf(operand, "%s is not a subtype of int", operandType.toChars()); *def = f.unary<MNot>(operandDef); *type = Type::Int; return true; } static bool CheckNeg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) @@ -3537,17 +3632,17 @@ CheckNeg(FunctionCompiler &f, ParseNode } if (operandType.isDoublish()) { *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Double); *type = Type::Double; return true; } - return f.fail("Operand to unary - must be an int", operand); + return f.failf(operand, "%s is not a subtype of int or doublish", operandType.toChars()); } static bool CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) { JS_ASSERT(expr->isKind(PNK_BITNOT)); ParseNode *operand = UnaryKid(expr); @@ -3558,17 +3653,17 @@ CheckCoerceToInt(FunctionCompiler &f, Pa if (operandType.isDouble()) { *def = f.unary<MTruncateToInt32>(operandDef); *type = Type::Signed; return true; } if (!operandType.isIntish()) - return f.fail("Operand to ~ must be intish or double", operand); + return f.failf(operand, "%s is not a subtype of double or intish", operandType.toChars()); *def = operandDef; *type = Type::Signed; return true; } static bool CheckBitNot(FunctionCompiler &f, ParseNode *neg, MDefinition **def, Type *type) @@ -3580,17 +3675,17 @@ CheckBitNot(FunctionCompiler &f, ParseNo return CheckCoerceToInt(f, operand, def, type); MDefinition *operandDef; Type operandType; if (!CheckExpr(f, operand, Use::ToInt32, &operandDef, &operandType)) return false; if (!operandType.isIntish()) - return f.fail("Operand to ~ must be intish", operand); + return f.failf(operand, "%s is not a subtype of intish", operandType.toChars()); *def = f.bitwise<MBitNot>(operandDef); *type = Type::Signed; return true; } static bool CheckComma(FunctionCompiler &f, ParseNode *comma, Use use, MDefinition **def, Type *type) @@ -3619,17 +3714,17 @@ CheckConditional(FunctionCompiler &f, Pa ParseNode *elseExpr = TernaryKid3(ternary); MDefinition *condDef; Type condType; if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType)) return false; if (!condType.isInt()) - return f.fail("Condition of if must be boolish", cond); + return f.failf(cond, "%s is not a subtype of int", condType.toChars()); MBasicBlock *thenBlock, *elseBlock; if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock)) return false; MDefinition *thenDef; Type thenType; if (!CheckExpr(f, thenExpr, Use::NoCoercion, &thenDef, &thenType)) @@ -3643,22 +3738,24 @@ CheckConditional(FunctionCompiler &f, Pa if (!CheckExpr(f, elseExpr, Use::NoCoercion, &elseDef, &elseType)) return false; f.pushPhiInput(elseDef); if (!f.joinIfElse(thenEnd)) return false; *def = f.popPhiOutput(); - if (thenType.isInt() && elseType.isInt()) + if (thenType.isInt() && elseType.isInt()) { *type = Type::Int; - else if (thenType.isDouble() && elseType.isDouble()) + } else if (thenType.isDouble() && elseType.isDouble()) { *type = Type::Double; - else - return f.fail("Then/else branches of conditional must both be int or double", ternary); + } else { + return f.failf(ternary, "then/else branches of conditional must both produce int or double, " + "current types are %s and %s", thenType.toChars(), elseType.toChars()); + } return true; } static bool IsValidIntMultiplyConstant(ParseNode *expr) { if (!IsNumericLiteral(expr)) @@ -3695,43 +3792,44 @@ CheckMultiply(FunctionCompiler &f, Parse MDefinition *rhsDef; Type rhsType; if (!CheckExpr(f, rhs, Use::ToNumber, &rhsDef, &rhsType)) return false; if (lhsType.isInt() && rhsType.isInt()) { if (!IsValidIntMultiplyConstant(lhs) && !IsValidIntMultiplyConstant(rhs)) - return f.fail("One arg to int multiply must be small (-2^20, 2^20) int literal", star); + return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal"); *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer); *type = Type::Signed; return true; } - if (lhsType.isDoublish() && rhsType.isDoublish()) { - *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal); - *type = Type::Double; - return true; - } - - return f.fail("Arguments to * must both be doubles", star); + if (!lhsType.isDoublish()) + return f.failf(lhs, "%s is not a subtype of doublish", lhsType.toChars()); + if (!rhsType.isDoublish()) + return f.failf(rhs, "%s is not a subtype of doublish", rhsType.toChars()); + + *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal); + *type = Type::Double; + return true; } static bool CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, Use use, MDefinition **def, Type *type) { JS_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB)); ParseNode *lhs = BinaryLeft(expr); ParseNode *rhs = BinaryRight(expr); Use argUse; unsigned addOrSubCount = 1; if (use.which() == Use::AddOrSub) { if (++use.addOrSubCount() > (1<<20)) - return f.fail("Too many + or - without intervening coercion", expr); + return f.fail(expr, "too many + or - without intervening coercion"); argUse = use; } else { argUse = Use(&addOrSubCount); } MDefinition *lhsDef, *rhsDef; Type lhsType, rhsType; if (!CheckExpr(f, lhs, argUse, &lhsDef, &lhsType)) @@ -3745,18 +3843,20 @@ CheckAddOrSub(FunctionCompiler &f, Parse : f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32); if (use.which() == Use::AddOrSub) *type = Type::Int; else *type = Type::Intish; return true; } - if (!lhsType.isDouble() || !rhsType.isDouble()) - return f.fail("Arguments to + or - must both be ints or doubles", expr); + if (!lhsType.isDouble()) + return f.failf(lhs, "%s is not a subtype of double", lhsType.toChars()); + if (!rhsType.isDouble()) + return f.failf(rhs, "%s is not a subtype of double", rhsType.toChars()); *def = expr->isKind(PNK_ADD) ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double) : f.binary<MSub>(lhsDef, rhsDef, MIRType_Double); *type = Type::Double; return true; } @@ -3799,17 +3899,18 @@ CheckDivOrMod(FunctionCompiler &f, Parse *type = Type::Intish; } else { *def = f.binary<MAsmJSUMod>(lhsDef, rhsDef); *type = Type::Int; } return true; } - return f.fail("Arguments to / or % must both be double, signed, or unsigned", expr); + return f.failf(expr, "arguments to / or &% must both be double, signed, or unsigned, " + "%s and %s are given", lhsType.toChars(), rhsType.toChars()); } static bool CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type) { JS_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) || comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE)); ParseNode *lhs = BinaryLeft(comp); @@ -3832,17 +3933,18 @@ CheckComparison(FunctionCompiler &f, Par } if (lhsType.isDouble() && rhsType.isDouble()) { *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Double); *type = Type::Int; return true; } - return f.fail("The arguments to a comparison must both be signed, unsigned or doubles", comp); + return f.failf(comp, "arguments to a comparison must both be signed, unsigned or doubles, " + "%s and %s are given", lhsType.toChars(), rhsType.toChars()); } static bool CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type) { ParseNode *lhs = BinaryLeft(bitwise); ParseNode *rhs = BinaryRight(bitwise); @@ -3858,41 +3960,43 @@ CheckBitwise(FunctionCompiler &f, ParseN default: JS_NOT_REACHED("not a bitwise op"); } if (!onlyOnRight && IsBits32(lhs, identityElement)) { Type rhsType; if (!CheckExpr(f, rhs, Use::ToInt32, def, &rhsType)) return false; if (!rhsType.isIntish()) - return f.fail("Operands to bitwise ops must be intish", bitwise); + return f.failf(bitwise, "%s is not a subtype of intish", rhsType.toChars()); return true; } if (IsBits32(rhs, identityElement)) { Type lhsType; if (!CheckExpr(f, lhs, Use::ToInt32, def, &lhsType)) return false; if (!lhsType.isIntish()) - return f.fail("Operands to bitwise ops must be intish", bitwise); + return f.failf(bitwise, "%s is not a subtype of intish", lhsType.toChars()); return true; } MDefinition *lhsDef; Type lhsType; if (!CheckExpr(f, lhs, Use::ToInt32, &lhsDef, &lhsType)) return false; MDefinition *rhsDef; Type rhsType; if (!CheckExpr(f, rhs, Use::ToInt32, &rhsDef, &rhsType)) return false; - if (!lhsType.isIntish() || !rhsType.isIntish()) - return f.fail("Operands to bitwise ops must be intish", bitwise); + if (!lhsType.isIntish()) + return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars()); + if (!rhsType.isIntish()) + return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars()); switch (bitwise->getKind()) { case PNK_BITOR: *def = f.bitwise<MBitOr>(lhsDef, rhsDef); break; case PNK_BITAND: *def = f.bitwise<MBitAnd>(lhsDef, rhsDef); break; case PNK_BITXOR: *def = f.bitwise<MBitXor>(lhsDef, rhsDef); break; case PNK_LSH: *def = f.bitwise<MLsh>(lhsDef, rhsDef); break; case PNK_RSH: *def = f.bitwise<MRsh>(lhsDef, rhsDef); break; case PNK_URSH: *def = f.bitwise<MUrsh>(lhsDef, rhsDef); break; @@ -3945,17 +4049,17 @@ CheckExpr(FunctionCompiler &f, ParseNode case PNK_BITXOR: case PNK_LSH: case PNK_RSH: case PNK_URSH: return CheckBitwise(f, expr, def, type); default:; } - return f.fail("Unsupported expression", expr); + return f.fail(expr, "unsupported expression"); } static bool CheckStatement(FunctionCompiler &f, ParseNode *stmt, LabelVector *maybeLabels = NULL); static bool CheckExprStatement(FunctionCompiler &f, ParseNode *exprStmt) { @@ -3985,17 +4089,17 @@ CheckWhile(FunctionCompiler &f, ParseNod return false; MDefinition *condDef; Type condType; if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType)) return false; if (!condType.isInt()) - return f.fail("Condition of while loop must be boolish", cond); + return f.failf(cond, "%s is not a subtype of int", condType.toChars()); MBasicBlock *afterLoop; if (!f.branchAndStartLoopBody(condDef, &afterLoop)) return false; if (!CheckStatement(f, body)) return false; @@ -4008,17 +4112,17 @@ CheckWhile(FunctionCompiler &f, ParseNod static bool CheckFor(FunctionCompiler &f, ParseNode *forStmt, const LabelVector *maybeLabels) { JS_ASSERT(forStmt->isKind(PNK_FOR)); ParseNode *forHead = BinaryLeft(forStmt); ParseNode *body = BinaryRight(forStmt); if (!forHead->isKind(PNK_FORHEAD)) - return f.fail("Unsupported for-loop statement", forHead); + return f.fail(forHead, "unsupported for-loop statement"); ParseNode *maybeInit = TernaryKid1(forHead); ParseNode *maybeCond = TernaryKid2(forHead); ParseNode *maybeInc = TernaryKid3(forHead); if (maybeInit) { MDefinition *_1; Type _2; @@ -4032,17 +4136,17 @@ CheckFor(FunctionCompiler &f, ParseNode MDefinition *condDef; if (maybeCond) { Type condType; if (!CheckExpr(f, maybeCond, Use::NoCoercion, &condDef, &condType)) return false; if (!condType.isInt()) - return f.fail("Condition of while loop must be boolish", maybeCond); + return f.failf(maybeCond, "%s is not a subtype of int", condType.toChars()); } else { condDef = f.constant(Int32Value(1)); } MBasicBlock *afterLoop; if (!f.branchAndStartLoopBody(condDef, &afterLoop)) return false; @@ -4080,17 +4184,17 @@ CheckDoWhile(FunctionCompiler &f, ParseN return false; MDefinition *condDef; Type condType; if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType)) return false; if (!condType.isInt()) - return f.fail("Condition of while loop must be boolish", cond); + return f.failf(cond, "%s is not a subtype of int", condType.toChars()); return f.branchAndCloseDoWhileLoop(condDef, loopEntry); } static bool CheckLabel(FunctionCompiler &f, ParseNode *labeledStmt, LabelVector *maybeLabels) { JS_ASSERT(labeledStmt->isKind(PNK_COLON)); @@ -4124,17 +4228,17 @@ CheckIf(FunctionCompiler &f, ParseNode * ParseNode *elseStmt = TernaryKid3(ifStmt); MDefinition *condDef; Type condType; if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType)) return false; if (!condType.isInt()) - return f.fail("Condition of if must be boolish", cond); + return f.failf(cond, "%s is not a subtype of int", condType.toChars()); MBasicBlock *thenBlock, *elseBlock; if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock)) return false; if (!CheckStatement(f, thenStmt)) return false; @@ -4150,41 +4254,41 @@ CheckIf(FunctionCompiler &f, ParseNode * return true; } static bool CheckCaseExpr(FunctionCompiler &f, ParseNode *caseExpr, int32_t *value) { if (!IsNumericLiteral(caseExpr)) - return f.fail("Switch case expression must be an integer literal", caseExpr); + return f.fail(caseExpr, "switch case expression must be an integer literal"); NumLit literal = ExtractNumericLiteral(caseExpr); switch (literal.which()) { case NumLit::Fixnum: case NumLit::NegativeInt: *value = literal.toInt32(); break; case NumLit::OutOfRangeInt: case NumLit::BigUnsigned: - return f.fail("Switch case expression out of integer range", caseExpr); + return f.fail(caseExpr, "switch case expression out of integer range"); case NumLit::Double: - return f.fail("Switch case expression must be an integer literal", caseExpr); + return f.fail(caseExpr, "switch case expression must be an integer literal"); } return true; } static bool CheckDefaultAtEnd(FunctionCompiler &f, ParseNode *stmt) { for (; stmt; stmt = NextNode(stmt)) { JS_ASSERT(stmt->isKind(PNK_CASE) || stmt->isKind(PNK_DEFAULT)); if (stmt->isKind(PNK_DEFAULT) && NextNode(stmt) != NULL) - return f.fail("default label must be at the end", stmt); + return f.fail(stmt, "default label must be at the end"); } return true; } static bool CheckSwitchRange(FunctionCompiler &f, ParseNode *stmt, int32_t *low, int32_t *high, int32_t *tableLength) @@ -4207,40 +4311,40 @@ CheckSwitchRange(FunctionCompiler &f, Pa if (!CheckCaseExpr(f, CaseExpr(stmt), &i)) return false; *low = Min(*low, i); *high = Max(*high, i); } int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1; - if (i64 > 512*1024*1024) - return f.fail("All switch statements generate tables; this table would be bigger than 512MiB", stmt); + if (i64 > 128*1024*1024) + return f.fail(stmt, "all switch statements generate tables; this table would be too big"); *tableLength = int32_t(i64); return true; } static bool CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt) { JS_ASSERT(switchStmt->isKind(PNK_SWITCH)); ParseNode *switchExpr = BinaryLeft(switchStmt); ParseNode *switchBody = BinaryRight(switchStmt); if (!switchBody->isKind(PNK_STATEMENTLIST)) - return f.fail("Switch body may not contain 'let' declarations", switchBody); + return f.fail(switchBody, "switch body may not contain 'let' declarations"); MDefinition *exprDef; Type exprType; if (!CheckExpr(f, switchExpr, Use::NoCoercion, &exprDef, &exprType)) return false; if (!exprType.isSigned()) - return f.fail("Switch expression must be a signed integer", switchExpr); + return f.failf(switchExpr, "%s is not a subtype of signed", exprType.toChars()); ParseNode *stmt = ListHead(switchBody); if (!CheckDefaultAtEnd(f, stmt)) return false; if (!stmt) return true; @@ -4257,17 +4361,17 @@ CheckSwitch(FunctionCompiler &f, ParseNo if (!f.startSwitch(switchStmt, exprDef, low, high, &switchBlock)) return false; for (; stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) { int32_t caseValue = ExtractNumericLiteral(CaseExpr(stmt)).toInt32(); unsigned caseIndex = caseValue - low; if (cases[caseIndex]) - return f.fail("No duplicate case labels", stmt); + return f.fail(stmt, "no duplicate case labels"); if (!f.startSwitchCase(switchBlock, &cases[caseIndex])) return false; if (!CheckStatement(f, CaseBody(stmt))) return false; } @@ -4285,30 +4389,33 @@ CheckSwitch(FunctionCompiler &f, ParseNo static bool CheckReturn(FunctionCompiler &f, ParseNode *returnStmt) { JS_ASSERT(returnStmt->isKind(PNK_RETURN)); ParseNode *expr = UnaryKid(returnStmt); if (!expr) { - if (f.func().returnType().which() != RetType::Void) - return f.fail("All return statements must return void", returnStmt); + if (f.func().returnType().which() != RetType::Void) { + return f.failName(returnStmt, "all return statements in %s must return void", + FunctionName(f.func().fn())); + } f.returnVoid(); return true; } MDefinition *def; Type type; if (!CheckExpr(f, expr, Use::NoCoercion, &def, &type)) return false; - if (!(type <= f.func().returnType())) - return f.fail("All returns must return the same type", expr); + RetType retType = f.func().returnType(); + if (!(type <= retType)) + return f.failf(expr, "%s is not a subtype of %s", type.toChars(), retType.toType().toChars()); if (f.func().returnType().which() == RetType::Void) f.returnVoid(); else f.returnExpr(def); return true; } @@ -4348,55 +4455,55 @@ CheckStatement(FunctionCompiler &f, Pars case PNK_SWITCH: return CheckSwitch(f, stmt); case PNK_RETURN: return CheckReturn(f, stmt); case PNK_STATEMENTLIST: return CheckStatementList(f, stmt); case PNK_BREAK: return f.addBreak(LoopControlMaybeLabel(stmt)); case PNK_CONTINUE: return f.addContinue(LoopControlMaybeLabel(stmt)); default:; } - return f.fail("Unexpected statement kind", stmt); + return f.fail(stmt, "unexpected statement kind"); } static bool CheckVariableDecl(ModuleCompiler &m, ParseNode *var, FunctionCompiler::LocalMap *locals) { if (!IsDefinition(var)) - return m.fail("Local variable names must not restate argument names", var); + return m.fail(var, "local variable names must not restate argument names"); PropertyName *name = var->name(); if (!CheckIdentifier(m, name, var)) return false; ParseNode *initNode = MaybeDefinitionInitializer(var); if (!initNode) - return m.fail("Variable needs explicit type declaration via an initial value", var); + return m.failName(var, "var '%s' needs explicit type declaration via an initial value", name); if (!IsNumericLiteral(initNode)) - return m.fail("Variable initialization value needs to be a numeric literal", initNode); + return m.failName(initNode, "initializer for '%s' needs to be a numeric literal", name); NumLit literal = ExtractNumericLiteral(initNode); VarType type; switch (literal.which()) { case NumLit::Fixnum: case NumLit::NegativeInt: case NumLit::BigUnsigned: type = VarType::Int; break; case NumLit::Double: type = VarType::Double; break; case NumLit::OutOfRangeInt: - return m.fail("Variable initializer is out of representable integer range", initNode); + return m.failName(initNode, "initializer for '%s' is out of range", name); } FunctionCompiler::LocalMap::AddPtr p = locals->lookupForAdd(name); if (p) - return m.fail("Local names must be unique", initNode); + return m.failName(initNode, "duplicate local name '%s' not allowed", name); unsigned slot = locals->count(); if (!locals->add(p, name, FunctionCompiler::Local(type, slot, literal.value()))) return false; return true; } @@ -4466,17 +4573,17 @@ CheckFunctionBody(ModuleCompiler &m, Mod static bool GenerateAsmJSCode(ModuleCompiler &m, ModuleCompiler::Func &func, MIRGenerator &mirGen, LIRGraph &lir) { m.masm().bind(func.codeLabel()); ScopedJSDeletePtr<CodeGenerator> codegen(GenerateCode(&mirGen, &lir, &m.masm())); if (!codegen) - return m.fail("Internal codegen failure (probably out of memory)", func.fn()); + return m.fail(func.fn(), "internal codegen failure (probably out of memory)"); if (!m.collectAccesses(mirGen)) return false; ion::IonScriptCounts *counts = codegen->extractUnassociatedScriptCounts(); if (counts && !m.addFunctionCounts(counts)) { js_delete(counts); return false; @@ -4517,21 +4624,21 @@ CheckFunctionBodiesSequential(ModuleComp if (!mirGen) return false; IonSpewNewFunction(&mirGen->graph(), NullPtr()); IonContext icx(m.cx()->compartment, &mirGen->temp()); if (!OptimizeMIR(mirGen)) - return m.fail("Internal compiler failure (probably out of memory)", func.fn()); + return m.fail(func.fn(), "internal compiler failure (probably out of memory)"); LIRGraph *lir = GenerateLIR(mirGen); if (!lir) - return m.fail("Internal compiler failure (probably out of memory)", func.fn()); + return m.fail(func.fn(), "internal compiler failure (probably out of memory)"); if (!GenerateAsmJSCode(m, func, *mirGen, *lir)) return false; IonSpewEndFunction(); } return true; @@ -4710,17 +4817,17 @@ CheckFunctionBodiesParallel(ModuleCompil ParallelGroupState group(state, tasks); if (!CheckFunctionBodiesParallelImpl(m, group)) { CancelOutstandingJobs(m, group); // If failure was triggered by a worker thread, report error. int32_t maybeFailureIndex = state.maybeGetAsmJSFailedFunctionIndex(); if (maybeFailureIndex >= 0) { ParseNode *fn = m.function(maybeFailureIndex).fn(); - return m.fail("Internal compiler failure (probably out of memory)", fn); + return m.fail(fn, "internal compiler failure (probably out of memory)"); } // Otherwise, the error occurred on the main thread and was already reported. return false; } return true; } #endif // JS_PARALLEL_COMPILATION @@ -5499,17 +5606,17 @@ CheckModule(JSContext *cx, TokenStream & if (!CheckFuncPtrTables(m, &stmtIter)) return false; if (!CheckModuleExports(m, fn, &stmtIter)) return false; if (stmtIter) - return m.fail("The top-level export (return) must be the last statement.", stmtIter); + return m.fail(stmtIter, "top-level export (return) must be the last statement"); m.setFirstPassComplete(); #ifdef JS_PARALLEL_COMPILATION if (OffThreadCompilationEnabled(cx)) { if (!CheckFunctionBodiesParallel(m)) return false; } else {
--- a/js/src/jit-test/lib/asm.js +++ b/js/src/jit-test/lib/asm.js @@ -1,13 +1,13 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const ASM_OK_STRING = "Successfully compiled asm.js code"; +const ASM_OK_STRING = "successfully compiled asm.js code"; const ASM_TYPE_FAIL_STRING = "asm.js type error:"; const USE_ASM = "'use asm';"; const HEAP_IMPORTS = "var i8=new glob.Int8Array(b);var u8=new glob.Uint8Array(b);"+ "var i16=new glob.Int16Array(b);var u16=new glob.Uint16Array(b);"+ "var i32=new glob.Int32Array(b);var u32=new glob.Uint32Array(b);"+ "var f32=new glob.Float32Array(b);var f64=new glob.Float64Array(b);"; const BUF_64KB = new ArrayBuffer(64 * 1024);
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -389,14 +389,14 @@ MSG_DEF(JSMSG_UNDEFINED_CURRENCY, 33 MSG_DEF(JSMSG_INVALID_TIME_ZONE, 336, 1, JSEXN_RANGEERR, "invalid time zone in DateTimeFormat(): {0}") MSG_DEF(JSMSG_DATE_NOT_FINITE, 337, 0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()") MSG_DEF(JSMSG_MODULE_STATEMENT, 338, 0, JSEXN_SYNTAXERR, "module declarations may only appear at the top level of a program or module body") MSG_DEF(JSMSG_CURLY_BEFORE_MODULE, 339, 0, JSEXN_SYNTAXERR, "missing { before module body") MSG_DEF(JSMSG_CURLY_AFTER_MODULE, 340, 0, JSEXN_SYNTAXERR, "missing } after module body") MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 341, 0, JSEXN_SYNTAXERR, "'use asm' directive only works on function code") MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 342, 1, JSEXN_TYPEERR, "asm.js type error: {0}") MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 343, 1, JSEXN_TYPEERR, "asm.js link error: {0}") -MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 344, 0, JSEXN_ERR, "Successfully compiled asm.js code") +MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 344, 0, JSEXN_ERR, "successfully compiled asm.js code") MSG_DEF(JSMSG_BAD_ARROW_ARGS, 345, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)") MSG_DEF(JSMSG_YIELD_IN_ARROW, 346, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield") MSG_DEF(JSMSG_WRONG_VALUE, 347, 2, JSEXN_ERR, "expected {0} but found {1}") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 348, 1, JSEXN_ERR, "target for index {0} is not an integer") MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME,349, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")