Update js parser

This commit is contained in:
Fedor 2019-05-20 09:02:17 +03:00
parent 0437f42ca6
commit bafa2e7e3f
45 changed files with 1562 additions and 1003 deletions

View File

@ -24,7 +24,7 @@ var inputTests = [
{
input: "(function() { return 42; })",
output: "function ()",
printOutput: "function () { return 42; }",
printOutput: "function() { return 42; }",
suppressClick: true
},
@ -40,7 +40,7 @@ var inputTests = [
{
input: "testobj1.testfn2",
output: "function testfn2()",
printOutput: "function () { return 42; }",
printOutput: "function() { return 42; }",
suppressClick: true
},

View File

@ -29,12 +29,12 @@ function run_test_with_server(server, cb) {
addSources(debuggee);
threadClient.getSources(Task.async(function* (res) {
do_check_true(res.sources.length === 3, "3 sources exist");
do_check_eq(res.sources.length, 3, "3 sources exist");
yield threadClient.reconfigure({ useSourceMaps: false });
threadClient.getSources(function(res) {
do_check_true(res.sources.length === 1, "1 source exist");
do_check_eq(res.sources.length, 1, "1 source exist");
client.close().then(cb);
});
}));

View File

@ -9,11 +9,13 @@
#include "mozilla/ArrayUtils.h"
#include "jscntxt.h"
#include "jsstr.h"
#include "builtin/Eval.h"
#include "frontend/BytecodeCompiler.h"
#include "jit/InlinableNatives.h"
#include "js/UniquePtr.h"
#include "vm/AsyncFunction.h"
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
@ -124,6 +126,27 @@ obj_toSource(JSContext* cx, unsigned argc, Value* vp)
return true;
}
template <typename CharT>
static bool
Consume(const CharT*& s, const CharT* e, const char *chars)
{
size_t len = strlen(chars);
if (s + len >= e)
return false;
if (!EqualChars(s, chars, len))
return false;
s += len;
return true;
}
template <typename CharT>
static void
ConsumeSpaces(const CharT*& s, const CharT* e)
{
while (*s == ' ' && s < e)
s++;
}
/*
* Given a function source string, return the offset and length of the part
* between '(function $name' and ')'.
@ -133,37 +156,53 @@ static bool
ArgsAndBodySubstring(mozilla::Range<const CharT> chars, size_t* outOffset, size_t* outLen)
{
const CharT* const start = chars.begin().get();
const CharT* const end = chars.end().get();
const CharT* s = start;
const CharT* e = chars.end().get();
uint8_t parenChomp = 0;
if (s[0] == '(') {
if (s == e)
return false;
// Remove enclosing parentheses.
if (*s == '(' && *(e - 1) == ')') {
s++;
parenChomp = 1;
e--;
}
/* Try to jump "function" keyword. */
s = js_strchr_limit(s, ' ', end);
if (!s)
return false;
(void) Consume(s, e, "async");
ConsumeSpaces(s, e);
(void) (Consume(s, e, "function") || Consume(s, e, "get") || Consume(s, e, "set"));
ConsumeSpaces(s, e);
(void) Consume(s, e, "*");
ConsumeSpaces(s, e);
/*
* Jump over the function's name: it can't be encoded as part
* of an ECMA getter or setter.
*/
s = js_strchr_limit(s, '(', end);
if (!s)
return false;
if (*s == ' ')
// Jump over the function's name.
if (Consume(s, e, "[")) {
s = js_strchr_limit(s, ']', e);
if (!s)
return false;
s++;
ConsumeSpaces(s, e);
if (*s != '(')
return false;
} else {
s = js_strchr_limit(s, '(', e);
if (!s)
return false;
}
*outOffset = s - start;
*outLen = end - s - parenChomp;
*outLen = e - s;
MOZ_ASSERT(*outOffset + *outLen <= chars.length());
return true;
}
enum class PropertyKind {
Getter,
Setter,
Method,
Normal
};
JSString*
js::ObjectToSource(JSContext* cx, HandleObject obj)
{
@ -182,59 +221,28 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
if (!buf.append('{'))
return nullptr;
RootedValue v0(cx), v1(cx);
MutableHandleValue val[2] = {&v0, &v1};
RootedString str0(cx), str1(cx);
MutableHandleString gsop[2] = {&str0, &str1};
AutoIdVector idv(cx);
if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv))
return nullptr;
bool comma = false;
for (size_t i = 0; i < idv.length(); ++i) {
RootedId id(cx, idv[i]);
Rooted<PropertyDescriptor> desc(cx);
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
return nullptr;
int valcnt = 0;
if (desc.object()) {
if (desc.isAccessorDescriptor()) {
if (desc.hasGetterObject() && desc.getterObject()) {
val[valcnt].setObject(*desc.getterObject());
gsop[valcnt].set(cx->names().get);
valcnt++;
}
if (desc.hasSetterObject() && desc.setterObject()) {
val[valcnt].setObject(*desc.setterObject());
gsop[valcnt].set(cx->names().set);
valcnt++;
}
} else {
valcnt = 1;
val[0].set(desc.value());
gsop[0].set(nullptr);
}
}
auto AddProperty = [cx, &comma, &buf](HandleId id, HandleValue val, PropertyKind kind) -> bool {
/* Convert id to a string. */
RootedString idstr(cx);
if (JSID_IS_SYMBOL(id)) {
RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id)));
idstr = ValueToSource(cx, v);
if (!idstr)
return nullptr;
return false;
} else {
RootedValue idv(cx, IdToValue(id));
idstr = ToString<CanGC>(cx, idv);
if (!idstr)
return nullptr;
return false;
/*
* If id is a string that's not an identifier, or if it's a negative
* integer, then it must be quoted.
* If id is a string that's not an identifier, or if it's a
* negative integer, then it must be quoted.
*/
if (JSID_IS_ATOM(id)
? !IsIdentifier(JSID_TO_ATOM(id))
@ -242,28 +250,65 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
{
idstr = QuoteString(cx, idstr, char16_t('\''));
if (!idstr)
return nullptr;
return false;
}
}
for (int j = 0; j < valcnt; j++) {
/* Convert val[j] to its canonical source form. */
JSString* valsource = ValueToSource(cx, val[j]);
if (!valsource)
return nullptr;
RootedString valsource(cx, ValueToSource(cx, val));
if (!valsource)
return false;
RootedLinearString valstr(cx, valsource->ensureLinear(cx));
if (!valstr)
return nullptr;
RootedLinearString valstr(cx, valsource->ensureLinear(cx));
if (!valstr)
return false;
size_t voffset = 0;
size_t vlength = valstr->length();
if (comma && !buf.append(", "))
return false;
comma = true;
/*
* Remove '(function ' from the beginning of valstr and ')' from the
* end so that we can put "get" in front of the function definition.
*/
if (gsop[j] && IsFunctionObject(val[j])) {
size_t voffset, vlength;
// Methods and accessors can return exact syntax of source, that fits
// into property without adding property name or "get"/"set" prefix.
// Use the exact syntax when the following conditions are met:
//
// * It's a function object
// (exclude proxies)
// * Function's kind and property's kind are same
// (this can be false for dynamically defined properties)
// * Function has explicit name
// (this can be false for computed property and dynamically defined
// properties)
// * Function's name and property's name are same
// (this can be false for dynamically defined properties)
if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
kind == PropertyKind::Method)
{
RootedFunction fun(cx);
if (val.toObject().is<JSFunction>()) {
fun = &val.toObject().as<JSFunction>();
// Method's case should be checked on caller.
if (((fun->isGetter() && kind == PropertyKind::Getter) ||
(fun->isSetter() && kind == PropertyKind::Setter) ||
kind == PropertyKind::Method) &&
fun->explicitName())
{
bool result;
if (!EqualStrings(cx, fun->explicitName(), idstr, &result))
return false;
if (result) {
if (!buf.append(valstr))
return false;
return true;
}
}
}
{
// When falling back try to generate a better string
// representation by skipping the prelude, and also removing
// the enclosing parentheses.
bool success;
JS::AutoCheckCannotGC nogc;
if (valstr->hasLatin1Chars())
@ -271,29 +316,90 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
else
success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength);
if (!success)
gsop[j].set(nullptr);
kind = PropertyKind::Normal;
}
if (comma && !buf.append(", "))
return nullptr;
comma = true;
if (kind == PropertyKind::Getter) {
if (!buf.append("get "))
return false;
} else if (kind == PropertyKind::Setter) {
if (!buf.append("set "))
return false;
} else if (kind == PropertyKind::Method && fun) {
if (IsWrappedAsyncFunction(fun)) {
if (!buf.append("async "))
return false;
}
if (gsop[j]) {
if (!buf.append(gsop[j]) || !buf.append(' '))
if (fun->isStarGenerator()) {
if (!buf.append('*'))
return false;
}
}
}
bool needsBracket = JSID_IS_SYMBOL(id);
if (needsBracket && !buf.append('['))
return false;
if (!buf.append(idstr))
return false;
if (needsBracket && !buf.append(']'))
return false;
if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
kind == PropertyKind::Method)
{
if (!buf.appendSubstring(valstr, voffset, vlength))
return false;
} else {
if (!buf.append(':'))
return false;
if (!buf.append(valstr))
return false;
}
return true;
};
RootedId id(cx);
Rooted<PropertyDescriptor> desc(cx);
RootedValue val(cx);
RootedFunction fun(cx);
for (size_t i = 0; i < idv.length(); ++i) {
id = idv[i];
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
return nullptr;
if (!desc.object())
continue;
if (desc.isAccessorDescriptor()) {
if (desc.hasGetterObject() && desc.getterObject()) {
val.setObject(*desc.getterObject());
if (!AddProperty(id, val, PropertyKind::Getter))
return nullptr;
}
if (JSID_IS_SYMBOL(id) && !buf.append('['))
return nullptr;
if (!buf.append(idstr))
return nullptr;
if (JSID_IS_SYMBOL(id) && !buf.append(']'))
return nullptr;
if (!buf.append(gsop[j] ? ' ' : ':'))
return nullptr;
if (!buf.appendSubstring(valstr, voffset, vlength))
return nullptr;
if (desc.hasSetterObject() && desc.setterObject()) {
val.setObject(*desc.setterObject());
if (!AddProperty(id, val, PropertyKind::Setter))
return nullptr;
}
continue;
}
val.set(desc.value());
if (IsFunctionObject(val, fun.address())) {
if (IsWrappedAsyncFunction(fun))
fun = GetUnwrappedAsyncFunction(fun);
if (fun->isMethod()) {
if (!AddProperty(id, val, PropertyKind::Method))
return nullptr;
continue;
}
}
if (!AddProperty(id, val, PropertyKind::Normal))
return nullptr;
}
if (!buf.append('}'))

