Update js parser
parent
0437f42ca6
commit
bafa2e7e3f
|
@ -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
|
||||
},
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}));
|
||||
|
|
|
@ -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('}'))
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
@ -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; }
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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; }})");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
let g = newGlobal();
|
||||
let dbg = new Debugger(g);
|
||||
|
||||
var text;
|
||||
var count = 0;
|
||||
dbg.onNewScript = function (script) {
|
||||
++count;
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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, ¶meterListEnd))
|
||||
if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf,
|
||||
&funStr, ¶meterListEnd)) {
|
||||
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)
|
||||
|
|
|
@ -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] == ')');
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ with (x)
|
|||
}
|
||||
status = inSection(5);
|
||||
actual = x.g.toString();
|
||||
expect = (function () {}).toString();
|
||||
expect = (function() {}).toString();
|
||||
addThis();
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; })');
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 ();")) ();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue