Bug 1216630 - Print class source when calling toString on the constructor. (r=Yoric)

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.
This commit is contained in:
Shu-yu Guo 2017-04-17 19:51:34 -07:00
Родитель 1d8b4e0ef0
Коммит 159df826fb
14 изменённых файлов: 248 добавлений и 75 удалений

Просмотреть файл

@ -61,7 +61,13 @@ class MOZ_STACK_CLASS BytecodeCompiler
bool canLazilyParse();
bool createParser();
bool createSourceAndParser(const 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);
@ -292,11 +298,11 @@ BytecodeCompiler::createSourceAndParser(const Maybe<uint32_t>& parameterListEnd
}
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;
}
@ -508,7 +514,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;
@ -729,7 +735,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;

Просмотреть файл

@ -7949,7 +7949,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;
@ -10469,6 +10470,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))

Просмотреть файл

@ -323,8 +323,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));

Просмотреть файл

@ -1311,8 +1311,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>() ||

Просмотреть файл

@ -142,7 +142,8 @@ StatementKindIsBraced(StatementKind kind)
kind == StatementKind::Switch ||
kind == StatementKind::Try ||
kind == StatementKind::Catch ||
kind == StatementKind::Finally;
kind == StatementKind::Finally ||
kind == StatementKind::Class;
}
void
@ -473,6 +474,7 @@ FunctionBox::FunctionBox(JSContext* cx, LifoAlloc& alloc, ObjectBox* traceListHe
startLine(1),
startColumn(0),
preludeStart(preludeStart),
postludeEnd(0),
length(0),
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
asyncKindBits_(AsyncKindAsBits(asyncKind)),
@ -543,10 +545,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)
{
@ -3434,6 +3452,7 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct
// correctness.
parser->clearAbortedSyntaxParse();
usedNames.rewind(token);
funbox->resetForAbortedSyntaxParse(pc, kind);
MOZ_ASSERT_IF(!parser->context->helperThread(),
!parser->context->isExceptionPending());
break;
@ -3725,14 +3744,14 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
JSMSG_CURLY_OPENED, openedPos));
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;
}
@ -7023,6 +7042,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
uint32_t classStartOffset = pos().begin;
bool savedStrictness = setLocalStrictMode(true);
TokenKind tt;
@ -7048,16 +7068,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();
}
@ -7084,7 +7108,6 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!classMethods)
return null();
bool seenConstructor = false;
for (;;) {
TokenKind tt;
if (!tokenStream.getToken(&tt))
@ -7135,16 +7158,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);
@ -7169,7 +7193,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();
@ -7180,6 +7209,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) {
@ -7191,15 +7229,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) {
@ -7219,7 +7257,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>
@ -8484,7 +8523,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);

Просмотреть файл

@ -87,6 +87,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.
@ -442,6 +467,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_;
}
@ -542,6 +572,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()

Просмотреть файл

@ -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>(); }
@ -615,6 +618,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;
};

Просмотреть файл

@ -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. */ \

Просмотреть файл

@ -287,7 +287,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; }

Просмотреть файл

@ -882,7 +882,7 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
sourceObject,
begin,
ss->length(),
0));
0, 0));
if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto))
return nullptr;
@ -1019,7 +1019,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.
@ -1060,7 +1066,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;
@ -1080,27 +1086,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();
}

Просмотреть файл