View File

@ -77,7 +77,7 @@ class MOZ_STACK_CLASS BytecodeCompiler
bool canLazilyParse();
bool createParser();
bool createSourceAndParser(Maybe<uint32_t> parameterListEnd = Nothing());
bool createScript();
bool createScript(uint32_t preludeStart = 0);
bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext);
bool handleParseFailure(const Directives& newDirectives);
bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment);
@ -242,10 +242,11 @@ BytecodeCompiler::createSourceAndParser(Maybe<uint32_t> parameterListEnd /* = No
}
bool
BytecodeCompiler::createScript()
BytecodeCompiler::createScript(uint32_t preludeStart /* = 0 */)
{
script = JSScript::Create(cx, options,
sourceObject, /* sourceStart = */ 0, sourceBuffer.length());
sourceObject, /* sourceStart = */ 0, sourceBuffer.length(),
preludeStart);
return script != nullptr;
}
@ -456,7 +457,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
if (fn->pn_funbox->function()->isInterpreted()) {
MOZ_ASSERT(fun == fn->pn_funbox->function());
if (!createScript())
if (!createScript(fn->pn_funbox->preludeStart))
return false;
Maybe<BytecodeEmitter> emitter;
@ -650,7 +651,8 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
MOZ_ASSERT(sourceObject);
Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
lazy->begin(), lazy->end()));
lazy->begin(), lazy->end(),
lazy->preludeStart()));
if (!script)
return false;

View File

@ -109,6 +109,8 @@ IsIdentifier(JSLinearString* str);
* As above, but taking chars + length.
*/
bool
IsIdentifier(const char* chars, size_t length);
bool
IsIdentifier(const char16_t* chars, size_t length);
/* True if str is a keyword. Defined in TokenStream.cpp. */

View File

@ -3559,9 +3559,11 @@ BytecodeEmitter::maybeSetSourceMap()
if (parser->options().sourceMapURL()) {
// Warn about the replacement, but use the new one.
if (parser->ss->hasSourceMapURL()) {
if(!parser->report(ParseWarning, false, nullptr, JSMSG_ALREADY_HAS_PRAGMA,
parser->ss->filename(), "//# sourceMappingURL"))
if (!parser->reportNoOffset(ParseWarning, false, JSMSG_ALREADY_HAS_PRAGMA,
parser->ss->filename(), "//# sourceMappingURL"))
{
return false;
}
}
if (!parser->ss->setSourceMapURL(cx, parser->options().sourceMapURL()))
@ -3606,13 +3608,13 @@ BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
}
bool
BytecodeEmitter::reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...)
BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
{
TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos;
va_list args;
va_start(args, errorNumber);
bool result = tokenStream()->reportStrictWarningErrorNumberVA(pos.begin, errorNumber, args);
bool result = tokenStream()->reportExtraWarningErrorNumberVA(pos.begin, errorNumber, args);
va_end(args);
return result;
}
@ -7834,7 +7836,8 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
Rooted<JSObject*> sourceObject(cx, script->sourceObject());
Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
funbox->bufStart, funbox->bufEnd));
funbox->bufStart, funbox->bufEnd,
funbox->preludeStart));
if (!script)
return false;
@ -8704,13 +8707,13 @@ BytecodeEmitter::emitStatement(ParseNode* pn)
}
if (directive) {
if (!reportStrictWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
if (!reportExtraWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
return false;
}
} else {
current->currentLine = parser->tokenStream.srcCoords.lineNum(pn2->pn_pos.begin);
current->lastColumn = 0;
if (!reportStrictWarning(pn2, JSMSG_USELESS_EXPR))
if (!reportExtraWarning(pn2, JSMSG_USELESS_EXPR))
return false;
}
}

View File

@ -388,7 +388,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
}
bool reportError(ParseNode* pn, unsigned errorNumber, ...);
bool reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...);
bool reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...);
bool reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...);
// If pn contains a useful expression, return true with *answer set to true.

View File

