Bug 1500822 - Make TryNote::start relative to JSScript::code() r=jorendorff
authorTed Campbell <tcampbell@mozilla.com>
Mon, 22 Oct 2018 21:42:55 +0000
changeset 490839 38e27cc84adc0c40e60e13faac5bdd632591ab4f
parent 490838 00049c15c6e5091c2134979250ce31fa9837c81c
child 490840 147e4ace74cc3035a8f995b043529d46ab77ae17
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersjorendorff
bugs1500822
milestone65.0a1
Bug 1500822 - Make TryNote::start relative to JSScript::code() r=jorendorff Currently it is relative to JSScript::main(), but it is the only part of the engine that uses this definition to describe a "pcOffset". This is unnecessarily confusing, and fixing it is easy. Differential Revision: https://phabricator.services.mozilla.com/D9358
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/CForEmitter.cpp
js/src/frontend/DoWhileEmitter.cpp
js/src/frontend/ForInEmitter.cpp
js/src/frontend/ForOfEmitter.cpp
js/src/frontend/ForOfLoopControl.cpp
js/src/frontend/TryEmitter.cpp
js/src/frontend/WhileEmitter.cpp
js/src/jit/BaselineBailouts.cpp
js/src/jit/BytecodeAnalysis.cpp
js/src/jit/JitFrames.cpp
js/src/shell/js.cpp
js/src/vm/BytecodeUtil.cpp
js/src/vm/Debugger.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/JSScript.cpp
js/src/vm/JSScript.h
js/src/vm/ObjectGroup.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -679,17 +679,19 @@ NonLocalExitControl::leaveScope(EmitterS
     // record the extent of the enclosing scope. These notes will have
     // their ends recorded in ~NonLocalExitControl().
     uint32_t enclosingScopeIndex = ScopeNote::NoScopeIndex;
     if (es->enclosingInFrame()) {
         enclosingScopeIndex = es->enclosingInFrame()->index();
     }
     if (!bce_->scopeNoteList.append(enclosingScopeIndex, bce_->offset(), bce_->inPrologue(),
                                     openScopeNoteIndex_))
-        return false;
+    {
+        return false;
+    }
     openScopeNoteIndex_ = bce_->scopeNoteList.length() - 1;
 
     return true;
 }
 
 /*
  * Emit additional bytecode(s) for non-local jumps.
  */
@@ -2933,17 +2935,17 @@ BytecodeEmitter::wrapWithDestructuringIt
     }
 
     ptrdiff_t start = offset();
     if (!emitter(this)) {
         return false;
     }
     ptrdiff_t end = offset();
     if (start != end) {
-        return tryNoteList.append(JSTRY_DESTRUCTURING_ITERCLOSE, iterDepth, start, end);
+        return addTryNote(JSTRY_DESTRUCTURING_ITERCLOSE, iterDepth, start, end);
     }
     return true;
 }
 
 bool
 BytecodeEmitter::emitDefault(ParseNode* defaultExpr, ParseNode* pattern)
 {
     IfEmitter ifUndefined(this);
@@ -4857,18 +4859,18 @@ BytecodeEmitter::emitSpread(bool allowSe
     {
         return false;
     }
 
     // No breaks or continues should occur in spreads.
     MOZ_ASSERT(loopInfo.breaks.offset == -1);
     MOZ_ASSERT(loopInfo.continues.offset == -1);
 
-    if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, loopInfo.headOffset(),
-                            loopInfo.breakTargetOffset()))
+    if (!addTryNote(JSTRY_FOR_OF, stackDepth, loopInfo.headOffset(),
+                    loopInfo.breakTargetOffset()))
     {
         return false;
     }
 
     if (!emit2(JSOP_PICK, 4)) {                           // ITER ARR FINAL_INDEX RESULT NEXT
         return false;
     }
     if (!emit2(JSOP_PICK, 4)) {                           // ARR FINAL_INDEX RESULT NEXT ITER
@@ -8918,16 +8920,26 @@ AllocSrcNote(JSContext* cx, SrcNotesVect
         return false;
     }
 
     *index = notes.length() - 1;
     return true;
 }
 
 bool
