DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
/* -*- 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/. */

#ifndef vm_BytecodeUtil_inl_h
#define vm_BytecodeUtil_inl_h

#include "vm/BytecodeUtil.h"

#include "frontend/SourceNotes.h"
#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.
   */
  switch (JSOp(*pc)) {
    case JSOP_OR:
    case JSOP_AND:
      return 1;
    case JSOP_PICK:
    case JSOP_UNPICK:
      /*
       * Pick pops and pushes how deep it looks in the stack + 1
       * items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2
       * would pop b, c, and d to rearrange the stack to |a c[0]
       * d[1] b[2]|.
       */
      return pc[1] + 1;
    default:
      return StackDefs(pc);
  }
}

static inline unsigned GetUseCount(jsbytecode* pc) {
  if (JSOp(*pc) == JSOP_PICK || JSOp(*pc) == JSOP_UNPICK) {
    return pc[1] + 1;
  }

  return StackUses(pc);
}

static inline JSOp ReverseCompareOp(JSOp op) {
  switch (op) {
    case JSOP_GT:
      return JSOP_LT;
    case JSOP_GE:
      return JSOP_LE;
    case JSOP_LT:
      return JSOP_GT;
    case JSOP_LE:
      return JSOP_GE;
    case JSOP_EQ:
    case JSOP_NE:
    case JSOP_STRICTEQ:
    case JSOP_STRICTNE:
      return op;
    default:
      MOZ_CRASH("unrecognized op");
  }
}

static inline JSOp NegateCompareOp(JSOp op) {
  switch (op) {
    case JSOP_GT:
      return JSOP_LE;
    case JSOP_GE:
      return JSOP_LT;
    case JSOP_LT:
      return JSOP_GE;
    case JSOP_LE:
      return JSOP_GT;
    case JSOP_EQ:
      return JSOP_NE;
    case JSOP_NE:
      return JSOP_EQ;
    case JSOP_STRICTNE:
      return JSOP_STRICTEQ;
    case JSOP_STRICTEQ:
      return JSOP_STRICTNE;
    default:
      MOZ_CRASH("unrecognized op");
  }
}

class BytecodeRange {
 public:
  BytecodeRange(JSContext* cx, JSScript* script)
      : script(cx, script), pc(script->code()), end(pc + script->length()) {}
  bool empty() const { return pc == end; }
  jsbytecode* frontPC() const { return pc; }
  JSOp frontOpcode() const { return JSOp(*pc); }
  size_t frontOffset() const { return script->pcToOffset(pc); }
  void popFront() { pc += GetBytecodeLength(pc); }

 private:
  RootedScript script;
  jsbytecode* pc;
  jsbytecode* end;
};

class BytecodeRangeWithPosition : private BytecodeRange {
 public:
  using BytecodeRange::empty;
  using BytecodeRange::frontOffset;
  using BytecodeRange::frontOpcode;
  using BytecodeRange::frontPC;

  BytecodeRangeWithPosition(JSContext* cx, JSScript* script)
      : BytecodeRange(cx, script),
        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);
    }
    updatePosition();
    while (frontPC() != script->main()) {
      popFront();
    }

    if (frontOpcode() != JSOP_JUMPTARGET) {
      isEntryPoint = true;
    } else {
      wasArtifactEntryPoint = true;
    }
  }

  void popFront() {
    BytecodeRange::popFront();
    if (empty()) {
      isEntryPoint = false;
    } else {
      updatePosition();
    }

    // The following conditions are handling artifacts introduced by the
    // bytecode emitter, such that we do not add breakpoints on empty
    // statements of the source code of the user.
    if (wasArtifactEntryPoint) {
      wasArtifactEntryPoint = false;
      isEntryPoint = true;
    }

    if (isEntryPoint && frontOpcode() == JSOP_JUMPTARGET) {
      wasArtifactEntryPoint = isEntryPoint;
      isEntryPoint = false;
    }
  }

  size_t frontLineNumber() const { return lineno; }
  size_t frontColumnNumber() const { return column; }

  // Entry points are restricted to bytecode offsets that have an
  // explicit mention in the line table.  This restriction avoids a
  // number of failing cases caused by some instructions not having
  // sensible (to the user) line numbers, and it is one way to
  // implement the idea that the bytecode emitter should tell the
  // 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.
  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));
        MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
        column += colspan;
        lastLinePC = snpc;
      } else if (type == SRC_SETLINE) {
        lineno = size_t(GetSrcNoteOffset(sn, SrcNote::SetLine::Line));
        column = 0;
        lastLinePC = snpc;
      } else if (type == SRC_NEWLINE) {
        lineno++;
        column = 0;
        lastLinePC = snpc;
      } else if (type == SRC_BREAKPOINT) {
        isBreakpoint = true;
        lastLinePC = snpc;
      } else if (type == SRC_STEP_SEP) {
        seenStepSeparator = true;
        lastLinePC = snpc;
      }

      sn = SN_NEXT(sn);
      snpc += SN_DELTA(sn);
    }
    isEntryPoint = lastLinePC == frontPC();
  }

  size_t lineno;
  size_t column;
  jssrcnote* sn;
  jsbytecode* snpc;
  bool isEntryPoint;
  bool isBreakpoint;
  bool seenStepSeparator;
  bool wasArtifactEntryPoint;
};

}  // namespace js

#endif /* vm_BytecodeUtil_inl_h */