summaryrefslogtreecommitdiffstats
path: root/js/src/frontend
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2019-07-13 21:33:52 -0400
committerGaming4JC <g4jc@hyperbola.info>2019-07-18 22:38:41 -0400
commit53e46b1e12ef01ccaabb3256738ea1eac74b7941 (patch)
treed6d90d717876c7c15b8d851ee9ffaa6fd394939e /js/src/frontend
parentd1c146adf23e317facd03cd5c097f12a69947392 (diff)
downloadUXP-53e46b1e12ef01ccaabb3256738ea1eac74b7941.tar
UXP-53e46b1e12ef01ccaabb3256738ea1eac74b7941.tar.gz
UXP-53e46b1e12ef01ccaabb3256738ea1eac74b7941.tar.lz
UXP-53e46b1e12ef01ccaabb3256738ea1eac74b7941.tar.xz
UXP-53e46b1e12ef01ccaabb3256738ea1eac74b7941.zip
1216630 - Print class source when calling toString on the constructor.
This is accomplished in the following ways. LazyScripts and JSScripts now have 4 offsets: - Source begin and end for the actual source. This is used for lazy parsing. - toString begin and end for toString. Some kinds of functions, like async, only have a different begin offset. Class constructors have different offsets for both begin and end. For syntactically present (i.e. non-default) constructors, the class source span is remembered directly on the LazyScript or JSScript. The toString implementation then splices out the substring directly. For default constructors, a new SRC_CLASS SrcNote type is added. It's binary and has as its arguments the begin and end offsets of the class expression or statement. MakeDefaultConstructor reads the note and overrides the cloned self-hosted function's source object. This is probably the least intrusive way to accomplish this.
Diffstat (limited to 'js/src/frontend')
-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
9 files changed, 140 insertions, 35 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; }