+BytecodeEmitter::addTryNote(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end)
+{
+    // The tryNoteList stores offsets relative to current section should must
+    // be main section. During tryNoteList.finish(), the prologueLength will be
+    // added to correct offset.
+    MOZ_ASSERT(!inPrologue());
+    return tryNoteList.append(kind, stackDepth, start, end);
+}
+
+bool
 BytecodeEmitter::newSrcNote(SrcNoteType type, unsigned* indexp)
 {
     SrcNotesVector& notes = this->notes();
     unsigned index;
     if (!AllocSrcNote(cx, notes, &index)) {
         return false;
     }
 
@@ -9184,42 +9196,49 @@ CGScopeList::finish(mozilla::Span<GCPtrS
 
 bool
 CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end)
 {
     MOZ_ASSERT(start <= end);
     MOZ_ASSERT(size_t(uint32_t(start)) == start);
     MOZ_ASSERT(size_t(uint32_t(end)) == end);
 
+    // Offsets are given relative to sections, but we only expect main-section
+    // to have TryNotes. In finish() we will fixup base offset.
+
     JSTryNote note;
     note.kind = kind;
     note.stackDepth = stackDepth;
     note.start = uint32_t(start);
     note.length = uint32_t(end - start);
 
     return list.append(note);
 }
 
 void
-CGTryNoteList::finish(mozilla::Span<JSTryNote> array)
+CGTryNoteList::finish(mozilla::Span<JSTryNote> array, uint32_t prologueLength)
 {
     MOZ_ASSERT(length() == array.size());
 
     for (unsigned i = 0; i < length(); i++) {
+        list[i].start += prologueLength;
         array[i] = list[i];
     }
 }
 
 bool
 CGScopeNoteList::append(uint32_t scopeIndex, uint32_t offset, bool inPrologue,
                         uint32_t parent)
 {
     CGScopeNote note;
     mozilla::PodZero(&note);
 
+    // Offsets are given relative to sections. In finish() we will fixup base
+    // offset if needed.
+
     note.index = scopeIndex;
     note.start = offset;
     note.parent = parent;
     note.startInPrologue = inPrologue;
 
     return list.append(note);
 }
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -61,19 +61,22 @@ struct MOZ_STACK_CLASS CGScopeList {
     uint32_t length() const { return vector.length(); }
     void finish(mozilla::Span<GCPtrScope> array);
 };
 
 struct CGTryNoteList {
     Vector<JSTryNote> list;
     explicit CGTryNoteList(JSContext* cx) : list(cx) {}
 
+    // Start/end offset are relative to main section and will be patch in
+    // finish().
+
     MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end);
     size_t length() const { return list.length(); }
-    void finish(mozilla::Span<JSTryNote> array);
+    void finish(mozilla::Span<JSTryNote> array, uint32_t prologueLength);
 };
 
 struct CGScopeNote : public ScopeNote
 {
     // The end offset. Used to compute the length; may need adjusting first if
     // in the prologue.
     uint32_t end;
 
@@ -427,16 +430,20 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     // the main effect, the value left on the stack after the code executes,
     // will be discarded by a pop bytecode.
     MOZ_MUST_USE bool checkSideEffects(ParseNode* pn, bool* answer);
 
 #ifdef DEBUG
     MOZ_MUST_USE bool checkStrictOrSloppy(JSOp op);
 #endif
 
+    // Add TryNote to the tryNoteList array. The start and end offset are
+    // relative to current section.
+    MOZ_MUST_USE bool addTryNote(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t 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, unsigned* indexp = nullptr);
     MOZ_MUST_USE bool newSrcNote3(SrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2,
                                   unsigned* indexp = nullptr);
 
--- a/js/src/frontend/CForEmitter.cpp
+++ b/js/src/frontend/CForEmitter.cpp
@@ -227,18 +227,18 @@ CForEmitter::emitEnd()
     // The third note offset helps us find the loop-closing jump.
     if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::For::BackJumpOffset,
                                 loopInfo_->loopEndOffset() - biasedTop_))
 
     {
         return false;
     }
 
-    if (!bce_->tryNoteList.append(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(),
-                                  loopInfo_->breakTargetOffset()))
+    if (!bce_->addTryNote(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(),
+                          loopInfo_->breakTargetOffset()))
     {
         return false;
     }
 
     if (!loopInfo_->patchBreaksAndContinues(bce_)) {
         return false;
     }
 