@ -670,9 +670,18 @@ class FullParseHandler
pn->setDirectRHSAnonFunction(true);
}
ParseNode* newFunctionDefinition() {
return new_<CodeNode>(PNK_FUNCTION, pos());
ParseNode* newFunctionStatement() {
return new_<CodeNode>(PNK_FUNCTION, JSOP_NOP, pos());
}
ParseNode* newFunctionExpression() {
return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA, pos());
}
ParseNode* newArrowFunction() {
return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA_ARROW, pos());
}
bool setComprehensionLambdaBody(ParseNode* pn, ParseNode* body) {
MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
ParseNode* paramsBody = newList(PNK_PARAMSBODY, body);
@ -699,7 +708,7 @@ class FullParseHandler
}
ParseNode* newModule() {
return new_<CodeNode>(PNK_MODULE, pos());
return new_<CodeNode>(PNK_MODULE, JSOP_NOP, pos());
}
ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) {
@ -845,7 +854,7 @@ class FullParseHandler
MOZ_MUST_USE ParseNode* setLikelyIIFE(ParseNode* pn) {
return parenthesize(pn);
}
void setPrologue(ParseNode* pn) {
void setInDirectivePrologue(ParseNode* pn) {
pn->pn_prologue = true;
}

View File

@ -926,10 +926,14 @@ struct ListNode : public ParseNode
struct CodeNode : public ParseNode
{
CodeNode(ParseNodeKind kind, const TokenPos& pos)
: ParseNode(kind, JSOP_NOP, PN_CODE, pos)
CodeNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
: ParseNode(kind, op, PN_CODE, pos)
{
MOZ_ASSERT(kind == PNK_FUNCTION || kind == PNK_MODULE);
MOZ_ASSERT_IF(kind == PNK_MODULE, op == JSOP_NOP);
MOZ_ASSERT(op == JSOP_NOP || // statement, module
op == JSOP_LAMBDA_ARROW || // arrow function
op == JSOP_LAMBDA); // expression, method, comprehension, accessor, &c.
MOZ_ASSERT(!pn_body);
MOZ_ASSERT(!pn_objbox);
}

File diff suppressed because it is too large Load Diff

View File

@ -769,13 +769,13 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
*
* Ex:
* PossibleError possibleError(*this);
* possibleError.setPendingExpressionError(pn, JSMSG_BAD_PROP_ID);
* possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID);
* // A JSMSG_BAD_PROP_ID ParseError is reported, returns false.
* if (!possibleError.checkForExpressionError())
* return false; // we reach this point with a pending exception
*
* PossibleError possibleError(*this);
* possibleError.setPendingExpressionError(pn, JSMSG_BAD_PROP_ID);
* possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID);
* // Returns true, no error is reported.
* if (!possibleError.checkForDestructuringError())
* return false; // not reached, no pending exception
@ -815,7 +815,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// Set a pending error. Only a single error may be set per instance and
// error kind.
void setPending(ErrorKind kind, Node pn, unsigned errorNumber);
void setPending(ErrorKind kind, const TokenPos& pos, unsigned errorNumber);
// If there is a pending error, report it and return false, otherwise
// return true.
@ -830,12 +830,12 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// Set a pending destructuring error. Only a single error may be set
// per instance, i.e. subsequent calls to this method are ignored and
// won't overwrite the existing pending error.
void setPendingDestructuringError(Node pn, unsigned errorNumber);
void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber);
// Set a pending expression error. Only a single error may be set per
// instance, i.e. subsequent calls to this method are ignored and won't
// overwrite the existing pending error.
void setPendingExpressionError(Node pn, unsigned errorNumber);
void setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber);
// If there is a pending destructuring error, report it and return
// false, otherwise return true. Clears any pending expression error.
@ -903,14 +903,40 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
void freeTree(Node node) { handler.freeTree(node); }
private:
bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
unsigned errorNumber, va_list args);
public:
bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...);
bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber,
...);
/* Report the given error at the current offset. */
void error(unsigned errorNumber, ...);
/* Report the given error at the given offset. */
void errorAt(uint32_t offset, unsigned errorNumber, ...);
/*
* Handle a strict mode error at the current offset. Report an error if in
* strict mode code, or warn if not, using the given error number and
* arguments.
*/
MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...);
/*
* Handle a strict mode error at the given offset. Report an error if in
* strict mode code, or warn if not, using the given error number and
* arguments.
*/
MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...);
/* Report the given warning at the current offset. */
MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
/* Report the given warning at the given offset. */
MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...);
/*
* If extra warnings are enabled, report the given warning at the current
* offset.
*/
MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...);
Parser(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
@ -954,7 +980,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
* cx->tempLifoAlloc.
*/
ObjectBox* newObjectBox(JSObject* obj);
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, Directives directives,
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeStart,
Directives directives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB);
@ -1034,8 +1061,9 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// Parse an inner function given an enclosing ParseContext and a
// FunctionBox for the inner function.
bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, InHandling inHandling,
YieldHandling yieldHandling, FunctionSyntaxKind kind,
bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t preludeStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind,
Directives inheritedDirectives, Directives* newDirectives);
// Parse a function's formal parameters and its body assuming its function
@ -1088,9 +1116,10 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
* Some parsers have two versions: an always-inlined version (with an 'i'
* suffix) and a never-inlined version (with an 'n' suffix).
*/
Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling,
Node functionStmt(uint32_t preludeStart,
YieldHandling yieldHandling, DefaultHandling defaultHandling,
FunctionAsyncKind asyncKind = SyncFunction);
Node functionExpr(InvokedPrediction invoked = PredictUninvoked,
Node functionExpr(uint32_t preludeStart, InvokedPrediction invoked = PredictUninvoked,
FunctionAsyncKind asyncKind = SyncFunction);
Node statementList(YieldHandling yieldHandling);
@ -1106,7 +1135,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node* forInitialPart,
mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
Node* forInOrOfExpression);
bool validateForInOrOfLHSExpression(Node target, PossibleError* possibleError);
Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
Node switchStatement(YieldHandling yieldHandling);
@ -1222,7 +1250,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool tryNewTarget(Node& newTarget);
bool checkAndMarkSuperScope();
Node methodDefinition(PropertyType propType, HandleAtom funName);
Node methodDefinition(uint32_t preludeStart, PropertyType propType, HandleAtom funName);
/*
* Additional JS parsers.
@ -1230,10 +1258,11 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
Node funcpn);
Node functionDefinition(InHandling inHandling, YieldHandling yieldHandling, HandleAtom name,
Node functionDefinition(uint32_t preludeStart, Node pn,
InHandling inHandling, YieldHandling yieldHandling, HandleAtom name,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
InvokedPrediction invoked = PredictUninvoked);
bool tryAnnexB = false);
// Parse a function body. Pass StatementListBody if the body is a list of
// statements; pass ExpressionBody if the body is a single expression.
@ -1298,17 +1327,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
#endif
}
enum AssignmentFlavor {
PlainAssignment,
CompoundAssignment,
KeyedDestructuringAssignment,
IncrementAssignment,
DecrementAssignment,
ForInOrOfTarget
};
bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor,
PossibleError* possibleError=nullptr);
bool matchInOrOf(bool* isForInp, bool* isForOfp);
bool hasUsedFunctionSpecialName(HandlePropertyName name);
@ -1319,16 +1337,16 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node newDotGeneratorName();
bool declareDotGeneratorName();
bool checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind,
GeneratorKind generatorKind, bool* tryAnnexB);
bool skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, bool tryAnnexB);
bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
bool skipLazyInnerFunction(Node pn, uint32_t preludeStart, FunctionSyntaxKind kind,
bool tryAnnexB);
bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t preludeStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives);
bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling,
YieldHandling yieldHandling, FunctionSyntaxKind kind,
bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t preludeStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives);
@ -1346,10 +1364,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
private:
bool reportIfArgumentsEvalTarget(Node nameNode);
bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor);
bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor);
bool checkIncDecOperand(Node operand, uint32_t operandOffset);
bool checkStrictAssignment(Node lhs);
bool checkStrictBinding(PropertyName* name, TokenPos pos);
@ -1404,16 +1419,12 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool checkDestructuringObject(Node objectPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
bool checkDestructuringName(Node expr, mozilla::Maybe<DeclarationKind> maybeDecl);
bool checkAssignmentToCall(Node node, unsigned errnum);
Node newNumber(const Token& tok) {
return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
}
static Node null() { return ParseHandler::null(); }
bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
TokenPos pos() const { return tokenStream.currentToken().pos; }

View File

@ -450,6 +450,7 @@ class FunctionBox : public ObjectBox, public SharedContext
uint32_t bufEnd;
uint32_t startLine;
uint32_t startColumn;
uint32_t preludeStart;
uint16_t length;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
@ -476,8 +477,8 @@ class FunctionBox : public ObjectBox, public SharedContext
FunctionContextFlags funCxFlags;
FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun,
Directives directives, bool extraWarnings, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind);
uint32_t preludeStart, Directives directives, bool extraWarnings,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
MutableHandle<LexicalScope::Data*> namedLambdaBindings() {
MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms());

View File

@ -342,7 +342,10 @@ class SyntaxParseHandler
void checkAndSetIsDirectRHSAnonFunction(Node pn) {}
Node newFunctionDefinition() { return NodeFunctionDefinition; }
Node newFunctionStatement() { return NodeFunctionDefinition; }
Node newFunctionExpression() { return NodeFunctionDefinition; }
Node newArrowFunction() { return NodeFunctionDefinition; }
bool setComprehensionLambdaBody(Node pn, Node body) { return true; }
void setFunctionFormalParametersAndBody(Node pn, Node kid) {}
void setFunctionBody(Node pn, Node kid) {}
@ -519,7 +522,7 @@ class SyntaxParseHandler
MOZ_MUST_USE Node setLikelyIIFE(Node pn) {
return pn; // Remain in syntax-parse mode.
}
void setPrologue(Node pn) {}
void setInDirectivePrologue(Node pn) {}
bool isConstant(Node pn) { return false; }

View File

@ -171,6 +171,12 @@ frontend::IsIdentifier(JSLinearString* str)
: ::IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length());
}
bool
frontend::IsIdentifier(const char* chars, size_t length)
{
return ::IsIdentifier(chars, length);
}
bool
frontend::IsIdentifier(const char16_t* chars, size_t length)
{
@ -780,7 +786,7 @@ TokenStream::reportWarning(unsigned errorNumber, ...)
}
bool
TokenStream::reportStrictWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args)
TokenStream::reportExtraWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args)
{
if (!options().extraWarningsOption)
return true;

View File

@ -375,8 +375,7 @@ class MOZ_STACK_CLASS TokenStream
va_list args);
bool reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber,
va_list args);
bool reportStrictWarningErrorNumberVA(uint32_t offset, unsigned errorNumber,
va_list args);
bool reportExtraWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args);
// asm.js reporter
void reportAsmJSError(uint32_t offset, unsigned errorNumber, ...);
@ -570,6 +569,13 @@ class MOZ_STACK_CLASS TokenStream
return true;
}
MOZ_MUST_USE bool peekOffset(uint32_t* offset, Modifier modifier = None) {
TokenPos pos;
if (!peekTokenPos(&pos, modifier))
return false;
*offset = pos.begin;
return true;
}
// This is like peekToken(), with one exception: if there is an EOL
// between the end of the current token and the start of the next token, it
// return true and store TOK_EOL in |*ttp|. In that case, no token with

View File

@ -32,7 +32,7 @@ var f0 = function() {
}
funcBody1 = funcBody.replace('function f0','function ');
funcBody1 = funcBody.replace('function f0','function');
assertEq(f0.toString(), funcBody1);
assertEq(f0.toSource(), '(' + funcBody1 + ')');
@ -48,14 +48,14 @@ assertEq(g.toString(), funcBody2);
assertEq(g.toSource(), '(' + funcBody2 + ')');
f0 = new Function(bodyOnly);
assertEq(f0.toString(), "function anonymous() {\n" + bodyOnly + "\n}");
assertEq(f0.toSource(), "(function anonymous() {\n" + bodyOnly + "\n})");
assertEq(f0.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}");
assertEq(f0.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
var m = new Function(bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m.toString(), "function anonymous() {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous() {\n" + bodyOnly + "\n})");
assertEq(m.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})");
}
})();
@ -91,7 +91,7 @@ f1 = function(glob) {
}
funcBody1 = funcBody.replace('function f1', 'function ');
funcBody1 = funcBody.replace('function f1', 'function');
assertEq(f1.toString(), funcBody1);
assertEq(f1.toSource(), '(' + funcBody1 + ')');
@ -107,14 +107,14 @@ assertEq(g.toString(), funcBody2);
assertEq(g.toSource(), '(' + funcBody2 + ')');
f1 = new Function('glob', bodyOnly);
assertEq(f1.toString(), "function anonymous(glob) {\n" + bodyOnly + "\n}");
assertEq(f1.toSource(), "(function anonymous(glob) {\n" + bodyOnly + "\n})");
assertEq(f1.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}");
assertEq(f1.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
var m = new Function('glob', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m.toString(), "function anonymous(glob) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob) {\n" + bodyOnly + "\n})");
assertEq(m.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})");
}
})();
@ -144,14 +144,14 @@ var funcBody = 'function f2(glob, ffi) {\n\
assertEq(f2.toString(), funcBody);
assertEq(f2.toSource(), funcBody);
f2 = function (glob, ffi) {
f2 = function(glob, ffi) {
"use asm";
function g() {}
return g;
}
funcBody1 = funcBody.replace('function f2', 'function ');
funcBody1 = funcBody.replace('function f2', 'function');
assertEq(f2.toString(), funcBody1);
assertEq(f2.toSource(), '(' + funcBody1 + ')');
@ -167,14 +167,14 @@ assertEq(g.toString(), funcBody2);
assertEq(g.toSource(), '(' + funcBody2 + ')');
f2 = new Function('glob', 'ffi', bodyOnly);
assertEq(f2.toString(), "function anonymous(glob, ffi) {\n" + bodyOnly + "\n}");
assertEq(f2.toSource(), "(function anonymous(glob, ffi) {\n" + bodyOnly + "\n})");
assertEq(f2.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}");
assertEq(f2.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
var m = new Function('glob', 'ffi', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m.toString(), "function anonymous(glob, ffi) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob, ffi) {\n" + bodyOnly + "\n})");
assertEq(m.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})");
}
})();
@ -204,14 +204,14 @@ var funcBody = 'function f3(glob, ffi, heap) {\n\
assertEq(f3.toString(), funcBody);
assertEq(f3.toSource(), funcBody);
f3 = function (glob, ffi, heap) {
f3 = function(glob, ffi, heap) {
"use asm";
function g() {}
return g;
}
funcBody1 = funcBody.replace('function f3', 'function ');
funcBody1 = funcBody.replace('function f3', 'function');
assertEq(f3.toString(), funcBody1);
assertEq(f3.toSource(), '(' + funcBody1 + ')');
@ -227,14 +227,14 @@ assertEq(g.toString(), funcBody2);
assertEq(g.toSource(), '(' + funcBody2 + ')');
f3 = new Function('glob', 'ffi', 'heap', bodyOnly);
assertEq(f3.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}");
assertEq(f3.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})");
assertEq(f3.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}");
assertEq(f3.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
var m = new Function('glob', 'ffi', 'heap', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})");
assertEq(m.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})");
}
})();
@ -243,7 +243,7 @@ if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
(function() {
var funcSource =
`function (glob, ffi, heap) {
`function(glob, ffi, heap) {
"use asm";
function g() {}
return g;
@ -252,7 +252,7 @@ var funcSource =
var f4 = eval("\"use strict\";\n(" + funcSource + ")");
var expectedToString = funcSource;
var expectedToSource = '(' + expectedToString + ')'
var expectedToSource = '(' + expectedToString + ')';
assertEq(f4.toString(), expectedToString);
assertEq(f4.toSource(), expectedToSource);

View File

@ -5,4 +5,4 @@ for (var i=0; i<400; ++i) {
x += String.fromCharCode(i * 289);
}
var s = "'" + x + "'";
assertEq(Function("evt", s).toString(), "function anonymous(evt) {\n" + s + "\n}");
assertEq(Function("evt", s).toString(), "function anonymous(evt\n) {\n" + s + "\n}");

View File

@ -1,14 +1,14 @@
var f = Function("a", "b", "return a + b;");
assertEq(f.toString(), "function anonymous(a, b) {\nreturn a + b;\n}");
assertEq(f.toSource(), "(function anonymous(a, b) {\nreturn a + b;\n})");
assertEq(f.toString(), "function anonymous(a,b\n) {\nreturn a + b;\n}");
assertEq(f.toSource(), "(function anonymous(a,b\n) {\nreturn a + b;\n})");
assertEq(decompileFunction(f), f.toString());
f = Function("a", "...rest", "return rest[42] + b;");
assertEq(f.toString(), "function anonymous(a, ...rest) {\nreturn rest[42] + b;\n}");
assertEq(f.toSource(), "(function anonymous(a, ...rest) {\nreturn rest[42] + b;\n})")
assertEq(f.toString(), "function anonymous(a,...rest\n) {\nreturn rest[42] + b;\n}");
assertEq(f.toSource(), "(function anonymous(a,...rest\n) {\nreturn rest[42] + b;\n})")
assertEq(decompileFunction(f), f.toString());
f = Function("");
assertEq(f.toString(), "function anonymous() {\n\n}");
assertEq(f.toString(), "function anonymous(\n) {\n\n}");
f = Function("", "(abc)");
assertEq(f.toString(), "function anonymous() {\n(abc)\n}");
f = Function("", "return function (a, b) a + b;")();
assertEq(f.toString(), "function (a, b) a + b");
assertEq(f.toString(), "function anonymous(\n) {\n(abc)\n}");
f = Function("", "return function (a,b) a + b;")();
assertEq(f.toString(), "function (a,b) a + b");

View File

@ -1,7 +1,7 @@
var o = {get prop() a + b, set prop(x) a + b};
var prop = Object.getOwnPropertyDescriptor(o, "prop");
assertEq(prop.get.toString(), "function get prop() a + b");
assertEq(prop.get.toSource(), "(function get prop() a + b)");
assertEq(prop.set.toString(), "function set prop(x) a + b");
assertEq(prop.set.toSource(), "(function set prop(x) a + b)");
assertEq(o.toSource(), "({get prop () a + b, set prop (x) a + b})");
assertEq(prop.get.toString(), "get prop() { a + b; }");
assertEq(prop.get.toSource(), "get prop() { a + b; }");
assertEq(prop.set.toString(), "set prop(x) { a + b; }");
assertEq(prop.set.toSource(), "set prop(x) { a + b; }");
assertEq(o.toSource(), "({get prop() { a + b; }, set prop(x) { a + b; }})");

View File

@ -9,7 +9,7 @@ function test(str, arg, result)
var fun = new Function('x', str);
var got = fun.toSource();
var expect = '(function anonymous(x) {\n' + str + '\n})';
var expect = '(function anonymous(x\n) {\n' + str + '\n})';
if (got !== expect) {
print("GOT: " + got);
print("EXPECT: " + expect);

View File

@ -10,5 +10,5 @@ assertEq(arr.length, 10);
gc();
for (var i = 0; i < arr.length; i++)
assertEq(arr[i].lineCount, 3);
assertEq(arr[i].lineCount, 4);

View File

@ -10,6 +10,6 @@ assertEq(arr.length, 100);
gc(g);
for (var i = 0; i < arr.length; i++)
assertEq(arr[i].lineCount, 3);
assertEq(arr[i].lineCount, 4);
gc();

View File

@ -20,6 +20,6 @@ function test(string, range) {
}
test("eval('2 * 3')", [0, 5]);
test("new Function('2 * 3')", [0, 12]);
test("new Function('x', 'x * x')", [0, 13]);
test("new Function('2 * 3')", [0, 31]);
test("new Function('x', 'x * x')", [0, 32]);
assertEq(count, 6);

View File

@ -3,6 +3,7 @@
let g = newGlobal();
let dbg = new Debugger(g);
var text;
var count = 0;
dbg.onNewScript = function (script) {
++count;

View File

@ -12,18 +12,18 @@ var o = {};
Object.defineProperty(o, "prop", {get: function() { return 1; },
set: function() { return 2; },
enumerable: true, configurable: true});
assertEq(o.toSource(), "({get prop () { return 1; }, set prop () { return 2; }})");
assertEq(o.toSource(), "({get prop() { return 1; }, set prop() { return 2; }})");
// obj.toSource TwoByte
Object.defineProperty(o, "prop", {get: function() { return "\u1200"; },
set: function() { return "\u1200"; },
enumerable: true});
assertEq(o.toSource(), '({get prop () { return "\\u1200"; }, set prop () { return "\\u1200"; }})');
assertEq(o.toSource(), '({get prop() { return "\\u1200"; }, set prop() { return "\\u1200"; }})');
var ff = function() { return 10; };
ff.toSource = function() { return "((11))"; }
Object.defineProperty(o, "prop", {get: ff, set: ff, enumerable: true});
assertEq(o.toSource(), "({prop:((11)), prop:((11))})");
assertEq(o.toSource(), "({get prop(11), set prop(11)})");
// XDR
load(libdir + 'bytecode-cache.js');

View File

@ -6,11 +6,11 @@ function test() {
var f = Function(arg1TwoByte, arg2Latin1, bodyLatin1);
assertEq(f(10, 20), 60);
assertEq(f.toSource().includes("arg1\u1200, arg2"), true);
assertEq(f.toSource().includes("arg1\u1200,arg2"), true);
var bodyTwoByte = "return arg1\u1200 + arg2;";
f = Function(arg1TwoByte, arg2Latin1, bodyTwoByte);
assertEq(f(30, 40), 70);
assertEq(f.toSource().includes("arg1\u1200, arg2"), true);
assertEq(f.toSource().includes("arg1\u1200,arg2"), true);
}
test();

View File

@ -186,7 +186,6 @@ MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as'
MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async")
MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression")
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")
@ -200,12 +199,12 @@ MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loo
MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side")
MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding")
MSG_DEF(JSMSG_LET_STARTING_FOROF_LHS, 0, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with 'let'")
MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0} returns a value")
MSG_DEF(JSMSG_BAD_FUNCTION_YIELD, 0, JSEXN_TYPEERR, "can't use 'yield' in a function that can return a value")
MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "generator function can't return a value")
MSG_DEF(JSMSG_BAD_GENEXP_BODY, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand")
MSG_DEF(JSMSG_BAD_METHOD_DEF, 0, JSEXN_SYNTAXERR, "bad method definition")
MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand")
MSG_DEF(JSMSG_BAD_POW_LEFTSIDE, 0, JSEXN_SYNTAXERR, "unparenthesized unary expression can't appear on the left-hand side of '**'")
MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id")
MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function")
@ -262,6 +261,8 @@ MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage a
MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character")
MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL, 0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer")
MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer")
MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
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")

View File

@ -4250,7 +4250,7 @@ JS_GetFunctionScript(JSContext* cx, HandleFunction fun)
*/
static bool
CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
const char* name,
HandleAtom name, bool isInvalidName,
SourceBufferHolder& srcBuf, uint32_t parameterListEnd,
HandleObject enclosingEnv, HandleScope enclosingScope,
MutableHandleFunction fun)
@ -4261,13 +4261,8 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
assertSameCompartment(cx, enclosingEnv);
RootedAtom funAtom(cx);
if (name) {
funAtom = Atomize(cx, name, strlen(name));
if (!funAtom)
return false;
}
fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, funAtom,
fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
isInvalidName ? nullptr : name,
/* proto = */ nullptr,
gc::AllocKind::FUNCTION, TenuredObject,
enclosingEnv));
@ -4285,11 +4280,17 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
return false;
}
// When function name is not a valid identifier, the generated function
// source in srcBuf doesn't have a function name. Set it here.
if (isInvalidName)
fun->setAtom(name);
return true;
}
static MOZ_MUST_USE bool
BuildFunctionString(unsigned nargs, const char* const* argnames,
BuildFunctionString(const char* name, size_t nameLen,
unsigned nargs, const char* const* argnames,
const SourceBufferHolder& srcBuf, StringBuffer* out,
uint32_t* parameterListEnd)
{
@ -4298,6 +4299,12 @@ BuildFunctionString(unsigned nargs, const char* const* argnames,
if (!out->ensureTwoByteChars())
return false;
if (!out->append("function "))
return false;
if (name) {
if (!out->append(name, nameLen))
return false;
}
if (!out->append("("))
return false;
for (unsigned i = 0; i < nargs; i++) {
@ -4334,15 +4341,32 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain,
if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope))
return false;
size_t nameLen = 0;
bool isInvalidName = false;
RootedAtom nameAtom(cx);
if (name) {
nameLen = strlen(name);
nameAtom = Atomize(cx, name, nameLen);
if (!nameAtom)
return false;
// If name is not valid identifier
if (!js::frontend::IsIdentifier(name, nameLen))
isInvalidName = true;
}
uint32_t parameterListEnd;
StringBuffer funStr(cx);
if (!BuildFunctionString(nargs, argnames, srcBuf, &funStr, &parameterListEnd))
if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf,
&funStr, &parameterListEnd)) {
return false;
}
size_t newLen = funStr.length();
SourceBufferHolder newSrcBuf(funStr.stealChars(), newLen, SourceBufferHolder::GiveOwnership);
return CompileFunction(cx, options, name, newSrcBuf, parameterListEnd, env, scope, fun);
return CompileFunction(cx, options, nameAtom, isInvalidName, newSrcBuf, parameterListEnd, env,
scope, fun);
}
JS_PUBLIC_API(bool)

