author | Tooru Fujisawa <arai_a@mac.com> |
Sat, 28 Mar 2020 11:15:43 +0000 | |
changeset 520963 | 1f3269207d8c3dc01f7d3200a9989e323e975a70 |
parent 520962 | 8a6011666e5ff1ffe800424d0b600203bee63523 |
child 520964 | d54856a882de3195adde4cbc9d0580e2fa48de01 |
push id | 37261 |
push user | aiakab@mozilla.com |
push date | Sun, 29 Mar 2020 09:54:17 +0000 |
treeherder | mozilla-central@4f16fe418e32 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jorendorff |
bugs | 1622561 |
milestone | 76.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/frontend/BytecodeControlStructures.cpp +++ b/js/src/frontend/BytecodeControlStructures.cpp @@ -3,17 +3,16 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "frontend/BytecodeControlStructures.h" #include "frontend/BytecodeEmitter.h" // BytecodeEmitter #include "frontend/EmitterScope.h" // EmitterScope -#include "frontend/SourceNotes.h" // SRC_* #include "vm/Opcodes.h" // JSOp using namespace js; using namespace js::frontend; using mozilla::Maybe; NestableControl::NestableControl(BytecodeEmitter* bce, StatementKind kind)
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -46,16 +46,17 @@ #include "frontend/ModuleSharedContext.h" // ModuleSharedContext #include "frontend/NameFunctions.h" // NameFunctions #include "frontend/NameOpEmitter.h" // NameOpEmitter #include "frontend/ObjectEmitter.h" // PropertyEmitter, ObjectEmitter, ClassEmitter #include "frontend/OptionalEmitter.h" // OptionalEmitter #include "frontend/ParseNode.h" // ParseNodeKind, ParseNode and subclasses #include "frontend/Parser.h" // Parser #include "frontend/PropOpEmitter.h" // PropOpEmitter +#include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteWriter #include "frontend/SwitchEmitter.h" // SwitchEmitter #include "frontend/TDZCheckCache.h" // TDZCheckCache #include "frontend/TryEmitter.h" // TryEmitter #include "frontend/WhileEmitter.h" // WhileEmitter #include "js/CompileOptions.h" // TransitiveCompileOptions, CompileOptions #include "vm/AsyncFunction.h" // AsyncFunctionResolveKind #include "vm/BytecodeUtil.h" // IsArgOp, IsLocalOp, SET_UINT24, SET_ICINDEX, BytecodeFallsThrough, BytecodeIsJumpTarget #include "vm/GeneratorObject.h" // AbstractGeneratorObject @@ -187,21 +188,21 @@ bool BytecodeEmitter::markStepBreakpoint if (inPrologue()) { return true; } if (!emitInstrumentation(InstrumentationKind::Breakpoint)) { return false; } - if (!newSrcNote(SRC_STEP_SEP)) { - return false; - } - - if (!newSrcNote(SRC_BREAKPOINT)) { + if (!newSrcNote(SrcNoteType::StepSep)) { + return false; + } + + if (!newSrcNote(SrcNoteType::Breakpoint)) { return false; } // We track the location of the most recent separator for use in // markSimpleBreakpoint. Note that this means that the position must already // be set before markStepBreakpoint is called. bytecodeSection().updateSeparatorPosition(); @@ -217,17 +218,17 @@ bool BytecodeEmitter::markSimpleBreakpoi // expression start, we need to skip marking it breakable in order to avoid // having two breakpoints with the same line/column position. // Note: This assumes that the position for the call has already been set. if (!bytecodeSection().isDuplicateLocation()) { if (!emitInstrumentation(InstrumentationKind::Breakpoint)) { return false; } - if (!newSrcNote(SRC_BREAKPOINT)) { + if (!newSrcNote(SrcNoteType::Breakpoint)) { return false; } } return true; } bool BytecodeEmitter::emitCheck(JSOp op, ptrdiff_t delta, @@ -498,20 +499,16 @@ bool BytecodeEmitter::emitPopN(unsigned bool BytecodeEmitter::emitCheckIsObj(CheckIsObjectKind kind) { return emit2(JSOp::CheckIsObj, uint8_t(kind)); } bool BytecodeEmitter::emitCheckIsCallable(CheckIsCallableKind kind) { return emit2(JSOp::CheckIsCallable, uint8_t(kind)); } -static inline unsigned LengthOfSetLine(unsigned line) { - return 1 /* SRC_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1); -} - /* Updates line number notes, not column notes. */ bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) { // Don't emit line/column number notes in the prologue. if (inPrologue()) { return true; } ErrorReporter* er = &parser->errorReporter(); @@ -522,33 +519,34 @@ bool BytecodeEmitter::updateLineNumberNo } if (!onThisLine) { unsigned line = er->lineAt(offset); unsigned delta = line - bytecodeSection().currentLine(); /* * Encode any change in the current source line number by using - * either several SRC_NEWLINE notes or just one SRC_SETLINE note, - * whichever consumes less space. + * either several SrcNoteType::NewLine notes or just one + * SrcNoteType::SetLine note, whichever consumes less space. * * NB: We handle backward line number deltas (possible with for * loops where the update part is emitted after the body, but its * line number is <= any line number in the body) here by letting * unsigned delta_ wrap to a very large number, which triggers a - * SRC_SETLINE. + * SrcNoteType::SetLine. */ bytecodeSection().setCurrentLine(line, offset); - if (delta >= LengthOfSetLine(line)) { - if (!newSrcNote2(SRC_SETLINE, ptrdiff_t(line))) { + if (delta >= SrcNote::SetLine::lengthFor(line)) { + if (!newSrcNote2(SrcNoteType::SetLine, + SrcNote::SetLine::toOperand(line))) { return false; } } else { do { - if (!newSrcNote(SRC_NEWLINE)) { + if (!newSrcNote(SrcNoteType::NewLine)) { return false; } } while (--delta != 0); } bytecodeSection().updateSeparatorPositionIfPresent(); } return true; @@ -569,20 +567,21 @@ bool BytecodeEmitter::updateSourceCoordN ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(bytecodeSection().lastColumn()); if (colspan != 0) { // If the column span is so large that we can't store it, then just // discard this information. This can happen with minimized or otherwise // machine-generated code. Even gigantic column numbers are still // valuable if you have a source map to relate them to something real; // but it's better to fail soft here. - if (!SN_REPRESENTABLE_COLSPAN(colspan)) { + if (!SrcNote::ColSpan::isRepresentable(colspan)) { return true; } - if (!newSrcNote2(SRC_COLSPAN, SN_COLSPAN_TO_OFFSET(colspan))) { + if (!newSrcNote2(SrcNoteType::ColSpan, + SrcNote::ColSpan::toOperand(colspan))) { return false; } bytecodeSection().setLastColumn(columnIndex, offset); bytecodeSection().updateSeparatorPositionIfPresent(); } return true; } @@ -4299,17 +4298,17 @@ bool BytecodeEmitter::emitAssignmentOrIn if (!emitAssignmentRhs(offset)) { // [stack] ... VAL? RHS return false; } } /* If += etc., emit the binary operator with a source note. */ if (isCompound) { - if (!newSrcNote(SRC_ASSIGNOP)) { + if (!newSrcNote(SrcNoteType::AssignOp)) { return false; } if (!emit1(compoundOp)) { // [stack] ... VAL return false; } } @@ -10447,26 +10446,26 @@ bool BytecodeEmitter::emitTree( default: MOZ_ASSERT(0); } return true; } -static bool AllocSrcNote(JSContext* cx, SrcNotesVector& notes, +static bool AllocSrcNote(JSContext* cx, SrcNotesVector& notes, unsigned size, unsigned* index) { size_t oldLength = notes.length(); - if (MOZ_UNLIKELY(oldLength + 1 > MaxSrcNotesLength)) { + if (MOZ_UNLIKELY(oldLength + size > MaxSrcNotesLength)) { ReportAllocationOverflow(cx); return false; } - if (!notes.growByUninitialized(1)) { + if (!notes.growByUninitialized(size)) { return false; } *index = oldLength; return true; } bool BytecodeEmitter::addTryNote(TryNoteKind kind, uint32_t stackDepth, @@ -10475,169 +10474,67 @@ bool BytecodeEmitter::addTryNote(TryNote return bytecodeSection().tryNoteList().append(kind, stackDepth, start, end); } bool BytecodeEmitter::newSrcNote(SrcNoteType type, unsigned* indexp) { // Prologue shouldn't have source notes. MOZ_ASSERT(!inPrologue()); SrcNotesVector& notes = bytecodeSection().notes(); unsigned index; - if (!AllocSrcNote(cx, notes, &index)) { - return false; - } /* * Compute delta from the last annotated bytecode's offset. If it's too * big to fit in sn, allocate one or more xdelta notes and reset sn. */ BytecodeOffset offset = bytecodeSection().offset(); ptrdiff_t delta = (offset - bytecodeSection().lastNoteOffset()).value(); bytecodeSection().setLastNoteOffset(offset); - if (delta >= SN_DELTA_LIMIT) { - do { - ptrdiff_t xdelta = std::min(delta, SN_XDELTA_MASK); - SN_MAKE_XDELTA(¬es[index], xdelta); - delta -= xdelta; - if (!AllocSrcNote(cx, notes, &index)) { - return false; - } - } while (delta >= SN_DELTA_LIMIT); - } - - /* - * Initialize type and delta, then allocate the minimum number of notes - * needed for type's arity. Usually, we won't need more, but if an offset - * does take two bytes, setSrcNoteOffset will grow notes. - */ - SN_MAKE_NOTE(¬es[index], type, delta); - for (int n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) { - if (!newSrcNote(SRC_NULL)) { - return false; - } + + auto allocator = [&](unsigned size) -> SrcNote* { + if (!AllocSrcNote(cx, notes, size, &index)) { + return nullptr; + } + return ¬es[index]; + }; + + if (!SrcNoteWriter::writeNote(type, delta, allocator)) { + return false; } if (indexp) { *indexp = index; } return true; } bool BytecodeEmitter::newSrcNote2(SrcNoteType type, ptrdiff_t offset, unsigned* indexp) { unsigned index; if (!newSrcNote(type, &index)) { return false; } - if (!setSrcNoteOffset(index, 0, BytecodeOffsetDiff(offset))) { + if (!newSrcNoteOperand(offset)) { return false; } if (indexp) { *indexp = index; } return true; } -bool BytecodeEmitter::newSrcNote3(SrcNoteType type, ptrdiff_t offset1, - ptrdiff_t offset2, unsigned* indexp) { - unsigned index; - if (!newSrcNote(type, &index)) { - return false; - } - if (!setSrcNoteOffset(index, 0, BytecodeOffsetDiff(offset1))) { - return false; - } - if (!setSrcNoteOffset(index, 1, BytecodeOffsetDiff(offset2))) { - return false; - } - if (indexp) { - *indexp = index; - } - return true; -} - -bool BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, - BytecodeOffsetDiff offset) { - ptrdiff_t offsetValue = offset.value(); - - if (!SN_REPRESENTABLE_OFFSET(offsetValue)) { +bool BytecodeEmitter::newSrcNoteOperand(ptrdiff_t operand) { + if (!SrcNote::isRepresentableOperand(operand)) { reportError(nullptr, JSMSG_NEED_DIET, js_script_str); return false; } SrcNotesVector& notes = bytecodeSection().notes(); - /* Find the offset numbered which (i.e., skip exactly which offsets). */ - jssrcnote* sn = ¬es[index]; - MOZ_ASSERT(SN_TYPE(sn) != SRC_XDELTA); - MOZ_ASSERT((int)which < js_SrcNoteSpec[SN_TYPE(sn)].arity); - for (sn++; which; sn++, which--) { - if (*sn & SN_4BYTE_OFFSET_FLAG) { - sn += 3; - } - } - - /* - * See if the new offset requires four bytes either by being too big or if - * the offset has already been inflated (in which case, we need to stay big - * to not break the srcnote encoding if this isn't the last srcnote). - */ - if (offsetValue > (ptrdiff_t)SN_4BYTE_OFFSET_MASK || - (*sn & SN_4BYTE_OFFSET_FLAG)) { - /* Maybe this offset was already set to a four-byte value. */ - if (!(*sn & SN_4BYTE_OFFSET_FLAG)) { - /* Insert three dummy bytes that will be overwritten shortly. */ - if (MOZ_UNLIKELY(notes.length() + 3 > MaxSrcNotesLength)) { - ReportAllocationOverflow(cx); - return false; - } - jssrcnote dummy = 0; - if (!(sn = notes.insert(sn, dummy)) || !(sn = notes.insert(sn, dummy)) || - !(sn = notes.insert(sn, dummy))) { - return false; - } - } - *sn++ = (jssrcnote)(SN_4BYTE_OFFSET_FLAG | (offsetValue >> 24)); - *sn++ = (jssrcnote)(offsetValue >> 16); - *sn++ = (jssrcnote)(offsetValue >> 8); - } - *sn = (jssrcnote)offsetValue; - return true; -} - -const JSSrcNoteSpec js_SrcNoteSpec[] = { -#define DEFINE_SRC_NOTE_SPEC(sym, name, arity) {name, arity}, - FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_SPEC) -#undef DEFINE_SRC_NOTE_SPEC -}; - -static int SrcNoteArity(jssrcnote* sn) { - MOZ_ASSERT(SN_TYPE(sn) < SRC_LAST); - return js_SrcNoteSpec[SN_TYPE(sn)].arity; -} - -JS_FRIEND_API unsigned js::SrcNoteLength(jssrcnote* sn) { - unsigned arity; - jssrcnote* base; - - arity = SrcNoteArity(sn); - for (base = sn++; arity; sn++, arity--) { - if (*sn & SN_4BYTE_OFFSET_FLAG) { - sn += 3; - } - } - return sn - base; -} - -JS_FRIEND_API ptrdiff_t js::GetSrcNoteOffset(jssrcnote* sn, unsigned which) { - /* Find the offset numbered which (i.e., skip exactly which offsets). */ - MOZ_ASSERT(SN_TYPE(sn) != SRC_XDELTA); - MOZ_ASSERT((int)which < SrcNoteArity(sn)); - for (sn++; which; sn++, which--) { - if (*sn & SN_4BYTE_OFFSET_FLAG) { - sn += 3; - } - } - if (*sn & SN_4BYTE_OFFSET_FLAG) { - return (ptrdiff_t)(((uint32_t)(sn[0] & SN_4BYTE_OFFSET_MASK) << 24) | - (sn[1] << 16) | (sn[2] << 8) | sn[3]); - } - return (ptrdiff_t)*sn; -} + auto allocator = [&](unsigned size) -> SrcNote* { + unsigned index; + if (!AllocSrcNote(cx, notes, size, &index)) { + return nullptr; + } + return ¬es[index]; + }; + + return SrcNoteWriter::writeOperand(operand, allocator); +}
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -347,23 +347,20 @@ struct MOZ_STACK_CLASS BytecodeEmitter { // relative to current section. MOZ_MUST_USE bool addTryNote(TryNoteKind kind, uint32_t stackDepth, BytecodeOffset start, BytecodeOffset end); // Append a new source note of the given type (and therefore size) to the // notes dynamic array, updating noteCount. Return the new note's index // within the array pointed at by current->notes as outparam. MOZ_MUST_USE bool newSrcNote(SrcNoteType type, unsigned* indexp = nullptr); - MOZ_MUST_USE bool newSrcNote2(SrcNoteType type, ptrdiff_t offset, + MOZ_MUST_USE bool newSrcNote2(SrcNoteType type, ptrdiff_t operand, unsigned* indexp = nullptr); - MOZ_MUST_USE bool newSrcNote3(SrcNoteType type, ptrdiff_t offset1, - ptrdiff_t offset2, unsigned* indexp = nullptr); - MOZ_MUST_USE bool setSrcNoteOffset(unsigned index, unsigned which, - BytecodeOffsetDiff offset); + MOZ_MUST_USE bool newSrcNoteOperand(ptrdiff_t operand); // Control whether emitTree emits a line number note. enum EmitLineNumberNote { EMIT_LINENOTE, SUPPRESS_LINENOTE }; // Emit code for the tree rooted at pn. MOZ_MUST_USE bool emitTree(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue, EmitLineNumberNote emitLineNote = EMIT_LINENOTE,
--- a/js/src/frontend/BytecodeSection.h +++ b/js/src/frontend/BytecodeSection.h @@ -19,17 +19,17 @@ #include "frontend/AbstractScopePtr.h" // AbstractScopePtr #include "frontend/BytecodeOffset.h" // BytecodeOffset #include "frontend/CompilationInfo.h" // CompilationInfo #include "frontend/JumpList.h" // JumpTarget #include "frontend/NameCollections.h" // AtomIndexMap, PooledMapPtr #include "frontend/ObjLiteral.h" // ObjLiteralCreationData #include "frontend/ParseNode.h" // BigIntLiteral -#include "frontend/SourceNotes.h" // jssrcnote +#include "frontend/SourceNotes.h" // SrcNote #include "frontend/Stencil.h" // Stencils #include "gc/Barrier.h" // GCPtrObject, GCPtrScope, GCPtrValue #include "gc/Rooting.h" // JS::Rooted #include "js/GCVariant.h" // GCPolicy<mozilla::Variant> #include "js/GCVector.h" // GCVector #include "js/TypeDecls.h" // jsbytecode, JSContext #include "js/Value.h" // JS::Vector #include "js/Vector.h" // Vector @@ -155,17 +155,17 @@ struct CGResumeOffsetList { }; static constexpr size_t MaxBytecodeLength = INT32_MAX; static constexpr size_t MaxSrcNotesLength = INT32_MAX; // Have a few inline elements, so as to avoid heap allocation for tiny // sequences. See bug 1390526. typedef Vector<jsbytecode, 64> BytecodeVector; -typedef Vector<jssrcnote, 64> SrcNotesVector; +typedef Vector<js::SrcNote, 64> SrcNotesVector; // Bytecode and all data directly associated with specific opcode/index inside // bytecode is stored in this class. class BytecodeSection { public: BytecodeSection(JSContext* cx, uint32_t lineNum); // ---- Bytecode ---- @@ -345,18 +345,18 @@ class BytecodeSection { // ---- Line and column ---- // Line number for srcnotes. // // WARNING: If this becomes out of sync with already-emitted srcnotes, // we can get undefined behavior. uint32_t currentLine_; - // Zero-based column index on currentLine_ of last SRC_COLSPAN-annotated - // opcode. + // Zero-based column index on currentLine_ of last + // SrcNoteType::ColSpan-annotated opcode. // // WARNING: If this becomes out of sync with already-emitted srcnotes, // we can get undefined behavior. uint32_t lastColumn_ = 0; // The last code unit used for srcnotes. uint32_t lastSourceOffset_ = 0;
--- a/js/src/frontend/CForEmitter.cpp +++ b/js/src/frontend/CForEmitter.cpp @@ -3,17 +3,16 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "frontend/CForEmitter.h" #include "frontend/BytecodeEmitter.h" // BytecodeEmitter #include "frontend/EmitterScope.h" // EmitterScope -#include "frontend/SourceNotes.h" // SRC_*, SrcNote #include "vm/JSScript.h" // JSTRY_LOOP #include "vm/Opcodes.h" // JSOp #include "vm/Scope.h" // ScopeKind using namespace js; using namespace js::frontend; using mozilla::Maybe;
--- a/js/src/frontend/Frontend2.cpp +++ b/js/src/frontend/Frontend2.cpp @@ -13,17 +13,17 @@ #include <stddef.h> // size_t #include <stdint.h> // uint8_t, uint32_t #include "jsapi.h" #include "frontend/AbstractScopePtr.h" // ScopeIndex #include "frontend/CompilationInfo.h" // CompilationInfo #include "frontend/smoosh_generated.h" // CVec, SmooshResult, SmooshCompileOptions, free_smoosh, run_smoosh -#include "frontend/SourceNotes.h" // jssrcnote +#include "frontend/SourceNotes.h" // SrcNote #include "frontend/Stencil.h" // ScopeCreationData #include "gc/Rooting.h" // RootedScriptSourceObject #include "js/HeapAPI.h" // JS::GCCellPtr #include "js/RootingAPI.h" // JS::Handle, JS::Rooted #include "js/TypeDecls.h" // Rooted{Script,Value,String,Object} #include "vm/JSAtom.h" // AtomizeUTF8Chars #include "vm/JSScript.h" // JSScript #include "vm/Scope.h" // BindingName @@ -80,17 +80,17 @@ class SmooshScriptStencil : public Scrip js_script_str); return false; } immutableScriptData = ImmutableScriptData::new_( cx, result_.main_offset, nfixed, uint32_t(nslots64), result_.body_scope_index, result_.num_ic_entries, result_.num_type_sets, result_.is_function, /* funLength = */ 0, mozilla::MakeSpan(result_.bytecode.data, result_.bytecode.len), - mozilla::Span<const jssrcnote>(), mozilla::Span<const uint32_t>(), + mozilla::Span<const SrcNote>(), mozilla::Span<const uint32_t>(), scopeNotes, mozilla::Span<const TryNote>()); if (!immutableScriptData) { return false; } strict = result_.strict; bindingsAccessedDynamically = result_.bindings_accessed_dynamically; hasCallSiteObj = result_.has_call_site_obj;
--- a/js/src/frontend/ObjectEmitter.cpp +++ b/js/src/frontend/ObjectEmitter.cpp @@ -6,17 +6,16 @@ #include "frontend/ObjectEmitter.h" #include "mozilla/Assertions.h" // MOZ_ASSERT #include "frontend/BytecodeEmitter.h" // BytecodeEmitter #include "frontend/IfEmitter.h" // IfEmitter #include "frontend/SharedContext.h" // SharedContext -#include "frontend/SourceNotes.h" // SRC_* #include "gc/AllocKind.h" // AllocKind #include "js/Id.h" // jsid #include "js/Value.h" // UndefinedHandleValue #include "vm/BytecodeUtil.h" // IsHiddenInitOp #include "vm/JSContext.h" // JSContext #include "vm/NativeObject.h" // NativeDefineDataProperty #include "vm/ObjectGroup.h" // TenuredObject #include "vm/Opcodes.h" // JSOp
new file mode 100644 --- /dev/null +++ b/js/src/frontend/SourceNotes.cpp @@ -0,0 +1,13 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "frontend/SourceNotes.h" + +const js::SrcNote::Spec js::SrcNote::specs_[] = { +#define DEFINE_SRC_NOTE_SPEC(sym, name, arity) {name, arity}, + FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_SPEC) +#undef DEFINE_SRC_NOTE_SPEC +};
--- a/js/src/frontend/SourceNotes.h +++ b/js/src/frontend/SourceNotes.h @@ -2,194 +2,419 @@ * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef frontend_SourceNotes_h #define frontend_SourceNotes_h -#include <stddef.h> // ptrdiff_t -#include <stdint.h> +#include "mozilla/Assertions.h" // MOZ_ASSERT -#include "jstypes.h" +#include <algorithm> // std::min +#include <stddef.h> // ptrdiff_t, size_t +#include <stdint.h> // int8_t, uint8_t, uint32_t -using jssrcnote = uint8_t; +#include "jstypes.h" // js::{Bit, BitMask} namespace js { /* * Source notes generated along with bytecode for decompiling and debugging. * A source note is a uint8_t with 4 bits of type and 4 of offset from the pc * of the previous note. If 4 bits of offset aren't enough, extended delta - * notes (SRC_XDELTA) consisting of 1 set high order bit followed by 7 offset + * notes (XDelta) consisting of 1 set high order bit followed by 7 offset * bits are emitted before the next note. Some notes have operand offsets * encoded immediately after them, in note bytes or byte-triples. * * Source Note Extended Delta * +7-6-5-4+3-2-1-0+ +7+6-5-4-3-2-1-0+ * | type | delta | |1| ext-delta | * +-------+-------+ +-+-------------+ * - * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE, - * SRC_COLSPAN, SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode. + * At most one "gettable" note (i.e., a note of type other than NewLine, + * ColSpan, SetLine, and XDelta) applies to a given bytecode. * - * NB: the js_SrcNoteSpec array in BytecodeEmitter.cpp is indexed by this - * enum, so its initializers need to match the order here. + * NB: the js::SrcNote::specs_ array is indexed by this enum, so its + * initializers need to match the order here. */ +#define FOR_EACH_SRC_NOTE_TYPE(M) \ + /* Terminates a note vector. */ \ + M(Null, "null", 0) \ + /* += or another assign-op follows. */ \ + M(AssignOp, "assignop", 0) \ + /* All notes above here are "gettable". See SrcNote::isGettable below. */ \ + M(ColSpan, "colspan", int8_t(SrcNote::ColSpan::Operands::Count)) \ + /* Bytecode follows a source newline. */ \ + M(NewLine, "newline", 0) \ + M(SetLine, "setline", int8_t(SrcNote::SetLine::Operands::Count)) \ + /* Bytecode is a recommended breakpoint. */ \ + M(Breakpoint, "breakpoint", 0) \ + /* Bytecode is the first in a new steppable area. */ \ + M(StepSep, "step-sep", 0) \ + M(Unused7, "unused", 0) \ + /* 8-15 (0b1xxx) are for extended delta notes. */ \ + M(XDelta, "xdelta", 0) + +// Note: need to add a new source note? If there's no Unused* note left, +// consider bumping SrcNoteType::XDelta to 12-15 and change +// SrcNote::XDeltaBits from 7 to 6. + +enum class SrcNoteType : uint8_t { +#define DEFINE_SRC_NOTE_TYPE(sym, name, arity) sym, + FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_TYPE) +#undef DEFINE_SRC_NOTE_TYPE + + Last, + LastGettable = AssignOp +}; + +static_assert(uint8_t(SrcNoteType::XDelta) == 8, "XDelta should be 8"); + class SrcNote { + struct Spec { + const char* name_; + int8_t arity_; + }; + + static const Spec specs_[]; + + static constexpr unsigned TypeBits = 4; + static constexpr unsigned DeltaBits = 4; + static constexpr unsigned XDeltaBits = 7; + + static constexpr uint8_t TypeMask = js::BitMask(TypeBits) << DeltaBits; + static constexpr ptrdiff_t DeltaMask = js::BitMask(DeltaBits); + static constexpr ptrdiff_t XDeltaMask = js::BitMask(XDeltaBits); + + static constexpr ptrdiff_t DeltaLimit = js::Bit(DeltaBits); + static constexpr ptrdiff_t XDeltaLimit = js::Bit(XDeltaBits); + + static constexpr inline uint8_t toShiftedTypeBits(SrcNoteType type) { + return (uint8_t(type) << DeltaBits); + } + + static inline uint8_t noteValue(SrcNoteType type, ptrdiff_t delta) { + MOZ_ASSERT((delta & DeltaMask) == delta); + return noteValueUnchecked(type, delta); + } + + static constexpr inline uint8_t noteValueUnchecked(SrcNoteType type, + ptrdiff_t delta) { + return toShiftedTypeBits(type) | (delta & DeltaMask); + } + + static inline uint8_t xDeltaValue(ptrdiff_t delta) { + return toShiftedTypeBits(SrcNoteType::XDelta) | (delta & XDeltaMask); + } + + uint8_t value_; + + constexpr explicit SrcNote(uint8_t value) : value_(value) {} + public: - // SRC_COLSPAN: Source note for arbitrary ops. + constexpr SrcNote() : value_(noteValueUnchecked(SrcNoteType::Null, 0)){}; + + SrcNote(const SrcNote& other) = default; + SrcNote& operator=(const SrcNote& other) = default; + + SrcNote(SrcNote&& other) = default; + SrcNote& operator=(SrcNote&& other) = default; + + static constexpr SrcNote terminator() { return SrcNote(); } + + private: + inline uint8_t typeBits() const { return (value_ >> DeltaBits); } + + inline bool isXDelta() const { + return typeBits() >= uint8_t(SrcNoteType::XDelta); + } + + inline bool isFourBytesOperand() const { + return value_ & FourBytesOperandFlag; + } + + // number of operands + inline unsigned arity() const { + MOZ_ASSERT(uint8_t(type()) < uint8_t(SrcNoteType::Last)); + return specs_[uint8_t(type())].arity_; + } + + public: + inline SrcNoteType type() const { + if (isXDelta()) { + return SrcNoteType::XDelta; + } + return SrcNoteType(typeBits()); + } + + // name for disassembly/debugging output + const char* name() const { + MOZ_ASSERT(uint8_t(type()) < uint8_t(SrcNoteType::Last)); + return specs_[uint8_t(type())].name_; + } + + inline bool isGettable() const { + return uint8_t(type()) <= uint8_t(SrcNoteType::LastGettable); + } + + inline bool isTerminator() const { + return value_ == uint8_t(SrcNoteType::Null); + } + + inline ptrdiff_t delta() const { + if (isXDelta()) { + return value_ & XDeltaMask; + } + return value_ & DeltaMask; + } + + private: + /* + * Operand fields follow certain notes and are frequency-encoded: an operand + * in [0,0x7f] consumes one byte, an operand in [0x80,0x7fffffff] takes four, + * and the high bit of the first byte is set. + */ + static constexpr unsigned FourBytesOperandFlag = 0x80; + static constexpr unsigned FourBytesOperandMask = 0x7f; + + static constexpr unsigned OperandBits = 31; + + public: + static constexpr size_t MaxOperand = (size_t(1) << OperandBits) - 1; + + static inline bool isRepresentableOperand(ptrdiff_t operand) { + return 0 <= operand && size_t(operand) <= MaxOperand; + } + class ColSpan { public: - enum Fields { + enum class Operands { // The column span (the diff between the column corresponds to the // current op and last known column). Span, Count }; + + private: + /* + * SrcNoteType::ColSpan values represent changes to the column number. + * Colspans are signed: negative changes arise in describing constructs like + * for(;;) loops, that generate code in non-source order. (Negative colspans + * also have a history of indicating bugs in updating ParseNodes' source + * locations.) + * + * We store colspans in operands. However, unlike normal operands, colspans + * are signed, so we truncate colspans (toOperand) for storage as + * operands, and sign-extend operands into colspans when we read them + * (fromOperand). + */ + static constexpr ptrdiff_t ColSpanSignBit = 1 << (OperandBits - 1); + static constexpr ptrdiff_t MinColSpan = -ColSpanSignBit; + static constexpr ptrdiff_t MaxColSpan = ColSpanSignBit - 1; + + static inline ptrdiff_t fromOperand(ptrdiff_t operand) { + // There should be no bits set outside the field we're going to + // sign-extend. + MOZ_ASSERT(!(operand & ~((1U << OperandBits) - 1))); + + // Sign-extend the least significant OperandBits bits. + return (operand ^ ColSpanSignBit) - ColSpanSignBit; + } + + public: + static inline bool isRepresentable(ptrdiff_t colspan) { + return MinColSpan <= colspan && colspan <= MaxColSpan; + } + + static inline ptrdiff_t toOperand(ptrdiff_t colspan) { + // Truncate the two's complement colspan, for storage as an operand. + ptrdiff_t operand = colspan & ((1U << OperandBits) - 1); + + // When we read this back, we'd better get the value we stored. + MOZ_ASSERT(fromOperand(operand) == colspan); + return operand; + } + + static inline ptrdiff_t getSpan(const SrcNote* sn); }; - // SRC_SETLINE: Source note for arbitrary ops. + class SetLine { public: - enum Fields { + enum class Operands { // The file-absolute source line number of the current op. Line, Count }; + + private: + static inline size_t fromOperand(ptrdiff_t operand) { + return size_t(operand); + } + + public: + static inline unsigned lengthFor(unsigned line) { + return 1 /* SetLine */ + (line > SrcNote::FourBytesOperandMask ? 4 : 1); + } + + static inline ptrdiff_t toOperand(size_t line) { return ptrdiff_t(line); } + + static inline size_t getLine(const SrcNote* sn); }; + + friend class SrcNoteWriter; + friend class SrcNoteReader; + friend class SrcNoteIterator; }; -// clang-format off -#define FOR_EACH_SRC_NOTE_TYPE(M) \ - M(SRC_NULL, "null", 0) /* Terminates a note vector. */ \ - M(SRC_ASSIGNOP, "assignop", 0) /* += or another assign-op follows. */ \ - /* All notes above here are "gettable". See SN_IS_GETTABLE below. */ \ - M(SRC_COLSPAN, "colspan", SrcNote::ColSpan::Count) \ - M(SRC_NEWLINE, "newline", 0) /* Bytecode follows a source newline. */ \ - M(SRC_SETLINE, "setline", SrcNote::SetLine::Count) \ - M(SRC_BREAKPOINT, "breakpoint", 0) /* Bytecode is a recommended breakpoint. */ \ - M(SRC_STEP_SEP, "step-sep", 0) /* Bytecode is the first in a new steppable area. */ \ - M(SRC_UNUSED7, "unused", 0) \ - M(SRC_XDELTA, "xdelta", 0) /* 8-15 (0b1xxx) are for extended delta notes. */ - // Note: need to add a new source note? If there's no SRC_UNUSED* note left, - // consider bumping SRC_XDELTA to 12-15 and change SN_XDELTA_BITS from 7 to 6. -// clang-format on +class SrcNoteWriter { + public: + // Write a source note with given `type`, and `delta` from the last source + // note. This writes the source note itself, and `XDelta`s if necessary. + // + // This doesn't write or allocate space for operands. + // If the source note is not nullary, the caller is responsible for calling + // `writeOperand` immediately after this. + // + // `allocator` is called with the number of bytes required to store the notes. + // `allocator` can be called multiple times for each source note. + // The last call corresponds to the source note for `type`. + template <typename T> + static bool writeNote(SrcNoteType type, ptrdiff_t delta, T allocator) { + while (delta >= SrcNote::DeltaLimit) { + ptrdiff_t xdelta = std::min(delta, SrcNote::XDeltaMask); + SrcNote* sn = allocator(1); + if (!sn) { + return false; + } + sn->value_ = SrcNote::xDeltaValue(xdelta); + delta -= xdelta; + } -enum SrcNoteType { -#define DEFINE_SRC_NOTE_TYPE(sym, name, arity) sym, - FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_TYPE) -#undef DEFINE_SRC_NOTE_TYPE + SrcNote* sn = allocator(1); + sn->value_ = SrcNote::noteValue(type, delta); + return true; + } - SRC_LAST, - SRC_LAST_GETTABLE = SRC_ASSIGNOP + // Write source note operand. + // + // `allocator` is called with the number of bytes required to store the + // operand. `allocator` is called only once. + template <typename T> + static bool writeOperand(ptrdiff_t operand, T allocator) { + if (operand > ptrdiff_t(SrcNote::FourBytesOperandMask)) { + SrcNote* sn = allocator(4); + if (!sn) { + return false; + } + + sn[0].value_ = (SrcNote::FourBytesOperandFlag | (operand >> 24)); + sn[1].value_ = operand >> 16; + sn[2].value_ = operand >> 8; + sn[3].value_ = operand; + } else { + SrcNote* sn = allocator(1); + if (!sn) { + return false; + } + + sn[0].value_ = operand; + } + + return true; + } }; -static_assert(SRC_XDELTA == 8, "SRC_XDELTA should be 8"); - -/* A source note array is terminated by an all-zero element. */ -inline void SN_MAKE_TERMINATOR(jssrcnote* sn) { *sn = SRC_NULL; } - -inline bool SN_IS_TERMINATOR(jssrcnote* sn) { return *sn == SRC_NULL; } - -} // namespace js +class SrcNoteReader { + template <typename T> + static T getOperandHead(T sn, unsigned which) { + MOZ_ASSERT(sn->type() != SrcNoteType::XDelta); + MOZ_ASSERT(uint8_t(which) < sn->arity()); -#define SN_TYPE_BITS 4 -#define SN_DELTA_BITS 4 -#define SN_XDELTA_BITS 7 - -#define SN_TYPE_MASK (js::BitMask(SN_TYPE_BITS) << SN_DELTA_BITS) -#define SN_DELTA_MASK ((ptrdiff_t)js::BitMask(SN_DELTA_BITS)) -#define SN_XDELTA_MASK ((ptrdiff_t)js::BitMask(SN_XDELTA_BITS)) - -#define SN_MAKE_NOTE(sn, t, d) \ - (*(sn) = (jssrcnote)(((t) << SN_DELTA_BITS) | ((d)&SN_DELTA_MASK))) -#define SN_MAKE_XDELTA(sn, d) \ - (*(sn) = (jssrcnote)((SRC_XDELTA << SN_DELTA_BITS) | ((d)&SN_XDELTA_MASK))) + T curr = sn + 1; + for (; which; which--) { + if (curr->isFourBytesOperand()) { + curr += 4; + } else { + curr++; + } + } + return curr; + } -#define SN_IS_XDELTA(sn) ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA) -#define SN_TYPE(sn) \ - ((js::SrcNoteType)(SN_IS_XDELTA(sn) ? SRC_XDELTA : *(sn) >> SN_DELTA_BITS)) -#define SN_SET_TYPE(sn, type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn)) -#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) <= SRC_LAST_GETTABLE) + public: + // Return the operand of source note `sn`, specified by `which`. + static ptrdiff_t getOperand(const SrcNote* sn, unsigned which) { + const SrcNote* head = getOperandHead(sn, which); -#define SN_DELTA(sn) \ - ((ptrdiff_t)(SN_IS_XDELTA(sn) ? *(sn)&SN_XDELTA_MASK : *(sn)&SN_DELTA_MASK)) -#define SN_SET_DELTA(sn, delta) \ - (SN_IS_XDELTA(sn) ? SN_MAKE_XDELTA(sn, delta) \ - : SN_MAKE_NOTE(sn, SN_TYPE(sn), delta)) + if (head->isFourBytesOperand()) { + return ptrdiff_t( + (uint32_t(head[0].value_ & SrcNote::FourBytesOperandMask) << 24) | + (uint32_t(head[1].value_) << 16) | (uint32_t(head[2].value_) << 8) | + uint32_t(head[3].value_)); + } -#define SN_DELTA_LIMIT ((ptrdiff_t)js::Bit(SN_DELTA_BITS)) -#define SN_XDELTA_LIMIT ((ptrdiff_t)js::Bit(SN_XDELTA_BITS)) + return ptrdiff_t(head[0].value_); + } +}; -/* - * Offset fields follow certain notes and are frequency-encoded: an offset in - * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffffff] takes four, and - * the high bit of the first byte is set. - */ -#define SN_4BYTE_OFFSET_FLAG 0x80 -#define SN_4BYTE_OFFSET_MASK 0x7f +/* static */ +inline ptrdiff_t SrcNote::ColSpan::getSpan(const SrcNote* sn) { + return fromOperand(SrcNoteReader::getOperand(sn, unsigned(Operands::Span))); +} -#define SN_OFFSET_BITS 31 -#define SN_MAX_OFFSET (((size_t)1 << SN_OFFSET_BITS) - 1) - -inline bool SN_REPRESENTABLE_OFFSET(ptrdiff_t offset) { - return 0 <= offset && size_t(offset) <= SN_MAX_OFFSET; +/* static */ +inline size_t SrcNote::SetLine::getLine(const SrcNote* sn) { + return fromOperand(SrcNoteReader::getOperand(sn, unsigned(Operands::Line))); } -/* - * SRC_COLSPAN values represent changes to the column number. Colspans are - * signed: negative changes arise in describing constructs like for(;;) loops, - * that generate code in non-source order. (Negative colspans also have a - * history of indicating bugs in updating ParseNodes' source locations.) - * - * We store colspans using the same variable-length encoding as offsets, - * described above. However, unlike offsets, colspans are signed, so we truncate - * colspans (SN_COLSPAN_TO_OFFSET) for storage as offsets, and sign-extend - * offsets into colspans when we read them (SN_OFFSET_TO_COLSPAN). - */ -#define SN_COLSPAN_SIGN_BIT (1 << (SN_OFFSET_BITS - 1)) -#define SN_MIN_COLSPAN (-SN_COLSPAN_SIGN_BIT) -#define SN_MAX_COLSPAN (SN_COLSPAN_SIGN_BIT - 1) +// Iterate over SrcNote array, until it hits terminator. +// +// Usage: +// for (SrcNoteIterator iter(notes); !iter.atEnd(); ++iter) { +// auto sn = *iter; // `sn` is `const SrcNote*` typed. +// ... +// } +class SrcNoteIterator { + const SrcNote* current_; -inline bool SN_REPRESENTABLE_COLSPAN(ptrdiff_t colspan) { - return SN_MIN_COLSPAN <= colspan && colspan <= SN_MAX_COLSPAN; -} + void next() { + unsigned arity = current_->arity(); + current_++; -inline ptrdiff_t SN_OFFSET_TO_COLSPAN(ptrdiff_t offset) { - // There should be no bits set outside the field we're going to sign-extend. - MOZ_ASSERT(!(offset & ~((1U << SN_OFFSET_BITS) - 1))); - // Sign-extend the least significant SN_OFFSET_BITS bits. - return (offset ^ SN_COLSPAN_SIGN_BIT) - SN_COLSPAN_SIGN_BIT; -} + for (; arity; arity--) { + if (current_->isFourBytesOperand()) { + current_ += 4; + } else { + current_++; + } + } + } -inline ptrdiff_t SN_COLSPAN_TO_OFFSET(ptrdiff_t colspan) { - // Truncate the two's complement colspan, for storage as an offset. - ptrdiff_t offset = colspan & ((1U << SN_OFFSET_BITS) - 1); - // When we read this back, we'd better get the value we stored. - MOZ_ASSERT(SN_OFFSET_TO_COLSPAN(offset) == colspan); - return offset; -} + public: + SrcNoteIterator() = delete; -#define SN_LENGTH(sn) \ - ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 : js::SrcNoteLength(sn)) -#define SN_NEXT(sn) ((sn) + SN_LENGTH(sn)) + SrcNoteIterator(const SrcNoteIterator& other) = delete; + SrcNoteIterator& operator=(const SrcNoteIterator& other) = delete; + + SrcNoteIterator(SrcNoteIterator&& other) = default; + SrcNoteIterator& operator=(SrcNoteIterator&& other) = default; + + explicit SrcNoteIterator(const SrcNote* sn) : current_(sn) {} -struct JSSrcNoteSpec { - const char* name; /* name for disassembly/debugging output */ - int8_t arity; /* number of offset operands */ -}; + bool atEnd() const { return current_->isTerminator(); } -extern JS_FRIEND_DATA const JSSrcNoteSpec js_SrcNoteSpec[]; + const SrcNote* operator*() const { return current_; } -namespace js { - -extern JS_FRIEND_API unsigned SrcNoteLength(jssrcnote* sn); + // Pre-increment + SrcNoteIterator& operator++() { + next(); + return *this; + } -/* - * Get and set the offset operand identified by which (0 for the first, etc.). - */ -extern JS_FRIEND_API ptrdiff_t GetSrcNoteOffset(jssrcnote* sn, unsigned which); + // Post-increment + SrcNoteIterator operator++(int) = delete; +}; } // namespace js #endif /* frontend_SourceNotes_h */
--- a/js/src/frontend/TryEmitter.cpp +++ b/js/src/frontend/TryEmitter.cpp @@ -5,17 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "frontend/TryEmitter.h" #include "mozilla/Assertions.h" // MOZ_ASSERT #include "frontend/BytecodeEmitter.h" // BytecodeEmitter #include "frontend/SharedContext.h" // StatementKind -#include "frontend/SourceNotes.h" // SrcNote, SRC_* #include "vm/JSScript.h" // JSTRY_CATCH, JSTRY_FINALLY #include "vm/Opcodes.h" // JSOp using namespace js; using namespace js::frontend; using mozilla::Maybe;
--- a/js/src/frontend/moz.build +++ b/js/src/frontend/moz.build @@ -56,16 +56,17 @@ UNIFIED_SOURCES += [ 'ObjectEmitter.cpp', 'ObjLiteral.cpp', 'OptionalEmitter.cpp', 'ParseContext.cpp', 'ParseNode.cpp', 'ParseNodeVerify.cpp', 'PropOpEmitter.cpp', 'SharedContext.cpp', + 'SourceNotes.cpp', 'Stencil.cpp', 'SwitchEmitter.cpp', 'TDZCheckCache.cpp', 'TokenStream.cpp', 'TryEmitter.cpp', 'WhileEmitter.cpp', ]
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -76,16 +76,17 @@ # include "frontend/BinASTParser.h" #endif // defined(JS_BUILD_BINAST) #include "frontend/CompilationInfo.h" #ifdef JS_ENABLE_SMOOSH # include "frontend/Frontend2.h" #endif #include "frontend/ModuleSharedContext.h" #include "frontend/Parser.h" +#include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteIterator #include "gc/PublicIterators.h" #include "jit/arm/Simulator-arm.h" #include "jit/InlinableNatives.h" #include "jit/Ion.h" #include "jit/JitcodeMap.h" #include "jit/JitRealm.h" #include "jit/shared/CodeGenerator-shared.h" #include "js/Array.h" // JS::NewArrayObject @@ -3007,51 +3008,52 @@ static MOZ_MUST_USE bool SrcNotes(JSCont "desc", "args") || !sp->put("---- ---- ----- ------ -------- ------\n")) { return false; } unsigned offset = 0; unsigned colspan = 0; unsigned lineno = script->lineno(); - jssrcnote* notes = script->notes(); - for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - unsigned delta = SN_DELTA(sn); + SrcNote* notes = script->notes(); + for (SrcNoteIterator iter(notes); !iter.atEnd(); ++iter) { + auto sn = *iter; + + unsigned delta = sn->delta(); offset += delta; - SrcNoteType type = SN_TYPE(sn); - const char* name = js_SrcNoteSpec[type].name; + SrcNoteType type = sn->type(); + const char* name = sn->name(); if (!sp->jsprintf("%3u: %4u %5u [%4u] %-8s", unsigned(sn - notes), lineno, offset, delta, name)) { return false; } switch (type) { - case SRC_NULL: - case SRC_ASSIGNOP: - case SRC_BREAKPOINT: - case SRC_STEP_SEP: - case SRC_XDELTA: + case SrcNoteType::Null: + case SrcNoteType::AssignOp: + case SrcNoteType::Breakpoint: + case SrcNoteType::StepSep: + case SrcNoteType::XDelta: break; - case SRC_COLSPAN: - colspan = - SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, SrcNote::ColSpan::Span)); + case SrcNoteType::ColSpan: + colspan = SrcNote::ColSpan::getSpan(sn); if (!sp->jsprintf("%d", colspan)) { return false; } break; - case SRC_SETLINE: - lineno = GetSrcNoteOffset(sn, SrcNote::SetLine::Line); + case SrcNoteType::SetLine: + lineno = SrcNote::SetLine::getLine(sn); if (!sp->jsprintf(" lineno %u", lineno)) { return false; } break; - case SRC_NEWLINE: + case SrcNoteType::NewLine: ++lineno; break; default: MOZ_ASSERT_UNREACHABLE("unrecognized srcnote"); } if (!sp->put("\n")) { return false;
--- a/js/src/vm/BytecodeUtil-inl.h +++ b/js/src/vm/BytecodeUtil-inl.h @@ -4,17 +4,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef vm_BytecodeUtil_inl_h #define vm_BytecodeUtil_inl_h #include "vm/BytecodeUtil.h" -#include "frontend/SourceNotes.h" +#include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteIterator #include "vm/JSScript.h" namespace js { static inline unsigned GetDefCount(jsbytecode* pc) { /* * Add an extra pushed value for Or/And opcodes, so that they are included * in the pushed array of stack values for type inference. @@ -117,18 +117,18 @@ class BytecodeRangeWithPosition : privat lineno(script->lineno()), column(0), sn(script->notes()), snpc(script->code()), isEntryPoint(false), isBreakpoint(false), seenStepSeparator(false), wasArtifactEntryPoint(false) { - if (!SN_IS_TERMINATOR(sn)) { - snpc += SN_DELTA(sn); + if (!sn->isTerminator()) { + snpc += sn->delta(); } updatePosition(); while (frontPC() != script->main()) { popFront(); } if (frontOpcode() != JSOp::JumpTarget) { isEntryPoint = true; @@ -170,65 +170,67 @@ class BytecodeRangeWithPosition : privat // debugger exactly which offsets represent "interesting" (to the // user) places to stop. bool frontIsEntryPoint() const { return isEntryPoint; } // Breakable points are explicitly marked by the emitter as locations where // the debugger may want to allow users to pause. bool frontIsBreakablePoint() const { return isBreakpoint; } - // Breakable step points are the first breakable point after a SRC_STEP_SEP - // note has been encountered. + // Breakable step points are the first breakable point after a + // SrcNote::StepSep note has been encountered. bool frontIsBreakableStepPoint() const { return isBreakpoint && seenStepSeparator; } private: void updatePosition() { if (isBreakpoint) { isBreakpoint = false; seenStepSeparator = false; } // Determine the current line number by reading all source notes up to // and including the current offset. jsbytecode* lastLinePC = nullptr; - while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) { - SrcNoteType type = SN_TYPE(sn); - if (type == SRC_COLSPAN) { - ptrdiff_t colspan = - SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, SrcNote::ColSpan::Span)); + SrcNoteIterator iter(sn); + for (; !iter.atEnd() && snpc <= frontPC(); + ++iter, snpc += (*iter)->delta()) { + auto sn = *iter; + + SrcNoteType type = sn->type(); + if (type == SrcNoteType::ColSpan) { + ptrdiff_t colspan = SrcNote::ColSpan::getSpan(sn); MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0); column += colspan; lastLinePC = snpc; - } else if (type == SRC_SETLINE) { - lineno = size_t(GetSrcNoteOffset(sn, SrcNote::SetLine::Line)); + } else if (type == SrcNoteType::SetLine) { + lineno = SrcNote::SetLine::getLine(sn); column = 0; lastLinePC = snpc; - } else if (type == SRC_NEWLINE) { + } else if (type == SrcNoteType::NewLine) { lineno++; column = 0; lastLinePC = snpc; - } else if (type == SRC_BREAKPOINT) { + } else if (type == SrcNoteType::Breakpoint) { isBreakpoint = true; lastLinePC = snpc; - } else if (type == SRC_STEP_SEP) { + } else if (type == SrcNoteType::StepSep) { seenStepSeparator = true; lastLinePC = snpc; } + } - sn = SN_NEXT(sn); - snpc += SN_DELTA(sn); - } + sn = *iter; isEntryPoint = lastLinePC == frontPC(); } size_t lineno; size_t column; - jssrcnote* sn; + const SrcNote* sn; jsbytecode* snpc; bool isEntryPoint; bool isBreakpoint; bool seenStepSeparator; bool wasArtifactEntryPoint; }; } // namespace js
--- a/js/src/vm/BytecodeUtil.cpp +++ b/js/src/vm/BytecodeUtil.cpp @@ -24,17 +24,17 @@ #include <string.h> #include <type_traits> #include "jsapi.h" #include "jsnum.h" #include "jstypes.h" #include "frontend/BytecodeCompiler.h" -#include "frontend/SourceNotes.h" +#include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteIterator #include "gc/PublicIterators.h" #include "js/CharacterEncoding.h" #include "js/Printf.h" #include "js/Symbol.h" #include "util/Memory.h" #include "util/StringBuffer.h" #include "util/Text.h" #include "vm/BytecodeLocation.h" @@ -1062,28 +1062,32 @@ static MOZ_MUST_USE bool DisassembleAtPC } } if (pc != nullptr) { if (!sp->put(pc == next ? "--> " : " ")) { return false; } } if (showAll) { - jssrcnote* sn = GetSrcNote(cx, script, next); + const SrcNote* sn = GetSrcNote(cx, script, next); if (sn) { - MOZ_ASSERT(!SN_IS_TERMINATOR(sn)); - jssrcnote* next = SN_NEXT(sn); - while (!SN_IS_TERMINATOR(next) && SN_DELTA(next) == 0) { - if (!sp->jsprintf("%02u\n ", SN_TYPE(sn))) { + MOZ_ASSERT(!sn->isTerminator()); + SrcNoteIterator iter(sn); + while (true) { + ++iter; + auto next = *iter; + if (!(!next->isTerminator() && next->delta() == 0)) { + break; + } + if (!sp->jsprintf("%s\n ", sn->name())) { return false; } - sn = next; - next = SN_NEXT(sn); + sn = *iter; } - if (!sp->jsprintf("%02u ", SN_TYPE(sn))) { + if (!sp->jsprintf("%s ", sn->name())) { return false; } } else { if (!sp->put(" ")) { return false; } } if (parser && parser->isReachable(next)) { @@ -1737,18 +1741,18 @@ bool ExpressionDecompiler::decompilePC(j if (const char* token = CodeToken[uint8_t(op)]) { MOZ_ASSERT(defIndex == 0); MOZ_ASSERT(CodeSpec(op).ndefs == 1); // Handle simple cases of binary and unary operators. switch (CodeSpec(op).nuses) { case 2: { - jssrcnote* sn = GetSrcNote(cx, script, pc); - if (!sn || SN_TYPE(sn) != SRC_ASSIGNOP) { + const SrcNote* sn = GetSrcNote(cx, script, pc); + if (!sn || sn->type() != SrcNoteType::AssignOp) { return write("(") && decompilePCForStackOperand(pc, -2) && write(" ") && write(token) && write(" ") && decompilePCForStackOperand(pc, -1) && write(")"); } break; } case 1: return write("(") && write(token) &&
--- a/js/src/vm/BytecodeUtil.h +++ b/js/src/vm/BytecodeUtil.h @@ -204,17 +204,17 @@ static const unsigned UINT32_INDEX_LEN = static MOZ_ALWAYS_INLINE uint32_t GET_UINT32_INDEX(const jsbytecode* pc) { return GET_UINT32(pc); } static MOZ_ALWAYS_INLINE void SET_UINT32_INDEX(jsbytecode* pc, uint32_t index) { SET_UINT32(pc, index); } -// Index limit is determined by SN_4BYTE_OFFSET_FLAG, see +// Index limit is determined by SrcNote::FourByteOffsetFlag, see // frontend/BytecodeEmitter.h. static const unsigned INDEX_LIMIT_LOG2 = 31; static const uint32_t INDEX_LIMIT = uint32_t(1) << INDEX_LIMIT_LOG2; static inline jsbytecode ARGC_HI(uint16_t argc) { return UINT16_HI(argc); } static inline jsbytecode ARGC_LO(uint16_t argc) { return UINT16_LO(argc); }
--- a/js/src/vm/Caches.h +++ b/js/src/vm/Caches.h @@ -6,17 +6,17 @@ #ifndef vm_Caches_h #define vm_Caches_h #include <new> #include "jsmath.h" -#include "frontend/SourceNotes.h" +#include "frontend/SourceNotes.h" // SrcNote #include "gc/Tracer.h" #include "js/RootingAPI.h" #include "js/TypeDecls.h" #include "js/UniquePtr.h" #include "util/Memory.h" #include "vm/ArrayObject.h" #include "vm/JSAtom.h" #include "vm/JSObject.h" @@ -27,17 +27,17 @@ namespace js { /* * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a * given pc in a script. We use the script->code pointer to tag the cache, * instead of the script address itself, so that source notes are always found * by offset from the bytecode with which they were generated. */ struct GSNCache { - typedef HashMap<jsbytecode*, jssrcnote*, PointerHasher<jsbytecode*>, + typedef HashMap<jsbytecode*, const SrcNote*, PointerHasher<jsbytecode*>, SystemAllocPolicy> Map; jsbytecode* code; Map map; GSNCache() : code(nullptr) {}
--- a/js/src/vm/CodeCoverage.cpp +++ b/js/src/vm/CodeCoverage.cpp @@ -13,16 +13,17 @@ #include <utility> #ifdef XP_WIN # include <process.h> # define getpid _getpid #else # include <unistd.h> #endif +#include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteIterator #include "gc/Zone.h" #include "util/Text.h" #include "vm/BytecodeUtil.h" #include "vm/JSScript.h" #include "vm/Realm.h" #include "vm/Runtime.h" #include "vm/Time.h" @@ -139,19 +140,19 @@ void LCovSource::writeScript(JSScript* s outFNDA_.printf("FNDA:%" PRIu64 ",%s\n", counts->numExec(), scriptName); // Set the hit count of the pre-main code to 1, if the function ever got // visited. hits = 1; } jsbytecode* snpc = script->code(); - jssrcnote* sn = script->notes(); - if (!SN_IS_TERMINATOR(sn)) { - snpc += SN_DELTA(sn); + const SrcNote* sn = script->notes(); + if (!sn->isTerminator()) { + snpc += sn->delta(); } size_t lineno = script->lineno(); jsbytecode* end = script->codeEnd(); size_t branchId = 0; bool firstLineHasBeenWritten = false; for (jsbytecode* pc = script->code(); pc != end; pc = GetNextPc(pc)) { MOZ_ASSERT(script->code() <= pc && pc < end); @@ -167,27 +168,29 @@ void LCovSource::writeScript(JSScript* s hits = counts->numExec(); } } // If we have additional source notes, walk all the source notes of the // current pc. if (snpc <= pc || !firstLineHasBeenWritten) { size_t oldLine = lineno; - while (!SN_IS_TERMINATOR(sn) && snpc <= pc) { - SrcNoteType type = SN_TYPE(sn); - if (type == SRC_SETLINE) { - lineno = size_t(GetSrcNoteOffset(sn, SrcNote::SetLine::Line)); - } else if (type == SRC_NEWLINE) { + SrcNoteIterator iter(sn); + while (!iter.atEnd() && snpc <= pc) { + sn = *iter; + SrcNoteType type = sn->type(); + if (type == SrcNoteType::SetLine) { + lineno = SrcNote::SetLine::getLine(sn); + } else if (type == SrcNoteType::NewLine) { lineno++; } - - sn = SN_NEXT(sn); - snpc += SN_DELTA(sn); + ++iter; + snpc += (*iter)->delta(); } + sn = *iter; if ((oldLine != lineno || !firstLineHasBeenWritten) && pc >= script->main() && fallsthrough) { auto p = linesHit_.lookupForAdd(lineno); if (!p) { if (!linesHit_.add(p, lineno, hits)) { hadOOM_ = true; return;
--- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -30,16 +30,17 @@ #include "jsapi.h" #include "jstypes.h" #include "frontend/BinASTRuntimeSupport.h" // BinASTSourceMetadata{,Multipart,Context} #include "frontend/BinASTTokenReaderContext.h" // HuffmanDictionaryForMetadata,AutoClearHuffmanDictionaryForMetadata #include "frontend/BytecodeCompiler.h" #include "frontend/BytecodeEmitter.h" #include "frontend/SharedContext.h" +#include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteIterator #include "gc/FreeOp.h" #include "jit/BaselineJIT.h" #include "jit/Ion.h" #include "jit/IonCode.h" #include "jit/JitOptions.h" #include "jit/JitRealm.h" #include "js/CompileOptions.h" #include "js/MemoryMetrics.h" @@ -728,17 +729,17 @@ XDRResult js::PrivateScriptData::XDR(XDR /* static */ size_t ImmutableScriptData::AllocationSize( uint32_t codeLength, uint32_t noteLength, uint32_t numResumeOffsets, uint32_t numScopeNotes, uint32_t numTryNotes) { size_t size = sizeof(ImmutableScriptData); size += sizeof(Flags); size += codeLength * sizeof(jsbytecode); - size += noteLength * sizeof(jssrcnote); + size += noteLength * sizeof(SrcNote); #ifdef DEBUG // The compact arrays need to maintain uint32_t alignment. This should have // been done by padding out source notes. MOZ_ASSERT(size % sizeof(uint32_t) == 0, "Source notes should have been padded already"); #endif @@ -852,20 +853,20 @@ ImmutableScriptData::ImmutableScriptData new (offsetToPointer<void>(cursor)) Flags{}; cursor += sizeof(Flags); static_assert(alignof(Flags) >= alignof(jsbytecode), "Incompatible alignment"); initElements<jsbytecode>(cursor, codeLength); cursor += codeLength * sizeof(jsbytecode); - static_assert(alignof(jsbytecode) >= alignof(jssrcnote), + static_assert(alignof(jsbytecode) >= alignof(SrcNote), "Incompatible alignment"); - initElements<jssrcnote>(cursor, noteLength); - cursor += noteLength * sizeof(jssrcnote); + initElements<SrcNote>(cursor, noteLength); + cursor += noteLength * sizeof(SrcNote); MOZ_ASSERT(cursor % CodeNoteAlign == 0); } // Initialization for remaining arrays. initOptionalArrays(&cursor, &flagsRef(), numResumeOffsets, numScopeNotes, numTryNotes); @@ -918,20 +919,20 @@ XDRResult ImmutableScriptData::XDR(XDRSt MOZ_TRY(xdr->codeUint32(&isd->nfixed)); MOZ_TRY(xdr->codeUint32(&isd->nslots)); MOZ_TRY(xdr->codeUint32(&isd->bodyScopeIndex)); MOZ_TRY(xdr->codeUint32(&isd->numICEntries)); MOZ_TRY(xdr->codeUint16(&isd->funLength)); MOZ_TRY(xdr->codeUint16(&isd->numBytecodeTypeSets)); static_assert(sizeof(jsbytecode) == 1); - static_assert(sizeof(jssrcnote) == 1); + static_assert(sizeof(SrcNote) == 1); jsbytecode* code = isd->code(); - jssrcnote* notes = isd->notes(); + SrcNote* notes = isd->notes(); MOZ_TRY(xdr->codeBytes(code, codeLength)); MOZ_TRY(xdr->codeBytes(notes, noteLength)); for (uint32_t& elem : isd->resumeOffsets()) { MOZ_TRY(xdr->codeUint32(&elem)); } for (ScopeNote& elem : isd->scopeNotes()) { @@ -4562,102 +4563,105 @@ js::GlobalObject& JSScript::uninlinedGlo static const uint32_t GSN_CACHE_THRESHOLD = 100; void GSNCache::purge() { code = nullptr; map.clearAndCompact(); } -jssrcnote* js::GetSrcNote(GSNCache& cache, JSScript* script, jsbytecode* pc) { +const js::SrcNote* js::GetSrcNote(GSNCache& cache, JSScript* script, + jsbytecode* pc) { size_t target = pc - script->code(); if (target >= script->length()) { return nullptr; } if (cache.code == script->code()) { GSNCache::Map::Ptr p = cache.map.lookup(pc); return p ? p->value() : nullptr; } size_t offset = 0; - jssrcnote* result; - for (jssrcnote* sn = script->notes();; sn = SN_NEXT(sn)) { - if (SN_IS_TERMINATOR(sn)) { + const js::SrcNote* result; + for (SrcNoteIterator iter(script->notes());; ++iter) { + auto sn = *iter; + if (sn->isTerminator()) { result = nullptr; break; } - offset += SN_DELTA(sn); - if (offset == target && SN_IS_GETTABLE(sn)) { + offset += sn->delta(); + if (offset == target && sn->isGettable()) { result = sn; break; } } if (cache.code != script->code() && script->length() >= GSN_CACHE_THRESHOLD) { unsigned nsrcnotes = 0; - for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - if (SN_IS_GETTABLE(sn)) { + for (SrcNoteIterator iter(script->notes()); !iter.atEnd(); ++iter) { + auto sn = *iter; + if (sn->isGettable()) { ++nsrcnotes; } } if (cache.code) { cache.map.clear(); cache.code = nullptr; } if (cache.map.reserve(nsrcnotes)) { pc = script->code(); - for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - pc += SN_DELTA(sn); - if (SN_IS_GETTABLE(sn)) { + for (SrcNoteIterator iter(script->notes()); !iter.atEnd(); ++iter) { + auto sn = *iter; + pc += sn->delta(); + if (sn->isGettable()) { cache.map.putNewInfallible(pc, sn); } } cache.code = script->code(); } } return result; } -jssrcnote* js::GetSrcNote(JSContext* cx, JSScript* script, jsbytecode* pc) { +const js::SrcNote* js::GetSrcNote(JSContext* cx, JSScript* script, + jsbytecode* pc) { return GetSrcNote(cx->caches().gsnCache, script, pc); } -unsigned js::PCToLineNumber(unsigned startLine, jssrcnote* notes, +unsigned js::PCToLineNumber(unsigned startLine, SrcNote* notes, jsbytecode* code, jsbytecode* pc, unsigned* columnp) { unsigned lineno = startLine; unsigned column = 0; /* * Walk through source notes accumulating their deltas, keeping track of * line-number notes, until we pass the note for pc's offset within * script->code. */ ptrdiff_t offset = 0; ptrdiff_t target = pc - code; - for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - offset += SN_DELTA(sn); + for (SrcNoteIterator iter(notes); !iter.atEnd(); ++iter) { + auto sn = *iter; + offset += sn->delta(); if (offset > target) { break; } - SrcNoteType type = SN_TYPE(sn); - if (type == SRC_SETLINE) { - lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line)); + SrcNoteType type = sn->type(); + if (type == SrcNoteType::SetLine) { + lineno = SrcNote::SetLine::getLine(sn); column = 0; - } else if (type == SRC_NEWLINE) { + } else if (type == SrcNoteType::NewLine) { lineno++; column = 0; - } else if (type == SRC_COLSPAN) { - ptrdiff_t colspan = - SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, SrcNote::ColSpan::Span)); + } else if (type == SrcNoteType::ColSpan) { + ptrdiff_t colspan = SrcNote::ColSpan::getSpan(sn); MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0); column += colspan; } } if (columnp) { *columnp = column; } @@ -4675,57 +4679,57 @@ unsigned js::PCToLineNumber(JSScript* sc return PCToLineNumber(script->lineno(), script->notes(), script->code(), pc, columnp); } jsbytecode* js::LineNumberToPC(JSScript* script, unsigned target) { ptrdiff_t offset = 0; ptrdiff_t best = -1; unsigned lineno = script->lineno(); - unsigned bestdiff = SN_MAX_OFFSET; - for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { + unsigned bestdiff = SrcNote::MaxOperand; + for (SrcNoteIterator iter(script->notes()); !iter.atEnd(); ++iter) { + auto sn = *iter; /* * Exact-match only if offset is not in the prologue; otherwise use * nearest greater-or-equal line number match. */ if (lineno == target && offset >= ptrdiff_t(script->mainOffset())) { goto out; } if (lineno >= target) { unsigned diff = lineno - target; if (diff < bestdiff) { bestdiff = diff; best = offset; } } - offset += SN_DELTA(sn); - SrcNoteType type = SN_TYPE(sn); - if (type == SRC_SETLINE) { - lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line)); - } else if (type == SRC_NEWLINE) { + offset += sn->delta(); + SrcNoteType type = sn->type(); + if (type == SrcNoteType::SetLine) { + lineno = SrcNote::SetLine::getLine(sn); + } else if (type == SrcNoteType::NewLine) { lineno++; } } if (best >= 0) { offset = best; } out: return script->offsetToPC(offset); } JS_FRIEND_API unsigned js::GetScriptLineExtent(JSScript* script) { unsigned lineno = script->lineno(); unsigned maxLineNo = lineno; - for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - SrcNoteType type = SN_TYPE(sn); - if (type == SRC_SETLINE) { - lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line)); - } else if (type == SRC_NEWLINE) { + for (SrcNoteIterator iter(script->notes()); !iter.atEnd(); ++iter) { + auto sn = *iter; + SrcNoteType type = sn->type(); + if (type == SrcNoteType::SetLine) { + lineno = SrcNote::SetLine::getLine(sn); + } else if (type == SrcNoteType::NewLine) { lineno++; } if (maxLineNo < lineno) { maxLineNo = lineno; } } @@ -5103,23 +5107,23 @@ void CopySpan(const SourceSpan& source, std::copy(source.cbegin(), source.cend(), target.begin()); } /* static */ js::UniquePtr<ImmutableScriptData> ImmutableScriptData::new_( JSContext* cx, uint32_t mainOffset, uint32_t nfixed, uint32_t nslots, uint32_t bodyScopeIndex, uint32_t numICEntries, uint32_t numBytecodeTypeSets, bool isFunction, uint16_t funLength, - mozilla::Span<const jsbytecode> code, mozilla::Span<const jssrcnote> notes, + mozilla::Span<const jsbytecode> code, mozilla::Span<const SrcNote> notes, mozilla::Span<const uint32_t> resumeOffsets, mozilla::Span<const ScopeNote> scopeNotes, mozilla::Span<const TryNote> tryNotes) { MOZ_RELEASE_ASSERT(code.Length() <= frontend::MaxBytecodeLength); - // There are 1-4 copies of SN_MAKE_TERMINATOR appended after the source + // There are 1-4 copies of SrcNoteType::Null appended after the source // notes. These are a combination of sentinel and padding values. static_assert(frontend::MaxSrcNotesLength <= UINT32_MAX - CodeNoteAlign, "Length + CodeNoteAlign shouldn't overflow UINT32_MAX"); size_t noteLength = notes.Length(); MOZ_RELEASE_ASSERT(noteLength <= frontend::MaxSrcNotesLength); size_t nullLength = ComputeNotePadding(code.Length(), noteLength); @@ -5142,17 +5146,17 @@ js::UniquePtr<ImmutableScriptData> Immut if (isFunction) { data->funLength = funLength; } // Initialize trailing arrays CopySpan(code, data->codeSpan()); CopySpan(notes, data->notesSpan().To(noteLength)); - std::fill_n(data->notes() + noteLength, nullLength, SRC_NULL); + std::fill_n(data->notes() + noteLength, nullLength, SrcNote::terminator()); CopySpan(resumeOffsets, data->resumeOffsets()); CopySpan(scopeNotes, data->scopeNotes()); CopySpan(tryNotes, data->tryNotes()); return data; } /* static */
--- a/js/src/vm/JSScript.h +++ b/js/src/vm/JSScript.h @@ -23,16 +23,17 @@ #include <type_traits> // std::is_same #include <utility> // std::move #include "jstypes.h" #include "frontend/BinASTRuntimeSupport.h" #include "frontend/NameAnalysisTypes.h" +#include "frontend/SourceNotes.h" // SrcNote #include "gc/Barrier.h" #include "gc/Rooting.h" #include "jit/IonCode.h" #include "js/CompileOptions.h" #include "js/UbiNode.h" #include "js/UniquePtr.h" #include "js/Utility.h" #include "util/StructuredSpewer.h" @@ -2573,17 +2574,17 @@ class JSScript : public js::BaseScript { } bool hasLoops(); uint32_t numNotes() const { MOZ_ASSERT(sharedData_); return immutableScriptData()->noteLength(); } - jssrcnote* notes() const { + js::SrcNote* notes() const { MOZ_ASSERT(sharedData_); return immutableScriptData()->notes(); } size_t natoms() const { MOZ_ASSERT(sharedData_); return sharedData_->natoms(); } @@ -2742,32 +2743,34 @@ struct ScriptAndCounts { extern JS::UniqueChars FormatIntroducedFilename(JSContext* cx, const char* filename, unsigned lineno, const char* introducer); struct GSNCache; -jssrcnote* GetSrcNote(GSNCache& cache, JSScript* script, jsbytecode* pc); - -extern jssrcnote* GetSrcNote(JSContext* cx, JSScript* script, jsbytecode* pc); +const js::SrcNote* GetSrcNote(GSNCache& cache, JSScript* script, + jsbytecode* pc); + +extern const js::SrcNote* GetSrcNote(JSContext* cx, JSScript* script, + jsbytecode* pc); extern jsbytecode* LineNumberToPC(JSScript* script, unsigned lineno); extern JS_FRIEND_API unsigned GetScriptLineExtent(JSScript* script); } /* namespace js */ namespace js { extern unsigned PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp = nullptr); -extern unsigned PCToLineNumber(unsigned startLine, jssrcnote* notes, +extern unsigned PCToLineNumber(unsigned startLine, SrcNote* notes, jsbytecode* code, jsbytecode* pc, unsigned* columnp = nullptr); /* * This function returns the file and line number of the script currently * executing on cx. If there is no current script executing on cx (e.g., a * native called directly through JSAPI (e.g., by setTimeout)), nullptr and 0 * are returned as the file and line.
--- a/js/src/vm/SharedStencil.h +++ b/js/src/vm/SharedStencil.h @@ -389,17 +389,17 @@ class MutableScriptFlags : public Script // // Note: The '----' separators are for readability only. // // ---- // <ImmutableScriptData itself> // ---- // (REQUIRED) Flags structure // (REQUIRED) Array of jsbytecode constituting code() -// (REQUIRED) Array of jssrcnote constituting notes() +// (REQUIRED) Array of SrcNote constituting notes() // ---- // (OPTIONAL) Array of uint32_t optional-offsets // optArrayOffset: // ---- // L0: // (OPTIONAL) Array of uint32_t constituting resumeOffsets() // L1: // (OPTIONAL) Array of ScopeNote constituting scopeNotes() @@ -547,18 +547,17 @@ class alignas(uint32_t) ImmutableScriptD return this_->offsetToPointer<Offset>(optArrayOffset_)[-index]; } public: static js::UniquePtr<ImmutableScriptData> new_( JSContext* cx, uint32_t mainOffset, uint32_t nfixed, uint32_t nslots, uint32_t bodyScopeIndex, uint32_t numICEntries, uint32_t numBytecodeTypeSets, bool isFunction, uint16_t funLength, - mozilla::Span<const jsbytecode> code, - mozilla::Span<const jssrcnote> notes, + mozilla::Span<const jsbytecode> code, mozilla::Span<const SrcNote> notes, mozilla::Span<const uint32_t> resumeOffsets, mozilla::Span<const ScopeNote> scopeNotes, mozilla::Span<const TryNote> tryNotes); static js::UniquePtr<ImmutableScriptData> new_( JSContext* cx, uint32_t codeLength, uint32_t noteLength, uint32_t numResumeOffsets, uint32_t numScopeNotes, uint32_t numTryNotes); @@ -590,18 +589,18 @@ class alignas(uint32_t) ImmutableScriptD return const_cast<ImmutableScriptData*>(this)->flagsRef(); } uint32_t codeLength() const { return codeLength_; } jsbytecode* code() { return offsetToPointer<jsbytecode>(codeOffset()); } mozilla::Span<jsbytecode> codeSpan() { return {code(), codeLength()}; } uint32_t noteLength() const { return optionalOffsetsOffset() - noteOffset(); } - jssrcnote* notes() { return offsetToPointer<jssrcnote>(noteOffset()); } - mozilla::Span<jssrcnote> notesSpan() { return {notes(), noteLength()}; } + SrcNote* notes() { return offsetToPointer<SrcNote>(noteOffset()); } + mozilla::Span<SrcNote> notesSpan() { return {notes(), noteLength()}; } mozilla::Span<uint32_t> resumeOffsets() { return mozilla::MakeSpan(offsetToPointer<uint32_t>(resumeOffsetsOffset()), offsetToPointer<uint32_t>(scopeNotesOffset())); } mozilla::Span<ScopeNote> scopeNotes() { return mozilla::MakeSpan(offsetToPointer<ScopeNote>(scopeNotesOffset()), offsetToPointer<ScopeNote>(tryNotesOffset()));