--- a/js/src/frontend/DoWhileEmitter.cpp
+++ b/js/src/frontend/DoWhileEmitter.cpp
@@ -77,18 +77,18 @@ bool
 DoWhileEmitter::emitEnd()
 {
     MOZ_ASSERT(state_ == State::Cond);
 
     if (!loopInfo_->emitLoopEnd(bce_, JSOP_IFNE)) {
         return false;
     }
 
-    if (!bce_->tryNoteList.append(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(),
-                                  loopInfo_->breakTargetOffset()))
+    if (!bce_->addTryNote(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(),
+                          loopInfo_->breakTargetOffset()))
     {
         return false;
     }
 
     // Update the annotations with the update and back edge positions, for
     // IonBuilder.
     if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::DoWhile::CondOffset,
                                 loopInfo_->continueTargetOffsetFromLoopHead()))
--- a/js/src/frontend/ForInEmitter.cpp
+++ b/js/src/frontend/ForInEmitter.cpp
@@ -163,18 +163,18 @@ ForInEmitter::emitEnd(const Maybe<uint32
         return false;
     }
 
     // Pop the enumeration value.
     if (!bce_->emit1(JSOP_POP)) {                     // ITER
         return false;
     }
 
-    if (!bce_->tryNoteList.append(JSTRY_FOR_IN, bce_->stackDepth, loopInfo_->headOffset(),
-                                  bce_->offset()))
+    if (!bce_->addTryNote(JSTRY_FOR_IN, bce_->stackDepth, loopInfo_->headOffset(),
+                          bce_->offset()))
     {
         return false;
     }
 
     if (!bce_->emit1(JSOP_ENDITER)) {                 //
         return false;
     }
 
--- a/js/src/frontend/ForOfEmitter.cpp
+++ b/js/src/frontend/ForOfEmitter.cpp
@@ -238,18 +238,18 @@ ForOfEmitter::emitEnd(const Maybe<uint32
     {
         return false;
     }
 
     if (!loopInfo_->patchBreaksAndContinues(bce_)) {
         return false;
     }
 
-    if (!bce_->tryNoteList.append(JSTRY_FOR_OF, bce_->stackDepth, loopInfo_->headOffset(),
-                                  loopInfo_->breakTargetOffset()))
+    if (!bce_->addTryNote(JSTRY_FOR_OF, bce_->stackDepth, loopInfo_->headOffset(),
+                          loopInfo_->breakTargetOffset()))
     {
         return false;
     }
 
     if (!bce_->emitPopN(3)) {                         //
         return false;
     }
 
--- a/js/src/frontend/ForOfLoopControl.cpp
+++ b/js/src/frontend/ForOfLoopControl.cpp
@@ -134,17 +134,17 @@ ForOfLoopControl::emitIteratorCloseInSco
 {
     ptrdiff_t start = bce->offset();
     if (!bce->emitIteratorCloseInScope(currentScope, iterKind_, completionKind,
                                        allowSelfHosted_))
     {
         return false;
     }
     ptrdiff_t end = bce->offset();
-    return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end);
+    return bce->addTryNote(JSTRY_FOR_OF_ITERCLOSE, 0, start, end);
 }
 
 // Since we're in the middle of emitting code that will leave
 // |bce->innermostEmitterScope()|, passing the innermost emitter scope to
 // emitIteratorCloseInScope and looking up .generator there would be very,
 // very wrong.  We'd find .generator in the function environment, and we'd
 // compute a NameLocation with the correct slot, but we'd compute a
 // hop-count to the function environment that was too big.  At runtime we'd