View File

@ -781,8 +781,10 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
RootedFunction functionProto(cx, &functionProto_->as<JSFunction>());
const char* rawSource = "() {\n}";
const char* rawSource = "function () {\n}";
size_t sourceLen = strlen(rawSource);
size_t begin = 9;
MOZ_ASSERT(rawSource[begin] == '(');
mozilla::UniquePtr<char16_t[], JS::FreePolicy> source(InflateString(cx, rawSource, &sourceLen));
if (!source)
return nullptr;
@ -804,8 +806,9 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
RootedScript script(cx, JSScript::Create(cx,
options,
sourceObject,
0,
ss->length()));
begin,
ss->length(),
0));
if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto))
return nullptr;
@ -985,53 +988,62 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
}
}
if (fun->isAsync()) {
if (!out.append("async "))
return nullptr;
}
bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() ||
fun->isGetter() || fun->isSetter();
bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow();
bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin();
// If we're not in pretty mode, put parentheses around lambda functions and methods.
if (haveSource && !prettyPrint && funIsMethodOrNonArrowLambda) {
// If we're not in pretty mode, put parentheses around lambda functions
// so that eval returns lambda, not function statement.
if (haveSource && !prettyPrint && funIsNonArrowLambda) {
if (!out.append("("))
return nullptr;
}
if (!fun->isArrow()) {
bool ok;
if (fun->isStarGenerator() && !fun->isAsync())
ok = out.append("function* ");
else
ok = out.append("function ");
if (!ok)
return nullptr;
}
if (fun->explicitName()) {
if (!out.append(fun->explicitName()))
return nullptr;
}
if (haveSource && !script->scriptSource()->hasSourceData() &&
!JSScript::loadSource(cx, script->scriptSource(), &haveSource))
{
return nullptr;
}
auto AppendPrelude = [&out, &fun]() {
if (fun->isAsync()) {
if (!out.append("async "))
return false;
}
if (!fun->isArrow()) {
if (!out.append("function"))
return false;
if (fun->isStarGenerator()) {
if (!out.append('*'))
return false;
}
}
if (fun->explicitName()) {
if (!out.append(' '))
return false;
if (!out.append(fun->explicitName()))
return false;
}
return true;
};
if (haveSource) {
Rooted<JSFlatString*> src(cx, script->sourceData(cx));
Rooted<JSFlatString*> src(cx, script->sourceDataWithPrelude(cx));
if (!src)
return nullptr;
if (!out.append(src))
return nullptr;
if (!prettyPrint && funIsMethodOrNonArrowLambda) {
if (!prettyPrint && funIsNonArrowLambda) {
if (!out.append(")"))
return nullptr;
}
} else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) {
if (!out.append("() {\n ") ||
if (!AppendPrelude() ||
!out.append("() {\n ") ||
!out.append("[sourceless code]") ||
!out.append("\n}"))
{
@ -1042,13 +1054,15 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
bool derived = fun->infallibleIsDefaultClassConstructor(cx);
if (derived && fun->isDerivedClassConstructor()) {
if (!out.append("(...args) {\n ") ||
if (!AppendPrelude() ||
!out.append("(...args) {\n ") ||
!out.append("super(...args);\n}"))
{
return nullptr;
}
} else {
if (!out.append("() {\n "))
if (!AppendPrelude() ||
!out.append("() {\n "))
return nullptr;
if (!derived) {
@ -1635,7 +1649,18 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
StringBuffer sb(cx);
if (!sb.append('('))
if (isAsync) {
if (!sb.append("async "))
return false;
}
if (!sb.append("function"))
return false;
if (isStarGenerator && !isAsync) {
if (!sb.append('*'))
return false;
}
if (!sb.append(" anonymous("))
return false;
if (args.length() > 1) {
@ -1656,12 +1681,15 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
if (i < args.length() - 2) {
// Step 9.d.iii.
if (!sb.append(", "))
if (!sb.append(","))
return false;
}
}
}
if (!sb.append('\n'))
return false;
// Remember the position of ")".
Maybe<uint32_t> parameterListEnd = Some(uint32_t(sb.length()));
MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')');

View File

@ -235,6 +235,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
{
uint32_t begin = script->sourceStart();
uint32_t end = script->sourceEnd();
uint32_t preludeStart = script->preludeStart();
uint32_t lineno = script->lineno();
uint32_t column = script->column();
@ -242,6 +243,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
packedFields = lazy->packedFields();
MOZ_ASSERT(begin == lazy->begin());
MOZ_ASSERT(end == lazy->end());
MOZ_ASSERT(preludeStart == lazy->preludeStart());
MOZ_ASSERT(lineno == lazy->lineno());
MOZ_ASSERT(column == lazy->column());
// We can assert we have no inner functions because we don't
@ -255,7 +257,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
if (mode == XDR_DECODE) {
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
packedFields, begin, end, lineno, column));
packedFields, begin, end, preludeStart, lineno, column));
// As opposed to XDRLazyScript, we need to restore the runtime bits
// of the script, as we are trying to match the fact this function
@ -517,7 +519,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
}
script = JSScript::Create(cx, options, sourceObject, 0, 0);
script = JSScript::Create(cx, options, sourceObject, 0, 0, 0);
if (!script)
return false;
@ -600,6 +602,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
return false;
if (!xdr->codeUint32(&script->sourceEnd_))
return false;
if (!xdr->codeUint32(&script->preludeStart_))
return false;
if (!xdr->codeUint32(&lineno) ||
!xdr->codeUint32(&column) ||
@ -930,6 +934,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
{
uint32_t begin;
uint32_t end;
uint32_t preludeStart;
uint32_t lineno;
uint32_t column;
uint64_t packedFields;
@ -943,12 +948,14 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
begin = lazy->begin();
end = lazy->end();
preludeStart = lazy->preludeStart();
lineno = lazy->lineno();
column = lazy->column();
packedFields = lazy->packedFields();
}
if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
!xdr->codeUint32(&preludeStart) ||
!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
!xdr->codeUint64(&packedFields))
{
@ -957,7 +964,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
if (mode == XDR_DECODE) {
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
packedFields, begin, end, lineno, column));
packedFields, begin, end, preludeStart, lineno, column));
if (!lazy)
return false;
fun->initLazyScript(lazy);
@ -1430,6 +1437,13 @@ JSScript::sourceData(JSContext* cx)
return scriptSource()->substring(cx, sourceStart(), sourceEnd());
}
JSFlatString*
JSScript::sourceDataWithPrelude(JSContext* cx)
{
MOZ_ASSERT(scriptSource()->hasSourceData());
return scriptSource()->substring(cx, preludeStart(), sourceEnd());
}
UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry()
: cache_(nullptr), sourceChunk_()
{
@ -2428,7 +2442,8 @@ JSScript::initCompartment(ExclusiveContext* cx)
/* static */ JSScript*
JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd)
HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd,
uint32_t preludeStart)
{
MOZ_ASSERT(bufStart <= bufEnd);
@ -2450,6 +2465,7 @@ JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
script->setSourceObject(sourceObject);
script->sourceStart_ = bufStart;
script->sourceEnd_ = bufEnd;
script->preludeStart_ = preludeStart;
return script;
}
@ -3382,7 +3398,8 @@ CreateEmptyScriptForClone(JSContext* cx, HandleScript src)
.setNoScriptRval(src->noScriptRval())
.setVersion(src->getVersion());
return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd());
return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(),
src->preludeStart());
}
JSScript*
@ -3932,7 +3949,8 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
}
LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
uint32_t begin, uint32_t end,
uint32_t preludeStart, uint32_t lineno, uint32_t column)
: script_(nullptr),
function_(fun),
enclosingScope_(nullptr),
@ -3941,6 +3959,7 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
packedFields_(packedFields),
begin_(begin),
end_(end),
preludeStart_(preludeStart),
lineno_(lineno),
column_(column)
{
@ -3990,7 +4009,7 @@ LazyScript::maybeForwardedScriptSource() const
/* static */ LazyScript*
LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
uint64_t packedFields, uint32_t begin, uint32_t end,
uint32_t lineno, uint32_t column)
uint32_t preludeStart, uint32_t lineno, uint32_t column)
{
union {
PackedView p;
@ -4018,7 +4037,8 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
cx->compartment()->scheduleDelazificationForDebugger();
return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column);
return new (res) LazyScript(fun, table.forget(), packed, begin, end,
preludeStart, lineno, column);
}
/* static */ LazyScript*
@ -4026,7 +4046,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
const frontend::AtomVector& closedOverBindings,
Handle<GCVector<JSFunction*, 8>> innerFunctions,
JSVersion version,
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
uint32_t begin, uint32_t end,
uint32_t preludeStart, uint32_t lineno, uint32_t column)
{
union {
PackedView p;
@ -4049,7 +4070,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
p.isDerivedClassConstructor = false;
p.needsHomeObject = false;
LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, preludeStart,
lineno, column);
if (!res)
return nullptr;
@ -4070,7 +4092,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
HandleScript script, HandleScope enclosingScope,
HandleScript enclosingScript,
uint64_t packedFields, uint32_t begin, uint32_t end,
uint32_t lineno, uint32_t column)
uint32_t preludeStart, uint32_t lineno, uint32_t column)
{
// Dummy atom which is not a valid property name.
RootedAtom dummyAtom(cx, cx->names().comma);
@ -4079,7 +4101,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
// holding this lazy script.
HandleFunction dummyFun = fun;
LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, preludeStart,
lineno, column);
if (!res)
return nullptr;