@ -238,6 +238,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();
@ -246,6 +247,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
@ -261,6 +263,10 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
RootedScriptSource sourceObject(cx, &script->scriptSourceUnwrap());
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, sourceObject,
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
@ -545,7 +551,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
}
}
script = JSScript::Create(cx, *options, sourceObject, 0, 0, 0);
script = JSScript::Create(cx, *options, sourceObject, 0, 0, 0, 0);
if (!script)
return false;
@ -636,6 +642,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
return false;
if (!xdr->codeUint32(&script->preludeStart_))
return false;
if (!xdr->codeUint32(&script->postludeEnd_))
return false;
if (!xdr->codeUint32(&lineno) ||
!xdr->codeUint32(&column) ||
@ -956,6 +964,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
uint32_t begin;
uint32_t end;
uint32_t preludeStart;
uint32_t postludeEnd;
uint32_t lineno;
uint32_t column;
uint64_t packedFields;
@ -970,6 +979,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
begin = lazy->begin();
end = lazy->end();
preludeStart = lazy->preludeStart();
postludeEnd = lazy->postludeEnd();
lineno = lazy->lineno();
column = lazy->column();
packedFields = lazy->packedFields();
@ -977,6 +987,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
!xdr->codeUint32(&preludeStart) ||
!xdr->codeUint32(&postludeEnd) ||
!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
!xdr->codeUint64(&packedFields))
{
@ -988,6 +999,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
packedFields, begin, end, preludeStart, lineno, column));
if (!lazy)
return false;
lazy->setPostludeEnd(postludeEnd);
fun->initLazyScript(lazy);
}
}
@ -1031,6 +1043,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>();
@ -1459,10 +1480,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()
@ -2549,7 +2570,7 @@ JSScript::initCompartment(JSContext* cx)
/* static */ JSScript*
JSScript::Create(JSContext* 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);
@ -2572,6 +2593,7 @@ JSScript::Create(JSContext* cx, const ReadOnlyCompileOptions& options,
script->sourceStart_ = bufStart;
script->sourceEnd_ = bufEnd;
script->preludeStart_ = preludeStart;
script->postludeEnd_ = postludeEnd;
#ifdef MOZ_VTUNE
script->vtuneMethodId_ = vtune::GenerateUniqueMethodID();
@ -3521,7 +3543,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*
@ -4074,7 +4096,7 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
uint32_t begin, uint32_t end,
uint32_t preludeStart, uint32_t lineno, uint32_t column)
uint32_t preludeStart, uint32_t lineno, uint32_t column)
: script_(nullptr),
function_(fun),
enclosingScope_(nullptr),
@ -4084,6 +4106,7 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
begin_(begin),
end_(end),
preludeStart_(preludeStart),
postludeEnd_(end),
lineno_(lineno),
column_(column)
{

Просмотреть файл

@ -949,9 +949,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_;
#ifdef MOZ_VTUNE
// Unique Method ID passed to the VTune profiler, or 0 if unset.
@ -1121,7 +1131,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
//
@ -1131,8 +1141,9 @@ class JSScript : public js::gc::TenuredCell
public:
static JSScript* Create(JSContext* 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(JSContext* cx);
@ -1283,10 +1294,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_;
}
@ -1620,7 +1635,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);
@ -1631,6 +1646,9 @@ class JSScript : public js::gc::TenuredCell
js::ScriptSourceObject& scriptSourceUnwrap() const;
js::ScriptSource* scriptSource() const;
js::ScriptSource* maybeForwardedScriptSource() const;
void setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start, uint32_t end);
bool mutedErrors() const { return scriptSource()->mutedErrors(); }
const char* filename() const { return scriptSource()->filename(); }
const char* maybeForwardedFilename() const { return maybeForwardedScriptSource()->filename(); }
@ -2058,7 +2076,7 @@ class LazyScript : public 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
private:
@ -2107,6 +2125,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_;
@ -2332,6 +2351,9 @@ class LazyScript : public gc::TenuredCell
uint32_t preludeStart() const {
return preludeStart_;
}
uint32_t postludeEnd() const {
return postludeEnd_;
}
uint32_t lineno() const {
return lineno_;
}
@ -2339,6 +2361,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;

Просмотреть файл

@ -2674,6 +2674,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");
}

Просмотреть файл

@ -246,11 +246,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;
@ -267,9 +272,19 @@ MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
ctor->setIsConstructor();
ctor->setIsClassConstructor();
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;
}
@ -4216,8 +4231,7 @@ 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;
@ -4227,8 +4241,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);