--- a/js/src/frontend/TryEmitter.cpp
+++ b/js/src/frontend/TryEmitter.cpp
@@ -278,26 +278,26 @@ TryEmitter::emitEnd()
     // Fix up the end-of-try/catch jumps to come here.
     if (!bce_->emitJumpTargetAndPatch(catchAndFinallyJump_)) {
         return false;
     }
 
     // Add the try note last, to let post-order give us the right ordering
     // (first to last for a given nesting level, inner to outer by level).
     if (hasCatch()) {
-        if (!bce_->tryNoteList.append(JSTRY_CATCH, depth_, tryStart_, tryEnd_.offset)) {
+        if (!bce_->addTryNote(JSTRY_CATCH, depth_, tryStart_, tryEnd_.offset)) {
             return false;
         }
     }
 
     // If we've got a finally, mark try+catch region with additional
     // trynote to catch exceptions (re)thrown from a catch block or
     // for the try{}finally{} case.
     if (hasFinally()) {
-        if (!bce_->tryNoteList.append(JSTRY_FINALLY, depth_, tryStart_, finallyStart_.offset)) {
+        if (!bce_->addTryNote(JSTRY_FINALLY, depth_, tryStart_, finallyStart_.offset)) {
             return false;
         }
     }
 
 #ifdef DEBUG
     state_ = State::End;
 #endif
     return true;
--- a/js/src/frontend/WhileEmitter.cpp
+++ b/js/src/frontend/WhileEmitter.cpp
@@ -104,18 +104,18 @@ bool
 WhileEmitter::emitEnd()
 {
     MOZ_ASSERT(state_ == State::Cond);
 
     if (!loopInfo_->emitLoopEnd(bce_, JSOP_IFNE)) {
         return false;
     }
 
-    if (!bce_->tryNoteList.append(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(),
-                                  loopInfo_->breakTargetOffset()))
+    if (!bce_->addTryNote(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(),
+                          loopInfo_->breakTargetOffset()))
     {
         return false;
     }
 
     if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::While::BackJumpOffset,
                                 loopInfo_->loopEndOffsetFromEntryJump()))
     {
         return false;
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -484,17 +484,17 @@ GetNextNonLoopEntryPc(jsbytecode* pc, js
 
 static bool
 HasLiveStackValueAtDepth(JSScript* script, jsbytecode* pc, uint32_t stackDepth)
 {
     if (!script->hasTrynotes()) {
         return false;
     }
 
-    uint32_t pcOffset = uint32_t(pc - script->main());
+    uint32_t pcOffset = script->pcToOffset(pc);
 
     for (const JSTryNote& tn : script->trynotes()) {
         if (pcOffset < tn.start) {
             continue;
         }
         if (pcOffset >= tn.start + tn.length) {
             continue;
         }
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -113,19 +113,18 @@ BytecodeAnalysis::init(TempAllocator& al
                 }
                 pc2 += JUMP_OFFSET_LEN;
             }
             break;
           }
 
           case JSOP_TRY: {
             for (const JSTryNote& tn : script_->trynotes()) {
-                unsigned startOffset = script_->mainOffset() + tn.start;
-                if (startOffset == offset + 1) {
-                    unsigned catchOffset = startOffset + tn.length;
+                if (tn.start == offset + 1) {
+                    unsigned catchOffset = tn.start + tn.length;
 
                     if (tn.kind != JSTRY_FOR_IN) {
                         infos_[catchOffset].init(stackDepth);
                         infos_[catchOffset].jumpTarget = true;
                     }
                 }
             }
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -229,17 +229,17 @@ HandleExceptionIon(JSContext* cx, const 
           case JSTRY_FOR_IN:
           case JSTRY_DESTRUCTURING_ITERCLOSE:
             // See corresponding comment in ProcessTryNotes.
             if (inForOfIterClose) {
                 break;
             }
 
             MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN,
-                          JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
+                          JSOp(*(script->offsetToPC(tn->start + tn->length))) == JSOP_ENDITER);
             CloseLiveIteratorIon(cx, frame, tn);
             break;
 
           case JSTRY_FOR_OF_ITERCLOSE:
             inForOfIterClose = true;
             break;
 
           case JSTRY_FOR_OF:
@@ -257,17 +257,17 @@ HandleExceptionIon(JSContext* cx, const 
                 }
 
                 // Ion can compile try-catch, but bailing out to catch
                 // exceptions is slow. Reset the warm-up counter so that if we
                 // catch many exceptions we won't Ion-compile the script.
                 script->resetWarmUpCounter();
 
                 // Bailout at the start of the catch block.
-                jsbytecode* catchPC = script->main() + tn->start + tn->length;
+                jsbytecode* catchPC = script->offsetToPC(tn->start + tn->length);
                 ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth);
                 uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed);
                 if (retval == BAILOUT_RETURN_OK) {
                     // Record exception locations to allow scope unwinding in
                     // |FinishBailoutToBaseline|
                     MOZ_ASSERT(cx->isExceptionPending());
                     rfe->bailoutInfo->tryPC = UnwindEnvironmentToTryPc(frame.script(), tn);
                     rfe->bailoutInfo->faultPC = frame.pc();
@@ -324,17 +324,17 @@ SettleOnTryNote(JSContext* cx, const JST
     if (cx->isExceptionPending()) {
         UnwindEnvironment(cx, ei, UnwindEnvironmentToTryPc(script, tn));
     }
 
     // Compute base pointer and stack pointer.
     BaselineFrameAndStackPointersFromTryNote(tn, frame, &rfe->framePointer, &rfe->stackPointer);
 
     // Compute the pc.
-    *pc = script->main() + tn->start + tn->length;
+    *pc = script->offsetToPC(tn->start + tn->length);
 }
 
 struct AutoBaselineHandlingException
 {
     BaselineFrame* frame;
     AutoBaselineHandlingException(BaselineFrame* frame, jsbytecode* pc)
       : frame(frame)
     {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3097,20 +3097,19 @@ TryNotes(JSContext* cx, HandleScript scr
         return true;
     }
 
     if (!sp->put("\nException table:\nkind               stack    start      end\n")) {
         return false;
     }
 
     for (const JSTryNote& tn : script->trynotes()) {
-        uint32_t startOff = script->pcToOffset(script->main()) + tn.start;
         if (!sp->jsprintf(" %-16s %6u %8u %8u\n",
                           TryNoteName(static_cast<JSTryNoteKind>(tn.kind)),
-                          tn.stackDepth, startOff, startOff + tn.length))
+                          tn.stackDepth, tn.start, tn.start + tn.length))
         {
             return false;
         }
     }
     return true;
 }
 
 static MOZ_MUST_USE bool
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -942,19 +942,18 @@ BytecodeParser::parse()
           }
 
           case JSOP_TRY: {
             // Everything between a try and corresponding catch or finally is conditional.
             // Note that there is no problem with code which is skipped by a thrown
             // exception but is not caught by a later handler in the same function:
             // no more code will execute, and it does not matter what is defined.
             for (const JSTryNote& tn : script_->trynotes()) {
-                uint32_t startOffset = script_->mainOffset() + tn.start;
-                if (startOffset == offset + 1) {
-                    uint32_t catchOffset = startOffset + tn.length;
+                if (tn.start == offset + 1) {
+                    uint32_t catchOffset = tn.start + tn.length;
                     if (tn.kind == JSTRY_CATCH) {
                         if (!addJump(catchOffset, &nextOffset, stackDepth, offsetStack,
                                      pc, JumpKind::TryCatch))
                         {
                             return false;
                         }
                     } else if (tn.kind == JSTRY_FINALLY) {
                         if (!addJump(catchOffset, &nextOffset, stackDepth, offsetStack,
@@ -1449,19 +1448,18 @@ Disassemble1(JSContext* cx, HandleScript
     int i;
     switch (JOF_TYPE(cs->format)) {
       case JOF_BYTE:
           // Scan the trynotes to find the associated catch block
           // and make the try opcode look like a jump instruction
           // with an offset. This simplifies code coverage analysis
           // based on this disassembled output.
           if (op == JSOP_TRY) {
-              size_t mainOffset = script->mainOffset();
               for (const JSTryNote& tn : script->trynotes()) {
-                  if (tn.kind == JSTRY_CATCH && tn.start + mainOffset == loc + 1) {
+                  if (tn.kind == JSTRY_CATCH && tn.start == loc + 1) {
                       if (!sp->jsprintf(" %u (%+d)",
                                         unsigned(loc + tn.length + 1),
                                         int(tn.length + 1)))
                       {
                           return 0;
                       }
                       break;
                   }
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6444,19 +6444,18 @@ class FlowGraphSummary {
                 }
             } else if (op == JSOP_TRY) {
                 // As there is no literal incoming edge into the catch block, we
                 // make a fake one by copying the JSOP_TRY location, as-if this
                 // was an incoming edge of the catch block. This is needed
                 // because we only report offsets of entry points which have
                 // valid incoming edges.
                 for (const JSTryNote& tn : script->trynotes()) {
-                    uint32_t startOffset = script->mainOffset() + tn.start;
-                    if (startOffset == r.frontOffset() + 1) {
-                        uint32_t catchOffset = startOffset + tn.length;
+                    if (tn.start == r.frontOffset() + 1) {
+                        uint32_t catchOffset = tn.start + tn.length;
                         if (tn.kind == JSTRY_CATCH || tn.kind == JSTRY_FINALLY) {
                             addEdge(lineno, column, catchOffset);
                         }
                     }
                 }
             }
 
             prevLineno = lineno;
@@ -7462,24 +7461,20 @@ class DebuggerScriptIsInCatchScopeMatche
 
     inline bool isInCatch() const { return isInCatch_; }
 
     ReturnType match(HandleScript script) {
         if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
             return false;
         }
 
-        // Try note ranges are relative to the mainOffset of the script, so adjust
-        // offset accordingly.
-        size_t offset = offset_ - script->mainOffset();
-
         if (script->hasTrynotes()) {
             for (const JSTryNote& tn : script->trynotes()) {
-                if (tn.start <= offset &&
-                    offset <= tn.start + tn.length &&
+                if (tn.start <= offset_ &&
+                    offset_ <= tn.start + tn.length &&
                     tn.kind == JSTRY_CATCH)
                 {
                     isInCatch_ = true;
                     return true;
                 }
             }
         }
         isInCatch_ = false;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1311,17 +1311,17 @@ js::UnwindAllEnvironmentsInFrame(JSConte
 //
 // try { { let x; } }
 //
 // will have no pc location distinguishing the try block scope from the inner
 // let block scope.
 jsbytecode*
 js::UnwindEnvironmentToTryPc(JSScript* script, const JSTryNote* tn)
 {
-    jsbytecode* pc = script->main() + tn->start;
+    jsbytecode* pc = script->offsetToPC(tn->start);
     if (tn->kind == JSTRY_CATCH || tn->kind == JSTRY_FINALLY) {
         pc -= JSOP_TRY_LENGTH;
         MOZ_ASSERT(*pc == JSOP_TRY);
     } else if (tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE) {
         pc -= JSOP_TRY_DESTRUCTURING_ITERCLOSE_LENGTH;
         MOZ_ASSERT(*pc == JSOP_TRY_DESTRUCTURING_ITERCLOSE);
     }
     return pc;
@@ -1340,17 +1340,17 @@ ForcedReturn(JSContext* cx, InterpreterR
 static void
 SettleOnTryNote(JSContext* cx, const JSTryNote* tn, EnvironmentIter& ei, InterpreterRegs& regs)
 {
     // Unwind the environment to the beginning of the JSOP_TRY.
     UnwindEnvironment(cx, ei, UnwindEnvironmentToTryPc(regs.fp()->script(), tn));
 
     // Set pc to the first bytecode after the the try note to point
     // to the beginning of catch or finally.
-    regs.pc = regs.fp()->script()->main() + tn->start + tn->length;
+    regs.pc = regs.fp()->script()->offsetToPC(tn->start + tn->length);
     regs.sp = regs.spForStackDepth(tn->stackDepth);
 }
 
 class InterpreterFrameStackDepthOp
 {
     const InterpreterRegs& regs_;
   public:
     explicit InterpreterFrameStackDepthOp(const InterpreterRegs& regs)
@@ -1463,17 +1463,17 @@ ProcessTryNotes(JSContext* cx, Environme
             // Don't let (extra) values pushed on the stack while closing a
             // for-of iterator confuse us into thinking we still have to close
             // an inner for-in iterator.
             if (inForOfIterClose) {
                 break;
             }
 
             /* This is similar to JSOP_ENDITER in the interpreter loop. */
-            DebugOnly<jsbytecode*> pc = regs.fp()->script()->main() + tn->start + tn->length;
+            DebugOnly<jsbytecode*> pc = regs.fp()->script()->offsetToPC(tn->start + tn->length);
             MOZ_ASSERT(JSOp(*pc) == JSOP_ENDITER);
             Value* sp = regs.spForStackDepth(tn->stackDepth);
             JSObject* obj = &sp[-1].toObject();
             CloseIterator(obj);
             break;
           }
 
           case JSTRY_DESTRUCTURING_ITERCLOSE: {
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -388,17 +388,17 @@ class MOZ_STACK_CLASS TryNoteIter
             }
         }
     }
 
   public:
     TryNoteIter(JSContext* cx, JSScript* script, jsbytecode* pc,
                 StackDepthOp getStackDepth)
       : script_(cx, script),
-        pcOffset_(pc - script->main()),
+        pcOffset_(script->pcToOffset(pc)),
         getStackDepth_(getStackDepth)
     {
         if (script->hasTrynotes()) {
             // NOTE: The Span is a temporary so we can't use begin()/end()
             // here or the iterator will outlive the span.
             auto trynotes = script->trynotes();
             tn_ = trynotes.data();
             tnEnd_ = tn_ + trynotes.size();
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -3517,17 +3517,17 @@ JSScript::fullyInitFromEmitter(JSContext
     }
     if (bce->objectList.length != 0) {
         bce->objectList.finish(script->objects());
     }
     if (bce->scopeList.length() != 0) {
         bce->scopeList.finish(script->scopes());
     }
     if (bce->tryNoteList.length() != 0) {
-        bce->tryNoteList.finish(script->trynotes());
+        bce->tryNoteList.finish(script->trynotes(), prologueLength);
     }
     if (bce->scopeNoteList.length() != 0) {
         bce->scopeNoteList.finish(script->scopeNotes(), prologueLength);
     }
     script->bitFields_.strict_ = bce->sc->strict();
     script->bitFields_.explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
     script->bitFields_.bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
     script->bitFields_.hasSingletons_ = bce->hasSingletons;
@@ -3603,17 +3603,17 @@ JSScript::assertValidJumpTargets() const
                 MOZ_ASSERT_IF(off, BytecodeIsJumpTarget(JSOp(*(pc + off))));
             }
         }
     }
 
     // Check catch/finally blocks as jump targets.
     if (hasTrynotes()) {
         for (const JSTryNote& tn : trynotes()) {
-            jsbytecode* tryStart = mainEntry + tn.start;
+            jsbytecode* tryStart = offsetToPC(tn.start);
             jsbytecode* tryPc = tryStart - 1;
             if (tn.kind != JSTRY_CATCH && tn.kind != JSTRY_FINALLY) {
                 continue;
             }
 
             MOZ_ASSERT(JSOp(*tryPc) == JSOP_TRY);
             jsbytecode* tryTarget = tryStart + tn.length;
             MOZ_ASSERT(mainEntry <= tryTarget && tryTarget < end);
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -104,18 +104,18 @@ enum JSTryNoteKind {
 };
 
 /*
  * Exception handling record.
  */
 struct JSTryNote {
     uint8_t         kind;       /* one of JSTryNoteKind */
     uint32_t        stackDepth; /* stack depth upon exception handler entry */
-    uint32_t        start;      /* start of the try statement or loop
-                                   relative to script->main */
+    uint32_t        start;      /* start of the try statement or loop relative
+                                   to script->code() */
     uint32_t        length;     /* length of the try statement or loop */
 };
 
 namespace js {
 
 // A block scope has a range in bytecode: it is entered at some offset, and left
 // at some later offset.  Scopes can be nested.  Given an offset, the
 // ScopeNote containing that offset whose with the highest start value
@@ -134,18 +134,18 @@ struct ScopeNote {
     static const uint32_t NoScopeIndex = UINT32_MAX;
 
     // Sentinel index for no ScopeNote.
     static const uint32_t NoScopeNoteIndex = UINT32_MAX;
 
     uint32_t        index;      // Index of Scope in the scopes array, or
                                 // NoScopeIndex if there is no block scope in
                                 // this range.
-    uint32_t        start;      // Bytecode offset at which this scope starts,
-                                // from script->main().
+    uint32_t        start;      // Bytecode offset at which this scope starts
+                                // relative to script->code().
     uint32_t        length;     // Bytecode length of scope.
     uint32_t        parent;     // Index of parent block scope in notes, or NoScopeNote.
 };
 
 struct ConstArray {
     js::GCPtrValue* vector;     // array of indexed constant values
     uint32_t length;
 };
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -233,27 +233,24 @@ ObjectGroup::useSingletonForAllocationSi
     }
 
     // All loops in the script will have a try note indicating their boundary.
 
     if (!script->hasTrynotes()) {
         return SingletonObject;
     }
 
-    unsigned offset = script->pcToOffset(pc);
+    uint32_t offset = script->pcToOffset(pc);
 
     for (const JSTryNote& tn : script->trynotes()) {
         if (tn.kind != JSTRY_FOR_IN && tn.kind != JSTRY_FOR_OF && tn.kind != JSTRY_LOOP) {
             continue;
         }
 
-        unsigned startOffset = script->mainOffset() + tn.start;
-        unsigned endOffset = startOffset + tn.length;
-
-        if (offset >= startOffset && offset < endOffset) {
+        if (tn.start <= offset && offset < tn.start + tn.length) {
             return GenericObject;
         }
     }
 
     return SingletonObject;
 }
 
 /* static */ bool