View File

@ -575,10 +575,6 @@ class ScriptSource
introductionOffset_ = offset;
hasIntroductionOffset_ = true;
}
uint32_t parameterListEnd() const {
return parameterListEnd_;
}
};
class ScriptSourceHolder
@ -857,9 +853,19 @@ class JSScript : public js::gc::TenuredCell
uint32_t bodyScopeIndex_; /* index into the scopes array of the body scope */
/* Range of characters in scriptSource which contains this script's source. */
// Range of characters in scriptSource which contains this script's source.
// each field points the following location.
//
// function * f(a, b) { return a + b; }
// ^ ^ ^
// | | |
// | sourceStart_ sourceEnd_
// |
// preludeStart_
//
uint32_t sourceStart_;
uint32_t sourceEnd_;
uint32_t preludeStart_;
// Number of times the script has been called or has had backedges taken.
// When running in ion, also increased for any inlined scripts. Reset if
@ -1020,7 +1026,7 @@ class JSScript : public js::gc::TenuredCell
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
#if JS_BITS_PER_WORD == 32
// Currently no padding is needed.
uint32_t padding;
#endif
//
@ -1031,7 +1037,7 @@ class JSScript : public js::gc::TenuredCell
static JSScript* Create(js::ExclusiveContext* cx,
const JS::ReadOnlyCompileOptions& options,
js::HandleObject sourceObject, uint32_t sourceStart,
uint32_t sourceEnd);
uint32_t sourceEnd, uint32_t preludeStart);
void initCompartment(js::ExclusiveContext* cx);
@ -1178,6 +1184,10 @@ class JSScript : public js::gc::TenuredCell
return sourceEnd_;
}
size_t preludeStart() const {
return preludeStart_;
}
bool noScriptRval() const {
return noScriptRval_;
}
@ -1501,7 +1511,8 @@ class JSScript : public js::gc::TenuredCell
bool mayReadFrameArgsDirectly();
JSFlatString* sourceData(JSContext* cx);
JSFlatString* sourceDataWithPrelude(JSContext* cx);
static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked);
void setSourceObject(JSObject* object);
@ -1920,7 +1931,8 @@ class LazyScript : public gc::TenuredCell
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
#if JS_BITS_PER_WORD == 32
uint32_t padding;
// uint32_t padding;
// Currently no padding is needed.
#endif
private:
@ -1960,20 +1972,25 @@ class LazyScript : public gc::TenuredCell
};
// Source location for the script.
// See the comment in JSScript for the details.
uint32_t begin_;
uint32_t end_;
uint32_t preludeStart_;
// Line and column of |begin_| position, that is the position where we
// start parsing.
uint32_t lineno_;
uint32_t column_;
LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
uint32_t begin, uint32_t end, uint32_t preludeStart,
uint32_t lineno, uint32_t column);
// Create a LazyScript without initializing the closedOverBindings and the
// innerFunctions. To be GC-safe, the caller must initialize both vectors
// with valid atoms and functions.
static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun,
uint64_t packedData, uint32_t begin, uint32_t end,
uint32_t lineno, uint32_t column);
uint32_t preludeStart, uint32_t lineno, uint32_t column);
public:
static const uint32_t NumClosedOverBindingsLimit = 1 << NumClosedOverBindingsBits;
@ -1985,7 +2002,7 @@ class LazyScript : public gc::TenuredCell
const frontend::AtomVector& closedOverBindings,
Handle<GCVector<JSFunction*, 8>> innerFunctions,
JSVersion version, uint32_t begin, uint32_t end,
uint32_t lineno, uint32_t column);
uint32_t preludeStart, uint32_t lineno, uint32_t column);
// Create a LazyScript and initialize the closedOverBindings and the
// innerFunctions with dummy values to be replaced in a later initialization
@ -2000,7 +2017,7 @@ class LazyScript : public gc::TenuredCell
HandleScript script, HandleScope enclosingScope,
HandleScript enclosingScript,
uint64_t packedData, uint32_t begin, uint32_t end,
uint32_t lineno, uint32_t column);
uint32_t preludeStart, uint32_t lineno, uint32_t column);
void initRuntimeFields(uint64_t packedFields);
@ -2173,6 +2190,9 @@ class LazyScript : public gc::TenuredCell
uint32_t end() const {
return end_;
}
uint32_t preludeStart() const {
return preludeStart_;
}
uint32_t lineno() const {
return lineno_;
}
@ -2199,7 +2219,8 @@ class LazyScript : public gc::TenuredCell
};
/* If this fails, add/remove padding within LazyScript. */
JS_STATIC_ASSERT(sizeof(LazyScript) % js::gc::CellSize == 0);
static_assert(sizeof(LazyScript) % js::gc::CellSize == 0,
"Size of LazyScript must be an integral multiple of js::gc::CellSize");
struct ScriptAndCounts
{

View File

@ -0,0 +1,370 @@
var BUGNUMBER = 1317400;
var summary = "Function string representation in Object.prototype.toSource";
print(BUGNUMBER + ": " + summary);
// Methods.
assertEq(({ foo(){} }).toSource(),
"({foo(){}})");
assertEq(({ *foo(){} }).toSource(),
"({*foo(){}})");
assertEq(({ async foo(){} }).toSource(),
"({async foo(){}})");
assertEq(({ 1(){} }).toSource(),
"({1(){}})");
// Methods with more spacing.
// Spacing is kept.
assertEq(({ foo (){} }).toSource(),
"({foo (){}})");
assertEq(({ foo () {} }).toSource(),
"({foo () {}})");
// Methods with computed name.
// Method syntax is composed.
let name = "foo";
assertEq(({ [name](){} }).toSource(),
"({foo(){}})");
assertEq(({ *[name](){} }).toSource(),
"({*foo(){}})");
assertEq(({ async [name](){} }).toSource(),
"({async foo(){}})");
assertEq(({ [ Symbol.iterator ](){} }).toSource(),
"({[Symbol.iterator](){}})");
// Accessors.
assertEq(({ get foo(){} }).toSource(),
"({get foo(){}})");
assertEq(({ set foo(v){} }).toSource(),
"({set foo(v){}})");
// Accessors with computed name.
// Method syntax is composed.
assertEq(({ get [name](){} }).toSource(),
"({get foo(){}})");
assertEq(({ set [name](v){} }).toSource(),
"({set foo(v){}})");
assertEq(({ get [ Symbol.iterator ](){} }).toSource(),
"({get [Symbol.iterator](){}})");
assertEq(({ set [ Symbol.iterator ](v){} }).toSource(),
"({set [Symbol.iterator](v){}})");
// Getter and setter with same name.
// Getter always comes before setter.
assertEq(({ get foo(){}, set foo(v){} }).toSource(),
"({get foo(){}, set foo(v){}})");
assertEq(({ set foo(v){}, get foo(){} }).toSource(),
"({get foo(){}, set foo(v){}})");
// Normal properties.
assertEq(({ foo: function(){} }).toSource(),
"({foo:(function(){})})");
assertEq(({ foo: function bar(){} }).toSource(),
"({foo:(function bar(){})})");
assertEq(({ foo: function*(){} }).toSource(),
"({foo:(function*(){})})");
assertEq(({ foo: async function(){} }).toSource(),
"({foo:(async function(){})})");
// Normal properties with computed name.
assertEq(({ [ Symbol.iterator ]: function(){} }).toSource(),
"({[Symbol.iterator]:(function(){})})");
// Dynamically defined properties with function expression.
// Never become a method syntax.
let obj = {};
obj.foo = function() {};
assertEq(obj.toSource(),
"({foo:(function() {})})");
obj = {};
Object.defineProperty(obj, "foo", {value: function() {}});
assertEq(obj.toSource(),
"({})");
obj = {};
Object.defineProperty(obj, "foo", {value: function() {}, enumerable: true});
assertEq(obj.toSource(),
"({foo:(function() {})})");
obj = {};
Object.defineProperty(obj, "foo", {value: function bar() {}, enumerable: true});
assertEq(obj.toSource(),
"({foo:(function bar() {})})");
obj = {};
Object.defineProperty(obj, Symbol.iterator, {value: function() {}, enumerable: true});
assertEq(obj.toSource(),
"({[Symbol.iterator]:(function() {})})");
// Dynamically defined property with other object's method.
// Method syntax is composed.
let method = ({foo() {}}).foo;
obj = {};
Object.defineProperty(obj, "foo", {value: method, enumerable: true});
assertEq(obj.toSource(),
"({foo() {}})");
obj = {};
Object.defineProperty(obj, "bar", {value: method, enumerable: true});
assertEq(obj.toSource(),
"({bar() {}})");
method = ({*foo() {}}).foo;
obj = {};
Object.defineProperty(obj, "bar", {value: method, enumerable: true});
assertEq(obj.toSource(),
"({*bar() {}})");
method = ({async foo() {}}).foo;
obj = {};
Object.defineProperty(obj, "bar", {value: method, enumerable: true});
assertEq(obj.toSource(),
"({async bar() {}})");
// Dynamically defined accessors.
// Accessor syntax is composed.
obj = {};
Object.defineProperty(obj, "foo", {get: function() {}, enumerable: true});
assertEq(obj.toSource(),
"({get foo() {}})");
obj = {};
Object.defineProperty(obj, "foo", {set: function() {}, enumerable: true});
assertEq(obj.toSource(),
"({set foo() {}})");
obj = {};
Object.defineProperty(obj, Symbol.iterator, {get: function() {}, enumerable: true});
assertEq(obj.toSource(),
"({get [Symbol.iterator]() {}})");
obj = {};
Object.defineProperty(obj, Symbol.iterator, {set: function() {}, enumerable: true});
assertEq(obj.toSource(),
"({set [Symbol.iterator]() {}})");
// Dynamically defined accessors with other object's accessors.
// Accessor syntax is composed.
let accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
obj = {};
Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
assertEq(obj.toSource(),
"({get foo() {}})");
accessor = Object.getOwnPropertyDescriptor({ get bar() {} }, "bar").get;
obj = {};
Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
assertEq(obj.toSource(),
"({get foo() {}})");
accessor = Object.getOwnPropertyDescriptor({ set foo(v) {} }, "foo").set;
obj = {};
Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
assertEq(obj.toSource(),
"({get foo(v) {}})");
accessor = Object.getOwnPropertyDescriptor({ set bar(v) {} }, "bar").set;
obj = {};
Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
assertEq(obj.toSource(),
"({get foo(v) {}})");
accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
obj = {};
Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
assertEq(obj.toSource(),
"({set foo() {}})");
accessor = Object.getOwnPropertyDescriptor({ get bar() {} }, "bar").get;
obj = {};
Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
assertEq(obj.toSource(),
"({set foo() {}})");
accessor = Object.getOwnPropertyDescriptor({ set foo(v) {} }, "foo").set;
obj = {};
Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
assertEq(obj.toSource(),
"({set foo(v) {}})");
accessor = Object.getOwnPropertyDescriptor({ set bar(v) {} }, "bar").set;
obj = {};
Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
assertEq(obj.toSource(),
"({set foo(v) {}})");
// Methods with proxy.
// Treated as normal property.
method = ({foo() {}}).foo;
let handler = {
get(that, name) {
if (name == "toSource") {
return function() {
return that.toSource();
};
}
return that[name];
}
};
let proxy = new Proxy(method, handler);
obj = {};
Object.defineProperty(obj, "foo", {value: proxy, enumerable: true});
assertEq(obj.toSource(),
"({foo:foo() {}})");
// Accessors with proxy.
// Accessor syntax is composed.
accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
proxy = new Proxy(accessor, handler);
obj = {};
Object.defineProperty(obj, "foo", {get: proxy, enumerable: true});
assertEq(obj.toSource(),
"({get foo() {}})");
obj = {};
Object.defineProperty(obj, "foo", {set: proxy, enumerable: true});
assertEq(obj.toSource(),
"({set foo() {}})");
// Methods from other global.
// Treated as normal property.
let g = newGlobal();
method = g.eval("({ foo() {} }).foo");
obj = {};
Object.defineProperty(obj, "foo", {value: method, enumerable: true});
assertEq(obj.toSource(),
"({foo:foo() {}})");
// Accessors from other global.
// Accessor syntax is composed.
accessor = g.eval("Object.getOwnPropertyDescriptor({ get foo() {} }, 'foo').get");
obj = {};
Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
assertEq(obj.toSource(),
"({get foo() {}})");
accessor = g.eval("Object.getOwnPropertyDescriptor({ get bar() {} }, 'bar').get");
obj = {};
Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
assertEq(obj.toSource(),
"({get foo() {}})");
accessor = g.eval("Object.getOwnPropertyDescriptor({ set foo(v) {} }, 'foo').set");
obj = {};
Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
assertEq(obj.toSource(),
"({get foo(v) {}})");
accessor = g.eval("Object.getOwnPropertyDescriptor({ set bar(v) {} }, 'bar').set");
obj = {};
Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
assertEq(obj.toSource(),
"({get foo(v) {}})");
accessor = g.eval("Object.getOwnPropertyDescriptor({ get foo() {} }, 'foo').get");
obj = {};
Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
assertEq(obj.toSource(),
"({set foo() {}})");
accessor = g.eval("Object.getOwnPropertyDescriptor({ get bar() {} }, 'bar').get");
obj = {};
Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
assertEq(obj.toSource(),
"({set foo() {}})");
accessor = g.eval("Object.getOwnPropertyDescriptor({ set foo(v) {} }, 'foo').set");
obj = {};
Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
assertEq(obj.toSource(),
"({set foo(v) {}})");
accessor = g.eval("Object.getOwnPropertyDescriptor({ set bar(v) {} }, 'bar').set");
obj = {};
Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
assertEq(obj.toSource(),
"({set foo(v) {}})");
// **** Some weird cases ****
// Accessors with generator or async.
obj = {};
Object.defineProperty(obj, "foo", {get: function*() {}, enumerable: true});
assertEq(obj.toSource(),
"({get foo() {}})");
obj = {};
Object.defineProperty(obj, "foo", {set: async function() {}, enumerable: true});
assertEq(obj.toSource(),
"({set foo() {}})");
// Modified toSource.
obj = { foo() {} };
obj.foo.toSource = () => "hello";
assertEq(obj.toSource(),
"({hello})");
obj = { foo() {} };
obj.foo.toSource = () => "bar() {}";
assertEq(obj.toSource(),
"({bar() {}})");
// Modified toSource with different method name.
obj = {};
Object.defineProperty(obj, "foo", {value: function bar() {}, enumerable: true});
obj.foo.toSource = () => "hello";
assertEq(obj.toSource(),
"({foo:hello})");
obj = {};
Object.defineProperty(obj, "foo", {value: function* bar() {}, enumerable: true});
obj.foo.toSource = () => "hello";
assertEq(obj.toSource(),
"({foo:hello})");
obj = {};
Object.defineProperty(obj, "foo", {value: async function bar() {}, enumerable: true});
obj.foo.toSource = () => "hello";
assertEq(obj.toSource(),
"({foo:hello})");
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

View File

@ -109,7 +109,7 @@ function TestGeneratorFunction() {
// Doesn't matter particularly what string gets serialized, as long
// as it contains "function*" and "yield 10".
assertEq(GeneratorFunction('yield 10').toString(),
"function* anonymous() {\nyield 10\n}");
"function* anonymous(\n) {\nyield 10\n}");
}
TestGeneratorFunction();

