diff options
author | Gaming4JC <g4jc@hyperbola.info> | 2019-07-13 21:33:52 -0400 |
---|---|---|
committer | Gaming4JC <g4jc@hyperbola.info> | 2019-07-18 22:38:41 -0400 |
commit | 53e46b1e12ef01ccaabb3256738ea1eac74b7941 (patch) | |
tree | d6d90d717876c7c15b8d851ee9ffaa6fd394939e /js | |
parent | d1c146adf23e317facd03cd5c097f12a69947392 (diff) | |
download | uxp-53e46b1e12ef01ccaabb3256738ea1eac74b7941.tar.gz |
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')
-rw-r--r-- | js/src/frontend/BytecodeCompiler.cpp | 16 | ||||
-rw-r--r-- | js/src/frontend/BytecodeEmitter.cpp | 10 | ||||
-rw-r--r-- | js/src/frontend/FullParseHandler.h | 6 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.h | 5 | ||||
-rw-r--r-- | js/src/frontend/Parser.cpp | 85 | ||||
-rw-r--r-- | js/src/frontend/Parser.h | 37 | ||||
-rw-r--r-- | js/src/frontend/SharedContext.h | 11 | ||||
-rw-r--r-- | js/src/frontend/SourceNotes.h | 3 | ||||
-rw-r--r-- | js/src/frontend/SyntaxParseHandler.h | 2 | ||||
-rw-r--r-- | js/src/jsfun.cpp | 41 | ||||
-rw-r--r-- | js/src/jsscript.cpp | 34 | ||||
-rw-r--r-- | js/src/jsscript.h | 39 | ||||
-rw-r--r-- | js/src/shell/js.cpp | 8 | ||||
-rw-r--r-- | js/src/vm/Interpreter.cpp | 25 |
14 files changed, 248 insertions, 74 deletions
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index a4b7dba6b0..e0bb64e051 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 c15d67b7c6..c788e00863 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 f0fc7700d7..7ddd8d3068 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 c0669d85ec..1f20f39887 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 d62a26f769..299b8b93ad 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 6b5ade425a..efb543efa6 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 f5a74e18c9..213a0a4613 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 dd2a95ad1e..6ae184ae4b 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 f492bab66f..895ba64169 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 470746d503..4f15e78c2a 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 ddc98ef3fc..ad4cb2338b 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 62502a3c7c..8a21d394aa 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 1e24359557..51cd11fe83 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 23a1ad2a56..ad9e87a50c 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, ®S.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); |