Coverage Report

Created: 2023-02-22 06:51

/src/hermes/include/hermes/VM/Interpreter.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#ifndef HERMES_VM_INTERPRETER_H
9
#define HERMES_VM_INTERPRETER_H
10
#include <cstdint>
11
12
#include "hermes/VM/Runtime.h"
13
14
class CodeBlock;
15
16
namespace hermes {
17
namespace vm {
18
/// This class is a convenience wrapper for the interpreter implementation that
19
/// needs access to the private fields of Runtime, but doesn't belong in
20
/// Runtime.
21
class Interpreter {
22
 public:
23
  /// Allocate a generator for the specified function and the specified
24
  /// environment. \param funcIndex function index in the global function table.
25
  static CallResult<PseudoHandle<JSGenerator>> createGenerator_RJS(
26
      Runtime &runtime,
27
      RuntimeModule *runtimeModule,
28
      unsigned funcIndex,
29
      Handle<Environment> envHandle,
30
      NativeArgs args);
31
32
  /// Suspend the generator function and yield to the caller.
33
  /// \param resumeIP Is the IP where the generator should resume from when it
34
  ///   is resumed.
35
  static void saveGenerator(
36
      Runtime &runtime,
37
      PinnedHermesValue *frameRegs,
38
      const Inst *resumeIP);
39
40
  /// Slow path for ReifyArguments resReg, lazyReg
41
  /// It assumes that the fast path has handled the case when 'lazyReg' is
42
  /// already initialized. It creates a new 'arguments' object and populates it
43
  /// with the argument values.
44
  static CallResult<Handle<Arguments>> reifyArgumentsSlowPath(
45
      Runtime &runtime,
46
      Handle<Callable> curFunction,
47
      bool strictMode);
48
49
  /// Slow path for GetArgumentsPropByVal resReg, propNameReg, lazyReg.
50
  ///
51
  /// It assumes that the "fast path" has already taken care of the case when
52
  /// the 'lazyReg' is still uninitialized and 'propNameReg' is a valid integer
53
  /// index less than 'argCount'. So we arrive here when either of these is
54
  /// true:
55
  /// - 'lazyReg' is initialized.
56
  /// - index is >= argCount
57
  /// - index is not an integer
58
  /// In the first case we simply perform a normal property get. In the latter
59
  /// we ultimately need to reify the arguments object, but we try to avoid
60
  /// doing that by:
61
  /// - checking if the property name is "length". In that case we can just
62
  ///   return the value.
63
  /// - checking if the property name in the prototype is not an accessor. In
64
  /// that
65
  ///   case we can also just return the value read from the prototype.
66
  /// Only if all else fails, we reify.
67
  /// The FRAME in question is obtained from \p runtime, and the registers
68
  /// \p lazyReg and \p valueReg are passed directly to make this function
69
  /// easier to use outside the interpeter.
70
  static CallResult<PseudoHandle<>> getArgumentsPropByValSlowPath_RJS(
71
      Runtime &runtime,
72
      PinnedHermesValue *lazyReg,
73
      PinnedHermesValue *valueReg,
74
      Handle<Callable> curFunction,
75
      bool strictMode);
76
77
  /// Implement the slow path of OpCode::Call/CallLong/Construct/ConstructLong.
78
  /// The callee frame must have been initialized already and the fast path
79
  /// (calling a \c JSFunction) must have been handled.
80
  /// This handles the rest of the cases (native function, bound funcation, and
81
  /// not even a function).
82
  /// \param callTarget the register containing the function object
83
  /// \return ExecutionStatus::EXCEPTION if the call threw.
84
  static CallResult<PseudoHandle<>> handleCallSlowPath(
85
      Runtime &runtime,
86
      PinnedHermesValue *callTarget);
87
88
  /// Fast path to get primitive value \p base's own properties by name \p id
89
  /// without boxing.
90
  /// Primitive own properties are properties fetching values from primitive
91
  /// value itself.
92
  /// Currently the only primitive own property is String.prototype.length.
93
  /// If the fast path property does not exist, return Empty.
94
  static PseudoHandle<>
95
  tryGetPrimitiveOwnPropertyById(Runtime &runtime, Handle<> base, SymbolID id);
96
97
  /// Implement OpCode::GetById/TryGetById when the base is not an object.
98
  static CallResult<PseudoHandle<>>
99
  getByIdTransient_RJS(Runtime &runtime, Handle<> base, SymbolID id);
100
101
  /// Fast path for getByValTransient() -- avoid boxing for \p base if it is
102
  /// string primitive and \p nameHandle is an array index.
103
  /// If the property does not exist, return Empty.
104
  static PseudoHandle<>
105
  getByValTransientFast(Runtime &runtime, Handle<> base, Handle<> nameHandle);
106
107
  /// Implement OpCode::GetByVal when the base is not an object.
108
  static CallResult<PseudoHandle<>>
109
  getByValTransient_RJS(Runtime &runtime, Handle<> base, Handle<> name);
110
111
  /// Implement OpCode::PutById/TryPutById when the base is not an object.
112
  static ExecutionStatus putByIdTransient_RJS(
113
      Runtime &runtime,
114
      Handle<> base,
115
      SymbolID id,
116
      Handle<> value,
117
      bool strictMode);
118
119
  /// Implement OpCode::PutByVal when the base is not an object.
120
  static ExecutionStatus putByValTransient_RJS(
121
      Runtime &runtime,
122
      Handle<> base,
123
      Handle<> name,
124
      Handle<> value,
125
      bool strictMode);
126
127
  /// Inlining this function is forbidden because it stores label values in a
128
  /// local static variable. Due to a bug in LLVM, it may sometimes be inlined
129
  /// anyway, so explicitly mark it as noinline.
130
  template <bool SingleStep, bool EnableCrashTrace>
131
  LLVM_ATTRIBUTE_NOINLINE static CallResult<HermesValue> interpretFunction(
132
      Runtime &runtime,
133
      InterpreterState &state);
134
135
  /// Populates an object with literal values from the object buffer.
136
  /// \param numLiterals the amount of literals to read from the buffer.
137
  /// \param keyBufferIndex the first element of the key buffer to read.
138
  /// \param valBufferIndex the first element of the val buffer to read.
139
  /// \return ExecutionStatus::EXCEPTION if the property definitions throw.
140
  static CallResult<PseudoHandle<>> createObjectFromBuffer(
141
      Runtime &runtime,
142
      CodeBlock *curCodeBlock,
143
      unsigned numLiterals,
144
      unsigned keyBufferIndex,
145
      unsigned valBufferIndex);
146
147
  /// Populates an array with literal values from the array buffer.
148
  /// \param numLiterals the amount of literals to read from the buffer.
149
  /// \param bufferIndex the first element of the buffer to read.
150
  /// \return ExecutionStatus::EXCEPTION if the property definitions throw.
151
  static CallResult<PseudoHandle<>> createArrayFromBuffer(
152
      Runtime &runtime,
153
      CodeBlock *curCodeBlock,
154
      unsigned numElements,
155
      unsigned numLiterals,
156
      unsigned bufferIndex);
157
158
  /// Implements global variable declaration as per ES2023 16.1.7.10.a.i.
159
  /// \return ExecutionStatus::EXCEPTION if the global object cannot be
160
  /// expanded.
161
  static ExecutionStatus declareGlobalVarImpl(
162
      Runtime &runtime,
163
      CodeBlock *curCodeBlock,
164
      const Inst *ip);
165
166
#ifdef HERMES_ENABLE_DEBUGGER
167
  /// Wrapper around runDebugger() that reapplies the interpreter state.
168
  /// Constructs an interpreter state from the given \p codeBlock and \p ip.
169
  /// It then invokes the debugger and returns the new code block, and offset by
170
  /// reference, and updates frameRegs to its new value. Note this function is
171
  /// inline to allow the compiler to verify that the parameters do not escape,
172
  /// which might otherwise prevent them from being promoted to registers.
173
  LLVM_ATTRIBUTE_ALWAYS_INLINE
174
  static inline ExecutionStatus runDebuggerUpdatingState(
175
      Debugger::RunReason reason,
176
      Runtime &runtime,
177
      CodeBlock *&codeBlock,
178
      const Inst *&ip,
179
0
      PinnedHermesValue *&frameRegs) {
180
    // Hack: if we are already debugging, do nothing. TODO: in the event that we
181
    // are already debugging and we get an async debugger request, abort the
182
    // current debugging command (e.g. eval something infinite).
183
0
    if (runtime.debugger_.isDebugging())
184
0
      return ExecutionStatus::RETURNED;
185
0
    uint32_t offset = codeBlock->getOffsetOf(ip);
186
0
    InterpreterState state(codeBlock, offset);
187
0
    ExecutionStatus status = runtime.debugger_.runDebugger(reason, state);
188
0
    codeBlock = state.codeBlock;
189
0
    ip = state.codeBlock->getOffsetPtr(state.offset);
190
0
    frameRegs = &runtime.currentFrame_.getFirstLocalRef();
191
0
    return status;
192
0
  }
193
#endif
194
195
  //===========================================================================
196
  // Out-of-line implementations of entire instructions.
197
198
  /// Partial implementation of ES6 18.2.1.1
199
  /// `PerformEval(x, evalRealm, strictCaller=true, direct=true)`.
200
  /// The difference is that we don't support actual lexical scope, of course.
201
  static ExecutionStatus caseDirectEval(
202
      Runtime &runtime,
203
      PinnedHermesValue *frameRegs,
204
      const inst::Inst *ip);
205
206
  static ExecutionStatus casePutOwnByVal(
207
      Runtime &runtime,
208
      PinnedHermesValue *frameRegs,
209
      const inst::Inst *ip);
210
211
  static ExecutionStatus casePutOwnGetterSetterByVal(
212
      Runtime &runtime,
213
      PinnedHermesValue *frameRegs,
214
      const inst::Inst *ip);
215
216
  static ExecutionStatus caseIteratorBegin(
217
      Runtime &runtime,
218
      PinnedHermesValue *frameRegs,
219
      const inst::Inst *ip);
220
  static ExecutionStatus caseIteratorNext(
221
      Runtime &runtime,
222
      PinnedHermesValue *frameRegs,
223
      const inst::Inst *ip);
224
225
  static ExecutionStatus caseGetPNameList(
226
      Runtime &runtime,
227
      PinnedHermesValue *frameRegs,
228
      const Inst *ip);
229
230
  /// Evaluate callBuiltin and store the result in the register stack. it must
231
  /// must be invoked with CallBuiltin or CallBuiltinLong. \p op3 contains the
232
  /// value of operand3, which is the only difference in encoding between the
233
  /// two.
234
  static ExecutionStatus implCallBuiltin(
235
      Runtime &runtime,
236
      PinnedHermesValue *frameRegs,
237
      CodeBlock *curCodeBlock,
238
      uint32_t op3);
239
};
240
241
#ifndef NDEBUG
242
/// A tag used to instruct the output stream to dump more details about the
243
/// HermesValue, like the length of the string, etc.
244
struct DumpHermesValue {
245
  const HermesValue hv;
246
0
  DumpHermesValue(HermesValue hv) : hv(hv) {}
247
};
248
249
llvh::raw_ostream &operator<<(llvh::raw_ostream &OS, DumpHermesValue dhv);
250
251
/// Dump the arguments from a callee frame.
252
void dumpCallArguments(
253
    llvh::raw_ostream &OS,
254
    Runtime &runtime,
255
    StackFramePtr calleeFrame);
256
257
#endif
258
259
} // namespace vm
260
} // namespace hermes
261
262
#endif