View File

@ -94,7 +94,7 @@ with (x)
}
status = inSection(5);
actual = x.g.toString();
expect = (function () {}).toString();
expect = (function() {}).toString();
addThis();

View File

@ -6,7 +6,7 @@
//-----------------------------------------------------------------------------
var BUGNUMBER = 354945;
var summary = 'Do not crash with new Iterator';
var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value';
var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function(){ })}) returned a primitive value';
var actual;

View File

@ -20,7 +20,7 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value';
expect = 'TypeError: trap __iterator__ for ({__iterator__:(function(){ })}) returned a primitive value';
var obj = {};
obj.__iterator__ = function(){ };
try

View File

@ -20,7 +20,7 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
expect = /TypeError: anonymous generator function returns a value/;
expect = /TypeError: can't use 'yield' in a function that can return a value/;
try
{
var gen = eval('(function() { { return 5; } yield 3; })');

View File

@ -20,7 +20,7 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
expect = "generator function foo returns a value";
expect = "can't use 'yield' in a function that can return a value";
try
{
actual = 'No Error';
@ -32,7 +32,7 @@ function test()
}
reportCompare(expect, actual, summary + ": 1");
expect = "generator function foo returns a value";
expect = "generator function can't return a value";
try
{
actual = 'No Error';
@ -44,7 +44,7 @@ function test()
}
reportCompare(expect, actual, summary + ": 2");
expect = "generator function foo returns a value";
expect = "can't use 'yield' in a function that can return a value";
try
{
actual = 'No Error';
@ -56,7 +56,7 @@ function test()
}
reportCompare(expect, actual, summary + ": 3");
expect = "generator function foo returns a value";
expect = "generator function can't return a value";
try
{
actual = 'No Error';

View File

@ -1,5 +1,5 @@
var actual;
var expect = "function f() { ff (); }";
var expect = "function f () { ff (); }";
function fun() {
(new Function ("function ff () { actual = '' + ff. caller; } function f () { ff (); } f ();")) ();
}

View File

@ -318,6 +318,7 @@ struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod
// Function constructor, this will be the first character in the function
// source. Otherwise, it will be the opening parenthesis of the arguments
// list.
uint32_t preludeStart;
uint32_t srcStart;
uint32_t srcBodyStart;
bool strict;
@ -1758,6 +1759,7 @@ class MOZ_STACK_CLASS ModuleValidator
if (!asmJSMetadata_)
return false;
asmJSMetadata_->preludeStart = moduleFunctionNode_->pn_funbox->preludeStart;
asmJSMetadata_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin;
asmJSMetadata_->srcBodyStart = parser_.tokenStream.currentToken().pos.end;
asmJSMetadata_->strict = parser_.pc->sc()->strict() &&
@ -7049,6 +7051,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
TokenStream& tokenStream = m.tokenStream();
tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
uint32_t preludeStart = tokenStream.currentToken().pos.begin;
*line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end);
TokenKind tk;
@ -7061,7 +7064,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
if (!name)
return false;
ParseNode* fn = m.parser().handler.newFunctionDefinition();
ParseNode* fn = m.parser().handler.newFunctionStatement();
if (!fn)
return false;
@ -7071,7 +7074,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
ParseContext* outerpc = m.parser().pc;
Directives directives(outerpc);
FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, directives, NotGenerator,
FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, preludeStart, directives, NotGenerator,
SyncFunction, /* tryAnnexB = */ false);
if (!funbox)
return false;
@ -8054,7 +8057,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me
return false;
}
uint32_t begin = metadata.srcStart;
uint32_t begin = metadata.preludeStart;
uint32_t end = metadata.srcEndAfterCurly();
Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
if (!src)
@ -8085,7 +8088,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me
SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, stableChars.twoByteRange().length(), ownership);
SourceBufferHolder srcBuf(chars, end - begin, ownership);
if (!frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, Nothing()))
return false;
@ -8537,6 +8540,7 @@ LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loaded
return true;
// See AsmJSMetadata comment as well as ModuleValidator::init().
asmJSMetadata->preludeStart = parser.pc->functionBox()->preludeStart;
asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin;
asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end;
asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
@ -8834,7 +8838,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
MOZ_ASSERT(IsAsmJSModule(fun));
const AsmJSMetadata& metadata = AsmJSModuleFunctionToModule(fun).metadata().asAsmJS();
uint32_t begin = metadata.srcStart;
uint32_t begin = metadata.preludeStart;
uint32_t end = metadata.srcEndAfterCurly();
ScriptSource* source = metadata.scriptSource.get();
@ -8843,17 +8847,15 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
if (addParenToLambda && fun->isLambda() && !out.append("("))
return nullptr;
if (!out.append("function "))
return nullptr;
if (fun->explicitName() && !out.append(fun->explicitName()))
return nullptr;
bool haveSource = source->hasSourceData();
if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
return nullptr;
if (!haveSource) {
if (!out.append("function "))
return nullptr;
if (fun->explicitName() && !out.append(fun->explicitName()))
return nullptr;
if (!out.append("() {\n [sourceless code]\n}"))
return nullptr;
} else {

View File

@ -22,21 +22,21 @@
storage.b = 0;
assert_equals(storage.b, "0");
storage.c = function(){};
assert_equals(storage.c, "function (){}");
assert_equals(storage.c, "function(){}");
storage.setItem('d', null);
assert_equals(storage.d, "null");
storage.setItem('e', 0);
assert_equals(storage.e, "0");
storage.setItem('f', function(){});
assert_equals(storage.f, "function (){}");
assert_equals(storage.f, "function(){}");
storage['g'] = null;
assert_equals(storage.g, "null");
storage['h'] = 0;
assert_equals(storage.h, "0");
storage['i'] = function(){};
assert_equals(storage.f, "function (){}");
assert_equals(storage.f, "function(){}");
}, name + " only stores strings");
});

