summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp16
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp10
-rw-r--r--js/src/frontend/FullParseHandler.h6
-rw-r--r--js/src/frontend/ParseNode.h5
-rw-r--r--js/src/frontend/Parser.cpp85
-rw-r--r--js/src/frontend/Parser.h37
-rw-r--r--js/src/frontend/SharedContext.h11
-rw-r--r--js/src/frontend/SourceNotes.h3
-rw-r--r--js/src/frontend/SyntaxParseHandler.h2
-rw-r--r--js/src/jsfun.cpp41
-rw-r--r--js/src/jsscript.cpp34
-rw-r--r--js/src/jsscript.h39
-rw-r--r--js/src/shell/js.cpp8
-rw-r--r--js/src/vm/Interpreter.cpp25
14 files changed, 248 insertions, 74 deletions
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
index a4b7dba6b..e0bb64e05 100644
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -77,7 +77,13 @@ class MOZ_STACK_CLASS BytecodeCompiler
bool canLazilyParse();
bool createParser();
bool createSourceAndParser(Maybe<uint32_t> parameterListEnd = Nothing());
- bool createScript(uint32_t preludeStart = 0);
+
+ // If toString{Start,End} are not explicitly passed, assume the script's
+ // offsets in the source used to parse it are the same as what should be
+ // used to compute its Function.prototype.toString() value.
+ bool createScript();
+ bool createScript(uint32_t toStringStart, uint32_t toStringEnd);
+
bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext);
bool handleParseFailure(const Directives& newDirectives);
bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment);
@@ -242,11 +248,11 @@ BytecodeCompiler::createSourceAndParser(Maybe<uint32_t> parameterListEnd /* = No
}
bool
-BytecodeCompiler::createScript(uint32_t preludeStart /* = 0 */)
+BytecodeCompiler::createScript(uint32_t preludeStart /* = 0 */, uint32_t postludeEnd /* = 0 */)
{
script = JSScript::Create(cx, options,
sourceObject, /* sourceStart = */ 0, sourceBuffer.length(),
- preludeStart);
+ preludeStart, postludeEnd);
return script != nullptr;
}
@@ -458,7 +464,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
if (fn->pn_funbox->function()->isInterpreted()) {
MOZ_ASSERT(fun == fn->pn_funbox->function());
- if (!createScript(fn->pn_funbox->preludeStart))
+ if (!createScript(fn->pn_funbox->preludeStart, fn->pn_funbox->postludeEnd))
return false;
Maybe<BytecodeEmitter> emitter;
@@ -653,7 +659,7 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
lazy->begin(), lazy->end(),
- lazy->preludeStart()));
+ lazy->preludeStart(), lazy->postludeEnd()));
if (!script)
return false;
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index c15d67b7c..c788e0086 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7846,7 +7846,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->preludeStart));
+ funbox->preludeStart,
+ funbox->postludeEnd));
if (!script)
return false;
@@ -10226,6 +10227,13 @@ BytecodeEmitter::emitClass(ParseNode* pn)
return false;
}
} else {
+ // In the case of default class constructors, emit the start and end
+ // offsets in the source buffer as source notes so that when we
+ // actually make the constructor during execution, we can give it the
+ // correct toString output.
+ if (!newSrcNote3(SRC_CLASS_SPAN, ptrdiff_t(pn->pn_pos.begin), ptrdiff_t(pn->pn_pos.end)))
+ return false;
+
JSAtom *name = names ? names->innerBinding()->pn_atom : cx->names().empty;
if (heritageExpression) {
if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR))
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
index f0fc7700d..7ddd8d306 100644
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -333,8 +333,10 @@ class FullParseHandler
return literal;
}
- ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock) {
- return new_<ClassNode>(name, heritage, methodBlock);
+ ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock,
+ const TokenPos& pos)
+ {
+ return new_<ClassNode>(name, heritage, methodBlock, pos);
}
ParseNode* newClassMethodList(uint32_t begin) {
return new_<ListNode>(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1));
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index c0669d85e..1f20f3988 100644
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -1309,8 +1309,9 @@ struct ClassNames : public BinaryNode {
};
struct ClassNode : public TernaryNode {
- ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock)
- : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock)
+ ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock,
+ const TokenPos& pos)
+ : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock, pos)
{
MOZ_ASSERT_IF(names, names->is<ClassNames>());
MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() ||
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index d62a26f76..299b8b93a 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -141,7 +141,8 @@ StatementKindIsBraced(StatementKind kind)
kind == StatementKind::Switch ||
kind == StatementKind::Try ||
kind == StatementKind::Catch ||
- kind == StatementKind::Finally;
+ kind == StatementKind::Finally ||
+ kind == StatementKind::Class;
}
void
@@ -472,6 +473,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac
startLine(1),
startColumn(0),
preludeStart(preludeStart),
+ postludeEnd(0),
length(0),
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
asyncKindBits_(AsyncKindAsBits(asyncKind)),
@@ -542,10 +544,16 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt
allowNewTarget_ = true;
allowSuperProperty_ = fun->allowSuperProperty();
- if (kind == DerivedClassConstructor) {
- setDerivedClassConstructor();
- allowSuperCall_ = true;
- needsThisTDZChecks_ = true;
+ if (kind == ClassConstructor || kind == DerivedClassConstructor) {
+ auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>();
+ MOZ_ASSERT(stmt);
+ stmt->setConstructorBox(this);
+
+ if (kind == DerivedClassConstructor) {
+ setDerivedClassConstructor();
+ allowSuperCall_ = true;
+ needsThisTDZChecks_ = true;
+ }
}
if (isGenexpLambda)
@@ -566,6 +574,16 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt
}
void
+FunctionBox::resetForAbortedSyntaxParse(ParseContext* enclosing, FunctionSyntaxKind kind)
+{
+ if (kind == ClassConstructor || kind == DerivedClassConstructor) {
+ auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>();
+ MOZ_ASSERT(stmt);
+ stmt->clearConstructorBoxForAbortedSyntaxParse(this);
+ }
+}
+
+void
FunctionBox::initWithEnclosingScope(Scope* enclosingScope)
{
if (!function()->isArrow()) {
@@ -3389,6 +3407,7 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct
// correctness.
parser->clearAbortedSyntaxParse();
usedNames.rewind(token);
+ funbox->resetForAbortedSyntaxParse(pc, kind);
MOZ_ASSERT_IF(parser->context->isJSContext(),
!parser->context->asJSContext()->isExceptionPending());
break;
@@ -3670,14 +3689,14 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
error(JSMSG_CURLY_AFTER_BODY);
return false;
}
- funbox->bufEnd = pos().end;
+ funbox->setEnd(pos().end);
} else {
#if !JS_HAS_EXPR_CLOSURES
MOZ_ASSERT(kind == Arrow);
#endif
if (tokenStream.hadError())
return false;
- funbox->bufEnd = pos().end;
+ funbox->setEnd(pos().end);
if (kind == Statement && !matchOrInsertSemicolonAfterExpression())
return false;
}
@@ -6935,6 +6954,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+ uint32_t classStartOffset = pos().begin;
bool savedStrictness = setLocalStrictMode(true);
TokenKind tt;
@@ -6960,16 +6980,20 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
tokenStream.ungetToken();
}
+ // Push a ParseContext::ClassStatement to keep track of the constructor
+ // funbox.
+ ParseContext::ClassStatement classStmt(pc);
+
RootedAtom propAtom(context);
// A named class creates a new lexical scope with a const binding of the
- // class name.
- Maybe<ParseContext::Statement> classStmt;
- Maybe<ParseContext::Scope> classScope;
+ // class name for the "inner name".
+ Maybe<ParseContext::Statement> innerScopeStmt;
+ Maybe<ParseContext::Scope> innerScope;
if (name) {
- classStmt.emplace(pc, StatementKind::Block);
- classScope.emplace(this);
- if (!classScope->init(pc))
+ innerScopeStmt.emplace(pc, StatementKind::Block);
+ innerScope.emplace(this);
+ if (!innerScope->init(pc))
return null();
}
@@ -6996,7 +7020,6 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!classMethods)
return null();
- bool seenConstructor = false;
for (;;) {
TokenKind tt;
if (!tokenStream.getToken(&tt))
@@ -7048,16 +7071,17 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
propType = PropertyType::GetterNoExpressionClosure;
if (propType == PropertyType::Setter)
propType = PropertyType::SetterNoExpressionClosure;
- if (!isStatic && propAtom == context->names().constructor) {
+
+ bool isConstructor = !isStatic && propAtom == context->names().constructor;
+ if (isConstructor) {
if (propType != PropertyType::Method) {
errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
return null();
}
- if (seenConstructor) {
+ if (classStmt.constructorBox()) {
errorAt(nameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor");
return null();
}
- seenConstructor = true;
propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor;
} else if (isStatic && propAtom == context->names().prototype) {
errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
@@ -7082,7 +7106,12 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!tokenStream.isCurrentTokenType(TOK_RB))
funName = propAtom;
}
- Node fn = methodDefinition(nameOffset, propType, funName);
+
+ // Calling toString on constructors need to return the source text for
+ // the entire class. The end offset is unknown at this point in
+ // parsing and will be amended when class parsing finishes below.
+ Node fn = methodDefinition(isConstructor ? classStartOffset : nameOffset,
+ propType, funName);
if (!fn)
return null();
@@ -7093,6 +7122,15 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
return null();
}
+ // Amend the postlude offset for the constructor now that we've finished
+ // parsing the class.
+ uint32_t classEndOffset = pos().end;
+ if (FunctionBox* ctorbox = classStmt.constructorBox()) {
+ if (ctorbox->function()->isInterpretedLazy())
+ ctorbox->function()->lazyScript()->setPostludeEnd(classEndOffset);
+ ctorbox->postludeEnd = classEndOffset;
+ }
+
Node nameNode = null();
Node methodsOrBlock = classMethods;
if (name) {
@@ -7104,15 +7142,15 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!innerName)
return null();
- Node classBlock = finishLexicalScope(*classScope, classMethods);
+ Node classBlock = finishLexicalScope(*innerScope, classMethods);
if (!classBlock)
return null();
methodsOrBlock = classBlock;
// Pop the inner scope.
- classScope.reset();
- classStmt.reset();
+ innerScope.reset();
+ innerScopeStmt.reset();
Node outerName = null();
if (classContext == ClassStatement) {
@@ -7132,7 +7170,8 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
- return handler.newClass(nameNode, classHeritage, methodsOrBlock);
+ return handler.newClass(nameNode, classHeritage, methodsOrBlock,
+ TokenPos(classStartOffset, classEndOffset));
}
template <class ParseHandler>
@@ -8388,7 +8427,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
uint32_t end = pos().end;
handler.setBeginPosition(comp, begin);
handler.setEndPosition(comp, end);
- genFunbox->bufEnd = end;
+ genFunbox->setEnd(end);
handler.addStatementToList(body, comp);
handler.setEndPosition(body, end);
handler.setBeginPosition(genfn, begin);
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index 6b5ade425..efb543efa 100644
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -85,6 +85,31 @@ class ParseContext : public Nestable<ParseContext>
}
};
+ class ClassStatement : public Statement
+ {
+ FunctionBox* constructorBox_;
+
+ public:
+ explicit ClassStatement(ParseContext* pc)
+ : Statement(pc, StatementKind::Class),
+ constructorBox_(nullptr)
+ { }
+
+ void clearConstructorBoxForAbortedSyntaxParse(FunctionBox* funbox) {
+ MOZ_ASSERT(constructorBox_ == funbox);
+ constructorBox_ = nullptr;
+ }
+
+ void setConstructorBox(FunctionBox* funbox) {
+ MOZ_ASSERT(!constructorBox_);
+ constructorBox_ = funbox;
+ }
+
+ FunctionBox* constructorBox() const {
+ return constructorBox_;
+ }
+ };
+
// The intra-function scope stack.
//
// Tracks declared and used names within a scope.
@@ -432,6 +457,11 @@ class ParseContext : public Nestable<ParseContext>
return Statement::findNearest<T>(innermostStatement_, predicate);
}
+ template <typename T>
+ T* findInnermostStatement() {
+ return Statement::findNearest<T>(innermostStatement_);
+ }
+
AtomVector& positionalFormalParameterNames() {
return *positionalFormalParameterNames_;
}
@@ -532,6 +562,13 @@ ParseContext::Statement::is<ParseContext::LabelStatement>() const
return kind_ == StatementKind::Label;
}
+template <>
+inline bool
+ParseContext::Statement::is<ParseContext::ClassStatement>() const
+{
+ return kind_ == StatementKind::Class;
+}
+
template <typename T>
inline T&
ParseContext::Statement::as()
diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h
index f5a74e18c..213a0a461 100644
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -38,6 +38,7 @@ enum class StatementKind : uint8_t
ForOfLoop,
DoLoop,
WhileLoop,
+ Class,
// Used only by BytecodeEmitter.
Spread
@@ -451,6 +452,7 @@ class FunctionBox : public ObjectBox, public SharedContext
uint32_t startLine;
uint32_t startColumn;
uint32_t preludeStart;
+ uint32_t postludeEnd;
uint16_t length;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
@@ -501,6 +503,7 @@ class FunctionBox : public ObjectBox, public SharedContext
void initFromLazyFunction();
void initStandaloneFunction(Scope* enclosingScope);
void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind);
+ void resetForAbortedSyntaxParse(ParseContext* enclosing, FunctionSyntaxKind kind);
ObjectBox* toObjectBox() override { return this; }
JSFunction* function() const { return &object->as<JSFunction>(); }
@@ -603,6 +606,14 @@ class FunctionBox : public ObjectBox, public SharedContext
tokenStream.srcCoords.lineNumAndColumnIndex(bufStart, &startLine, &startColumn);
}
+ void setEnd(uint32_t end) {
+ // For all functions except class constructors, the buffer and
+ // postlude ending positions are the same. Class constructors override
+ // the postlude ending position with the end of the class definition.
+ bufEnd = end;
+ postludeEnd = end;
+ }
+
void trace(JSTracer* trc) override;
};
diff --git a/js/src/frontend/SourceNotes.h b/js/src/frontend/SourceNotes.h
index dd2a95ad1..6ae184ae4 100644
--- a/js/src/frontend/SourceNotes.h
+++ b/js/src/frontend/SourceNotes.h
@@ -56,13 +56,14 @@ namespace js {
M(SRC_NEXTCASE, "nextcase", 1) /* Distance forward from one CASE in a CONDSWITCH to \
the next. */ \
M(SRC_ASSIGNOP, "assignop", 0) /* += or another assign-op follows. */ \
+ M(SRC_CLASS_SPAN, "class", 2) /* The starting and ending offsets for the class, used \
+ for toString correctness for default ctors. */ \
M(SRC_TRY, "try", 1) /* JSOP_TRY, offset points to goto at the end of the \
try block. */ \
/* All notes above here are "gettable". See SN_IS_GETTABLE below. */ \
M(SRC_COLSPAN, "colspan", 1) /* Number of columns this opcode spans. */ \
M(SRC_NEWLINE, "newline", 0) /* Bytecode follows a source newline. */ \
M(SRC_SETLINE, "setline", 1) /* A file-absolute source line number note. */ \
- M(SRC_UNUSED20, "unused20", 0) /* Unused. */ \
M(SRC_UNUSED21, "unused21", 0) /* Unused. */ \
M(SRC_UNUSED22, "unused22", 0) /* Unused. */ \
M(SRC_UNUSED23, "unused23", 0) /* Unused. */ \
diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
index f492bab66..895ba6416 100644
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -289,7 +289,7 @@ class SyntaxParseHandler
Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; }
- Node newClass(Node name, Node heritage, Node methodBlock) { return NodeGeneric; }
+ Node newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index 470746d50..4f15e78c2 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -821,7 +821,7 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
sourceObject,
begin,
ss->length(),
- 0));
+ 0, 0));
if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto))
return nullptr;
@@ -954,7 +954,13 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
}
bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow();
- bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin();
+
+ // Default class constructors are self-hosted, but have their source
+ // objects overridden to refer to the span of the class statement or
+ // expression. Non-default class constructors are never self-hosted. So,
+ // all class constructors always have source.
+ bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
+ !fun->isSelfHostedBuiltin());
// If we're not in pretty mode, put parentheses around lambda functions
// so that eval returns lambda, not function statement.
@@ -995,7 +1001,7 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
};
if (haveSource) {
- Rooted<JSFlatString*> src(cx, JSScript::sourceDataWithPrelude(cx, script));
+ Rooted<JSFlatString*> src(cx, JSScript::sourceDataForToString(cx, script));
if (!src)
return nullptr;
@@ -1015,27 +1021,18 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
return nullptr;
}
} else {
- bool derived = fun->infallibleIsDefaultClassConstructor(cx);
- if (derived && fun->isDerivedClassConstructor()) {
- if (!AppendPrelude() ||
- !out.append("(...args) {\n ") ||
- !out.append("super(...args);\n}"))
- {
- return nullptr;
- }
- } else {
- if (!AppendPrelude() ||
- !out.append("() {\n "))
- return nullptr;
+ // Default class constructors should always haveSource.
+ MOZ_ASSERT(!fun->infallibleIsDefaultClassConstructor(cx));
- if (!derived) {
- if (!out.append("[native code]"))
- return nullptr;
- }
+ if (!AppendPrelude() ||
+ !out.append("() {\n "))
+ return nullptr;
- if (!out.append("\n}"))
- return nullptr;
- }
+ if (!out.append("[native code]"))
+ return nullptr;
+
+ if (!out.append("\n}"))
+ return nullptr;
}
return out.finishString();
}
diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp
index ddc98ef3f..ad4cb2338 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -236,6 +236,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 postludeEnd = script->postludeEnd();
uint32_t lineno = script->lineno();
uint32_t column = script->column();
@@ -244,6 +245,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
MOZ_ASSERT(begin == lazy->begin());
MOZ_ASSERT(end == lazy->end());
MOZ_ASSERT(preludeStart == lazy->preludeStart());
+ MOZ_ASSERT(postludeEnd == lazy->postludeEnd());
MOZ_ASSERT(lineno == lazy->lineno());
MOZ_ASSERT(column == lazy->column());
// We can assert we have no inner functions because we don't
@@ -259,6 +261,11 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
packedFields, begin, end, preludeStart, lineno, column));
+ if (!lazy)
+ return false;
+
+ lazy->setPostludeEnd(postludeEnd);
+
// As opposed to XDRLazyScript, we need to restore the runtime bits
// of the script, as we are trying to match the fact this function
// has already been parsed and that it would need to be re-lazified.
@@ -522,7 +529,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
}
- script = JSScript::Create(cx, options, sourceObject, 0, 0, 0);
+ script = JSScript::Create(cx, options, sourceObject, 0, 0, 0, 0);
if (!script)
return false;
@@ -609,6 +616,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
return false;
if (!xdr->codeUint32(&script->preludeStart_))
return false;
+ if (!xdr->codeUint32(&script->postludeEnd_))
+ return false;
if (!xdr->codeUint32(&lineno) ||
!xdr->codeUint32(&column) ||
@@ -940,6 +949,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
uint32_t begin;
uint32_t end;
uint32_t preludeStart;
+ uint32_t postludeEnd;
uint32_t lineno;
uint32_t column;
uint64_t packedFields;
@@ -954,6 +964,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
begin = lazy->begin();
end = lazy->end();
preludeStart = lazy->preludeStart();
+ postludeEnd = lazy->postludeEnd();
lineno = lazy->lineno();
column = lazy->column();
packedFields = lazy->packedFields();
@@ -961,6 +972,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
!xdr->codeUint32(&preludeStart) ||
+ !xdr->codeUint32(&postludeEnd) ||
!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
!xdr->codeUint64(&packedFields))
{
@@ -972,6 +984,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
packedFields, begin, end, preludeStart, lineno, column));
if (!lazy)
return false;
+ lazy->setPostludeEnd(postludeEnd);
fun->initLazyScript(lazy);
}
}
@@ -1015,6 +1028,15 @@ JSScript::setSourceObject(JSObject* object)
sourceObject_ = object;
}
+void
+JSScript::setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start, uint32_t end)
+{
+ MOZ_ASSERT(isDefaultClassConstructor());
+ setSourceObject(sourceObject);
+ preludeStart_ = start;
+ postludeEnd_ = end;
+}
+
js::ScriptSourceObject&
JSScript::scriptSourceUnwrap() const {
return UncheckedUnwrap(sourceObject())->as<ScriptSourceObject>();
@@ -1443,10 +1465,10 @@ JSScript::sourceData(JSContext* cx, HandleScript script)
}
/* static */ JSFlatString*
-JSScript::sourceDataWithPrelude(JSContext* cx, HandleScript script)
+JSScript::sourceDataForToString(JSContext* cx, HandleScript script)
{
MOZ_ASSERT(script->scriptSource()->hasSourceData());
- return script->scriptSource()->substring(cx, script->preludeStart(), script->sourceEnd());
+ return script->scriptSource()->substring(cx, script->preludeStart(), script->postludeEnd());
}
UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry()
@@ -2448,7 +2470,7 @@ JSScript::initCompartment(ExclusiveContext* cx)
/* static */ JSScript*
JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd,
- uint32_t preludeStart)
+ uint32_t preludeStart, uint32_t postludeEnd)
{
MOZ_ASSERT(bufStart <= bufEnd);
@@ -2471,6 +2493,7 @@ JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
script->sourceStart_ = bufStart;
script->sourceEnd_ = bufEnd;
script->preludeStart_ = preludeStart;
+ script->postludeEnd_ = postludeEnd;
return script;
}
@@ -3407,7 +3430,7 @@ CreateEmptyScriptForClone(JSContext* cx, HandleScript src)
.setVersion(src->getVersion());
return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(),
- src->preludeStart());
+ src->preludeStart(), src->postludeEnd());
}
JSScript*
@@ -3968,6 +3991,7 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
begin_(begin),
end_(end),
preludeStart_(preludeStart),
+ postludeEnd_(end),
lineno_(lineno),
column_(column)
{
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index 62502a3c7..8a21d394a 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -863,9 +863,19 @@ class JSScript : public js::gc::TenuredCell
// |
// preludeStart_
//
+ // And, in the case of class constructors, an additional postlude offset
+ // is used for use with toString.
+ //
+ // class C { constructor() { this.field = 42; } }
+ // ^ ^ ^ ^
+ // | | | `---------`
+ // | sourceStart_ sourceEnd_ |
+ // | |
+ // preludeStart_ postludeEnd_
uint32_t sourceStart_;
uint32_t sourceEnd_;
uint32_t preludeStart_;
+ uint32_t postludeEnd_;
// 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
@@ -1027,7 +1037,7 @@ class JSScript : public js::gc::TenuredCell
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
#if JS_BITS_PER_WORD == 32
- uint32_t padding;
+ // Currently no padding is needed.
#endif
//
@@ -1037,8 +1047,9 @@ class JSScript : public js::gc::TenuredCell
public:
static JSScript* Create(js::ExclusiveContext* cx,
const JS::ReadOnlyCompileOptions& options,
- js::HandleObject sourceObject, uint32_t sourceStart,
- uint32_t sourceEnd, uint32_t preludeStart);
+ js::HandleObject sourceObject,
+ uint32_t sourceStart, uint32_t sourceEnd,
+ uint32_t preludeStart, uint32_t postludeEnd);
void initCompartment(js::ExclusiveContext* cx);
@@ -1185,10 +1196,14 @@ class JSScript : public js::gc::TenuredCell
return sourceEnd_;
}
- size_t preludeStart() const {
+ uint32_t preludeStart() const {
return preludeStart_;
}
+ uint32_t postludeEnd() const {
+ return postludeEnd_;
+ }
+
bool noScriptRval() const {
return noScriptRval_;
}
@@ -1519,7 +1534,7 @@ class JSScript : public js::gc::TenuredCell
bool mayReadFrameArgsDirectly();
static JSFlatString* sourceData(JSContext* cx, JS::HandleScript script);
- static JSFlatString* sourceDataWithPrelude(JSContext* cx, JS::HandleScript script);
+ static JSFlatString* sourceDataForToString(JSContext* cx, JS::HandleScript script);
static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked);
@@ -1534,6 +1549,8 @@ class JSScript : public js::gc::TenuredCell
const char* filename() const { return scriptSource()->filename(); }
const char* maybeForwardedFilename() const { return maybeForwardedScriptSource()->filename(); }
+ void setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start, uint32_t end);
+
public:
/* Return whether this script was compiled for 'eval' */
@@ -1939,8 +1956,7 @@ 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;
- // Currently no padding is needed.
+ uint32_t padding;
#endif
private:
@@ -1989,6 +2005,7 @@ class LazyScript : public gc::TenuredCell
uint32_t begin_;
uint32_t end_;
uint32_t preludeStart_;
+ uint32_t postludeEnd_;
// Line and column of |begin_| position, that is the position where we
// start parsing.
uint32_t lineno_;
@@ -2213,6 +2230,9 @@ class LazyScript : public gc::TenuredCell
uint32_t preludeStart() const {
return preludeStart_;
}
+ uint32_t postludeEnd() const {
+ return postludeEnd_;
+ }
uint32_t lineno() const {
return lineno_;
}
@@ -2220,6 +2240,11 @@ class LazyScript : public gc::TenuredCell
return column_;
}
+ void setPostludeEnd(uint32_t postludeEnd) {
+ MOZ_ASSERT(postludeEnd_ >= end_);
+ postludeEnd_ = postludeEnd;
+ }
+
bool hasUncompiledEnclosingScript() const;
friend class GCMarker;
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 1e2435955..51cd11fe8 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2550,6 +2550,14 @@ SrcNotes(JSContext* cx, HandleScript script, Sprinter* sp)
return false;
break;
+ case SRC_CLASS_SPAN: {
+ unsigned startOffset = GetSrcNoteOffset(sn, 0);
+ unsigned endOffset = GetSrcNoteOffset(sn, 1);
+ if (!sp->jsprintf(" %u %u", startOffset, endOffset))
+ return false;
+ break;
+ }
+
default:
MOZ_ASSERT_UNREACHABLE("unrecognized srcnote");
}
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index 23a1ad2a5..ad9e87a50 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -261,11 +261,16 @@ SetPropertyOperation(JSContext* cx, JSOp op, HandleValue lval, HandleId id, Hand
}
static JSFunction*
-MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
+MakeDefaultConstructor(JSContext* cx, HandleScript script, jsbytecode* pc, HandleObject proto)
{
+ JSOp op = JSOp(*pc);
+ JSAtom* atom = script->getAtom(pc);
bool derived = op == JSOP_DERIVEDCONSTRUCTOR;
MOZ_ASSERT(derived == !!proto);
+ jssrcnote* classNote = GetSrcNote(cx, script, pc);
+ MOZ_ASSERT(classNote && SN_TYPE(classNote) == SRC_CLASS_SPAN);
+
PropertyName* lookup = derived ? cx->names().DefaultDerivedClassConstructor
: cx->names().DefaultBaseClassConstructor;
@@ -285,6 +290,17 @@ MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
MOZ_ASSERT(ctor->infallibleIsDefaultClassConstructor(cx));
+ // Create the script now, as the source span needs to be overridden for
+ // toString. Calling toString on a class constructor must not return the
+ // source for just the constructor function.
+ JSScript *ctorScript = JSFunction::getOrCreateScript(cx, ctor);
+ if (!ctorScript)
+ return nullptr;
+ uint32_t classStartOffset = GetSrcNoteOffset(classNote, 0);
+ uint32_t classEndOffset = GetSrcNoteOffset(classNote, 1);
+ ctorScript->setDefaultClassConstructorSpan(script->sourceObject(), classStartOffset,
+ classEndOffset);
+
return ctor;
}
@@ -4174,8 +4190,8 @@ CASE(JSOP_DERIVEDCONSTRUCTOR)
MOZ_ASSERT(REGS.sp[-1].isObject());
ReservedRooted<JSObject*> proto(&rootObject0, &REGS.sp[-1].toObject());
- JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc),
- proto);
+ JSFunction* constructor = MakeDefaultConstructor(cx, script, REGS.pc, proto);
+
if (!constructor)
goto error;
@@ -4185,8 +4201,7 @@ END_CASE(JSOP_DERIVEDCONSTRUCTOR)
CASE(JSOP_CLASSCONSTRUCTOR)
{
- JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc),
- nullptr);
+ JSFunction* constructor = MakeDefaultConstructor(cx, script, REGS.pc, nullptr);
if (!constructor)
goto error;
PUSH_OBJECT(*constructor);