View File

@ -1162,7 +1162,7 @@ add_test({
self.assertTestResult(False)
self.assertInLog(TEST_FAIL_STRING)
self.assertInLog("TypeError: generator function run_test returns a value at")
self.assertInLog("TypeError: generator function can't return a value at")
self.assertInLog("test_error.js:4")
self.assertNotInLog(TEST_PASS_STRING)

View File

@ -381,12 +381,12 @@ add_task(function* log_message_with_params() {
ob = function() {};
ob.toJSON = function() {throw "oh noes JSON"};
do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
'Fail is (function () {})');
'Fail is (function() {})');
// Fall back to .toString if both .toJSON and .toSource fail.
ob.toSource = function() {throw "oh noes SOURCE"};
do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
'Fail is function () {}');
'Fail is function() {}');
// Fall back to '[object]' if .toJSON, .toSource and .toString fail.
ob.toString = function() {throw "oh noes STRING"};
@ -450,7 +450,7 @@ add_task(function* log_message_with_params() {
// doesn't cause the logger to fail.
let vOf = {a: 1, valueOf: function() {throw "oh noes valueOf"}};
do_check_eq(formatMessage("Broken valueOf ${}", vOf),
'Broken valueOf ({a:1, valueOf:(function () {throw "oh noes valueOf"})})');
'Broken valueOf ({a:1, valueOf:(function() {throw "oh noes valueOf"})})');
// Test edge cases of bad data to formatter:
// If 'params' is not an object, format it as a basic type.