/src/hermes/lib/VM/Interpreter.cpp
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 | | #define DEBUG_TYPE "vm" |
9 | | #include "hermes/VM/Interpreter.h" |
10 | | #include "hermes/VM/Runtime.h" |
11 | | |
12 | | #include "hermes/Inst/InstDecode.h" |
13 | | #include "hermes/Support/Conversions.h" |
14 | | #include "hermes/Support/SlowAssert.h" |
15 | | #include "hermes/Support/Statistic.h" |
16 | | #include "hermes/VM/BigIntPrimitive.h" |
17 | | #include "hermes/VM/Callable.h" |
18 | | #include "hermes/VM/CodeBlock.h" |
19 | | #include "hermes/VM/HandleRootOwner-inline.h" |
20 | | #include "hermes/VM/JSArray.h" |
21 | | #include "hermes/VM/JSError.h" |
22 | | #include "hermes/VM/JSGenerator.h" |
23 | | #include "hermes/VM/JSProxy.h" |
24 | | #include "hermes/VM/JSRegExp.h" |
25 | | #include "hermes/VM/JSTypedArray.h" |
26 | | #include "hermes/VM/Operations.h" |
27 | | #include "hermes/VM/Profiler.h" |
28 | | #include "hermes/VM/Profiler/CodeCoverageProfiler.h" |
29 | | #include "hermes/VM/PropertyAccessor.h" |
30 | | #include "hermes/VM/RuntimeModule-inline.h" |
31 | | #include "hermes/VM/StackFrame-inline.h" |
32 | | #include "hermes/VM/StringPrimitive.h" |
33 | | #include "hermes/VM/StringView.h" |
34 | | #include "hermes/VM/WeakRoot-inline.h" |
35 | | |
36 | | #include "llvh/Support/Debug.h" |
37 | | #include "llvh/Support/Format.h" |
38 | | #include "llvh/Support/raw_ostream.h" |
39 | | |
40 | | #include "Interpreter-internal.h" |
41 | | |
42 | | #pragma GCC diagnostic push |
43 | | |
44 | | #ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32 |
45 | | #pragma GCC diagnostic ignored "-Wshorten-64-to-32" |
46 | | #endif |
47 | | using llvh::dbgs; |
48 | | using namespace hermes::inst; |
49 | | |
50 | | HERMES_SLOW_STATISTIC( |
51 | | NumGetById, |
52 | | "NumGetById: Number of property 'read by id' accesses"); |
53 | | HERMES_SLOW_STATISTIC( |
54 | | NumGetByIdCacheHits, |
55 | | "NumGetByIdCacheHits: Number of property 'read by id' cache hits"); |
56 | | HERMES_SLOW_STATISTIC( |
57 | | NumGetByIdProtoHits, |
58 | | "NumGetByIdProtoHits: Number of property 'read by id' cache hits for the prototype"); |
59 | | HERMES_SLOW_STATISTIC( |
60 | | NumGetByIdCacheEvicts, |
61 | | "NumGetByIdCacheEvicts: Number of property 'read by id' cache evictions"); |
62 | | HERMES_SLOW_STATISTIC( |
63 | | NumGetByIdFastPaths, |
64 | | "NumGetByIdFastPaths: Number of property 'read by id' fast paths"); |
65 | | HERMES_SLOW_STATISTIC( |
66 | | NumGetByIdAccessor, |
67 | | "NumGetByIdAccessor: Number of property 'read by id' accessors"); |
68 | | HERMES_SLOW_STATISTIC( |
69 | | NumGetByIdProto, |
70 | | "NumGetByIdProto: Number of property 'read by id' in the prototype chain"); |
71 | | HERMES_SLOW_STATISTIC( |
72 | | NumGetByIdNotFound, |
73 | | "NumGetByIdNotFound: Number of property 'read by id' not found"); |
74 | | HERMES_SLOW_STATISTIC( |
75 | | NumGetByIdTransient, |
76 | | "NumGetByIdTransient: Number of property 'read by id' of non-objects"); |
77 | | HERMES_SLOW_STATISTIC( |
78 | | NumGetByIdDict, |
79 | | "NumGetByIdDict: Number of property 'read by id' of dictionaries"); |
80 | | HERMES_SLOW_STATISTIC( |
81 | | NumGetByIdSlow, |
82 | | "NumGetByIdSlow: Number of property 'read by id' slow path"); |
83 | | |
84 | | HERMES_SLOW_STATISTIC( |
85 | | NumPutById, |
86 | | "NumPutById: Number of property 'write by id' accesses"); |
87 | | HERMES_SLOW_STATISTIC( |
88 | | NumPutByIdCacheHits, |
89 | | "NumPutByIdCacheHits: Number of property 'write by id' cache hits"); |
90 | | HERMES_SLOW_STATISTIC( |
91 | | NumPutByIdCacheEvicts, |
92 | | "NumPutByIdCacheEvicts: Number of property 'write by id' cache evictions"); |
93 | | HERMES_SLOW_STATISTIC( |
94 | | NumPutByIdFastPaths, |
95 | | "NumPutByIdFastPaths: Number of property 'write by id' fast paths"); |
96 | | HERMES_SLOW_STATISTIC( |
97 | | NumPutByIdTransient, |
98 | | "NumPutByIdTransient: Number of property 'write by id' to non-objects"); |
99 | | |
100 | | HERMES_SLOW_STATISTIC( |
101 | | NumNativeFunctionCalls, |
102 | | "NumNativeFunctionCalls: Number of native function calls"); |
103 | | HERMES_SLOW_STATISTIC( |
104 | | NumBoundFunctionCalls, |
105 | | "NumBoundCalls: Number of bound function calls"); |
106 | | |
107 | | // Ensure that instructions declared as having matching layouts actually do. |
108 | | #include "InstLayout.inc" |
109 | | |
110 | | namespace hermes { |
111 | | namespace vm { |
112 | | |
113 | | /// Initialize the state of some internal variables based on the current |
114 | | /// code block. |
115 | | #define INIT_STATE_FOR_CODEBLOCK(codeBlock) \ |
116 | 3.84k | do { \ |
117 | 3.84k | strictMode = (codeBlock)->isStrictMode(); \ |
118 | 3.84k | defaultPropOpFlags = DEFAULT_PROP_OP_FLAGS(strictMode); \ |
119 | 3.84k | if (EnableCrashTrace) { \ |
120 | 0 | auto *bc = (codeBlock)->getRuntimeModule()->getBytecode(); \ |
121 | 0 | bytecodeFileStart = bc->getRawBuffer().data(); \ |
122 | 0 | auto hash = bc->getSourceHash(); \ |
123 | 0 | runtime.crashTrace_.recordModule( \ |
124 | 0 | bc->getSegmentID(), \ |
125 | 0 | (codeBlock)->getRuntimeModule()->getSourceURL(), \ |
126 | 0 | llvh::StringRef((const char *)&hash, sizeof(hash))); \ |
127 | 0 | } \ |
128 | 3.84k | } while (0) |
129 | | |
130 | | CallResult<PseudoHandle<JSGenerator>> Interpreter::createGenerator_RJS( |
131 | | Runtime &runtime, |
132 | | RuntimeModule *runtimeModule, |
133 | | unsigned funcIndex, |
134 | | Handle<Environment> envHandle, |
135 | 0 | NativeArgs args) { |
136 | 0 | auto gifRes = GeneratorInnerFunction::create( |
137 | 0 | runtime, |
138 | 0 | runtimeModule->getDomain(runtime), |
139 | 0 | Handle<JSObject>::vmcast(&runtime.functionPrototype), |
140 | 0 | envHandle, |
141 | 0 | runtimeModule->getCodeBlockMayAllocate(funcIndex), |
142 | 0 | args); |
143 | 0 | if (LLVM_UNLIKELY(gifRes == ExecutionStatus::EXCEPTION)) { |
144 | 0 | return ExecutionStatus::EXCEPTION; |
145 | 0 | } |
146 | | |
147 | 0 | auto generatorFunction = runtime.makeHandle(vmcast<JSGeneratorFunction>( |
148 | 0 | runtime.getCurrentFrame().getCalleeClosureUnsafe())); |
149 | |
|
150 | 0 | auto prototypeProp = JSObject::getNamed_RJS( |
151 | 0 | generatorFunction, |
152 | 0 | runtime, |
153 | 0 | Predefined::getSymbolID(Predefined::prototype)); |
154 | 0 | if (LLVM_UNLIKELY(prototypeProp == ExecutionStatus::EXCEPTION)) { |
155 | 0 | return ExecutionStatus::EXCEPTION; |
156 | 0 | } |
157 | 0 | Handle<JSObject> prototype = vmisa<JSObject>(prototypeProp->get()) |
158 | 0 | ? runtime.makeHandle<JSObject>(prototypeProp->get()) |
159 | 0 | : Handle<JSObject>::vmcast(&runtime.generatorPrototype); |
160 | |
|
161 | 0 | return JSGenerator::create(runtime, *gifRes, prototype); |
162 | 0 | } |
163 | | |
164 | | CallResult<Handle<Arguments>> Interpreter::reifyArgumentsSlowPath( |
165 | | Runtime &runtime, |
166 | | Handle<Callable> curFunction, |
167 | 2 | bool strictMode) { |
168 | 2 | auto frame = runtime.getCurrentFrame(); |
169 | 2 | uint32_t argCount = frame.getArgCount(); |
170 | | // Define each JavaScript argument. |
171 | 2 | auto argRes = Arguments::create(runtime, argCount, curFunction, strictMode); |
172 | 2 | if (LLVM_UNLIKELY(argRes == ExecutionStatus::EXCEPTION)) { |
173 | 0 | return ExecutionStatus::EXCEPTION; |
174 | 0 | } |
175 | 2 | Handle<Arguments> args = *argRes; |
176 | | |
177 | 2 | for (uint32_t argIndex = 0; argIndex < argCount; ++argIndex) { |
178 | 0 | SmallHermesValue shv = |
179 | 0 | SmallHermesValue::encodeHermesValue(frame.getArgRef(argIndex), runtime); |
180 | 0 | Arguments::unsafeSetExistingElementAt(*args, runtime, argIndex, shv); |
181 | 0 | } |
182 | | |
183 | | // The returned value should already be set from the create call. |
184 | 2 | return args; |
185 | 2 | } |
186 | | |
187 | | CallResult<PseudoHandle<>> Interpreter::getArgumentsPropByValSlowPath_RJS( |
188 | | Runtime &runtime, |
189 | | PinnedHermesValue *lazyReg, |
190 | | PinnedHermesValue *valueReg, |
191 | | Handle<Callable> curFunction, |
192 | 0 | bool strictMode) { |
193 | 0 | auto frame = runtime.getCurrentFrame(); |
194 | | |
195 | | // If the arguments object has already been created. |
196 | 0 | if (!lazyReg->isUndefined()) { |
197 | | // The arguments object has been created, so this is a regular property |
198 | | // get. |
199 | 0 | assert(lazyReg->isObject() && "arguments lazy register is not an object"); |
200 | | |
201 | 0 | return JSObject::getComputed_RJS( |
202 | 0 | Handle<JSObject>::vmcast(lazyReg), runtime, Handle<>(valueReg)); |
203 | 0 | } |
204 | | |
205 | 0 | if (!valueReg->isSymbol()) { |
206 | | // Attempt a fast path in the case that the key is not a symbol. |
207 | | // If it is a symbol, force reification for now. |
208 | | // Convert the value to a string. |
209 | 0 | auto strRes = toString_RJS(runtime, Handle<>(valueReg)); |
210 | 0 | if (strRes == ExecutionStatus::EXCEPTION) |
211 | 0 | return ExecutionStatus::EXCEPTION; |
212 | 0 | auto strPrim = runtime.makeHandle(std::move(*strRes)); |
213 | | |
214 | | // Check if the string is a valid argument index. |
215 | 0 | if (auto index = toArrayIndex(runtime, strPrim)) { |
216 | 0 | if (*index < frame.getArgCount()) { |
217 | 0 | return createPseudoHandle(frame.getArgRef(*index)); |
218 | 0 | } |
219 | | |
220 | 0 | auto objectPrototype = Handle<JSObject>::vmcast(&runtime.objectPrototype); |
221 | | |
222 | | // OK, they are requesting an index that either doesn't exist or is |
223 | | // somewhere up in the prototype chain. Since we want to avoid reifying, |
224 | | // check which it is: |
225 | 0 | MutableHandle<JSObject> inObject{runtime}; |
226 | 0 | MutableHandle<SymbolID> inNameTmpStorage{runtime}; |
227 | 0 | ComputedPropertyDescriptor desc; |
228 | 0 | JSObject::getComputedPrimitiveDescriptor( |
229 | 0 | objectPrototype, runtime, strPrim, inObject, inNameTmpStorage, desc); |
230 | | |
231 | | // If we couldn't find the property, just return 'undefined'. |
232 | 0 | if (!inObject) |
233 | 0 | return createPseudoHandle(HermesValue::encodeUndefinedValue()); |
234 | | |
235 | | // If the property isn't an accessor, we can just return it without |
236 | | // reifying. |
237 | 0 | if (!desc.flags.accessor) { |
238 | 0 | return JSObject::getComputedSlotValue( |
239 | 0 | createPseudoHandle(inObject.get()), |
240 | 0 | runtime, |
241 | 0 | inNameTmpStorage, |
242 | 0 | desc); |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | // Are they requesting "arguments.length"? |
247 | 0 | if (runtime.symbolEqualsToStringPrim( |
248 | 0 | Predefined::getSymbolID(Predefined::length), *strPrim)) { |
249 | 0 | return createPseudoHandle( |
250 | 0 | HermesValue::encodeUntrustedNumberValue(frame.getArgCount())); |
251 | 0 | } |
252 | 0 | } |
253 | | |
254 | | // Looking for an accessor or a property that needs reification. |
255 | 0 | auto argRes = reifyArgumentsSlowPath(runtime, curFunction, strictMode); |
256 | 0 | if (argRes == ExecutionStatus::EXCEPTION) { |
257 | 0 | return ExecutionStatus::EXCEPTION; |
258 | 0 | } |
259 | | |
260 | | // Update the register with the reified value. |
261 | 0 | *lazyReg = argRes->getHermesValue(); |
262 | | |
263 | | // For simplicity, call ourselves again. |
264 | 0 | return getArgumentsPropByValSlowPath_RJS( |
265 | 0 | runtime, lazyReg, valueReg, curFunction, strictMode); |
266 | 0 | } |
267 | | |
268 | | CallResult<PseudoHandle<>> Interpreter::handleCallSlowPath( |
269 | | Runtime &runtime, |
270 | 86.4k | PinnedHermesValue *callTarget) { |
271 | 86.4k | if (auto *native = dyn_vmcast<NativeFunction>(*callTarget)) { |
272 | 86.4k | ++NumNativeFunctionCalls; |
273 | | // Call the native function directly |
274 | 86.4k | return NativeFunction::_nativeCall(native, runtime); |
275 | 86.4k | } else if (auto *bound = dyn_vmcast<BoundFunction>(*callTarget)) { |
276 | 0 | ++NumBoundFunctionCalls; |
277 | | // Call the bound function. |
278 | 0 | return BoundFunction::_boundCall(bound, runtime.getCurrentIP(), runtime); |
279 | 0 | } else { |
280 | 0 | return runtime.raiseTypeErrorForCallable(Handle<>(callTarget)); |
281 | 0 | } |
282 | 86.4k | } |
283 | | |
284 | | inline PseudoHandle<> Interpreter::tryGetPrimitiveOwnPropertyById( |
285 | | Runtime &runtime, |
286 | | Handle<> base, |
287 | 3 | SymbolID id) { |
288 | 3 | if (base->isString() && id == Predefined::getSymbolID(Predefined::length)) { |
289 | 0 | return createPseudoHandle(HermesValue::encodeUntrustedNumberValue( |
290 | 0 | base->getString()->getStringLength())); |
291 | 0 | } |
292 | 3 | return createPseudoHandle(HermesValue::encodeEmptyValue()); |
293 | 3 | } |
294 | | |
295 | | CallResult<PseudoHandle<>> Interpreter::getByIdTransient_RJS( |
296 | | Runtime &runtime, |
297 | | Handle<> base, |
298 | 3 | SymbolID id) { |
299 | | // This is similar to what ES5.1 8.7.1 special [[Get]] internal |
300 | | // method did, but that section doesn't exist in ES9 anymore. |
301 | | // Instead, the [[Get]] Receiver argument serves a similar purpose. |
302 | | |
303 | | // Fast path: try to get primitive own property directly first. |
304 | 3 | PseudoHandle<> valOpt = tryGetPrimitiveOwnPropertyById(runtime, base, id); |
305 | 3 | if (!valOpt->isEmpty()) { |
306 | 0 | return valOpt; |
307 | 0 | } |
308 | | |
309 | | // get the property descriptor from primitive prototype without |
310 | | // boxing with vm::toObject(). This is where any properties will |
311 | | // be. |
312 | 3 | CallResult<Handle<JSObject>> primitivePrototypeResult = |
313 | 3 | getPrimitivePrototype(runtime, base); |
314 | 3 | if (primitivePrototypeResult == ExecutionStatus::EXCEPTION) { |
315 | | // If an exception is thrown, likely we are trying to read property on |
316 | | // undefined/null. Passing over the name of the property |
317 | | // so that we could emit more meaningful error messages. |
318 | 0 | return amendPropAccessErrorMsgWithPropName(runtime, base, "read", id); |
319 | 0 | } |
320 | | |
321 | 3 | return JSObject::getNamedWithReceiver_RJS( |
322 | 3 | *primitivePrototypeResult, runtime, id, base); |
323 | 3 | } |
324 | | |
325 | | PseudoHandle<> Interpreter::getByValTransientFast( |
326 | | Runtime &runtime, |
327 | | Handle<> base, |
328 | 0 | Handle<> nameHandle) { |
329 | 0 | if (base->isString()) { |
330 | | // Handle most common fast path -- array index property for string |
331 | | // primitive. |
332 | | // Since primitive string cannot have index like property we can |
333 | | // skip ObjectFlags::fastIndexProperties checking and directly |
334 | | // checking index storage from StringPrimitive. |
335 | |
|
336 | 0 | OptValue<uint32_t> arrayIndex = toArrayIndexFastPath(*nameHandle); |
337 | | // Get character directly from primitive if arrayIndex is within range. |
338 | | // Otherwise we need to fall back to prototype lookup. |
339 | 0 | if (arrayIndex && |
340 | 0 | arrayIndex.getValue() < base->getString()->getStringLength()) { |
341 | 0 | return createPseudoHandle( |
342 | 0 | runtime |
343 | 0 | .getCharacterString(base->getString()->at(arrayIndex.getValue())) |
344 | 0 | .getHermesValue()); |
345 | 0 | } |
346 | 0 | } |
347 | 0 | return createPseudoHandle(HermesValue::encodeEmptyValue()); |
348 | 0 | } |
349 | | |
350 | | CallResult<PseudoHandle<>> Interpreter::getByValTransient_RJS( |
351 | | Runtime &runtime, |
352 | | Handle<> base, |
353 | 0 | Handle<> name) { |
354 | | // This is similar to what ES5.1 8.7.1 special [[Get]] internal |
355 | | // method did, but that section doesn't exist in ES9 anymore. |
356 | | // Instead, the [[Get]] Receiver argument serves a similar purpose. |
357 | | |
358 | | // Optimization: check fast path first. |
359 | 0 | PseudoHandle<> fastRes = getByValTransientFast(runtime, base, name); |
360 | 0 | if (!fastRes->isEmpty()) { |
361 | 0 | return fastRes; |
362 | 0 | } |
363 | | |
364 | 0 | auto res = toObject(runtime, base); |
365 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) |
366 | 0 | return ExecutionStatus::EXCEPTION; |
367 | | |
368 | 0 | return JSObject::getComputedWithReceiver_RJS( |
369 | 0 | runtime.makeHandle<JSObject>(res.getValue()), runtime, name, base); |
370 | 0 | } |
371 | | |
372 | | static ExecutionStatus |
373 | 0 | transientObjectPutErrorMessage(Runtime &runtime, Handle<> base, SymbolID id) { |
374 | | // Emit an error message that looks like: |
375 | | // "Cannot create property '%{id}' on ${typeof base} '${String(base)}'". |
376 | 0 | StringView propName = runtime.getIdentifierTable().getStringView(runtime, id); |
377 | 0 | Handle<StringPrimitive> baseType = |
378 | 0 | runtime.makeHandle(vmcast<StringPrimitive>(typeOf(runtime, base))); |
379 | 0 | StringView baseTypeAsString = |
380 | 0 | StringPrimitive::createStringView(runtime, baseType); |
381 | 0 | MutableHandle<StringPrimitive> valueAsString{runtime}; |
382 | 0 | if (base->isSymbol()) { |
383 | | // Special workaround for Symbol which can't be stringified. |
384 | 0 | auto str = symbolDescriptiveString(runtime, Handle<SymbolID>::vmcast(base)); |
385 | 0 | if (str != ExecutionStatus::EXCEPTION) { |
386 | 0 | valueAsString = *str; |
387 | 0 | } else { |
388 | 0 | runtime.clearThrownValue(); |
389 | 0 | valueAsString = StringPrimitive::createNoThrow( |
390 | 0 | runtime, "<<Exception occurred getting the value>>"); |
391 | 0 | } |
392 | 0 | } else { |
393 | 0 | auto str = toString_RJS(runtime, base); |
394 | 0 | assert( |
395 | 0 | str != ExecutionStatus::EXCEPTION && |
396 | 0 | "Primitives should be convertible to string without exceptions"); |
397 | 0 | valueAsString = std::move(*str); |
398 | 0 | } |
399 | 0 | StringView valueAsStringPrintable = |
400 | 0 | StringPrimitive::createStringView(runtime, valueAsString); |
401 | |
|
402 | 0 | SmallU16String<32> tmp1; |
403 | 0 | SmallU16String<32> tmp2; |
404 | 0 | return runtime.raiseTypeError( |
405 | 0 | TwineChar16("Cannot create property '") + propName + "' on " + |
406 | 0 | baseTypeAsString.getUTF16Ref(tmp1) + " '" + |
407 | 0 | valueAsStringPrintable.getUTF16Ref(tmp2) + "'"); |
408 | 0 | } |
409 | | |
410 | | ExecutionStatus Interpreter::putByIdTransient_RJS( |
411 | | Runtime &runtime, |
412 | | Handle<> base, |
413 | | SymbolID id, |
414 | | Handle<> value, |
415 | 0 | bool strictMode) { |
416 | | // ES5.1 8.7.2 special [[Get]] internal method. |
417 | | // TODO: avoid boxing primitives unless we are calling an accessor. |
418 | | |
419 | | // 1. Let O be ToObject(base) |
420 | 0 | auto res = toObject(runtime, base); |
421 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
422 | | // If an exception is thrown, likely we are trying to convert |
423 | | // undefined/null to an object. Passing over the name of the property |
424 | | // so that we could emit more meaningful error messages. |
425 | 0 | return amendPropAccessErrorMsgWithPropName(runtime, base, "set", id); |
426 | 0 | } |
427 | | |
428 | 0 | auto O = runtime.makeHandle<JSObject>(res.getValue()); |
429 | |
|
430 | 0 | NamedPropertyDescriptor desc; |
431 | 0 | JSObject *propObj = JSObject::getNamedDescriptorUnsafe(O, runtime, id, desc); |
432 | | |
433 | | // Is this a missing property, or a data property defined in the prototype |
434 | | // chain? In both cases we would need to create an own property on the |
435 | | // transient object, which is prohibited. |
436 | 0 | if (!propObj || |
437 | 0 | (propObj != O.get() && |
438 | 0 | (!desc.flags.accessor && !desc.flags.proxyObject))) { |
439 | 0 | if (strictMode) { |
440 | 0 | return transientObjectPutErrorMessage(runtime, base, id); |
441 | 0 | } |
442 | 0 | return ExecutionStatus::RETURNED; |
443 | 0 | } |
444 | | |
445 | | // Modifying an own data property in a transient object is prohibited. |
446 | 0 | if (!desc.flags.accessor && !desc.flags.proxyObject) { |
447 | 0 | if (strictMode) { |
448 | 0 | return runtime.raiseTypeError( |
449 | 0 | "Cannot modify a property in a transient object"); |
450 | 0 | } |
451 | 0 | return ExecutionStatus::RETURNED; |
452 | 0 | } |
453 | | |
454 | 0 | if (desc.flags.accessor) { |
455 | | // This is an accessor. |
456 | 0 | auto *accessor = vmcast<PropertyAccessor>( |
457 | 0 | JSObject::getNamedSlotValueUnsafe(propObj, runtime, desc) |
458 | 0 | .getObject(runtime)); |
459 | | |
460 | | // It needs to have a setter. |
461 | 0 | if (!accessor->setter) { |
462 | 0 | if (strictMode) { |
463 | 0 | return runtime.raiseTypeError("Cannot modify a read-only accessor"); |
464 | 0 | } |
465 | 0 | return ExecutionStatus::RETURNED; |
466 | 0 | } |
467 | | |
468 | 0 | CallResult<PseudoHandle<>> setRes = |
469 | 0 | accessor->setter.getNonNull(runtime)->executeCall1( |
470 | 0 | runtime.makeHandle(accessor->setter), runtime, base, *value); |
471 | 0 | if (setRes == ExecutionStatus::EXCEPTION) { |
472 | 0 | return ExecutionStatus::EXCEPTION; |
473 | 0 | } |
474 | 0 | } else { |
475 | 0 | assert(desc.flags.proxyObject && "descriptor flags are impossible"); |
476 | 0 | CallResult<bool> setRes = JSProxy::setNamed( |
477 | 0 | runtime.makeHandle(propObj), runtime, id, value, base); |
478 | 0 | if (setRes == ExecutionStatus::EXCEPTION) { |
479 | 0 | return ExecutionStatus::EXCEPTION; |
480 | 0 | } |
481 | 0 | if (!*setRes && strictMode) { |
482 | 0 | return runtime.raiseTypeError("transient proxy set returned false"); |
483 | 0 | } |
484 | 0 | } |
485 | 0 | return ExecutionStatus::RETURNED; |
486 | 0 | } |
487 | | |
488 | | ExecutionStatus Interpreter::putByValTransient_RJS( |
489 | | Runtime &runtime, |
490 | | Handle<> base, |
491 | | Handle<> name, |
492 | | Handle<> value, |
493 | 0 | bool strictMode) { |
494 | 0 | auto idRes = valueToSymbolID(runtime, name); |
495 | 0 | if (idRes == ExecutionStatus::EXCEPTION) |
496 | 0 | return ExecutionStatus::EXCEPTION; |
497 | | |
498 | 0 | return putByIdTransient_RJS(runtime, base, **idRes, value, strictMode); |
499 | 0 | } |
500 | | |
501 | | static Handle<HiddenClass> getHiddenClassForBuffer( |
502 | | Runtime &runtime, |
503 | | CodeBlock *curCodeBlock, |
504 | | unsigned numLiterals, |
505 | 0 | unsigned keyBufferIndex) { |
506 | 0 | RuntimeModule *runtimeModule = curCodeBlock->getRuntimeModule(); |
507 | 0 | if (auto clazzOpt = runtimeModule->findCachedLiteralHiddenClass( |
508 | 0 | runtime, keyBufferIndex, numLiterals)) |
509 | 0 | return *clazzOpt; |
510 | | |
511 | 0 | MutableHandle<> tmpHandleKey{runtime}; |
512 | 0 | MutableHandle<HiddenClass> clazz = |
513 | 0 | runtime.makeMutableHandle(*runtime.getHiddenClassForPrototype( |
514 | 0 | vmcast<JSObject>(runtime.objectPrototype), |
515 | 0 | JSObject::numOverlapSlots<JSObject>())); |
516 | |
|
517 | 0 | GCScopeMarkerRAII marker{runtime}; |
518 | 0 | auto keyGen = |
519 | 0 | curCodeBlock->getObjectBufferKeyIter(keyBufferIndex, numLiterals); |
520 | 0 | while (keyGen.hasNext()) { |
521 | 0 | auto key = keyGen.get(runtime); |
522 | 0 | SymbolID sym = [&] { |
523 | 0 | if (key.isSymbol()) |
524 | 0 | return ID(key.getSymbol().unsafeGetIndex()); |
525 | | |
526 | 0 | assert(key.isNumber() && "Key must be symbol or number"); |
527 | 0 | tmpHandleKey = key; |
528 | | // Note that since this handle has been created, the associated symbol |
529 | | // will be automatically kept alive until we flush the marker. |
530 | | // valueToSymbolID cannot fail because the key is known to be uint32. |
531 | 0 | Handle<SymbolID> symHandle = *valueToSymbolID(runtime, tmpHandleKey); |
532 | 0 | return *symHandle; |
533 | 0 | }(); |
534 | 0 | auto addResult = HiddenClass::addProperty( |
535 | 0 | clazz, runtime, sym, PropertyFlags::defaultNewNamedPropertyFlags()); |
536 | 0 | clazz = addResult->first; |
537 | 0 | marker.flush(); |
538 | 0 | } |
539 | |
|
540 | 0 | if (LLVM_LIKELY(!clazz->isDictionary())) { |
541 | 0 | assert( |
542 | 0 | numLiterals == clazz->getNumProperties() && |
543 | 0 | "numLiterals should match hidden class property count."); |
544 | 0 | assert( |
545 | 0 | clazz->getNumProperties() < 256 && |
546 | 0 | "cached hidden class should have property count less than 256"); |
547 | 0 | runtimeModule->tryCacheLiteralHiddenClass(runtime, keyBufferIndex, *clazz); |
548 | 0 | } |
549 | | |
550 | 0 | return {clazz}; |
551 | 0 | } |
552 | | |
553 | | CallResult<PseudoHandle<>> Interpreter::createObjectFromBuffer( |
554 | | Runtime &runtime, |
555 | | CodeBlock *curCodeBlock, |
556 | | unsigned numLiterals, |
557 | | unsigned keyBufferIndex, |
558 | 0 | unsigned valBufferIndex) { |
559 | | // Create a new object using the built-in constructor or cached hidden class. |
560 | | // Note that the built-in constructor is empty, so we don't actually need to |
561 | | // call it. |
562 | 0 | auto clazz = getHiddenClassForBuffer( |
563 | 0 | runtime, curCodeBlock, numLiterals, keyBufferIndex); |
564 | 0 | auto obj = runtime.makeHandle(JSObject::create(runtime, clazz)); |
565 | |
|
566 | 0 | auto valGen = |
567 | 0 | curCodeBlock->getObjectBufferValueIter(valBufferIndex, numLiterals); |
568 | |
|
569 | 0 | #ifndef NDEBUG |
570 | 0 | auto keyGen = |
571 | 0 | curCodeBlock->getObjectBufferKeyIter(keyBufferIndex, numLiterals); |
572 | 0 | #endif |
573 | |
|
574 | 0 | uint32_t propIndex = 0; |
575 | | // keyGen should always have the same amount of elements as valGen |
576 | 0 | while (valGen.hasNext()) { |
577 | 0 | #ifndef NDEBUG |
578 | 0 | { |
579 | 0 | GCScopeMarkerRAII marker{runtime}; |
580 | | // keyGen points to an element in the key buffer, which means it will |
581 | | // only ever generate a Number or a Symbol. This means it will never |
582 | | // allocate memory, and it is safe to not use a Handle. |
583 | 0 | SymbolID stringIdResult{}; |
584 | 0 | auto key = keyGen.get(runtime); |
585 | 0 | if (key.isSymbol()) { |
586 | 0 | stringIdResult = ID(key.getSymbol().unsafeGetIndex()); |
587 | 0 | } else { |
588 | 0 | auto keyHandle = runtime.makeHandle( |
589 | 0 | HermesValue::encodeUntrustedNumberValue(key.getNumber())); |
590 | 0 | auto idRes = valueToSymbolID(runtime, keyHandle); |
591 | 0 | assert( |
592 | 0 | idRes != ExecutionStatus::EXCEPTION && |
593 | 0 | "valueToIdentifier() failed for uint32_t value"); |
594 | 0 | stringIdResult = **idRes; |
595 | 0 | } |
596 | 0 | NamedPropertyDescriptor desc; |
597 | 0 | auto pos = HiddenClass::findProperty( |
598 | 0 | clazz, |
599 | 0 | runtime, |
600 | 0 | stringIdResult, |
601 | 0 | PropertyFlags::defaultNewNamedPropertyFlags(), |
602 | 0 | desc); |
603 | 0 | assert( |
604 | 0 | pos && |
605 | 0 | "Should find this property in cached hidden class property table."); |
606 | 0 | assert( |
607 | 0 | desc.slot == propIndex && |
608 | 0 | "propIndex should be the same as recorded in hidden class table."); |
609 | 0 | } |
610 | 0 | #endif |
611 | | // Explicitly make sure valGen.get() is called before obj.get() so that |
612 | | // any allocation in valGen.get() won't invalidate the raw pointer |
613 | | // returned from obj.get(). |
614 | 0 | auto val = valGen.get(runtime); |
615 | 0 | auto shv = SmallHermesValue::encodeHermesValue(val, runtime); |
616 | | // We made this object, it's not a Proxy. |
617 | 0 | JSObject::setNamedSlotValueUnsafe(obj.get(), runtime, propIndex, shv); |
618 | 0 | ++propIndex; |
619 | 0 | } |
620 | | |
621 | 0 | return createPseudoHandle(HermesValue::encodeObjectValue(*obj)); |
622 | 0 | } |
623 | | |
624 | | CallResult<PseudoHandle<>> Interpreter::createArrayFromBuffer( |
625 | | Runtime &runtime, |
626 | | CodeBlock *curCodeBlock, |
627 | | unsigned numElements, |
628 | | unsigned numLiterals, |
629 | 12 | unsigned bufferIndex) { |
630 | | // Create a new array using the built-in constructor, and initialize |
631 | | // the elements from a literal array buffer. |
632 | 12 | auto arrRes = JSArray::create(runtime, numElements, numElements); |
633 | 12 | if (arrRes == ExecutionStatus::EXCEPTION) { |
634 | 0 | return ExecutionStatus::EXCEPTION; |
635 | 0 | } |
636 | | // Resize the array storage in advance. |
637 | 12 | auto arr = *arrRes; |
638 | 12 | JSArray::setStorageEndIndex(arr, runtime, numElements); |
639 | | |
640 | 12 | auto iter = curCodeBlock->getArrayBufferIter(bufferIndex, numLiterals); |
641 | 12 | JSArray::size_type i = 0; |
642 | 260k | while (iter.hasNext()) { |
643 | | // NOTE: we must get the value in a separate step to guarantee ordering. |
644 | 260k | const auto value = |
645 | 260k | SmallHermesValue::encodeHermesValue(iter.get(runtime), runtime); |
646 | 260k | JSArray::unsafeSetExistingElementAt(*arr, runtime, i++, value); |
647 | 260k | } |
648 | | |
649 | 12 | return createPseudoHandle(HermesValue::encodeObjectValue(*arr)); |
650 | 12 | } |
651 | | |
652 | | #ifndef NDEBUG |
653 | | |
654 | 0 | llvh::raw_ostream &operator<<(llvh::raw_ostream &OS, DumpHermesValue dhv) { |
655 | 0 | OS << dhv.hv; |
656 | | // If it is a string, dump the contents, truncated to 8 characters. |
657 | 0 | if (dhv.hv.isString()) { |
658 | 0 | SmallU16String<32> str; |
659 | 0 | dhv.hv.getString()->appendUTF16String(str); |
660 | 0 | UTF16Ref ref = str.arrayRef(); |
661 | 0 | if (str.size() <= 8) { |
662 | 0 | OS << ":'" << ref << "'"; |
663 | 0 | } else { |
664 | 0 | OS << ":'" << ref.slice(0, 8) << "'"; |
665 | 0 | OS << "...[" << str.size() << "]"; |
666 | 0 | } |
667 | 0 | } |
668 | 0 | return OS; |
669 | 0 | } |
670 | | |
671 | | void dumpCallArguments( |
672 | | llvh::raw_ostream &OS, |
673 | | Runtime &runtime, |
674 | 0 | StackFramePtr calleeFrame) { |
675 | 0 | OS << "arguments:\n"; |
676 | 0 | OS << " " << 0 << " " << DumpHermesValue(calleeFrame.getThisArgRef()) |
677 | 0 | << "\n"; |
678 | 0 | for (unsigned i = 0; i < calleeFrame.getArgCount(); ++i) { |
679 | 0 | OS << " " << (i + 1) << " " << DumpHermesValue(calleeFrame.getArgRef(i)) |
680 | 0 | << "\n"; |
681 | 0 | } |
682 | 0 | } |
683 | | |
684 | | LLVM_ATTRIBUTE_UNUSED |
685 | | static void printDebugInfo( |
686 | | CodeBlock *curCodeBlock, |
687 | | PinnedHermesValue *frameRegs, |
688 | 9.84M | const Inst *ip) { |
689 | | // Check if LLVm debugging is enabled for us. |
690 | 9.84M | bool debug = false; |
691 | 9.84M | SLOW_DEBUG(debug = true); |
692 | 9.84M | if (!debug) |
693 | 9.84M | return; |
694 | | |
695 | 0 | DecodedInstruction decoded = decodeInstruction(ip); |
696 | |
|
697 | 0 | dbgs() << llvh::format_decimal((const uint8_t *)ip - curCodeBlock->begin(), 4) |
698 | 0 | << " OpCode::" << getOpCodeString(decoded.meta.opCode); |
699 | |
|
700 | 0 | for (unsigned i = 0; i < decoded.meta.numOperands; ++i) { |
701 | 0 | auto operandType = decoded.meta.operandType[i]; |
702 | 0 | auto value = decoded.operandValue[i]; |
703 | |
|
704 | 0 | dbgs() << (i == 0 ? " " : ", "); |
705 | 0 | dumpOperand(dbgs(), operandType, value); |
706 | |
|
707 | 0 | if (operandType == OperandType::Reg8 || operandType == OperandType::Reg32) { |
708 | | // Print the register value, if source. |
709 | 0 | if (i != 0 || decoded.meta.numOperands == 1) |
710 | 0 | dbgs() << "=" |
711 | 0 | << DumpHermesValue(REG(static_cast<uint32_t>(value.integer))); |
712 | 0 | } |
713 | 0 | } |
714 | |
|
715 | 0 | dbgs() << "\n"; |
716 | 0 | } |
717 | | |
718 | | /// \return whether \p opcode is a call opcode (Call, CallDirect, Construct, |
719 | | /// CallLongIndex, etc). Note CallBuiltin is not really a Call. |
720 | | LLVM_ATTRIBUTE_UNUSED |
721 | 1.81k | static bool isCallType(OpCode opcode) { |
722 | 1.81k | switch (opcode) { |
723 | 0 | #define DEFINE_RET_TARGET(name) \ |
724 | 1.81k | case OpCode::name: \ |
725 | 1.81k | return true; |
726 | 0 | #include "hermes/BCGen/HBC/BytecodeList.def" |
727 | 0 | default: |
728 | 0 | return false; |
729 | 1.81k | } |
730 | 1.81k | } |
731 | | |
732 | | #endif |
733 | | |
734 | | /// \return the address of the next instruction after \p ip, which must be a |
735 | | /// call-type instruction. |
736 | | LLVM_ATTRIBUTE_ALWAYS_INLINE |
737 | 1.81k | static inline const Inst *nextInstCall(const Inst *ip) { |
738 | 1.81k | HERMES_SLOW_ASSERT(isCallType(ip->opCode) && "ip is not of call type"); |
739 | | |
740 | | // To avoid needing a large lookup table or switchcase, the following packs |
741 | | // information about the size of each call opcode into a uint32_t. Each call |
742 | | // type is represented with two bits, representing how much larger it is than |
743 | | // the smallest call instruction. |
744 | | // If we used 64 bits, we could fit the actual size of each call, without |
745 | | // needing the offset, and this may be necessary if new call instructions are |
746 | | // added in the future. For now however, due to limitations on loading large |
747 | | // immediates in ARM, it is significantly more efficient to use a uint32_t |
748 | | // than a uint64_t. |
749 | 1.81k | constexpr auto firstCall = std::min({ |
750 | 18.1k | #define DEFINE_RET_TARGET(name) static_cast<uint8_t>(OpCode::name), |
751 | 1.81k | #include "hermes/BCGen/HBC/BytecodeList.def" |
752 | 1.81k | }); |
753 | 1.81k | constexpr auto lastCall = std::max({ |
754 | 18.1k | #define DEFINE_RET_TARGET(name) static_cast<uint8_t>(OpCode::name), |
755 | 1.81k | #include "hermes/BCGen/HBC/BytecodeList.def" |
756 | 1.81k | }); |
757 | 1.81k | constexpr auto minSize = std::min({ |
758 | 18.1k | #define DEFINE_RET_TARGET(name) sizeof(inst::name##Inst), |
759 | 1.81k | #include "hermes/BCGen/HBC/BytecodeList.def" |
760 | 1.81k | }); |
761 | 1.81k | constexpr auto maxSize = std::max({ |
762 | 18.1k | #define DEFINE_RET_TARGET(name) sizeof(inst::name##Inst), |
763 | 1.81k | #include "hermes/BCGen/HBC/BytecodeList.def" |
764 | 1.81k | }); |
765 | | |
766 | 1.81k | constexpr uint32_t W = 2; |
767 | 1.81k | constexpr uint32_t mask = (1 << W) - 1; |
768 | | |
769 | 1.81k | static_assert(llvh::isUInt<W>(maxSize - minSize), "Size range too large."); |
770 | 1.81k | static_assert((lastCall - firstCall + 1) * W <= 32, "Too many call opcodes."); |
771 | | |
772 | 1.81k | constexpr uint32_t callSizes = 0 |
773 | 1.81k | #define DEFINE_RET_TARGET(name) \ |
774 | 18.1k | | \ |
775 | 18.1k | ((sizeof(inst::name##Inst) - minSize) \ |
776 | 18.1k | << (((uint8_t)OpCode::name - firstCall) * W)) |
777 | 1.81k | #include "hermes/BCGen/HBC/BytecodeList.def" |
778 | 1.81k | ; |
779 | 1.81k | #undef DEFINE_RET_TARGET |
780 | | |
781 | 1.81k | const uint8_t offset = static_cast<uint8_t>(ip->opCode) - firstCall; |
782 | 1.81k | return IPADD(((callSizes >> (offset * W)) & mask) + minSize); |
783 | 1.81k | } |
784 | | |
785 | | CallResult<HermesValue> Runtime::interpretFunctionImpl( |
786 | 215 | CodeBlock *newCodeBlock) { |
787 | 215 | if (LLVM_UNLIKELY( |
788 | 215 | newCodeBlock->lazyCompile(*this) == ExecutionStatus::EXCEPTION)) { |
789 | 0 | return ExecutionStatus::EXCEPTION; |
790 | 0 | } |
791 | | |
792 | 215 | #if defined(HERMES_MEMORY_INSTRUMENTATION) || !defined(NDEBUG) |
793 | | // We always call getCurrentIP() in a debug build as this has the effect |
794 | | // of asserting the IP is correctly set (not invalidated) at this point. |
795 | | // This allows us to leverage our whole test-suite to find missing cases |
796 | | // of CAPTURE_IP* macros in the interpreter loop. |
797 | 215 | const inst::Inst *ip = getCurrentIP(); |
798 | 215 | (void)ip; |
799 | 215 | #endif |
800 | 215 | #ifdef HERMES_MEMORY_INSTRUMENTATION |
801 | 215 | if (ip) { |
802 | 21 | const CodeBlock *codeBlock; |
803 | 21 | std::tie(codeBlock, ip) = getCurrentInterpreterLocation(ip); |
804 | | // All functions end in a Ret so we must match this with a pushCallStack() |
805 | | // before executing. |
806 | 21 | if (codeBlock) { |
807 | | // Push a call entry at the last location we were executing bytecode. |
808 | | // This will correctly attribute things like eval(). |
809 | 21 | pushCallStack(codeBlock, ip); |
810 | 21 | } else { |
811 | | // Push a call entry at the entry at the top of interpreted code. |
812 | 0 | pushCallStack(newCodeBlock, (const Inst *)newCodeBlock->begin()); |
813 | 0 | } |
814 | 194 | } else { |
815 | | // Push a call entry at the entry at the top of interpreted code. |
816 | 194 | pushCallStack(newCodeBlock, (const Inst *)newCodeBlock->begin()); |
817 | 194 | } |
818 | 215 | #endif |
819 | | |
820 | 215 | InterpreterState state{newCodeBlock, 0}; |
821 | 215 | if (HERMESVM_CRASH_TRACE && |
822 | 215 | (getVMExperimentFlags() & experiments::CrashTrace)) { |
823 | 0 | return Interpreter::interpretFunction<false, true>(*this, state); |
824 | 215 | } else { |
825 | 215 | return Interpreter::interpretFunction<false, false>(*this, state); |
826 | 215 | } |
827 | 215 | } |
828 | | |
829 | 215 | CallResult<HermesValue> Runtime::interpretFunction(CodeBlock *newCodeBlock) { |
830 | | // Make sure we are not re-entering JS execution from a context that doesn't |
831 | | // allow reentrancy |
832 | 215 | assert(this->noRJSLevel_ == 0 && "No JS execution allowed right now."); |
833 | 215 | return interpretFunctionImpl(newCodeBlock); |
834 | 215 | } |
835 | | |
836 | | #ifdef HERMES_ENABLE_DEBUGGER |
837 | 0 | ExecutionStatus Runtime::stepFunction(InterpreterState &state) { |
838 | 0 | if (HERMESVM_CRASH_TRACE && |
839 | 0 | (getVMExperimentFlags() & experiments::CrashTrace)) |
840 | 0 | return Interpreter::interpretFunction<true, true>(*this, state).getStatus(); |
841 | 0 | else |
842 | 0 | return Interpreter::interpretFunction<true, false>(*this, state) |
843 | 0 | .getStatus(); |
844 | 0 | } |
845 | | #endif |
846 | | |
847 | | template <bool SingleStep, bool EnableCrashTrace> |
848 | | CallResult<HermesValue> Interpreter::interpretFunction( |
849 | | Runtime &runtime, |
850 | 215 | InterpreterState &state) { |
851 | | // The interpreter is re-entrant and also saves/restores its IP via the |
852 | | // runtime whenever a call out is made (see the CAPTURE_IP_* macros). As such, |
853 | | // failure to preserve the IP across calls to interpreterFunction() disrupt |
854 | | // interpreter calls further up the C++ callstack. The RAII utility class |
855 | | // below makes sure we always do this correctly. |
856 | | // |
857 | | // TODO: The IPs stored in the C++ callstack via this holder will generally be |
858 | | // the same as in the JS stack frames via the Saved IP field. We can probably |
859 | | // get rid of one of these redundant stores. Doing this isn't completely |
860 | | // trivial as there are currently cases where we re-enter the interpreter |
861 | | // without calling Runtime::saveCallerIPInStackFrame(), and there are features |
862 | | // (I think mostly the debugger + stack traces) which implicitly rely on |
863 | | // this behavior. At least their tests break if this behavior is not |
864 | | // preserved. |
865 | 215 | struct IPSaver { |
866 | 215 | IPSaver(Runtime &runtime) |
867 | 215 | : ip_(runtime.getCurrentIP()), runtime_(runtime) {} hermes::vm::Interpreter::interpretFunction<false, false>(hermes::vm::Runtime&, hermes::vm::InterpreterState&)::IPSaver::IPSaver(hermes::vm::Runtime&) Line | Count | Source | 867 | 215 | : ip_(runtime.getCurrentIP()), runtime_(runtime) {} |
Unexecuted instantiation: hermes::vm::Interpreter::interpretFunction<true, false>(hermes::vm::Runtime&, hermes::vm::InterpreterState&)::IPSaver::IPSaver(hermes::vm::Runtime&) |
868 | | |
869 | 215 | ~IPSaver() { |
870 | 215 | runtime_.setCurrentIP(ip_); |
871 | 215 | } hermes::vm::Interpreter::interpretFunction<false, false>(hermes::vm::Runtime&, hermes::vm::InterpreterState&)::IPSaver::~IPSaver() Line | Count | Source | 869 | 215 | ~IPSaver() { | 870 | 215 | runtime_.setCurrentIP(ip_); | 871 | 215 | } |
Unexecuted instantiation: hermes::vm::Interpreter::interpretFunction<true, false>(hermes::vm::Runtime&, hermes::vm::InterpreterState&)::IPSaver::~IPSaver() |
872 | | |
873 | 215 | private: |
874 | 215 | const Inst *ip_; |
875 | 215 | Runtime &runtime_; |
876 | 215 | }; |
877 | 215 | IPSaver ipSaver(runtime); |
878 | | |
879 | | #ifndef HERMES_ENABLE_DEBUGGER |
880 | | static_assert(!SingleStep, "can't use single-step mode without the debugger"); |
881 | | #endif |
882 | | // Make sure that the cache can use an optimization by avoiding a branch to |
883 | | // access the property storage. |
884 | 215 | static_assert( |
885 | 215 | HiddenClass::kDictionaryThreshold <= |
886 | 215 | SegmentedArray::kValueToSegmentThreshold, |
887 | 215 | "Cannot avoid branches in cache check if the dictionary " |
888 | 215 | "crossover point is larger than the inline storage"); |
889 | | |
890 | 215 | CodeBlock *curCodeBlock = state.codeBlock; |
891 | 215 | const Inst *ip = nullptr; |
892 | | // Points to the first local register in the current frame. |
893 | | // This eliminates the indirect load from Runtime and the -1 offset. |
894 | 215 | PinnedHermesValue *frameRegs; |
895 | | // Strictness of current function. |
896 | 215 | bool strictMode; |
897 | | // Default flags when accessing properties. |
898 | 215 | PropOpFlags defaultPropOpFlags; |
899 | | |
900 | | // These CAPTURE_IP* macros should wrap around any major calls out of the |
901 | | // interpreter loop. They stash and retrieve the IP via the current Runtime |
902 | | // allowing the IP to be externally observed and even altered to change the flow |
903 | | // of execution. Explicitly saving AND restoring the IP from the Runtime in this |
904 | | // way means the C++ compiler will keep IP in a register within the rest of the |
905 | | // interpreter loop. |
906 | | // |
907 | | // When assertions are enabled we take the extra step of "invalidating" the IP |
908 | | // between captures so we can detect if it's erroneously accessed. |
909 | | // |
910 | | #ifdef NDEBUG |
911 | | |
912 | | #define CAPTURE_IP(expr) \ |
913 | | runtime.setCurrentIP(ip); \ |
914 | | (void)(expr); \ |
915 | | ip = runtime.getCurrentIP(); |
916 | | |
917 | | // Used when we want to declare a new variable and assign the expression to it. |
918 | | #define CAPTURE_IP_ASSIGN(decl, expr) \ |
919 | | runtime.setCurrentIP(ip); \ |
920 | | decl = (expr); \ |
921 | | ip = runtime.getCurrentIP(); |
922 | | |
923 | | #else // !NDEBUG |
924 | | |
925 | 215 | #define CAPTURE_IP(expr) \ |
926 | 2.70M | runtime.setCurrentIP(ip); \ |
927 | 2.70M | (void)(expr); \ |
928 | 2.70M | ip = runtime.getCurrentIP(); \ |
929 | 2.70M | runtime.invalidateCurrentIP(); |
930 | | |
931 | | // Used when we want to declare a new variable and assign the expression to it. |
932 | 215 | #define CAPTURE_IP_ASSIGN(decl, expr) \ |
933 | 3.77M | runtime.setCurrentIP(ip); \ |
934 | 3.77M | decl = (expr); \ |
935 | 3.77M | ip = runtime.getCurrentIP(); \ |
936 | 3.77M | runtime.invalidateCurrentIP(); |
937 | | |
938 | 215 | #endif // NDEBUG |
939 | | |
940 | | /// \def DONT_CAPTURE_IP(expr) |
941 | | /// \param expr A call expression to a function external to the interpreter. The |
942 | | /// expression should not make any allocations and the IP should be set |
943 | | /// immediately following this macro. |
944 | 215 | #define DONT_CAPTURE_IP(expr) \ |
945 | 215 | do { \ |
946 | 0 | NoAllocScope noAlloc(runtime); \ |
947 | 0 | (void)expr; \ |
948 | 0 | } while (false) |
949 | | |
950 | | // When performing a tail call, we need to set the runtime IP and leave it set. |
951 | 1.81k | #define CAPTURE_IP_SET() runtime.setCurrentIP(ip) |
952 | | |
953 | 215 | LLVM_DEBUG(dbgs() << "interpretFunction() called\n"); |
954 | | |
955 | 215 | ScopedNativeDepthTracker depthTracker{runtime}; |
956 | 215 | if (LLVM_UNLIKELY(depthTracker.overflowed())) { |
957 | 0 | return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack); |
958 | 0 | } |
959 | | |
960 | 215 | GCScope gcScope(runtime); |
961 | | // Avoid allocating a handle dynamically by reusing this one. |
962 | 215 | MutableHandle<> tmpHandle(runtime); |
963 | 215 | CallResult<HermesValue> res{ExecutionStatus::EXCEPTION}; |
964 | 215 | CallResult<PseudoHandle<>> resPH{ExecutionStatus::EXCEPTION}; |
965 | 215 | CallResult<Handle<Arguments>> resArgs{ExecutionStatus::EXCEPTION}; |
966 | 215 | CallResult<bool> boolRes{ExecutionStatus::EXCEPTION}; |
967 | | // Start of the bytecode file, used to calculate IP offset in crash traces. |
968 | 215 | const uint8_t *bytecodeFileStart; |
969 | | |
970 | | // Mark the gcScope so we can clear all allocated handles. |
971 | | // Remember how many handles the scope has so we can clear them in the loop. |
972 | 215 | static constexpr unsigned KEEP_HANDLES = 1; |
973 | 215 | assert( |
974 | 215 | gcScope.getHandleCountDbg() == KEEP_HANDLES && |
975 | 215 | "scope has unexpected number of handles"); |
976 | | |
977 | 215 | INIT_OPCODE_PROFILER; |
978 | | |
979 | 2.03k | tailCall: |
980 | 2.03k | PROFILER_ENTER_FUNCTION(curCodeBlock); |
981 | | |
982 | 2.03k | #ifdef HERMES_ENABLE_DEBUGGER |
983 | 2.03k | runtime.getDebugger().willEnterCodeBlock(curCodeBlock); |
984 | 2.03k | #endif |
985 | | |
986 | 2.03k | runtime.getCodeCoverageProfiler().markExecuted(curCodeBlock); |
987 | | |
988 | 2.03k | if (!SingleStep) { |
989 | 2.03k | auto newFrame = runtime.setCurrentFrameToTopOfStack(); |
990 | 2.03k | runtime.saveCallerIPInStackFrame(); |
991 | 2.03k | #ifndef NDEBUG |
992 | 2.03k | runtime.invalidateCurrentIP(); |
993 | 2.03k | #endif |
994 | | |
995 | | // Point frameRegs to the first register in the new frame. Note that at this |
996 | | // moment technically it points above the top of the stack, but we are never |
997 | | // going to access it. |
998 | 2.03k | frameRegs = &newFrame.getFirstLocalRef(); |
999 | | |
1000 | 2.03k | #ifndef NDEBUG |
1001 | 2.03k | LLVM_DEBUG( |
1002 | 2.03k | dbgs() << "function entry: stackLevel=" << runtime.getStackLevel() |
1003 | 2.03k | << ", argCount=" << runtime.getCurrentFrame().getArgCount() |
1004 | 2.03k | << ", frameSize=" << curCodeBlock->getFrameSize() << "\n"); |
1005 | | |
1006 | 2.03k | LLVM_DEBUG( |
1007 | 2.03k | dbgs() << " callee " |
1008 | 2.03k | << DumpHermesValue( |
1009 | 2.03k | runtime.getCurrentFrame().getCalleeClosureOrCBRef()) |
1010 | 2.03k | << "\n"); |
1011 | 2.03k | LLVM_DEBUG( |
1012 | 2.03k | dbgs() << " this " |
1013 | 2.03k | << DumpHermesValue(runtime.getCurrentFrame().getThisArgRef()) |
1014 | 2.03k | << "\n"); |
1015 | 3.48k | for (uint32_t i = 0; i != runtime.getCurrentFrame()->getArgCount(); ++i) { |
1016 | 1.45k | LLVM_DEBUG( |
1017 | 1.45k | dbgs() << " " << llvh::format_decimal(i, 4) << " " |
1018 | 1.45k | << DumpHermesValue(runtime.getCurrentFrame().getArgRef(i)) |
1019 | 1.45k | << "\n"); |
1020 | 1.45k | } |
1021 | 2.03k | #endif |
1022 | | |
1023 | | // Allocate the registers for the new frame. |
1024 | 2.03k | if (LLVM_UNLIKELY(!runtime.checkAndAllocStack( |
1025 | 2.03k | curCodeBlock->getFrameSize() + |
1026 | 2.03k | StackFrameLayout::CalleeExtraRegistersAtStart, |
1027 | 2.03k | HermesValue::encodeUndefinedValue()))) |
1028 | 0 | goto stackOverflow; |
1029 | | |
1030 | 2.03k | ip = (Inst const *)curCodeBlock->begin(); |
1031 | | |
1032 | | // Check for invalid invocation. |
1033 | 2.03k | if (LLVM_UNLIKELY(curCodeBlock->getHeaderFlags().isCallProhibited( |
1034 | 2.03k | newFrame.isConstructorCall()))) { |
1035 | 0 | if (!newFrame.isConstructorCall()) { |
1036 | 0 | CAPTURE_IP( |
1037 | 0 | runtime.raiseTypeError("Class constructor invoked without new")); |
1038 | 0 | } else { |
1039 | 0 | CAPTURE_IP(runtime.raiseTypeError("Function is not a constructor")); |
1040 | 0 | } |
1041 | 0 | goto handleExceptionInParent; |
1042 | 0 | } |
1043 | 2.03k | } else { |
1044 | | // Point frameRegs to the first register in the frame. |
1045 | 0 | frameRegs = &runtime.getCurrentFrame().getFirstLocalRef(); |
1046 | 0 | ip = (Inst const *)(curCodeBlock->begin() + state.offset); |
1047 | 0 | } |
1048 | | |
1049 | 2.03k | assert((const uint8_t *)ip < curCodeBlock->end() && "CodeBlock is empty"); |
1050 | | |
1051 | 2.03k | INIT_STATE_FOR_CODEBLOCK(curCodeBlock); |
1052 | | |
1053 | 2.03k | #define BEFORE_OP_CODE \ |
1054 | 10.3M | { \ |
1055 | 10.3M | UPDATE_OPCODE_TIME_SPENT; \ |
1056 | 10.3M | HERMES_SLOW_ASSERT( \ |
1057 | 10.3M | curCodeBlock->contains(ip) && "curCodeBlock must contain ip"); \ |
1058 | 10.3M | HERMES_SLOW_ASSERT((printDebugInfo(curCodeBlock, frameRegs, ip), true)); \ |
1059 | 9.84M | HERMES_SLOW_ASSERT( \ |
1060 | 9.84M | gcScope.getHandleCountDbg() == KEEP_HANDLES && \ |
1061 | 9.84M | "unaccounted handles were created"); \ |
1062 | 9.84M | HERMES_SLOW_ASSERT(tmpHandle->isUndefined() && "tmpHandle not cleared"); \ |
1063 | 9.84M | RECORD_OPCODE_START_TIME; \ |
1064 | 9.84M | INC_OPCODE_COUNT; \ |
1065 | 9.84M | if (EnableCrashTrace) { \ |
1066 | 0 | runtime.crashTrace_.recordInst( \ |
1067 | 0 | (uint32_t)((const uint8_t *)ip - bytecodeFileStart), ip->opCode); \ |
1068 | 0 | } \ |
1069 | 9.84M | } |
1070 | | |
1071 | 2.03k | #ifdef HERMESVM_INDIRECT_THREADING |
1072 | 2.03k | static void *opcodeDispatch[] = { |
1073 | 389k | #define DEFINE_OPCODE(name) &&case_##name, |
1074 | 2.03k | #include "hermes/BCGen/HBC/BytecodeList.def" |
1075 | 2.03k | &&case__last}; |
1076 | | |
1077 | 9.84M | #define CASE(name) case_##name: |
1078 | | // For indirect threading, there is no way to specify a default, leave it as |
1079 | | // an empty label. |
1080 | 2.03k | #define DEFAULT_CASE |
1081 | 2.03k | #define DISPATCH \ |
1082 | 10.3M | BEFORE_OP_CODE; \ |
1083 | 9.84M | if (SingleStep) { \ |
1084 | 0 | state.codeBlock = curCodeBlock; \ |
1085 | 0 | state.offset = CUROFFSET; \ |
1086 | 0 | return HermesValue::encodeUndefinedValue(); \ |
1087 | 0 | } \ |
1088 | 9.84M | goto *opcodeDispatch[(unsigned)ip->opCode] |
1089 | | |
1090 | | // Do nothing if we're not in a switch. |
1091 | 2.03k | #define INTERPRETER_FALLTHROUGH |
1092 | | |
1093 | | #else // HERMESVM_INDIRECT_THREADING |
1094 | | |
1095 | | #define CASE(name) case OpCode::name: |
1096 | | #define DEFAULT_CASE default: |
1097 | | #define DISPATCH \ |
1098 | | if (SingleStep) { \ |
1099 | | state.codeBlock = curCodeBlock; \ |
1100 | | state.offset = CUROFFSET; \ |
1101 | | return HermesValue::encodeUndefinedValue(); \ |
1102 | | } \ |
1103 | | continue |
1104 | | |
1105 | | // Fallthrough if we're in a switch. |
1106 | | #define INTERPRETER_FALLTHROUGH [[fallthrough]] |
1107 | | |
1108 | | #endif // HERMESVM_INDIRECT_THREADING |
1109 | | |
1110 | | // This macro is used when we detect that either the Implicit or Explicit |
1111 | | // AsyncBreak flags have been set. It checks to see which one was requested and |
1112 | | // propagate the corresponding RunReason. If both Implicit and Explicit have |
1113 | | // been requested, then we'll propagate the RunReasons for both. Once for |
1114 | | // Implicit and once for Explicit. |
1115 | 2.03k | #define RUN_DEBUGGER_ASYNC_BREAK(flags) \ |
1116 | 2.03k | bool requestedImplicit = (uint8_t)(flags) & \ |
1117 | 0 | (uint8_t)Runtime::AsyncBreakReasonBits::DebuggerImplicit; \ |
1118 | 0 | bool requestedExplicit = (uint8_t)(flags) & \ |
1119 | 0 | (uint8_t)Runtime::AsyncBreakReasonBits::DebuggerExplicit; \ |
1120 | 0 | do { \ |
1121 | 0 | if (requestedImplicit) { \ |
1122 | 0 | CAPTURE_IP_ASSIGN( \ |
1123 | 0 | auto dRes, \ |
1124 | 0 | runDebuggerUpdatingState( \ |
1125 | 0 | Debugger::RunReason::AsyncBreakImplicit, \ |
1126 | 0 | runtime, \ |
1127 | 0 | curCodeBlock, \ |
1128 | 0 | ip, \ |
1129 | 0 | frameRegs)); \ |
1130 | 0 | if (dRes == ExecutionStatus::EXCEPTION) \ |
1131 | 0 | goto exception; \ |
1132 | 0 | } \ |
1133 | 0 | if (requestedExplicit) { \ |
1134 | 0 | CAPTURE_IP_ASSIGN( \ |
1135 | 0 | auto dRes, \ |
1136 | 0 | runDebuggerUpdatingState( \ |
1137 | 0 | Debugger::RunReason::AsyncBreakExplicit, \ |
1138 | 0 | runtime, \ |
1139 | 0 | curCodeBlock, \ |
1140 | 0 | ip, \ |
1141 | 0 | frameRegs)); \ |
1142 | 0 | if (dRes == ExecutionStatus::EXCEPTION) \ |
1143 | 0 | goto exception; \ |
1144 | 0 | } \ |
1145 | 0 | } while (0) |
1146 | | |
1147 | 2.03k | for (;;) { |
1148 | 8.12k | BEFORE_OP_CODE; |
1149 | | |
1150 | 8.12k | #ifdef HERMESVM_INDIRECT_THREADING |
1151 | 8.12k | goto *opcodeDispatch[(unsigned)ip->opCode]; |
1152 | | #else |
1153 | | switch (ip->opCode) |
1154 | | #endif |
1155 | 8.12k | { |
1156 | 8.12k | const Inst *nextIP; |
1157 | 8.12k | uint32_t idVal; |
1158 | 8.12k | bool tryProp; |
1159 | 8.12k | uint32_t callArgCount; |
1160 | | // This is HermesValue::getRaw(), since HermesValue cannot be assigned |
1161 | | // to. It is meant to be used only for very short durations, in the |
1162 | | // dispatch of call instructions, when there is definitely no possibility |
1163 | | // of a GC. |
1164 | 8.12k | HermesValue::RawType callNewTarget; |
1165 | | |
1166 | | /// Handle an opcode \p name with an out-of-line implementation in a function |
1167 | | /// ExecutionStatus caseName( |
1168 | | /// Runtime &, |
1169 | | /// PinnedHermesValue *frameRegs, |
1170 | | /// Inst *ip) |
1171 | 8.12k | #define CASE_OUTOFLINE(name) \ |
1172 | 8.12k | CASE(name) { \ |
1173 | 27 | CAPTURE_IP_ASSIGN(auto res, case##name(runtime, frameRegs, ip)); \ |
1174 | 27 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { \ |
1175 | 0 | goto exception; \ |
1176 | 0 | } \ |
1177 | 27 | gcScope.flushToSmallCount(KEEP_HANDLES); \ |
1178 | 27 | ip = NEXTINST(name); \ |
1179 | 27 | DISPATCH; \ |
1180 | 27 | } |
1181 | | |
1182 | | /// Implement a binary arithmetic instruction with a fast path where both |
1183 | | /// operands are numbers. |
1184 | | /// \param name the name of the instruction. The fast path case will have a |
1185 | | /// "n" appended to the name. |
1186 | 8.12k | #define BINOP(name) \ |
1187 | 8.12k | CASE(name) { \ |
1188 | 321 | if (LLVM_LIKELY(O2REG(name).isNumber() && O3REG(name).isNumber())) { \ |
1189 | | /* Fast-path. */ \ |
1190 | 321 | INTERPRETER_FALLTHROUGH; \ |
1191 | 321 | CASE(name##N) { \ |
1192 | 321 | O1REG(name) = HermesValue::encodeTrustedNumberValue( \ |
1193 | 321 | do##name(O2REG(name).getNumber(), O3REG(name).getNumber())); \ |
1194 | 321 | ip = NEXTINST(name); \ |
1195 | 1.60k | DISPATCH; \ |
1196 | 1.60k | } \ |
1197 | 1.60k | } \ |
1198 | 321 | CAPTURE_IP( \ |
1199 | 321 | res = doOperSlowPath<do##name>( \ |
1200 | 321 | runtime, Handle<>(&O2REG(name)), Handle<>(&O3REG(name)))); \ |
1201 | 321 | if (res == ExecutionStatus::EXCEPTION) \ |
1202 | 321 | goto exception; \ |
1203 | 321 | O1REG(name) = *res; \ |
1204 | 321 | gcScope.flushToSmallCount(KEEP_HANDLES); \ |
1205 | 321 | ip = NEXTINST(name); \ |
1206 | 321 | DISPATCH; \ |
1207 | 0 | } |
1208 | | |
1209 | 8.12k | #define INCDECOP(name) \ |
1210 | 271k | CASE(name) { \ |
1211 | 271k | if (LLVM_LIKELY(O2REG(name).isNumber())) { \ |
1212 | 0 | O1REG(name) = HermesValue::encodeTrustedNumberValue( \ |
1213 | 0 | do##name(O2REG(name).getNumber())); \ |
1214 | 0 | ip = NEXTINST(name); \ |
1215 | 0 | DISPATCH; \ |
1216 | 0 | } \ |
1217 | 271k | CAPTURE_IP( \ |
1218 | 271k | res = \ |
1219 | 271k | doIncDecOperSlowPath<do##name>(runtime, Handle<>(&O2REG(name)))); \ |
1220 | 271k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { \ |
1221 | 0 | goto exception; \ |
1222 | 0 | } \ |
1223 | 271k | O1REG(name) = *res; \ |
1224 | 271k | gcScope.flushToSmallCount(KEEP_HANDLES); \ |
1225 | 271k | ip = NEXTINST(name); \ |
1226 | 271k | DISPATCH; \ |
1227 | 271k | } |
1228 | | |
1229 | | /// Implement a shift instruction with a fast path where both |
1230 | | /// operands are numbers. |
1231 | | /// \param name the name of the instruction. |
1232 | 8.12k | #define SHIFTOP(name) \ |
1233 | 8.12k | CASE(name) { \ |
1234 | 0 | if (LLVM_LIKELY( \ |
1235 | 0 | O2REG(name).isNumber() && \ |
1236 | 0 | O3REG(name).isNumber())) { /* Fast-path. */ \ |
1237 | 0 | auto lnum = hermes::truncateToInt32(O2REG(name).getNumber()); \ |
1238 | 0 | uint32_t rnum = hermes::truncateToInt32(O3REG(name).getNumber()) & 0x1f; \ |
1239 | 0 | O1REG(name) = \ |
1240 | 0 | HermesValue::encodeTrustedNumberValue(do##name(lnum, rnum)); \ |
1241 | 0 | ip = NEXTINST(name); \ |
1242 | 0 | DISPATCH; \ |
1243 | 0 | } \ |
1244 | 0 | CAPTURE_IP( \ |
1245 | 0 | res = doShiftOperSlowPath<do##name>( \ |
1246 | 0 | runtime, Handle<>(&O2REG(name)), Handle<>(&O3REG(name)))); \ |
1247 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { \ |
1248 | 0 | goto exception; \ |
1249 | 0 | } \ |
1250 | 0 | O1REG(name) = *res; \ |
1251 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); \ |
1252 | 0 | ip = NEXTINST(name); \ |
1253 | 0 | DISPATCH; \ |
1254 | 0 | } |
1255 | | |
1256 | | /// Implement a binary bitwise instruction with a fast path where both |
1257 | | /// operands are numbers. |
1258 | | /// \param name the name of the instruction. |
1259 | 8.12k | #define BITWISEBINOP(name) \ |
1260 | 8.12k | CASE(name) { \ |
1261 | 0 | if (LLVM_LIKELY(O2REG(name).isNumber() && O3REG(name).isNumber())) { \ |
1262 | | /* Fast-path. */ \ |
1263 | 0 | O1REG(name) = HermesValue::encodeTrustedNumberValue(do##name( \ |
1264 | 0 | hermes::truncateToInt32(O2REG(name).getNumber()), \ |
1265 | 0 | hermes::truncateToInt32(O3REG(name).getNumber()))); \ |
1266 | 0 | ip = NEXTINST(name); \ |
1267 | 0 | DISPATCH; \ |
1268 | 0 | } \ |
1269 | 0 | CAPTURE_IP( \ |
1270 | 0 | res = doBitOperSlowPath<do##name>( \ |
1271 | 0 | runtime, Handle<>(&O2REG(name)), Handle<>(&O3REG(name)))); \ |
1272 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { \ |
1273 | 0 | goto exception; \ |
1274 | 0 | } \ |
1275 | 0 | O1REG(name) = *res; \ |
1276 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); \ |
1277 | 0 | ip = NEXTINST(name); \ |
1278 | 0 | DISPATCH; \ |
1279 | 0 | } |
1280 | | |
1281 | | /// Implement a comparison instruction. |
1282 | | /// \param name the name of the instruction. |
1283 | | /// \param oper the C++ operator to use to actually perform the fast arithmetic |
1284 | | /// comparison. |
1285 | | /// \param operFuncName function to call for the slow-path comparison. |
1286 | 8.12k | #define CONDOP(name, oper, operFuncName) \ |
1287 | 8.12k | CASE(name) { \ |
1288 | 0 | if (LLVM_LIKELY(O2REG(name).isNumber() && O3REG(name).isNumber())) { \ |
1289 | | /* Fast-path. */ \ |
1290 | 0 | O1REG(name) = HermesValue::encodeBoolValue( \ |
1291 | 0 | O2REG(name).getNumber() oper O3REG(name).getNumber()); \ |
1292 | 0 | ip = NEXTINST(name); \ |
1293 | 0 | DISPATCH; \ |
1294 | 0 | } \ |
1295 | 0 | CAPTURE_IP( \ |
1296 | 0 | boolRes = operFuncName( \ |
1297 | 0 | runtime, Handle<>(&O2REG(name)), Handle<>(&O3REG(name)))); \ |
1298 | 0 | if (boolRes == ExecutionStatus::EXCEPTION) \ |
1299 | 0 | goto exception; \ |
1300 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); \ |
1301 | 0 | O1REG(name) = HermesValue::encodeBoolValue(boolRes.getValue()); \ |
1302 | 0 | ip = NEXTINST(name); \ |
1303 | 0 | DISPATCH; \ |
1304 | 0 | } |
1305 | | |
1306 | | /// Implement a comparison conditional jump with a fast path where both |
1307 | | /// operands are numbers. |
1308 | | /// \param name the name of the instruction. The fast path case will have a |
1309 | | /// "N" appended to the name. |
1310 | | /// \param suffix Optional suffix to be added to the end (e.g. Long) |
1311 | | /// \param oper the C++ operator to use to actually perform the fast arithmetic |
1312 | | /// comparison. |
1313 | | /// \param operFuncName function to call for the slow-path comparison. |
1314 | | /// \param trueDest ip value if the conditional evaluates to true |
1315 | | /// \param falseDest ip value if the conditional evaluates to false |
1316 | 8.12k | #define JCOND_IMPL(name, suffix, oper, operFuncName, trueDest, falseDest) \ |
1317 | 8.12k | CASE(name##suffix) { \ |
1318 | 0 | if (LLVM_LIKELY( \ |
1319 | 0 | O2REG(name##suffix).isNumber() && \ |
1320 | 0 | O3REG(name##suffix).isNumber())) { \ |
1321 | | /* Fast-path. */ \ |
1322 | 0 | INTERPRETER_FALLTHROUGH; \ |
1323 | 0 | CASE(name##N##suffix) { \ |
1324 | 0 | if (O2REG(name##N##suffix) \ |
1325 | 0 | .getNumber() oper O3REG(name##N##suffix) \ |
1326 | 0 | .getNumber()) { \ |
1327 | 0 | ip = trueDest; \ |
1328 | 0 | DISPATCH; \ |
1329 | 0 | } \ |
1330 | 0 | ip = falseDest; \ |
1331 | 0 | DISPATCH; \ |
1332 | 0 | } \ |
1333 | 0 | } \ |
1334 | 0 | CAPTURE_IP( \ |
1335 | 0 | boolRes = operFuncName( \ |
1336 | 0 | runtime, \ |
1337 | 0 | Handle<>(&O2REG(name##suffix)), \ |
1338 | 0 | Handle<>(&O3REG(name##suffix)))); \ |
1339 | 0 | if (boolRes == ExecutionStatus::EXCEPTION) \ |
1340 | 0 | goto exception; \ |
1341 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); \ |
1342 | 0 | if (boolRes.getValue()) { \ |
1343 | 0 | ip = trueDest; \ |
1344 | 0 | DISPATCH; \ |
1345 | 0 | } \ |
1346 | 0 | ip = falseDest; \ |
1347 | 0 | DISPATCH; \ |
1348 | 0 | } |
1349 | | |
1350 | | /// Implement a strict equality conditional jump |
1351 | | /// \param name the name of the instruction. |
1352 | | /// \param suffix Optional suffix to be added to the end (e.g. Long) |
1353 | | /// \param trueDest ip value if the conditional evaluates to true |
1354 | | /// \param falseDest ip value if the conditional evaluates to false |
1355 | 8.12k | #define JCOND_STRICT_EQ_IMPL(name, suffix, trueDest, falseDest) \ |
1356 | 8.12k | CASE(name##suffix) { \ |
1357 | 2.17k | if (strictEqualityTest(O2REG(name##suffix), O3REG(name##suffix))) { \ |
1358 | 2.17k | ip = trueDest; \ |
1359 | 10.8k | DISPATCH; \ |
1360 | 10.8k | } \ |
1361 | 2.17k | ip = falseDest; \ |
1362 | 2.17k | DISPATCH; \ |
1363 | 0 | } |
1364 | | |
1365 | | /// Implement an equality conditional jump |
1366 | | /// \param name the name of the instruction. |
1367 | | /// \param suffix Optional suffix to be added to the end (e.g. Long) |
1368 | | /// \param trueDest ip value if the conditional evaluates to true |
1369 | | /// \param falseDest ip value if the conditional evaluates to false |
1370 | 8.12k | #define JCOND_EQ_IMPL(name, suffix, trueDest, falseDest) \ |
1371 | 8.12k | CASE(name##suffix) { \ |
1372 | 242 | CAPTURE_IP_ASSIGN( \ |
1373 | 242 | auto eqRes, \ |
1374 | 242 | abstractEqualityTest_RJS( \ |
1375 | 242 | runtime, \ |
1376 | 242 | Handle<>(&O2REG(name##suffix)), \ |
1377 | 242 | Handle<>(&O3REG(name##suffix)))); \ |
1378 | 242 | if (eqRes == ExecutionStatus::EXCEPTION) { \ |
1379 | 0 | goto exception; \ |
1380 | 0 | } \ |
1381 | 242 | gcScope.flushToSmallCount(KEEP_HANDLES); \ |
1382 | 242 | if (*eqRes) { \ |
1383 | 0 | ip = trueDest; \ |
1384 | 0 | DISPATCH; \ |
1385 | 0 | } \ |
1386 | 242 | ip = falseDest; \ |
1387 | 242 | DISPATCH; \ |
1388 | 242 | } |
1389 | | |
1390 | | /// Implement the long and short forms of a conditional jump, and its negation. |
1391 | 8.12k | #define JCOND(name, oper, operFuncName) \ |
1392 | 8.12k | JCOND_IMPL( \ |
1393 | 0 | J##name, \ |
1394 | 0 | , \ |
1395 | 0 | oper, \ |
1396 | 0 | operFuncName, \ |
1397 | 0 | IPADD(ip->iJ##name.op1), \ |
1398 | 0 | NEXTINST(J##name)); \ |
1399 | 0 | JCOND_IMPL( \ |
1400 | 0 | J##name, \ |
1401 | 0 | Long, \ |
1402 | 0 | oper, \ |
1403 | 0 | operFuncName, \ |
1404 | 0 | IPADD(ip->iJ##name##Long.op1), \ |
1405 | 0 | NEXTINST(J##name##Long)); \ |
1406 | 0 | JCOND_IMPL( \ |
1407 | 0 | JNot##name, \ |
1408 | 0 | , \ |
1409 | 0 | oper, \ |
1410 | 0 | operFuncName, \ |
1411 | 0 | NEXTINST(JNot##name), \ |
1412 | 0 | IPADD(ip->iJNot##name.op1)); \ |
1413 | 0 | JCOND_IMPL( \ |
1414 | 0 | JNot##name, \ |
1415 | 0 | Long, \ |
1416 | 0 | oper, \ |
1417 | 0 | operFuncName, \ |
1418 | 0 | NEXTINST(JNot##name##Long), \ |
1419 | 0 | IPADD(ip->iJNot##name##Long.op1)); |
1420 | | |
1421 | | /// Load a constant. |
1422 | | /// \param value is the value to store in the output register. |
1423 | 8.12k | #define LOAD_CONST(name, value) \ |
1424 | 1.99M | CASE(name) { \ |
1425 | 1.99M | O1REG(name) = value; \ |
1426 | 1.99M | ip = NEXTINST(name); \ |
1427 | 1.99M | DISPATCH; \ |
1428 | 1.99M | } |
1429 | | |
1430 | 8.12k | #define LOAD_CONST_CAPTURE_IP(name, value) \ |
1431 | 416k | CASE(name) { \ |
1432 | 416k | CAPTURE_IP(O1REG(name) = value); \ |
1433 | 416k | ip = NEXTINST(name); \ |
1434 | 416k | DISPATCH; \ |
1435 | 416k | } |
1436 | | |
1437 | 1.17M | CASE(Mov) { |
1438 | 1.17M | O1REG(Mov) = O2REG(Mov); |
1439 | 1.17M | ip = NEXTINST(Mov); |
1440 | 5.85M | DISPATCH; |
1441 | 5.85M | } |
1442 | | |
1443 | 1.09M | CASE(MovLong) { |
1444 | 1.09M | O1REG(MovLong) = O2REG(MovLong); |
1445 | 1.09M | ip = NEXTINST(MovLong); |
1446 | 5.46M | DISPATCH; |
1447 | 5.46M | } |
1448 | | |
1449 | 2.17k | CASE(LoadParam) { |
1450 | 2.17k | if (LLVM_LIKELY(ip->iLoadParam.op2 <= FRAME.getArgCount())) { |
1451 | | // index 0 must load 'this'. Index 1 the first argument, etc. |
1452 | 2.17k | O1REG(LoadParam) = FRAME.getArgRef((int32_t)ip->iLoadParam.op2 - 1); |
1453 | 2.17k | ip = NEXTINST(LoadParam); |
1454 | 10.8k | DISPATCH; |
1455 | 10.8k | } |
1456 | 2.17k | O1REG(LoadParam) = HermesValue::encodeUndefinedValue(); |
1457 | 2.17k | ip = NEXTINST(LoadParam); |
1458 | 2.17k | DISPATCH; |
1459 | 0 | } |
1460 | | |
1461 | 0 | CASE(LoadParamLong) { |
1462 | 0 | if (LLVM_LIKELY(ip->iLoadParamLong.op2 <= FRAME.getArgCount())) { |
1463 | | // index 0 must load 'this'. Index 1 the first argument, etc. |
1464 | 0 | O1REG(LoadParamLong) = |
1465 | 0 | FRAME.getArgRef((int32_t)ip->iLoadParamLong.op2 - 1); |
1466 | 0 | ip = NEXTINST(LoadParamLong); |
1467 | 0 | DISPATCH; |
1468 | 0 | } |
1469 | 0 | O1REG(LoadParamLong) = HermesValue::encodeUndefinedValue(); |
1470 | 0 | ip = NEXTINST(LoadParamLong); |
1471 | 0 | DISPATCH; |
1472 | 0 | } |
1473 | | |
1474 | 0 | CASE(CoerceThisNS) { |
1475 | 0 | if (LLVM_LIKELY(O2REG(CoerceThisNS).isObject())) { |
1476 | 0 | O1REG(CoerceThisNS) = O2REG(CoerceThisNS); |
1477 | 0 | } else if ( |
1478 | 0 | O2REG(CoerceThisNS).isNull() || O2REG(CoerceThisNS).isUndefined()) { |
1479 | 0 | O1REG(CoerceThisNS) = runtime.global_; |
1480 | 0 | } else { |
1481 | 0 | tmpHandle = O2REG(CoerceThisNS); |
1482 | 0 | nextIP = NEXTINST(CoerceThisNS); |
1483 | 0 | goto coerceThisSlowPath; |
1484 | 0 | } |
1485 | 0 | ip = NEXTINST(CoerceThisNS); |
1486 | 0 | DISPATCH; |
1487 | 0 | } |
1488 | 6 | CASE(LoadThisNS) { |
1489 | 6 | if (LLVM_LIKELY(FRAME.getThisArgRef().isObject())) { |
1490 | 6 | O1REG(LoadThisNS) = FRAME.getThisArgRef(); |
1491 | 6 | } else if ( |
1492 | 0 | FRAME.getThisArgRef().isNull() || |
1493 | 0 | FRAME.getThisArgRef().isUndefined()) { |
1494 | 0 | O1REG(LoadThisNS) = runtime.global_; |
1495 | 0 | } else { |
1496 | 0 | tmpHandle = FRAME.getThisArgRef(); |
1497 | 0 | nextIP = NEXTINST(LoadThisNS); |
1498 | 0 | goto coerceThisSlowPath; |
1499 | 0 | } |
1500 | 6 | ip = NEXTINST(LoadThisNS); |
1501 | 30 | DISPATCH; |
1502 | 30 | } |
1503 | 0 | coerceThisSlowPath: { |
1504 | 0 | CAPTURE_IP(res = toObject(runtime, tmpHandle)); |
1505 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
1506 | 0 | goto exception; |
1507 | 0 | } |
1508 | 0 | O1REG(CoerceThisNS) = res.getValue(); |
1509 | 0 | tmpHandle.clear(); |
1510 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
1511 | 0 | ip = nextIP; |
1512 | 0 | DISPATCH; |
1513 | 0 | } |
1514 | | |
1515 | 0 | CASE(ConstructLong) { |
1516 | 0 | callArgCount = (uint32_t)ip->iConstructLong.op3; |
1517 | 0 | nextIP = NEXTINST(ConstructLong); |
1518 | 0 | callNewTarget = O2REG(ConstructLong).getRaw(); |
1519 | 0 | goto doCall; |
1520 | 0 | } |
1521 | 14 | CASE(CallLong) { |
1522 | 14 | callArgCount = (uint32_t)ip->iCallLong.op3; |
1523 | 14 | nextIP = NEXTINST(CallLong); |
1524 | 14 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); |
1525 | 14 | goto doCall; |
1526 | 0 | } |
1527 | | |
1528 | | // Note in Call1 through Call4, the first argument is 'this' which has |
1529 | | // argument index -1. |
1530 | | // Also note that we are writing to callNewTarget last, to avoid the |
1531 | | // possibility of it being aliased by the arg writes. |
1532 | 847 | CASE(Call1) { |
1533 | 847 | callArgCount = 1; |
1534 | 847 | nextIP = NEXTINST(Call1); |
1535 | 847 | StackFramePtr fr{runtime.stackPointer_}; |
1536 | 847 | fr.getArgRefUnsafe(-1) = O3REG(Call1); |
1537 | 847 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); |
1538 | 847 | goto doCall; |
1539 | 0 | } |
1540 | | |
1541 | 968 | CASE(Call2) { |
1542 | 968 | callArgCount = 2; |
1543 | 968 | nextIP = NEXTINST(Call2); |
1544 | 968 | StackFramePtr fr{runtime.stackPointer_}; |
1545 | 968 | fr.getArgRefUnsafe(-1) = O3REG(Call2); |
1546 | 968 | fr.getArgRefUnsafe(0) = O4REG(Call2); |
1547 | 968 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); |
1548 | 968 | goto doCall; |
1549 | 0 | } |
1550 | | |
1551 | 0 | CASE(Call3) { |
1552 | 0 | callArgCount = 3; |
1553 | 0 | nextIP = NEXTINST(Call3); |
1554 | 0 | StackFramePtr fr{runtime.stackPointer_}; |
1555 | 0 | fr.getArgRefUnsafe(-1) = O3REG(Call3); |
1556 | 0 | fr.getArgRefUnsafe(0) = O4REG(Call3); |
1557 | 0 | fr.getArgRefUnsafe(1) = O5REG(Call3); |
1558 | 0 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); |
1559 | 0 | goto doCall; |
1560 | 0 | } |
1561 | | |
1562 | 0 | CASE(Call4) { |
1563 | 0 | callArgCount = 4; |
1564 | 0 | nextIP = NEXTINST(Call4); |
1565 | 0 | StackFramePtr fr{runtime.stackPointer_}; |
1566 | 0 | fr.getArgRefUnsafe(-1) = O3REG(Call4); |
1567 | 0 | fr.getArgRefUnsafe(0) = O4REG(Call4); |
1568 | 0 | fr.getArgRefUnsafe(1) = O5REG(Call4); |
1569 | 0 | fr.getArgRefUnsafe(2) = O6REG(Call4); |
1570 | 0 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); |
1571 | 0 | goto doCall; |
1572 | 0 | } |
1573 | | |
1574 | 726 | CASE(Construct) { |
1575 | 726 | callArgCount = (uint32_t)ip->iConstruct.op3; |
1576 | 726 | nextIP = NEXTINST(Construct); |
1577 | 726 | callNewTarget = O2REG(Construct).getRaw(); |
1578 | 726 | goto doCall; |
1579 | 0 | } |
1580 | 85.7k | CASE(Call) { |
1581 | 85.7k | callArgCount = (uint32_t)ip->iCall.op3; |
1582 | 85.7k | nextIP = NEXTINST(Call); |
1583 | 85.7k | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); |
1584 | 85.7k | goto doCall; |
1585 | 0 | } |
1586 | | |
1587 | 88.2k | doCall: { |
1588 | 88.2k | #ifdef HERMES_ENABLE_DEBUGGER |
1589 | | // Check for an async debugger request. |
1590 | 88.2k | if (uint8_t asyncFlags = |
1591 | 88.2k | runtime.testAndClearDebuggerAsyncBreakRequest()) { |
1592 | 0 | RUN_DEBUGGER_ASYNC_BREAK(asyncFlags); |
1593 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
1594 | 0 | DISPATCH; |
1595 | 0 | } |
1596 | 88.2k | #endif |
1597 | | |
1598 | | // Subtract 1 from callArgCount as 'this' is considered an argument in the |
1599 | | // instruction, but not in the frame. |
1600 | 88.2k | auto newFrame = StackFramePtr::initFrame( |
1601 | 88.2k | runtime.stackPointer_, |
1602 | 88.2k | FRAME, |
1603 | 88.2k | ip, |
1604 | 88.2k | curCodeBlock, |
1605 | 88.2k | callArgCount - 1, |
1606 | 88.2k | O2REG(Call), |
1607 | 88.2k | HermesValue::fromRaw(callNewTarget)); |
1608 | 88.2k | (void)newFrame; |
1609 | | |
1610 | 88.2k | SLOW_DEBUG(dumpCallArguments(dbgs(), runtime, newFrame)); |
1611 | | |
1612 | 88.2k | if (auto *func = dyn_vmcast<JSFunction>(O2REG(Call))) { |
1613 | 1.81k | assert(!SingleStep && "can't single-step a call"); |
1614 | | |
1615 | 1.81k | #ifdef HERMES_MEMORY_INSTRUMENTATION |
1616 | 1.81k | runtime.pushCallStack(curCodeBlock, ip); |
1617 | 1.81k | #endif |
1618 | | |
1619 | 1.81k | CodeBlock *calleeBlock = func->getCodeBlock(runtime); |
1620 | 1.81k | CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime)); |
1621 | 1.81k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
1622 | 0 | goto exception; |
1623 | 0 | } |
1624 | 1.81k | curCodeBlock = calleeBlock; |
1625 | 1.81k | CAPTURE_IP_SET(); |
1626 | 1.81k | goto tailCall; |
1627 | 1.81k | } |
1628 | 86.4k | CAPTURE_IP( |
1629 | 86.4k | resPH = Interpreter::handleCallSlowPath(runtime, &O2REG(Call))); |
1630 | 86.4k | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
1631 | 2 | goto exception; |
1632 | 2 | } |
1633 | 86.4k | O1REG(Call) = std::move(resPH->get()); |
1634 | 86.4k | SLOW_DEBUG( |
1635 | 86.4k | dbgs() << "native return value r" << (unsigned)ip->iCall.op1 << "=" |
1636 | 86.4k | << DumpHermesValue(O1REG(Call)) << "\n"); |
1637 | 86.4k | gcScope.flushToSmallCount(KEEP_HANDLES); |
1638 | 86.4k | ip = nextIP; |
1639 | 432k | DISPATCH; |
1640 | 432k | } |
1641 | | |
1642 | 0 | CASE(CallDirect) |
1643 | 0 | CASE(CallDirectLongIndex) { |
1644 | 0 | #ifdef HERMES_ENABLE_DEBUGGER |
1645 | | // Check for an async debugger request. |
1646 | 0 | if (uint8_t asyncFlags = |
1647 | 0 | runtime.testAndClearDebuggerAsyncBreakRequest()) { |
1648 | 0 | RUN_DEBUGGER_ASYNC_BREAK(asyncFlags); |
1649 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
1650 | 0 | DISPATCH; |
1651 | 0 | } |
1652 | 0 | #endif |
1653 | | |
1654 | 0 | CAPTURE_IP_ASSIGN( |
1655 | 0 | CodeBlock * calleeBlock, |
1656 | 0 | ip->opCode == OpCode::CallDirect |
1657 | 0 | ? curCodeBlock->getRuntimeModule()->getCodeBlockMayAllocate( |
1658 | 0 | ip->iCallDirect.op3) |
1659 | 0 | : curCodeBlock->getRuntimeModule()->getCodeBlockMayAllocate( |
1660 | 0 | ip->iCallDirectLongIndex.op3)); |
1661 | |
|
1662 | 0 | auto newFrame = StackFramePtr::initFrame( |
1663 | 0 | runtime.stackPointer_, |
1664 | 0 | FRAME, |
1665 | 0 | ip, |
1666 | 0 | curCodeBlock, |
1667 | 0 | (uint32_t)ip->iCallDirect.op2 - 1, |
1668 | 0 | HermesValue::encodeNativePointer(calleeBlock), |
1669 | 0 | HermesValue::encodeUndefinedValue()); |
1670 | 0 | (void)newFrame; |
1671 | |
|
1672 | 0 | LLVM_DEBUG(dumpCallArguments(dbgs(), runtime, newFrame)); |
1673 | |
|
1674 | 0 | assert(!SingleStep && "can't single-step a call"); |
1675 | | |
1676 | 0 | CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime)); |
1677 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
1678 | 0 | goto exception; |
1679 | 0 | } |
1680 | 0 | curCodeBlock = calleeBlock; |
1681 | 0 | CAPTURE_IP_SET(); |
1682 | 0 | goto tailCall; |
1683 | 0 | } |
1684 | | |
1685 | 0 | CASE(GetBuiltinClosure) { |
1686 | 0 | uint8_t methodIndex = ip->iCallBuiltin.op2; |
1687 | 0 | Callable *closure = runtime.getBuiltinCallable(methodIndex); |
1688 | 0 | O1REG(GetBuiltinClosure) = HermesValue::encodeObjectValue(closure); |
1689 | 0 | ip = NEXTINST(GetBuiltinClosure); |
1690 | 0 | DISPATCH; |
1691 | 0 | } |
1692 | | |
1693 | 85.7k | CASE(CallBuiltin) { |
1694 | 85.7k | CAPTURE_IP_ASSIGN( |
1695 | 85.7k | auto cres, |
1696 | 85.7k | implCallBuiltin( |
1697 | 85.7k | runtime, frameRegs, curCodeBlock, ip->iCallBuiltin.op3)); |
1698 | 85.7k | if (LLVM_UNLIKELY(cres == ExecutionStatus::EXCEPTION)) |
1699 | 0 | goto exception; |
1700 | 85.7k | gcScope.flushToSmallCount(KEEP_HANDLES); |
1701 | 85.7k | ip = NEXTINST(CallBuiltin); |
1702 | 428k | DISPATCH; |
1703 | 428k | } |
1704 | 14 | CASE(CallBuiltinLong) { |
1705 | 14 | CAPTURE_IP_ASSIGN( |
1706 | 14 | auto cres, |
1707 | 14 | implCallBuiltin( |
1708 | 14 | runtime, frameRegs, curCodeBlock, ip->iCallBuiltinLong.op3)); |
1709 | 14 | if (LLVM_UNLIKELY(cres == ExecutionStatus::EXCEPTION)) |
1710 | 0 | goto exception; |
1711 | 14 | gcScope.flushToSmallCount(KEEP_HANDLES); |
1712 | 14 | ip = NEXTINST(CallBuiltinLong); |
1713 | 70 | DISPATCH; |
1714 | 70 | } |
1715 | | |
1716 | 0 | CASE(CompleteGenerator) { |
1717 | 0 | auto *innerFn = vmcast<GeneratorInnerFunction>( |
1718 | 0 | runtime.getCurrentFrame().getCalleeClosureUnsafe()); |
1719 | 0 | innerFn->setState(GeneratorInnerFunction::State::Completed); |
1720 | 0 | ip = NEXTINST(CompleteGenerator); |
1721 | 0 | DISPATCH; |
1722 | 0 | } |
1723 | | |
1724 | 0 | CASE(SaveGenerator) { |
1725 | 0 | DONT_CAPTURE_IP( |
1726 | 0 | saveGenerator(runtime, frameRegs, IPADD(ip->iSaveGenerator.op1))); |
1727 | 0 | ip = NEXTINST(SaveGenerator); |
1728 | 0 | DISPATCH; |
1729 | 0 | } |
1730 | 0 | CASE(SaveGeneratorLong) { |
1731 | 0 | DONT_CAPTURE_IP(saveGenerator( |
1732 | 0 | runtime, frameRegs, IPADD(ip->iSaveGeneratorLong.op1))); |
1733 | 0 | ip = NEXTINST(SaveGeneratorLong); |
1734 | 0 | DISPATCH; |
1735 | 0 | } |
1736 | | |
1737 | 0 | CASE(StartGenerator) { |
1738 | 0 | auto *innerFn = vmcast<GeneratorInnerFunction>( |
1739 | 0 | runtime.getCurrentFrame().getCalleeClosureUnsafe()); |
1740 | 0 | if (innerFn->getState() == |
1741 | 0 | GeneratorInnerFunction::State::SuspendedStart) { |
1742 | 0 | nextIP = NEXTINST(StartGenerator); |
1743 | 0 | } else { |
1744 | 0 | nextIP = innerFn->getNextIP(runtime); |
1745 | 0 | innerFn->restoreStack(runtime); |
1746 | 0 | } |
1747 | 0 | innerFn->setState(GeneratorInnerFunction::State::Executing); |
1748 | 0 | ip = nextIP; |
1749 | 0 | DISPATCH; |
1750 | 0 | } |
1751 | | |
1752 | 0 | CASE(ResumeGenerator) { |
1753 | 0 | auto *innerFn = vmcast<GeneratorInnerFunction>( |
1754 | 0 | runtime.getCurrentFrame().getCalleeClosureUnsafe()); |
1755 | 0 | O2REG(ResumeGenerator) = HermesValue::encodeBoolValue( |
1756 | 0 | innerFn->getAction() == GeneratorInnerFunction::Action::Return); |
1757 | | // Write the result last in case it is the same register as O2REG. |
1758 | 0 | O1REG(ResumeGenerator) = innerFn->getResult().unboxToHV(runtime); |
1759 | 0 | innerFn->clearResult(runtime); |
1760 | 0 | if (innerFn->getAction() == GeneratorInnerFunction::Action::Throw) { |
1761 | 0 | runtime.setThrownValue(O1REG(ResumeGenerator)); |
1762 | 0 | goto exception; |
1763 | 0 | } |
1764 | 0 | ip = NEXTINST(ResumeGenerator); |
1765 | 0 | DISPATCH; |
1766 | 0 | } |
1767 | | |
1768 | 2.00k | CASE(Ret) { |
1769 | 2.00k | #ifdef HERMES_ENABLE_DEBUGGER |
1770 | | // Check for an async debugger request, but skip it if we're single |
1771 | | // stepping. The only case where we'd be single stepping a Ret is if it |
1772 | | // was replaced with Debugger OpCode and we're coming here from |
1773 | | // stepFunction(). This does take away a chance to handle AsyncBreak. An |
1774 | | // AsyncBreak request could be either Explicit or Implicit. The Explicit |
1775 | | // case is to have the program being executed to pause. There isn't a |
1776 | | // need to pause at a particular location. Also, since we just came from |
1777 | | // a breakpoint, handling Explicit AsyncBreak for single step isn't so |
1778 | | // important. The other possible kind is an Implicit AsyncBreak, which |
1779 | | // is used for debug clients to interrupt the runtime to execute their |
1780 | | // own code. Not processing AsyncBreak just means that the Implicit |
1781 | | // AsyncBreak needs to wait for the next opportunity to interrupt the |
1782 | | // runtime, which should be fine. There is no contract for when the |
1783 | | // interrupt should happen. |
1784 | 2.00k | if (!SingleStep) { |
1785 | 2.00k | if (uint8_t asyncFlags = |
1786 | 2.00k | runtime.testAndClearDebuggerAsyncBreakRequest()) { |
1787 | 0 | RUN_DEBUGGER_ASYNC_BREAK(asyncFlags); |
1788 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
1789 | 0 | DISPATCH; |
1790 | 0 | } |
1791 | 2.00k | } |
1792 | 2.00k | #endif |
1793 | | |
1794 | 2.00k | PROFILER_EXIT_FUNCTION(curCodeBlock); |
1795 | | |
1796 | 2.00k | #ifdef HERMES_MEMORY_INSTRUMENTATION |
1797 | 2.00k | runtime.popCallStack(); |
1798 | 2.00k | #endif |
1799 | | |
1800 | | // Store the return value. |
1801 | 2.00k | res = O1REG(Ret); |
1802 | | |
1803 | 2.00k | ip = FRAME.getSavedIP(); |
1804 | 2.00k | curCodeBlock = FRAME.getSavedCodeBlock(); |
1805 | | |
1806 | 2.00k | frameRegs = |
1807 | 2.00k | &runtime.restoreStackAndPreviousFrame(FRAME).getFirstLocalRef(); |
1808 | | |
1809 | 2.00k | SLOW_DEBUG( |
1810 | 2.00k | dbgs() << "function exit: restored stackLevel=" |
1811 | 2.00k | << runtime.getStackLevel() << "\n"); |
1812 | | |
1813 | | // Are we returning to native code? |
1814 | 2.00k | if (!curCodeBlock) { |
1815 | 186 | SLOW_DEBUG(dbgs() << "function exit: returning to native code\n"); |
1816 | 186 | return res; |
1817 | 186 | } |
1818 | | |
1819 | 1.81k | INIT_STATE_FOR_CODEBLOCK(curCodeBlock); |
1820 | 1.81k | O1REG(Call) = res.getValue(); |
1821 | 1.81k | ip = nextInstCall(ip); |
1822 | 9.07k | DISPATCH; |
1823 | 9.07k | } |
1824 | | |
1825 | 0 | CASE(Catch) { |
1826 | 0 | assert(!runtime.thrownValue_.isEmpty() && "Invalid thrown value"); |
1827 | 0 | assert( |
1828 | 0 | !isUncatchableError(runtime.thrownValue_) && |
1829 | 0 | "Uncatchable thrown value was caught"); |
1830 | 0 | O1REG(Catch) = runtime.thrownValue_; |
1831 | 0 | runtime.clearThrownValue(); |
1832 | 0 | #ifdef HERMES_ENABLE_DEBUGGER |
1833 | | // Signal to the debugger that we're done unwinding an exception, |
1834 | | // and we can resume normal debugging flow. |
1835 | 0 | runtime.debugger_.finishedUnwindingException(); |
1836 | 0 | #endif |
1837 | 0 | ip = NEXTINST(Catch); |
1838 | 0 | DISPATCH; |
1839 | 0 | } |
1840 | | |
1841 | 0 | CASE(Throw) { |
1842 | 0 | runtime.thrownValue_ = O1REG(Throw); |
1843 | 0 | SLOW_DEBUG( |
1844 | 0 | dbgs() << "Exception thrown: " |
1845 | 0 | << DumpHermesValue(runtime.thrownValue_) << "\n"); |
1846 | 0 | goto exception; |
1847 | 0 | } |
1848 | | |
1849 | 0 | CASE(ThrowIfEmpty) { |
1850 | 0 | if (LLVM_UNLIKELY(O2REG(ThrowIfEmpty).isEmpty())) { |
1851 | 0 | SLOW_DEBUG(dbgs() << "Throwing ReferenceError for empty variable"); |
1852 | 0 | CAPTURE_IP(runtime.raiseReferenceError( |
1853 | 0 | "accessing an uninitialized variable")); |
1854 | 0 | goto exception; |
1855 | 0 | } |
1856 | 0 | O1REG(ThrowIfEmpty) = O2REG(ThrowIfEmpty); |
1857 | 0 | ip = NEXTINST(ThrowIfEmpty); |
1858 | 0 | DISPATCH; |
1859 | 0 | } |
1860 | | |
1861 | 0 | CASE(Debugger) { |
1862 | 0 | SLOW_DEBUG(dbgs() << "debugger statement executed\n"); |
1863 | 0 | #ifdef HERMES_ENABLE_DEBUGGER |
1864 | 0 | { |
1865 | 0 | if (!runtime.debugger_.isDebugging()) { |
1866 | | // Only run the debugger if we're not already debugging. |
1867 | | // Don't want to call it again and mess with its state. |
1868 | 0 | CAPTURE_IP_ASSIGN( |
1869 | 0 | auto res, |
1870 | 0 | runDebuggerUpdatingState( |
1871 | 0 | Debugger::RunReason::Opcode, |
1872 | 0 | runtime, |
1873 | 0 | curCodeBlock, |
1874 | 0 | ip, |
1875 | 0 | frameRegs)); |
1876 | 0 | if (res == ExecutionStatus::EXCEPTION) { |
1877 | | // If one of the internal steps threw, |
1878 | | // then handle that here by jumping to where we're supposed to go. |
1879 | | // If we're in mid-step, the breakpoint at the catch point |
1880 | | // will have been set by the debugger. |
1881 | | // We don't want to execute this instruction because it's already |
1882 | | // thrown. |
1883 | 0 | goto exception; |
1884 | 0 | } |
1885 | 0 | } |
1886 | 0 | auto breakpointOpt = runtime.debugger_.getBreakpointLocation(ip); |
1887 | 0 | if (breakpointOpt.hasValue()) { |
1888 | | // We're on a breakpoint but we're supposed to continue. |
1889 | 0 | curCodeBlock->uninstallBreakpointAtOffset( |
1890 | 0 | CUROFFSET, breakpointOpt->opCode); |
1891 | 0 | if (ip->opCode == OpCode::Debugger) { |
1892 | | // Breakpointed a debugger instruction, so move past it |
1893 | | // since we've already called the debugger on this instruction. |
1894 | 0 | ip = NEXTINST(Debugger); |
1895 | 0 | } else { |
1896 | 0 | InterpreterState newState{curCodeBlock, (uint32_t)CUROFFSET}; |
1897 | 0 | CAPTURE_IP_ASSIGN( |
1898 | 0 | ExecutionStatus status, runtime.stepFunction(newState)); |
1899 | 0 | curCodeBlock->installBreakpointAtOffset(CUROFFSET); |
1900 | 0 | if (status == ExecutionStatus::EXCEPTION) { |
1901 | 0 | goto exception; |
1902 | 0 | } |
1903 | 0 | curCodeBlock = newState.codeBlock; |
1904 | 0 | ip = newState.codeBlock->getOffsetPtr(newState.offset); |
1905 | 0 | INIT_STATE_FOR_CODEBLOCK(curCodeBlock); |
1906 | | // Single-stepping should handle call stack management for us. |
1907 | 0 | frameRegs = &runtime.getCurrentFrame().getFirstLocalRef(); |
1908 | 0 | } |
1909 | 0 | } else if (ip->opCode == OpCode::Debugger) { |
1910 | | // No breakpoint here and we've already run the debugger, |
1911 | | // just continue on. |
1912 | | // If the current instruction is no longer a debugger instruction, |
1913 | | // we're just going to keep executing from the current IP. |
1914 | 0 | ip = NEXTINST(Debugger); |
1915 | 0 | } |
1916 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
1917 | 0 | } |
1918 | 0 | DISPATCH; |
1919 | | #else |
1920 | | ip = NEXTINST(Debugger); |
1921 | | DISPATCH; |
1922 | | #endif |
1923 | 0 | } |
1924 | | |
1925 | 357k | CASE(AsyncBreakCheck) { |
1926 | 357k | if (LLVM_UNLIKELY(runtime.hasAsyncBreak())) { |
1927 | 0 | #ifdef HERMES_ENABLE_DEBUGGER |
1928 | 0 | if (uint8_t asyncFlags = |
1929 | 0 | runtime.testAndClearDebuggerAsyncBreakRequest()) { |
1930 | 0 | RUN_DEBUGGER_ASYNC_BREAK(asyncFlags); |
1931 | 0 | } |
1932 | 0 | #endif |
1933 | 0 | if (runtime.testAndClearTimeoutAsyncBreakRequest()) { |
1934 | 0 | CAPTURE_IP_ASSIGN(auto nRes, runtime.notifyTimeout()); |
1935 | 0 | if (nRes == ExecutionStatus::EXCEPTION) { |
1936 | 0 | goto exception; |
1937 | 0 | } |
1938 | 0 | } |
1939 | 0 | } |
1940 | 357k | gcScope.flushToSmallCount(KEEP_HANDLES); |
1941 | | |
1942 | 357k | ip = NEXTINST(AsyncBreakCheck); |
1943 | 1.78M | DISPATCH; |
1944 | 1.78M | } |
1945 | | |
1946 | 0 | CASE(ProfilePoint) { |
1947 | | #ifdef HERMESVM_PROFILER_BB |
1948 | | auto pointIndex = ip->iProfilePoint.op1; |
1949 | | SLOW_DEBUG(llvh::dbgs() << "ProfilePoint: " << pointIndex << "\n"); |
1950 | | CAPTURE_IP(runtime.getBasicBlockExecutionInfo().executeBlock( |
1951 | | curCodeBlock, pointIndex)); |
1952 | | #endif |
1953 | 0 | ip = NEXTINST(ProfilePoint); |
1954 | 0 | DISPATCH; |
1955 | 0 | } |
1956 | | |
1957 | | // Use a macro here to avoid clang-format issues with a literal default: |
1958 | | // label. |
1959 | 0 | DEFAULT_CASE |
1960 | 0 | CASE(Unreachable) { |
1961 | 0 | hermes_fatal("Unreachable instruction encountered"); |
1962 | | // The fatal call doesn't return, no need to set the IP differently and |
1963 | | // dispatch. |
1964 | 0 | } |
1965 | | |
1966 | 4.37k | CASE(CreateClosure) { |
1967 | 4.37k | idVal = ip->iCreateClosure.op3; |
1968 | 4.37k | nextIP = NEXTINST(CreateClosure); |
1969 | 4.37k | goto createClosure; |
1970 | 0 | } |
1971 | 0 | CASE(CreateClosureLongIndex) { |
1972 | 0 | idVal = ip->iCreateClosureLongIndex.op3; |
1973 | 0 | nextIP = NEXTINST(CreateClosureLongIndex); |
1974 | 0 | goto createClosure; |
1975 | 0 | } |
1976 | 4.37k | createClosure: { |
1977 | 4.37k | auto *runtimeModule = curCodeBlock->getRuntimeModule(); |
1978 | 4.37k | CAPTURE_IP( |
1979 | 4.37k | O1REG(CreateClosure) = |
1980 | 4.37k | JSFunction::create( |
1981 | 4.37k | runtime, |
1982 | 4.37k | runtimeModule->getDomain(runtime), |
1983 | 4.37k | Handle<JSObject>::vmcast(&runtime.functionPrototype), |
1984 | 4.37k | Handle<Environment>::vmcast(&O2REG(CreateClosure)), |
1985 | 4.37k | runtimeModule->getCodeBlockMayAllocate(idVal)) |
1986 | 4.37k | .getHermesValue()); |
1987 | 4.37k | gcScope.flushToSmallCount(KEEP_HANDLES); |
1988 | 4.37k | ip = nextIP; |
1989 | 21.8k | DISPATCH; |
1990 | 21.8k | } |
1991 | | |
1992 | 0 | CASE(CreateAsyncClosure) { |
1993 | 0 | idVal = ip->iCreateAsyncClosure.op3; |
1994 | 0 | nextIP = NEXTINST(CreateAsyncClosure); |
1995 | 0 | goto createAsyncClosure; |
1996 | 21.8k | } |
1997 | 0 | CASE(CreateAsyncClosureLongIndex) { |
1998 | 0 | idVal = ip->iCreateAsyncClosureLongIndex.op3; |
1999 | 0 | nextIP = NEXTINST(CreateAsyncClosureLongIndex); |
2000 | 0 | goto createAsyncClosure; |
2001 | 21.8k | } |
2002 | 0 | createAsyncClosure: { |
2003 | 0 | auto *runtimeModule = curCodeBlock->getRuntimeModule(); |
2004 | 0 | CAPTURE_IP_ASSIGN( |
2005 | 0 | O1REG(CreateAsyncClosure), |
2006 | 0 | JSAsyncFunction::create( |
2007 | 0 | runtime, |
2008 | 0 | runtimeModule->getDomain(runtime), |
2009 | 0 | Handle<JSObject>::vmcast(&runtime.asyncFunctionPrototype), |
2010 | 0 | Handle<Environment>::vmcast(&O2REG(CreateAsyncClosure)), |
2011 | 0 | runtimeModule->getCodeBlockMayAllocate(idVal)) |
2012 | 0 | .getHermesValue()); |
2013 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2014 | 0 | ip = nextIP; |
2015 | 0 | DISPATCH; |
2016 | 0 | } |
2017 | | |
2018 | 0 | CASE(CreateGeneratorClosure) { |
2019 | 0 | idVal = ip->iCreateGeneratorClosure.op3; |
2020 | 0 | nextIP = NEXTINST(CreateGeneratorClosure); |
2021 | 0 | goto createGeneratorClosure; |
2022 | 0 | } |
2023 | 0 | CASE(CreateGeneratorClosureLongIndex) { |
2024 | 0 | idVal = ip->iCreateGeneratorClosureLongIndex.op3; |
2025 | 0 | nextIP = NEXTINST(CreateGeneratorClosureLongIndex); |
2026 | 0 | goto createGeneratorClosure; |
2027 | 0 | } |
2028 | 0 | createGeneratorClosure: { |
2029 | 0 | auto *runtimeModule = curCodeBlock->getRuntimeModule(); |
2030 | 0 | CAPTURE_IP_ASSIGN( |
2031 | 0 | O1REG(CreateGeneratorClosure), |
2032 | 0 | JSGeneratorFunction::create( |
2033 | 0 | runtime, |
2034 | 0 | runtimeModule->getDomain(runtime), |
2035 | 0 | Handle<JSObject>::vmcast(&runtime.generatorFunctionPrototype), |
2036 | 0 | Handle<Environment>::vmcast(&O2REG(CreateGeneratorClosure)), |
2037 | 0 | runtimeModule->getCodeBlockMayAllocate(idVal)) |
2038 | 0 | .getHermesValue()); |
2039 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2040 | 0 | ip = nextIP; |
2041 | 0 | DISPATCH; |
2042 | 0 | } |
2043 | | |
2044 | 0 | CASE(CreateGenerator) { |
2045 | 0 | CAPTURE_IP_ASSIGN( |
2046 | 0 | auto res, |
2047 | 0 | createGenerator_RJS( |
2048 | 0 | runtime, |
2049 | 0 | curCodeBlock->getRuntimeModule(), |
2050 | 0 | ip->iCreateGenerator.op3, |
2051 | 0 | Handle<Environment>::vmcast(&O2REG(CreateGenerator)), |
2052 | 0 | FRAME.getNativeArgs())); |
2053 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
2054 | 0 | goto exception; |
2055 | 0 | } |
2056 | 0 | O1REG(CreateGenerator) = res->getHermesValue(); |
2057 | 0 | res->invalidate(); |
2058 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2059 | 0 | ip = NEXTINST(CreateGenerator); |
2060 | 0 | DISPATCH; |
2061 | 0 | } |
2062 | 0 | CASE(CreateGeneratorLongIndex) { |
2063 | 0 | CAPTURE_IP_ASSIGN( |
2064 | 0 | auto res, |
2065 | 0 | createGenerator_RJS( |
2066 | 0 | runtime, |
2067 | 0 | curCodeBlock->getRuntimeModule(), |
2068 | 0 | ip->iCreateGeneratorLongIndex.op3, |
2069 | 0 | Handle<Environment>::vmcast(&O2REG(CreateGeneratorLongIndex)), |
2070 | 0 | FRAME.getNativeArgs())); |
2071 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
2072 | 0 | goto exception; |
2073 | 0 | } |
2074 | 0 | O1REG(CreateGeneratorLongIndex) = res->getHermesValue(); |
2075 | 0 | res->invalidate(); |
2076 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2077 | 0 | ip = NEXTINST(CreateGeneratorLongIndex); |
2078 | 0 | DISPATCH; |
2079 | 0 | } |
2080 | | |
2081 | 1.57k | CASE(GetEnvironment) { |
2082 | | // The currently executing function must exist, so get the environment. |
2083 | 1.57k | Environment *curEnv = |
2084 | 1.57k | FRAME.getCalleeClosureUnsafe()->getEnvironment(runtime); |
2085 | 1.57k | for (unsigned level = ip->iGetEnvironment.op2; level; --level) { |
2086 | 0 | assert(curEnv && "invalid environment relative level"); |
2087 | 0 | curEnv = curEnv->getParentEnvironment(runtime); |
2088 | 0 | } |
2089 | 1.57k | O1REG(GetEnvironment) = HermesValue::encodeObjectValue(curEnv); |
2090 | 1.57k | ip = NEXTINST(GetEnvironment); |
2091 | 7.86k | DISPATCH; |
2092 | 7.86k | } |
2093 | | |
2094 | 0 | CASE(CreateInnerEnvironment) { |
2095 | 0 | CAPTURE_IP( |
2096 | 0 | O1REG(CreateInnerEnvironment) = Environment::create( |
2097 | 0 | runtime, |
2098 | 0 | Handle<Environment>::vmcast(&O2REG(CreateInnerEnvironment)), |
2099 | 0 | ip->iCreateInnerEnvironment.op3)); |
2100 | 0 | ip = NEXTINST(CreateInnerEnvironment); |
2101 | 0 | DISPATCH; |
2102 | 0 | } |
2103 | | |
2104 | 578 | CASE(CreateEnvironment) { |
2105 | 578 | tmpHandle = HermesValue::encodeObjectValueUnsafe( |
2106 | 578 | FRAME.getCalleeClosureUnsafe()->getEnvironment(runtime)); |
2107 | | |
2108 | 578 | CAPTURE_IP( |
2109 | 578 | O1REG(CreateEnvironment) = Environment::create( |
2110 | 578 | runtime, |
2111 | 578 | Handle<Environment>::vmcast_or_null(tmpHandle), |
2112 | 578 | curCodeBlock->getEnvironmentSize())); |
2113 | | |
2114 | 578 | tmpHandle = HermesValue::encodeUndefinedValue(); |
2115 | 578 | ip = NEXTINST(CreateEnvironment); |
2116 | 2.89k | DISPATCH; |
2117 | 2.89k | } |
2118 | | |
2119 | 4.24k | CASE(StoreToEnvironment) { |
2120 | 4.24k | vmcast<Environment>(O1REG(StoreToEnvironment)) |
2121 | 4.24k | ->slot(ip->iStoreToEnvironment.op2) |
2122 | 4.24k | .set(O3REG(StoreToEnvironment), runtime.getHeap()); |
2123 | 4.24k | ip = NEXTINST(StoreToEnvironment); |
2124 | 21.2k | DISPATCH; |
2125 | 21.2k | } |
2126 | 0 | CASE(StoreToEnvironmentL) { |
2127 | 0 | vmcast<Environment>(O1REG(StoreToEnvironmentL)) |
2128 | 0 | ->slot(ip->iStoreToEnvironmentL.op2) |
2129 | 0 | .set(O3REG(StoreToEnvironmentL), runtime.getHeap()); |
2130 | 0 | ip = NEXTINST(StoreToEnvironmentL); |
2131 | 0 | DISPATCH; |
2132 | 0 | } |
2133 | | |
2134 | 250 | CASE(StoreNPToEnvironment) { |
2135 | 250 | vmcast<Environment>(O1REG(StoreNPToEnvironment)) |
2136 | 250 | ->slot(ip->iStoreNPToEnvironment.op2) |
2137 | 250 | .setNonPtr(O3REG(StoreNPToEnvironment), runtime.getHeap()); |
2138 | 250 | ip = NEXTINST(StoreNPToEnvironment); |
2139 | 1.25k | DISPATCH; |
2140 | 1.25k | } |
2141 | 0 | CASE(StoreNPToEnvironmentL) { |
2142 | 0 | vmcast<Environment>(O1REG(StoreNPToEnvironmentL)) |
2143 | 0 | ->slot(ip->iStoreNPToEnvironmentL.op2) |
2144 | 0 | .setNonPtr(O3REG(StoreNPToEnvironmentL), runtime.getHeap()); |
2145 | 0 | ip = NEXTINST(StoreNPToEnvironmentL); |
2146 | 0 | DISPATCH; |
2147 | 0 | } |
2148 | | |
2149 | 1.57k | CASE(LoadFromEnvironment) { |
2150 | 1.57k | O1REG(LoadFromEnvironment) = |
2151 | 1.57k | vmcast<Environment>(O2REG(LoadFromEnvironment)) |
2152 | 1.57k | ->slot(ip->iLoadFromEnvironment.op3); |
2153 | 1.57k | ip = NEXTINST(LoadFromEnvironment); |
2154 | 7.86k | DISPATCH; |
2155 | 7.86k | } |
2156 | | |
2157 | 0 | CASE(LoadFromEnvironmentL) { |
2158 | 0 | O1REG(LoadFromEnvironmentL) = |
2159 | 0 | vmcast<Environment>(O2REG(LoadFromEnvironmentL)) |
2160 | 0 | ->slot(ip->iLoadFromEnvironmentL.op3); |
2161 | 0 | ip = NEXTINST(LoadFromEnvironmentL); |
2162 | 0 | DISPATCH; |
2163 | 0 | } |
2164 | | |
2165 | 529k | CASE(GetGlobalObject) { |
2166 | 529k | O1REG(GetGlobalObject) = runtime.global_; |
2167 | 529k | ip = NEXTINST(GetGlobalObject); |
2168 | 2.64M | DISPATCH; |
2169 | 2.64M | } |
2170 | | |
2171 | 6 | CASE(GetNewTarget) { |
2172 | 6 | O1REG(GetNewTarget) = FRAME.getNewTargetRef(); |
2173 | 6 | ip = NEXTINST(GetNewTarget); |
2174 | 30 | DISPATCH; |
2175 | 30 | } |
2176 | | |
2177 | 0 | CASE(DeclareGlobalVar) { |
2178 | 0 | CAPTURE_IP_ASSIGN( |
2179 | 0 | auto res, declareGlobalVarImpl(runtime, curCodeBlock, ip)); |
2180 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
2181 | 0 | goto exception; |
2182 | 0 | } |
2183 | 0 | ip = NEXTINST(DeclareGlobalVar); |
2184 | 0 | DISPATCH; |
2185 | 0 | } |
2186 | | |
2187 | 0 | CASE(ThrowIfHasRestrictedGlobalProperty) { |
2188 | 0 | CAPTURE_IP_ASSIGN( |
2189 | 0 | auto res, |
2190 | 0 | throwIfHasRestrictedGlobalPropertyImpl(runtime, curCodeBlock, ip)); |
2191 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
2192 | 0 | goto exception; |
2193 | 0 | } |
2194 | 0 | ip = NEXTINST(ThrowIfHasRestrictedGlobalProperty); |
2195 | 0 | DISPATCH; |
2196 | 0 | } |
2197 | | |
2198 | 0 | CASE(TryGetByIdLong) { |
2199 | 0 | tryProp = true; |
2200 | 0 | idVal = ip->iTryGetByIdLong.op4; |
2201 | 0 | nextIP = NEXTINST(TryGetByIdLong); |
2202 | 0 | goto getById; |
2203 | 0 | } |
2204 | 0 | CASE(GetByIdLong) { |
2205 | 0 | tryProp = false; |
2206 | 0 | idVal = ip->iGetByIdLong.op4; |
2207 | 0 | nextIP = NEXTINST(GetByIdLong); |
2208 | 0 | goto getById; |
2209 | 0 | } |
2210 | 2.90k | CASE(GetByIdShort) { |
2211 | 2.90k | tryProp = false; |
2212 | 2.90k | idVal = ip->iGetByIdShort.op4; |
2213 | 2.90k | nextIP = NEXTINST(GetByIdShort); |
2214 | 2.90k | goto getById; |
2215 | 0 | } |
2216 | 172k | CASE(TryGetById) { |
2217 | 172k | tryProp = true; |
2218 | 172k | idVal = ip->iTryGetById.op4; |
2219 | 172k | nextIP = NEXTINST(TryGetById); |
2220 | 172k | goto getById; |
2221 | 0 | } |
2222 | 0 | CASE(GetById) { |
2223 | 0 | tryProp = false; |
2224 | 0 | idVal = ip->iGetById.op4; |
2225 | 0 | nextIP = NEXTINST(GetById); |
2226 | 0 | } |
2227 | 175k | getById: { |
2228 | 175k | ++NumGetById; |
2229 | | // NOTE: it is safe to use OnREG(GetById) here because all instructions |
2230 | | // have the same layout: opcode, registers, non-register operands, i.e. |
2231 | | // they only differ in the width of the last "identifier" field. |
2232 | 175k | if (LLVM_LIKELY(O2REG(GetById).isObject())) { |
2233 | 175k | auto *obj = vmcast<JSObject>(O2REG(GetById)); |
2234 | 175k | auto cacheIdx = ip->iGetById.op3; |
2235 | 175k | auto *cacheEntry = curCodeBlock->getReadCacheEntry(cacheIdx); |
2236 | | |
2237 | | #ifdef HERMESVM_PROFILER_BB |
2238 | | { |
2239 | | HERMES_SLOW_ASSERT( |
2240 | | gcScope.getHandleCountDbg() == KEEP_HANDLES && |
2241 | | "unaccounted handles were created"); |
2242 | | auto objHandle = runtime.makeHandle(obj); |
2243 | | auto cacheHCPtr = vmcast_or_null<HiddenClass>(static_cast<GCCell *>( |
2244 | | cacheEntry->clazz.get(runtime, runtime.getHeap()))); |
2245 | | CAPTURE_IP(runtime.recordHiddenClass( |
2246 | | curCodeBlock, ip, ID(idVal), obj->getClass(runtime), cacheHCPtr)); |
2247 | | // obj may be moved by GC due to recordHiddenClass |
2248 | | obj = objHandle.get(); |
2249 | | } |
2250 | | gcScope.flushToSmallCount(KEEP_HANDLES); |
2251 | | #endif |
2252 | 175k | CompressedPointer clazzPtr{obj->getClassGCPtr()}; |
2253 | 175k | #ifndef NDEBUG |
2254 | 175k | if (vmcast<HiddenClass>(clazzPtr.getNonNull(runtime))->isDictionary()) |
2255 | 171k | ++NumGetByIdDict; |
2256 | | #else |
2257 | | (void)NumGetByIdDict; |
2258 | | #endif |
2259 | | |
2260 | | // If we have a cache hit, reuse the cached offset and immediately |
2261 | | // return the property. |
2262 | 175k | if (LLVM_LIKELY(cacheEntry->clazz == clazzPtr)) { |
2263 | 172k | ++NumGetByIdCacheHits; |
2264 | 172k | CAPTURE_IP( |
2265 | 172k | O1REG(GetById) = |
2266 | 172k | JSObject::getNamedSlotValueUnsafe<PropStorage::Inline::Yes>( |
2267 | 172k | obj, runtime, cacheEntry->slot) |
2268 | 172k | .unboxToHV(runtime)); |
2269 | 172k | ip = nextIP; |
2270 | 864k | DISPATCH; |
2271 | 864k | } |
2272 | 175k | auto id = ID(idVal); |
2273 | 175k | NamedPropertyDescriptor desc; |
2274 | 175k | CAPTURE_IP_ASSIGN( |
2275 | 175k | OptValue<bool> fastPathResult, |
2276 | 175k | JSObject::tryGetOwnNamedDescriptorFast(obj, runtime, id, desc)); |
2277 | 175k | if (LLVM_LIKELY( |
2278 | 175k | fastPathResult.hasValue() && fastPathResult.getValue()) && |
2279 | 175k | !desc.flags.accessor) { |
2280 | 2.44k | ++NumGetByIdFastPaths; |
2281 | | |
2282 | | // cacheIdx == 0 indicates no caching so don't update the cache in |
2283 | | // those cases. |
2284 | 2.44k | HiddenClass *clazz = |
2285 | 2.44k | vmcast<HiddenClass>(clazzPtr.getNonNull(runtime)); |
2286 | 2.44k | if (LLVM_LIKELY(!clazz->isDictionaryNoCache()) && |
2287 | 2.44k | LLVM_LIKELY(cacheIdx != hbc::PROPERTY_CACHING_DISABLED)) { |
2288 | 2.44k | #ifdef HERMES_SLOW_DEBUG |
2289 | 2.44k | if (cacheEntry->clazz && cacheEntry->clazz != clazzPtr) |
2290 | 484 | ++NumGetByIdCacheEvicts; |
2291 | | #else |
2292 | | (void)NumGetByIdCacheEvicts; |
2293 | | #endif |
2294 | | // Cache the class, id and property slot. |
2295 | 2.44k | cacheEntry->clazz = clazzPtr; |
2296 | 2.44k | cacheEntry->slot = desc.slot; |
2297 | 2.44k | } |
2298 | | |
2299 | 2.44k | assert( |
2300 | 2.44k | !obj->isProxyObject() && |
2301 | 2.44k | "tryGetOwnNamedDescriptorFast returned true on Proxy"); |
2302 | 2.44k | CAPTURE_IP( |
2303 | 2.44k | O1REG(GetById) = |
2304 | 2.44k | JSObject::getNamedSlotValueUnsafe(obj, runtime, desc) |
2305 | 2.44k | .unboxToHV(runtime)); |
2306 | 2.44k | ip = nextIP; |
2307 | 12.2k | DISPATCH; |
2308 | 12.2k | } |
2309 | | |
2310 | | // The cache may also be populated via the prototype of the object. |
2311 | | // This value is only reliable if the fast path was a definite |
2312 | | // not-found. |
2313 | 175k | if (fastPathResult.hasValue() && !fastPathResult.getValue() && |
2314 | 175k | LLVM_LIKELY(!obj->isProxyObject())) { |
2315 | 27 | CAPTURE_IP_ASSIGN(JSObject * parent, obj->getParent(runtime)); |
2316 | | // TODO: This isLazy check is because a lazy object is reported as |
2317 | | // having no properties and therefore cannot contain the property. |
2318 | | // This check does not belong here, it should be merged into |
2319 | | // tryGetOwnNamedDescriptorFast(). |
2320 | 27 | if (parent && cacheEntry->clazz == parent->getClassGCPtr() && |
2321 | 27 | LLVM_LIKELY(!obj->isLazy())) { |
2322 | 0 | ++NumGetByIdProtoHits; |
2323 | | // We've already checked that this isn't a Proxy. |
2324 | 0 | CAPTURE_IP( |
2325 | 0 | O1REG(GetById) = JSObject::getNamedSlotValueUnsafe( |
2326 | 0 | parent, runtime, cacheEntry->slot) |
2327 | 0 | .unboxToHV(runtime)); |
2328 | 0 | ip = nextIP; |
2329 | 0 | DISPATCH; |
2330 | 0 | } |
2331 | 27 | } |
2332 | | |
2333 | 175k | #ifdef HERMES_SLOW_DEBUG |
2334 | | // Call to getNamedDescriptorUnsafe is safe because `id` is kept alive |
2335 | | // by the IdentifierTable. |
2336 | 175k | CAPTURE_IP_ASSIGN( |
2337 | 175k | JSObject * propObj, |
2338 | 175k | JSObject::getNamedDescriptorUnsafe( |
2339 | 175k | Handle<JSObject>::vmcast(&O2REG(GetById)), runtime, id, desc)); |
2340 | 175k | if (propObj) { |
2341 | 244 | if (desc.flags.accessor) |
2342 | 0 | ++NumGetByIdAccessor; |
2343 | 244 | else if (propObj != vmcast<JSObject>(O2REG(GetById))) |
2344 | 122 | ++NumGetByIdProto; |
2345 | 175k | } else { |
2346 | 175k | ++NumGetByIdNotFound; |
2347 | 175k | } |
2348 | | #else |
2349 | | (void)NumGetByIdAccessor; |
2350 | | (void)NumGetByIdProto; |
2351 | | (void)NumGetByIdNotFound; |
2352 | | #endif |
2353 | 175k | #ifdef HERMES_SLOW_DEBUG |
2354 | 175k | auto *savedClass = cacheIdx != hbc::PROPERTY_CACHING_DISABLED |
2355 | 175k | ? cacheEntry->clazz.get(runtime, runtime.getHeap()) |
2356 | 175k | : nullptr; |
2357 | 175k | #endif |
2358 | 175k | ++NumGetByIdSlow; |
2359 | 175k | CAPTURE_IP( |
2360 | 175k | resPH = JSObject::getNamed_RJS( |
2361 | 175k | Handle<JSObject>::vmcast(&O2REG(GetById)), |
2362 | 175k | runtime, |
2363 | 175k | id, |
2364 | 175k | !tryProp ? defaultPropOpFlags |
2365 | 175k | : defaultPropOpFlags.plusMustExist(), |
2366 | 175k | cacheIdx != hbc::PROPERTY_CACHING_DISABLED ? cacheEntry |
2367 | 175k | : nullptr)); |
2368 | 175k | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
2369 | 27 | goto exception; |
2370 | 27 | } |
2371 | 175k | #ifdef HERMES_SLOW_DEBUG |
2372 | 175k | if (cacheIdx != hbc::PROPERTY_CACHING_DISABLED && savedClass && |
2373 | 175k | cacheEntry->clazz.get(runtime, runtime.getHeap()) != savedClass) { |
2374 | 0 | ++NumGetByIdCacheEvicts; |
2375 | 0 | } |
2376 | 175k | #endif |
2377 | 175k | } else { |
2378 | 3 | ++NumGetByIdTransient; |
2379 | 3 | assert(!tryProp && "TryGetById can only be used on the global object"); |
2380 | | /* Slow path. */ |
2381 | 3 | CAPTURE_IP( |
2382 | 3 | resPH = Interpreter::getByIdTransient_RJS( |
2383 | 3 | runtime, Handle<>(&O2REG(GetById)), ID(idVal))); |
2384 | 3 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
2385 | 0 | goto exception; |
2386 | 0 | } |
2387 | 3 | } |
2388 | 175k | O1REG(GetById) = resPH->get(); |
2389 | 175k | gcScope.flushToSmallCount(KEEP_HANDLES); |
2390 | 175k | ip = nextIP; |
2391 | 175k | DISPATCH; |
2392 | 1.23k | } |
2393 | | |
2394 | 0 | CASE(TryPutByIdLong) { |
2395 | 0 | tryProp = true; |
2396 | 0 | idVal = ip->iTryPutByIdLong.op4; |
2397 | 0 | nextIP = NEXTINST(TryPutByIdLong); |
2398 | 0 | goto putById; |
2399 | 1.23k | } |
2400 | 0 | CASE(PutByIdLong) { |
2401 | 0 | tryProp = false; |
2402 | 0 | idVal = ip->iPutByIdLong.op4; |
2403 | 0 | nextIP = NEXTINST(PutByIdLong); |
2404 | 0 | goto putById; |
2405 | 1.23k | } |
2406 | 0 | CASE(TryPutById) { |
2407 | 0 | tryProp = true; |
2408 | 0 | idVal = ip->iTryPutById.op4; |
2409 | 0 | nextIP = NEXTINST(TryPutById); |
2410 | 0 | goto putById; |
2411 | 1.23k | } |
2412 | 635k | CASE(PutById) { |
2413 | 635k | tryProp = false; |
2414 | 635k | idVal = ip->iPutById.op4; |
2415 | 635k | nextIP = NEXTINST(PutById); |
2416 | 635k | } |
2417 | 635k | putById: { |
2418 | 635k | ++NumPutById; |
2419 | 635k | if (LLVM_LIKELY(O1REG(PutById).isObject())) { |
2420 | 635k | CAPTURE_IP_ASSIGN( |
2421 | 635k | SmallHermesValue shv, |
2422 | 635k | SmallHermesValue::encodeHermesValue(O2REG(PutById), runtime)); |
2423 | 635k | auto *obj = vmcast<JSObject>(O1REG(PutById)); |
2424 | 635k | auto cacheIdx = ip->iPutById.op3; |
2425 | 635k | auto *cacheEntry = curCodeBlock->getWriteCacheEntry(cacheIdx); |
2426 | | |
2427 | | #ifdef HERMESVM_PROFILER_BB |
2428 | | { |
2429 | | HERMES_SLOW_ASSERT( |
2430 | | gcScope.getHandleCountDbg() == KEEP_HANDLES && |
2431 | | "unaccounted handles were created"); |
2432 | | auto shvHandle = runtime.makeHandle(shv.toHV(runtime)); |
2433 | | auto objHandle = runtime.makeHandle(obj); |
2434 | | auto cacheHCPtr = vmcast_or_null<HiddenClass>(static_cast<GCCell *>( |
2435 | | cacheEntry->clazz.get(runtime, runtime.getHeap()))); |
2436 | | CAPTURE_IP(runtime.recordHiddenClass( |
2437 | | curCodeBlock, ip, ID(idVal), obj->getClass(runtime), cacheHCPtr)); |
2438 | | // shv/obj may be invalidated by recordHiddenClass |
2439 | | if (shv.isPointer()) |
2440 | | shv.unsafeUpdatePointer( |
2441 | | static_cast<GCCell *>(shvHandle->getPointer()), runtime); |
2442 | | obj = objHandle.get(); |
2443 | | } |
2444 | | gcScope.flushToSmallCount(KEEP_HANDLES); |
2445 | | #endif |
2446 | 635k | CompressedPointer clazzPtr{obj->getClassGCPtr()}; |
2447 | | // If we have a cache hit, reuse the cached offset and immediately |
2448 | | // return the property. |
2449 | 635k | if (LLVM_LIKELY(cacheEntry->clazz == clazzPtr)) { |
2450 | 1.21k | ++NumPutByIdCacheHits; |
2451 | 1.21k | CAPTURE_IP( |
2452 | 1.21k | JSObject::setNamedSlotValueUnsafe<PropStorage::Inline::Yes>( |
2453 | 1.21k | obj, runtime, cacheEntry->slot, shv)); |
2454 | 1.21k | ip = nextIP; |
2455 | 6.05k | DISPATCH; |
2456 | 6.05k | } |
2457 | 635k | auto id = ID(idVal); |
2458 | 635k | NamedPropertyDescriptor desc; |
2459 | 635k | CAPTURE_IP_ASSIGN( |
2460 | 635k | OptValue<bool> hasOwnProp, |
2461 | 635k | JSObject::tryGetOwnNamedDescriptorFast(obj, runtime, id, desc)); |
2462 | 635k | if (LLVM_LIKELY(hasOwnProp.hasValue() && hasOwnProp.getValue()) && |
2463 | 635k | !desc.flags.accessor && desc.flags.writable && |
2464 | 635k | !desc.flags.internalSetter) { |
2465 | 357k | ++NumPutByIdFastPaths; |
2466 | | |
2467 | | // cacheIdx == 0 indicates no caching so don't update the cache in |
2468 | | // those cases. |
2469 | 357k | HiddenClass *clazz = |
2470 | 357k | vmcast<HiddenClass>(clazzPtr.getNonNull(runtime)); |
2471 | 357k | if (LLVM_LIKELY(!clazz->isDictionary()) && |
2472 | 357k | LLVM_LIKELY(cacheIdx != hbc::PROPERTY_CACHING_DISABLED)) { |
2473 | 242 | #ifdef HERMES_SLOW_DEBUG |
2474 | 242 | if (cacheEntry->clazz && cacheEntry->clazz != clazzPtr) |
2475 | 0 | ++NumPutByIdCacheEvicts; |
2476 | | #else |
2477 | | (void)NumPutByIdCacheEvicts; |
2478 | | #endif |
2479 | | // Cache the class and property slot. |
2480 | 242 | cacheEntry->clazz = clazzPtr; |
2481 | 242 | cacheEntry->slot = desc.slot; |
2482 | 242 | } |
2483 | | |
2484 | | // This must be valid because an own property was already found. |
2485 | 357k | CAPTURE_IP( |
2486 | 357k | JSObject::setNamedSlotValueUnsafe(obj, runtime, desc.slot, shv)); |
2487 | 357k | ip = nextIP; |
2488 | 1.78M | DISPATCH; |
2489 | 1.78M | } |
2490 | | |
2491 | 635k | CAPTURE_IP_ASSIGN( |
2492 | 635k | auto putRes, |
2493 | 635k | JSObject::putNamed_RJS( |
2494 | 635k | Handle<JSObject>::vmcast(&O1REG(PutById)), |
2495 | 635k | runtime, |
2496 | 635k | id, |
2497 | 635k | Handle<>(&O2REG(PutById)), |
2498 | 635k | !tryProp ? defaultPropOpFlags |
2499 | 635k | : defaultPropOpFlags.plusMustExist())); |
2500 | 635k | if (LLVM_UNLIKELY(putRes == ExecutionStatus::EXCEPTION)) { |
2501 | 0 | goto exception; |
2502 | 0 | } |
2503 | 635k | } else { |
2504 | 0 | ++NumPutByIdTransient; |
2505 | 0 | assert(!tryProp && "TryPutById can only be used on the global object"); |
2506 | 0 | CAPTURE_IP_ASSIGN( |
2507 | 0 | auto retStatus, |
2508 | 0 | Interpreter::putByIdTransient_RJS( |
2509 | 0 | runtime, |
2510 | 0 | Handle<>(&O1REG(PutById)), |
2511 | 0 | ID(idVal), |
2512 | 0 | Handle<>(&O2REG(PutById)), |
2513 | 0 | strictMode)); |
2514 | 0 | if (retStatus == ExecutionStatus::EXCEPTION) { |
2515 | 0 | goto exception; |
2516 | 0 | } |
2517 | 0 | } |
2518 | 635k | gcScope.flushToSmallCount(KEEP_HANDLES); |
2519 | 635k | ip = nextIP; |
2520 | 1.38M | DISPATCH; |
2521 | 1.38M | } |
2522 | | |
2523 | 357k | CASE(GetByVal) { |
2524 | 357k | if (LLVM_LIKELY(O2REG(GetByVal).isObject())) { |
2525 | 357k | CAPTURE_IP( |
2526 | 357k | resPH = JSObject::getComputed_RJS( |
2527 | 357k | Handle<JSObject>::vmcast(&O2REG(GetByVal)), |
2528 | 357k | runtime, |
2529 | 357k | Handle<>(&O3REG(GetByVal)))); |
2530 | 357k | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
2531 | 0 | goto exception; |
2532 | 0 | } |
2533 | 357k | } else { |
2534 | | // This is the "slow path". |
2535 | 0 | CAPTURE_IP( |
2536 | 0 | resPH = Interpreter::getByValTransient_RJS( |
2537 | 0 | runtime, |
2538 | 0 | Handle<>(&O2REG(GetByVal)), |
2539 | 0 | Handle<>(&O3REG(GetByVal)))); |
2540 | 0 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
2541 | 0 | goto exception; |
2542 | 0 | } |
2543 | 0 | } |
2544 | 357k | gcScope.flushToSmallCount(KEEP_HANDLES); |
2545 | 357k | O1REG(GetByVal) = resPH->get(); |
2546 | 357k | ip = NEXTINST(GetByVal); |
2547 | 1.78M | DISPATCH; |
2548 | 1.78M | } |
2549 | | |
2550 | 271k | CASE(PutByVal) { |
2551 | 271k | if (LLVM_LIKELY(O1REG(PutByVal).isObject())) { |
2552 | 271k | CAPTURE_IP_ASSIGN( |
2553 | 271k | auto putRes, |
2554 | 271k | JSObject::putComputed_RJS( |
2555 | 271k | Handle<JSObject>::vmcast(&O1REG(PutByVal)), |
2556 | 271k | runtime, |
2557 | 271k | Handle<>(&O2REG(PutByVal)), |
2558 | 271k | Handle<>(&O3REG(PutByVal)), |
2559 | 271k | defaultPropOpFlags)); |
2560 | 271k | if (LLVM_UNLIKELY(putRes == ExecutionStatus::EXCEPTION)) { |
2561 | 0 | goto exception; |
2562 | 0 | } |
2563 | 271k | } else { |
2564 | | // This is the "slow path". |
2565 | 0 | CAPTURE_IP_ASSIGN( |
2566 | 0 | auto retStatus, |
2567 | 0 | Interpreter::putByValTransient_RJS( |
2568 | 0 | runtime, |
2569 | 0 | Handle<>(&O1REG(PutByVal)), |
2570 | 0 | Handle<>(&O2REG(PutByVal)), |
2571 | 0 | Handle<>(&O3REG(PutByVal)), |
2572 | 0 | strictMode)); |
2573 | 0 | if (LLVM_UNLIKELY(retStatus == ExecutionStatus::EXCEPTION)) { |
2574 | 0 | goto exception; |
2575 | 0 | } |
2576 | 0 | } |
2577 | 271k | gcScope.flushToSmallCount(KEEP_HANDLES); |
2578 | 271k | ip = NEXTINST(PutByVal); |
2579 | 1.35M | DISPATCH; |
2580 | 1.35M | } |
2581 | | |
2582 | 639k | CASE(PutOwnByIndexL) { |
2583 | 639k | nextIP = NEXTINST(PutOwnByIndexL); |
2584 | 639k | idVal = ip->iPutOwnByIndexL.op3; |
2585 | 639k | goto putOwnByIndex; |
2586 | 1.35M | } |
2587 | 402 | CASE(PutOwnByIndex) { |
2588 | 402 | nextIP = NEXTINST(PutOwnByIndex); |
2589 | 402 | idVal = ip->iPutOwnByIndex.op3; |
2590 | 402 | } |
2591 | 639k | putOwnByIndex: { |
2592 | 639k | tmpHandle = HermesValue::encodeUntrustedNumberValue(idVal); |
2593 | 639k | CAPTURE_IP(JSObject::defineOwnComputedPrimitive( |
2594 | 639k | Handle<JSObject>::vmcast(&O1REG(PutOwnByIndex)), |
2595 | 639k | runtime, |
2596 | 639k | tmpHandle, |
2597 | 639k | DefinePropertyFlags::getDefaultNewPropertyFlags(), |
2598 | 639k | Handle<>(&O2REG(PutOwnByIndex)))); |
2599 | 639k | gcScope.flushToSmallCount(KEEP_HANDLES); |
2600 | 639k | tmpHandle.clear(); |
2601 | 639k | ip = nextIP; |
2602 | 3.19M | DISPATCH; |
2603 | 3.19M | } |
2604 | | |
2605 | 639k | CASE_OUTOFLINE(GetPNameList); |
2606 | | |
2607 | 357k | CASE(GetNextPName) { |
2608 | 357k | { |
2609 | 357k | assert( |
2610 | 357k | vmisa<BigStorage>(O2REG(GetNextPName)) && |
2611 | 357k | "GetNextPName's second op must be BigStorage"); |
2612 | 357k | auto obj = Handle<JSObject>::vmcast(&O3REG(GetNextPName)); |
2613 | 357k | auto arr = Handle<BigStorage>::vmcast(&O2REG(GetNextPName)); |
2614 | 357k | uint32_t idx = O4REG(GetNextPName).getNumber(); |
2615 | 357k | uint32_t size = O5REG(GetNextPName).getNumber(); |
2616 | 357k | MutableHandle<JSObject> propObj{runtime}; |
2617 | 357k | MutableHandle<SymbolID> tmpPropNameStorage{runtime}; |
2618 | | // Loop until we find a property which is present. |
2619 | 357k | while (idx < size) { |
2620 | 357k | tmpHandle = arr->at(runtime, idx); |
2621 | 357k | ComputedPropertyDescriptor desc; |
2622 | 357k | CAPTURE_IP_ASSIGN( |
2623 | 357k | ExecutionStatus status, |
2624 | 357k | JSObject::getComputedPrimitiveDescriptor( |
2625 | 357k | obj, |
2626 | 357k | runtime, |
2627 | 357k | tmpHandle, |
2628 | 357k | propObj, |
2629 | 357k | tmpPropNameStorage, |
2630 | 357k | desc)); |
2631 | 357k | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { |
2632 | 0 | goto exception; |
2633 | 0 | } |
2634 | 357k | if (LLVM_LIKELY(propObj)) |
2635 | 357k | break; |
2636 | 0 | ++idx; |
2637 | 0 | } |
2638 | 357k | if (idx < size) { |
2639 | | // We must return the property as a string |
2640 | 357k | if (tmpHandle->isNumber()) { |
2641 | 357k | CAPTURE_IP_ASSIGN(auto status, toString_RJS(runtime, tmpHandle)); |
2642 | 357k | assert( |
2643 | 357k | status == ExecutionStatus::RETURNED && |
2644 | 357k | "toString on number cannot fail"); |
2645 | 357k | tmpHandle = status->getHermesValue(); |
2646 | 357k | } |
2647 | 357k | O4REG(GetNextPName) = |
2648 | 357k | HermesValue::encodeUntrustedNumberValue(idx + 1); |
2649 | | // Write the result last in case it is the same register as O4REG. |
2650 | 357k | O1REG(GetNextPName) = tmpHandle.get(); |
2651 | 357k | } else { |
2652 | 3 | O1REG(GetNextPName) = HermesValue::encodeUndefinedValue(); |
2653 | 3 | } |
2654 | 357k | } |
2655 | 357k | gcScope.flushToSmallCount(KEEP_HANDLES); |
2656 | 357k | tmpHandle.clear(); |
2657 | 357k | ip = NEXTINST(GetNextPName); |
2658 | 1.78M | DISPATCH; |
2659 | 1.78M | } |
2660 | | |
2661 | 4 | CASE(ToNumber) { |
2662 | 4 | if (LLVM_LIKELY(O2REG(ToNumber).isNumber())) { |
2663 | 0 | O1REG(ToNumber) = O2REG(ToNumber); |
2664 | 0 | ip = NEXTINST(ToNumber); |
2665 | 4 | } else { |
2666 | 4 | CAPTURE_IP(res = toNumber_RJS(runtime, Handle<>(&O2REG(ToNumber)))); |
2667 | 4 | if (res == ExecutionStatus::EXCEPTION) |
2668 | 0 | goto exception; |
2669 | 4 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2670 | 4 | O1REG(ToNumber) = res.getValue(); |
2671 | 4 | ip = NEXTINST(ToNumber); |
2672 | 4 | } |
2673 | 24 | DISPATCH; |
2674 | 24 | } |
2675 | | |
2676 | 0 | CASE(ToNumeric) { |
2677 | 0 | if (LLVM_LIKELY(O2REG(ToNumeric).isNumber())) { |
2678 | 0 | O1REG(ToNumeric) = O2REG(ToNumeric); |
2679 | 0 | ip = NEXTINST(ToNumeric); |
2680 | 0 | } else { |
2681 | 0 | CAPTURE_IP(res = toNumeric_RJS(runtime, Handle<>(&O2REG(ToNumeric)))); |
2682 | 0 | if (res == ExecutionStatus::EXCEPTION) |
2683 | 0 | goto exception; |
2684 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2685 | 0 | O1REG(ToNumeric) = res.getValue(); |
2686 | 0 | ip = NEXTINST(ToNumeric); |
2687 | 0 | } |
2688 | 0 | DISPATCH; |
2689 | 0 | } |
2690 | | |
2691 | 0 | CASE(ToInt32) { |
2692 | 0 | CAPTURE_IP(res = toInt32_RJS(runtime, Handle<>(&O2REG(ToInt32)))); |
2693 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) |
2694 | 0 | goto exception; |
2695 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2696 | 0 | O1REG(ToInt32) = res.getValue(); |
2697 | 0 | ip = NEXTINST(ToInt32); |
2698 | 0 | DISPATCH; |
2699 | 0 | } |
2700 | | |
2701 | 0 | CASE(AddEmptyString) { |
2702 | 0 | if (LLVM_LIKELY(O2REG(AddEmptyString).isString())) { |
2703 | 0 | O1REG(AddEmptyString) = O2REG(AddEmptyString); |
2704 | 0 | ip = NEXTINST(AddEmptyString); |
2705 | 0 | } else { |
2706 | 0 | CAPTURE_IP( |
2707 | 0 | res = toPrimitive_RJS( |
2708 | 0 | runtime, |
2709 | 0 | Handle<>(&O2REG(AddEmptyString)), |
2710 | 0 | PreferredType::NONE)); |
2711 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) |
2712 | 0 | goto exception; |
2713 | 0 | tmpHandle = res.getValue(); |
2714 | 0 | CAPTURE_IP_ASSIGN(auto strRes, toString_RJS(runtime, tmpHandle)); |
2715 | 0 | if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) |
2716 | 0 | goto exception; |
2717 | 0 | tmpHandle.clear(); |
2718 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2719 | 0 | O1REG(AddEmptyString) = strRes->getHermesValue(); |
2720 | 0 | ip = NEXTINST(AddEmptyString); |
2721 | 0 | } |
2722 | 0 | DISPATCH; |
2723 | 0 | } |
2724 | | |
2725 | 357k | CASE(Jmp) { |
2726 | 357k | ip = IPADD(ip->iJmp.op1); |
2727 | 1.78M | DISPATCH; |
2728 | 1.78M | } |
2729 | 0 | CASE(JmpLong) { |
2730 | 0 | ip = IPADD(ip->iJmpLong.op1); |
2731 | 0 | DISPATCH; |
2732 | 0 | } |
2733 | 847 | CASE(JmpTrue) { |
2734 | 847 | if (toBoolean(O2REG(JmpTrue))) |
2735 | 121 | ip = IPADD(ip->iJmpTrue.op1); |
2736 | 726 | else |
2737 | 726 | ip = NEXTINST(JmpTrue); |
2738 | 4.23k | DISPATCH; |
2739 | 4.23k | } |
2740 | 0 | CASE(JmpTrueLong) { |
2741 | 0 | if (toBoolean(O2REG(JmpTrueLong))) |
2742 | 0 | ip = IPADD(ip->iJmpTrueLong.op1); |
2743 | 0 | else |
2744 | 0 | ip = NEXTINST(JmpTrueLong); |
2745 | 0 | DISPATCH; |
2746 | 0 | } |
2747 | 242 | CASE(JmpFalse) { |
2748 | 242 | if (!toBoolean(O2REG(JmpFalse))) |
2749 | 121 | ip = IPADD(ip->iJmpFalse.op1); |
2750 | 121 | else |
2751 | 121 | ip = NEXTINST(JmpFalse); |
2752 | 1.21k | DISPATCH; |
2753 | 1.21k | } |
2754 | 0 | CASE(JmpFalseLong) { |
2755 | 0 | if (!toBoolean(O2REG(JmpFalseLong))) |
2756 | 0 | ip = IPADD(ip->iJmpFalseLong.op1); |
2757 | 0 | else |
2758 | 0 | ip = NEXTINST(JmpFalseLong); |
2759 | 0 | DISPATCH; |
2760 | 0 | } |
2761 | 357k | CASE(JmpUndefined) { |
2762 | 357k | if (O2REG(JmpUndefined).isUndefined()) |
2763 | 3 | ip = IPADD(ip->iJmpUndefined.op1); |
2764 | 357k | else |
2765 | 357k | ip = NEXTINST(JmpUndefined); |
2766 | 1.78M | DISPATCH; |
2767 | 1.78M | } |
2768 | 2 | CASE(JmpUndefinedLong) { |
2769 | 2 | if (O2REG(JmpUndefinedLong).isUndefined()) |
2770 | 0 | ip = IPADD(ip->iJmpUndefinedLong.op1); |
2771 | 2 | else |
2772 | 2 | ip = NEXTINST(JmpUndefinedLong); |
2773 | 10 | DISPATCH; |
2774 | 10 | } |
2775 | 2.17M | INCDECOP(Inc) |
2776 | 271k | INCDECOP(Dec) |
2777 | 199k | CASE(Add) { |
2778 | 199k | if (LLVM_LIKELY( |
2779 | 199k | O2REG(Add).isNumber() && |
2780 | 199k | O3REG(Add).isNumber())) { /* Fast-path. */ |
2781 | 0 | INTERPRETER_FALLTHROUGH; |
2782 | 0 | CASE(AddN) { |
2783 | 0 | O1REG(Add) = HermesValue::encodeTrustedNumberValue( |
2784 | 0 | O2REG(Add).getNumber() + O3REG(Add).getNumber()); |
2785 | 0 | ip = NEXTINST(Add); |
2786 | 0 | DISPATCH; |
2787 | 0 | } |
2788 | 0 | } |
2789 | 199k | CAPTURE_IP( |
2790 | 199k | res = addOp_RJS( |
2791 | 199k | runtime, Handle<>(&O2REG(Add)), Handle<>(&O3REG(Add)))); |
2792 | 199k | if (res == ExecutionStatus::EXCEPTION) { |
2793 | 0 | goto exception; |
2794 | 0 | } |
2795 | 199k | gcScope.flushToSmallCount(KEEP_HANDLES); |
2796 | 199k | O1REG(Add) = res.getValue(); |
2797 | 199k | ip = NEXTINST(Add); |
2798 | 995k | DISPATCH; |
2799 | 995k | } |
2800 | | |
2801 | 0 | CASE(BitNot) { |
2802 | 0 | if (LLVM_LIKELY(O2REG(BitNot).isNumber())) { /* Fast-path. */ |
2803 | 0 | O1REG(BitNot) = HermesValue::encodeUntrustedNumberValue( |
2804 | 0 | ~hermes::truncateToInt32(O2REG(BitNot).getNumber())); |
2805 | 0 | ip = NEXTINST(BitNot); |
2806 | 0 | DISPATCH; |
2807 | 0 | } |
2808 | 0 | CAPTURE_IP(res = doBitNotSlowPath(runtime, Handle<>(&O2REG(BitNot)))); |
2809 | 0 | if (res == ExecutionStatus::EXCEPTION) { |
2810 | 0 | goto exception; |
2811 | 0 | } |
2812 | 0 | O1REG(BitNot) = *res; |
2813 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2814 | 0 | ip = NEXTINST(BitNot); |
2815 | 0 | DISPATCH; |
2816 | 0 | } |
2817 | | |
2818 | 0 | CASE(GetArgumentsLength) { |
2819 | | // If the arguments object hasn't been created yet. |
2820 | 0 | if (O2REG(GetArgumentsLength).isUndefined()) { |
2821 | 0 | O1REG(GetArgumentsLength) = |
2822 | 0 | HermesValue::encodeUntrustedNumberValue(FRAME.getArgCount()); |
2823 | 0 | ip = NEXTINST(GetArgumentsLength); |
2824 | 0 | DISPATCH; |
2825 | 0 | } |
2826 | | // The arguments object has been created, so this is a regular property |
2827 | | // get. |
2828 | 0 | assert( |
2829 | 0 | O2REG(GetArgumentsLength).isObject() && |
2830 | 0 | "arguments lazy register is not an object"); |
2831 | 0 | CAPTURE_IP( |
2832 | 0 | resPH = JSObject::getNamed_RJS( |
2833 | 0 | Handle<JSObject>::vmcast(&O2REG(GetArgumentsLength)), |
2834 | 0 | runtime, |
2835 | 0 | Predefined::getSymbolID(Predefined::length))); |
2836 | 0 | if (resPH == ExecutionStatus::EXCEPTION) { |
2837 | 0 | goto exception; |
2838 | 0 | } |
2839 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2840 | 0 | O1REG(GetArgumentsLength) = resPH->get(); |
2841 | 0 | ip = NEXTINST(GetArgumentsLength); |
2842 | 0 | DISPATCH; |
2843 | 0 | } |
2844 | | |
2845 | 0 | CASE(GetArgumentsPropByVal) { |
2846 | | // If the arguments object hasn't been created yet and we have a |
2847 | | // valid integer index, we use the fast path. |
2848 | 0 | if (O3REG(GetArgumentsPropByVal).isUndefined()) { |
2849 | | // If this is an integer index. |
2850 | 0 | if (auto index = toArrayIndexFastPath(O2REG(GetArgumentsPropByVal))) { |
2851 | | // Is this an existing argument? |
2852 | 0 | if (*index < FRAME.getArgCount()) { |
2853 | 0 | O1REG(GetArgumentsPropByVal) = FRAME.getArgRef(*index); |
2854 | 0 | ip = NEXTINST(GetArgumentsPropByVal); |
2855 | 0 | DISPATCH; |
2856 | 0 | } |
2857 | 0 | } |
2858 | 0 | } |
2859 | | // Slow path. |
2860 | 0 | CAPTURE_IP_ASSIGN( |
2861 | 0 | auto res, |
2862 | 0 | getArgumentsPropByValSlowPath_RJS( |
2863 | 0 | runtime, |
2864 | 0 | &O3REG(GetArgumentsPropByVal), |
2865 | 0 | &O2REG(GetArgumentsPropByVal), |
2866 | 0 | FRAME.getCalleeClosureHandleUnsafe(), |
2867 | 0 | strictMode)); |
2868 | 0 | if (res == ExecutionStatus::EXCEPTION) { |
2869 | 0 | goto exception; |
2870 | 0 | } |
2871 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2872 | 0 | O1REG(GetArgumentsPropByVal) = res->getHermesValue(); |
2873 | 0 | ip = NEXTINST(GetArgumentsPropByVal); |
2874 | 0 | DISPATCH; |
2875 | 0 | } |
2876 | | |
2877 | 2 | CASE(ReifyArguments) { |
2878 | | // If the arguments object was already created, do nothing. |
2879 | 2 | if (!O1REG(ReifyArguments).isUndefined()) { |
2880 | 0 | assert( |
2881 | 0 | O1REG(ReifyArguments).isObject() && |
2882 | 0 | "arguments lazy register is not an object"); |
2883 | 0 | ip = NEXTINST(ReifyArguments); |
2884 | 0 | DISPATCH; |
2885 | 0 | } |
2886 | 2 | CAPTURE_IP( |
2887 | 2 | resArgs = reifyArgumentsSlowPath( |
2888 | 2 | runtime, FRAME.getCalleeClosureHandleUnsafe(), strictMode)); |
2889 | 2 | if (LLVM_UNLIKELY(resArgs == ExecutionStatus::EXCEPTION)) { |
2890 | 0 | goto exception; |
2891 | 0 | } |
2892 | 2 | O1REG(ReifyArguments) = resArgs->getHermesValue(); |
2893 | 2 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2894 | 2 | ip = NEXTINST(ReifyArguments); |
2895 | 10 | DISPATCH; |
2896 | 10 | } |
2897 | | |
2898 | 508 | CASE(NewObject) { |
2899 | | // Create a new object using the built-in constructor. Note that the |
2900 | | // built-in constructor is empty, so we don't actually need to call |
2901 | | // it. |
2902 | 508 | CAPTURE_IP( |
2903 | 508 | O1REG(NewObject) = JSObject::create(runtime).getHermesValue()); |
2904 | 508 | assert( |
2905 | 508 | gcScope.getHandleCountDbg() == KEEP_HANDLES && |
2906 | 508 | "Should not create handles."); |
2907 | 508 | ip = NEXTINST(NewObject); |
2908 | 2.54k | DISPATCH; |
2909 | 2.54k | } |
2910 | 0 | CASE(NewObjectWithParent) { |
2911 | 0 | CAPTURE_IP( |
2912 | 0 | O1REG(NewObjectWithParent) = |
2913 | 0 | JSObject::create( |
2914 | 0 | runtime, |
2915 | 0 | O2REG(NewObjectWithParent).isObject() |
2916 | 0 | ? Handle<JSObject>::vmcast(&O2REG(NewObjectWithParent)) |
2917 | 0 | : O2REG(NewObjectWithParent).isNull() |
2918 | 0 | ? Runtime::makeNullHandle<JSObject>() |
2919 | 0 | : Handle<JSObject>::vmcast(&runtime.objectPrototype)) |
2920 | 0 | .getHermesValue()); |
2921 | 0 | assert( |
2922 | 0 | gcScope.getHandleCountDbg() == KEEP_HANDLES && |
2923 | 0 | "Should not create handles."); |
2924 | 0 | ip = NEXTINST(NewObjectWithParent); |
2925 | 0 | DISPATCH; |
2926 | 0 | } |
2927 | | |
2928 | 0 | CASE(NewObjectWithBuffer) { |
2929 | 0 | CAPTURE_IP( |
2930 | 0 | resPH = Interpreter::createObjectFromBuffer( |
2931 | 0 | runtime, |
2932 | 0 | curCodeBlock, |
2933 | 0 | ip->iNewObjectWithBuffer.op3, |
2934 | 0 | ip->iNewObjectWithBuffer.op4, |
2935 | 0 | ip->iNewObjectWithBuffer.op5)); |
2936 | 0 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
2937 | 0 | goto exception; |
2938 | 0 | } |
2939 | 0 | O1REG(NewObjectWithBuffer) = resPH->get(); |
2940 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2941 | 0 | ip = NEXTINST(NewObjectWithBuffer); |
2942 | 0 | DISPATCH; |
2943 | 0 | } |
2944 | | |
2945 | 0 | CASE(NewObjectWithBufferLong) { |
2946 | 0 | CAPTURE_IP( |
2947 | 0 | resPH = Interpreter::createObjectFromBuffer( |
2948 | 0 | runtime, |
2949 | 0 | curCodeBlock, |
2950 | 0 | ip->iNewObjectWithBufferLong.op3, |
2951 | 0 | ip->iNewObjectWithBufferLong.op4, |
2952 | 0 | ip->iNewObjectWithBufferLong.op5)); |
2953 | 0 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
2954 | 0 | goto exception; |
2955 | 0 | } |
2956 | 0 | O1REG(NewObjectWithBufferLong) = resPH->get(); |
2957 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2958 | 0 | ip = NEXTINST(NewObjectWithBufferLong); |
2959 | 0 | DISPATCH; |
2960 | 0 | } |
2961 | | |
2962 | 357k | CASE(NewArray) { |
2963 | | // Create a new array using the built-in constructor. Note that the |
2964 | | // built-in constructor is empty, so we don't actually need to call |
2965 | | // it. |
2966 | 357k | { |
2967 | 357k | CAPTURE_IP_ASSIGN( |
2968 | 357k | auto createRes, |
2969 | 357k | JSArray::create(runtime, ip->iNewArray.op2, ip->iNewArray.op2)); |
2970 | 357k | if (createRes == ExecutionStatus::EXCEPTION) { |
2971 | 0 | goto exception; |
2972 | 0 | } |
2973 | 357k | O1REG(NewArray) = createRes->getHermesValue(); |
2974 | 357k | } |
2975 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2976 | 357k | ip = NEXTINST(NewArray); |
2977 | 1.78M | DISPATCH; |
2978 | 1.78M | } |
2979 | | |
2980 | 12 | CASE(NewArrayWithBuffer) { |
2981 | 12 | CAPTURE_IP( |
2982 | 12 | resPH = Interpreter::createArrayFromBuffer( |
2983 | 12 | runtime, |
2984 | 12 | curCodeBlock, |
2985 | 12 | ip->iNewArrayWithBuffer.op2, |
2986 | 12 | ip->iNewArrayWithBuffer.op3, |
2987 | 12 | ip->iNewArrayWithBuffer.op4)); |
2988 | 12 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
2989 | 0 | goto exception; |
2990 | 0 | } |
2991 | 12 | O1REG(NewArrayWithBuffer) = resPH->get(); |
2992 | 12 | gcScope.flushToSmallCount(KEEP_HANDLES); |
2993 | 12 | tmpHandle.clear(); |
2994 | 12 | ip = NEXTINST(NewArrayWithBuffer); |
2995 | 60 | DISPATCH; |
2996 | 60 | } |
2997 | | |
2998 | 0 | CASE(NewArrayWithBufferLong) { |
2999 | 0 | CAPTURE_IP( |
3000 | 0 | resPH = Interpreter::createArrayFromBuffer( |
3001 | 0 | runtime, |
3002 | 0 | curCodeBlock, |
3003 | 0 | ip->iNewArrayWithBufferLong.op2, |
3004 | 0 | ip->iNewArrayWithBufferLong.op3, |
3005 | 0 | ip->iNewArrayWithBufferLong.op4)); |
3006 | 0 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { |
3007 | 0 | goto exception; |
3008 | 0 | } |
3009 | 0 | O1REG(NewArrayWithBufferLong) = resPH->get(); |
3010 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3011 | 0 | tmpHandle.clear(); |
3012 | 0 | ip = NEXTINST(NewArrayWithBufferLong); |
3013 | 0 | DISPATCH; |
3014 | 0 | } |
3015 | | |
3016 | 726 | CASE(CreateThis) { |
3017 | | // Registers: output, prototype, closure. |
3018 | 726 | if (LLVM_UNLIKELY(!vmisa<Callable>(O3REG(CreateThis)))) { |
3019 | 0 | CAPTURE_IP(runtime.raiseTypeError("constructor is not callable")); |
3020 | 0 | goto exception; |
3021 | 0 | } |
3022 | 726 | CAPTURE_IP_ASSIGN( |
3023 | 726 | auto res, |
3024 | 726 | Callable::newObject( |
3025 | 726 | Handle<Callable>::vmcast(&O3REG(CreateThis)), |
3026 | 726 | runtime, |
3027 | 726 | Handle<JSObject>::vmcast( |
3028 | 726 | O2REG(CreateThis).isObject() ? &O2REG(CreateThis) |
3029 | 726 | : &runtime.objectPrototype))); |
3030 | 726 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
3031 | 0 | goto exception; |
3032 | 0 | } |
3033 | 726 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3034 | 726 | O1REG(CreateThis) = res->getHermesValue(); |
3035 | 726 | ip = NEXTINST(CreateThis); |
3036 | 3.63k | DISPATCH; |
3037 | 3.63k | } |
3038 | | |
3039 | 726 | CASE(SelectObject) { |
3040 | | // Registers: output, thisObject, constructorReturnValue. |
3041 | 726 | O1REG(SelectObject) = O3REG(SelectObject).isObject() |
3042 | 726 | ? O3REG(SelectObject) |
3043 | 726 | : O2REG(SelectObject); |
3044 | 726 | ip = NEXTINST(SelectObject); |
3045 | 3.63k | DISPATCH; |
3046 | 3.63k | } |
3047 | | |
3048 | 726 | CASE(Eq) |
3049 | 726 | CASE(Neq) { |
3050 | 726 | CAPTURE_IP_ASSIGN( |
3051 | 726 | auto eqRes, |
3052 | 726 | abstractEqualityTest_RJS( |
3053 | 726 | runtime, Handle<>(&O2REG(Eq)), Handle<>(&O3REG(Eq)))); |
3054 | 726 | if (eqRes == ExecutionStatus::EXCEPTION) { |
3055 | 0 | goto exception; |
3056 | 0 | } |
3057 | 726 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3058 | 726 | O1REG(Eq) = HermesValue::encodeBoolValue( |
3059 | 726 | ip->opCode == OpCode::Eq ? *eqRes : !*eqRes); |
3060 | 726 | ip = NEXTINST(Eq); |
3061 | 3.63k | DISPATCH; |
3062 | 3.63k | } |
3063 | 0 | CASE(StrictEq) { |
3064 | 0 | O1REG(StrictEq) = HermesValue::encodeBoolValue( |
3065 | 0 | strictEqualityTest(O2REG(StrictEq), O3REG(StrictEq))); |
3066 | 0 | ip = NEXTINST(StrictEq); |
3067 | 0 | DISPATCH; |
3068 | 0 | } |
3069 | 0 | CASE(StrictNeq) { |
3070 | 0 | O1REG(StrictNeq) = HermesValue::encodeBoolValue( |
3071 | 0 | !strictEqualityTest(O2REG(StrictNeq), O3REG(StrictNeq))); |
3072 | 0 | ip = NEXTINST(StrictNeq); |
3073 | 0 | DISPATCH; |
3074 | 0 | } |
3075 | 1 | CASE(Not) { |
3076 | 1 | O1REG(Not) = HermesValue::encodeBoolValue(!toBoolean(O2REG(Not))); |
3077 | 1 | ip = NEXTINST(Not); |
3078 | 5 | DISPATCH; |
3079 | 5 | } |
3080 | 17.0k | CASE(Negate) { |
3081 | 17.0k | if (LLVM_LIKELY(O2REG(Negate).isNumber())) { |
3082 | 17.0k | O1REG(Negate) = HermesValue::encodeUntrustedNumberValue( |
3083 | 17.0k | -O2REG(Negate).getNumber()); |
3084 | 17.0k | ip = NEXTINST(Negate); |
3085 | 85.3k | DISPATCH; |
3086 | 85.3k | } |
3087 | 17.0k | CAPTURE_IP(res = doNegateSlowPath(runtime, Handle<>(&O2REG(Negate)))); |
3088 | 17.0k | if (res == ExecutionStatus::EXCEPTION) |
3089 | 0 | goto exception; |
3090 | 17.0k | O1REG(Negate) = *res; |
3091 | 17.0k | gcScope.flushToSmallCount(KEEP_HANDLES); |
3092 | 17.0k | ip = NEXTINST(Negate); |
3093 | 17.0k | DISPATCH; |
3094 | 110 | } |
3095 | 1.45k | CASE(TypeOf) { |
3096 | 1.45k | CAPTURE_IP(O1REG(TypeOf) = typeOf(runtime, Handle<>(&O2REG(TypeOf)))); |
3097 | 1.45k | ip = NEXTINST(TypeOf); |
3098 | 7.26k | DISPATCH; |
3099 | 7.26k | } |
3100 | 0 | CASE(Mod) { |
3101 | 0 | if (LLVM_LIKELY(O2REG(Mod).isNumber() && O3REG(Mod).isNumber())) { |
3102 | | /* Fast-path. */ |
3103 | 0 | O1REG(Mod) = HermesValue::encodeUntrustedNumberValue( |
3104 | 0 | doMod(O2REG(Mod).getNumber(), O3REG(Mod).getNumber())); |
3105 | 0 | ip = NEXTINST(Mod); |
3106 | 0 | DISPATCH; |
3107 | 0 | } |
3108 | 0 | CAPTURE_IP( |
3109 | 0 | res = doOperSlowPath<doMod>( |
3110 | 0 | runtime, Handle<>(&O2REG(Mod)), Handle<>(&O3REG(Mod)))); |
3111 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
3112 | 0 | goto exception; |
3113 | 0 | } |
3114 | 0 | O1REG(Mod) = *res; |
3115 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3116 | 0 | ip = NEXTINST(Mod); |
3117 | 0 | DISPATCH; |
3118 | 0 | } |
3119 | 0 | CASE(InstanceOf) { |
3120 | 0 | CAPTURE_IP_ASSIGN( |
3121 | 0 | auto result, |
3122 | 0 | instanceOfOperator_RJS( |
3123 | 0 | runtime, |
3124 | 0 | Handle<>(&O2REG(InstanceOf)), |
3125 | 0 | Handle<>(&O3REG(InstanceOf)))); |
3126 | 0 | if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) { |
3127 | 0 | goto exception; |
3128 | 0 | } |
3129 | 0 | O1REG(InstanceOf) = HermesValue::encodeBoolValue(*result); |
3130 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3131 | 0 | ip = NEXTINST(InstanceOf); |
3132 | 0 | DISPATCH; |
3133 | 0 | } |
3134 | 0 | CASE(IsIn) { |
3135 | 0 | { |
3136 | 0 | if (LLVM_UNLIKELY(!O3REG(IsIn).isObject())) { |
3137 | 0 | CAPTURE_IP(runtime.raiseTypeError( |
3138 | 0 | "right operand of 'in' is not an object")); |
3139 | 0 | goto exception; |
3140 | 0 | } |
3141 | 0 | CAPTURE_IP_ASSIGN( |
3142 | 0 | auto cr, |
3143 | 0 | JSObject::hasComputed( |
3144 | 0 | Handle<JSObject>::vmcast(&O3REG(IsIn)), |
3145 | 0 | runtime, |
3146 | 0 | Handle<>(&O2REG(IsIn)))); |
3147 | 0 | if (cr == ExecutionStatus::EXCEPTION) { |
3148 | 0 | goto exception; |
3149 | 0 | } |
3150 | 0 | O1REG(IsIn) = HermesValue::encodeBoolValue(*cr); |
3151 | 0 | } |
3152 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3153 | 0 | ip = NEXTINST(IsIn); |
3154 | 0 | DISPATCH; |
3155 | 0 | } |
3156 | | |
3157 | 316 | CASE(PutNewOwnByIdShort) { |
3158 | 316 | nextIP = NEXTINST(PutNewOwnByIdShort); |
3159 | 316 | idVal = ip->iPutNewOwnByIdShort.op3; |
3160 | 316 | goto putOwnById; |
3161 | 0 | } |
3162 | 0 | CASE(PutNewOwnNEByIdLong) |
3163 | 0 | CASE(PutNewOwnByIdLong) { |
3164 | 0 | nextIP = NEXTINST(PutNewOwnByIdLong); |
3165 | 0 | idVal = ip->iPutNewOwnByIdLong.op3; |
3166 | 0 | goto putOwnById; |
3167 | 0 | } |
3168 | 0 | CASE(PutNewOwnNEById) |
3169 | 0 | CASE(PutNewOwnById) { |
3170 | 0 | nextIP = NEXTINST(PutNewOwnById); |
3171 | 0 | idVal = ip->iPutNewOwnById.op3; |
3172 | 0 | } |
3173 | 316 | putOwnById: { |
3174 | 316 | assert( |
3175 | 316 | O1REG(PutNewOwnById).isObject() && |
3176 | 316 | "Object argument of PutNewOwnById must be an object"); |
3177 | 316 | CAPTURE_IP_ASSIGN( |
3178 | 316 | auto res, |
3179 | 316 | JSObject::defineNewOwnProperty( |
3180 | 316 | Handle<JSObject>::vmcast(&O1REG(PutNewOwnById)), |
3181 | 316 | runtime, |
3182 | 316 | ID(idVal), |
3183 | 316 | ip->opCode <= OpCode::PutNewOwnByIdLong |
3184 | 316 | ? PropertyFlags::defaultNewNamedPropertyFlags() |
3185 | 316 | : PropertyFlags::nonEnumerablePropertyFlags(), |
3186 | 316 | Handle<>(&O2REG(PutNewOwnById)))); |
3187 | 316 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
3188 | 0 | goto exception; |
3189 | 0 | } |
3190 | 316 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3191 | 316 | ip = nextIP; |
3192 | 1.58k | DISPATCH; |
3193 | 1.58k | } |
3194 | | |
3195 | 0 | CASE(DelByIdLong) { |
3196 | 0 | idVal = ip->iDelByIdLong.op3; |
3197 | 0 | nextIP = NEXTINST(DelByIdLong); |
3198 | 0 | goto DelById; |
3199 | 1.58k | } |
3200 | | |
3201 | 0 | CASE(DelById) { |
3202 | 0 | idVal = ip->iDelById.op3; |
3203 | 0 | nextIP = NEXTINST(DelById); |
3204 | 0 | } |
3205 | 0 | DelById: { |
3206 | 0 | if (LLVM_LIKELY(O2REG(DelById).isObject())) { |
3207 | 0 | CAPTURE_IP_ASSIGN( |
3208 | 0 | auto status, |
3209 | 0 | JSObject::deleteNamed( |
3210 | 0 | Handle<JSObject>::vmcast(&O2REG(DelById)), |
3211 | 0 | runtime, |
3212 | 0 | ID(idVal), |
3213 | 0 | defaultPropOpFlags)); |
3214 | 0 | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { |
3215 | 0 | goto exception; |
3216 | 0 | } |
3217 | 0 | O1REG(DelById) = HermesValue::encodeBoolValue(status.getValue()); |
3218 | 0 | } else { |
3219 | | // This is the "slow path". |
3220 | 0 | CAPTURE_IP(res = toObject(runtime, Handle<>(&O2REG(DelById)))); |
3221 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
3222 | | // If an exception is thrown, likely we are trying to convert |
3223 | | // undefined/null to an object. Passing over the name of the property |
3224 | | // so that we could emit more meaningful error messages. |
3225 | 0 | CAPTURE_IP(amendPropAccessErrorMsgWithPropName( |
3226 | 0 | runtime, Handle<>(&O2REG(DelById)), "delete", ID(idVal))); |
3227 | 0 | goto exception; |
3228 | 0 | } |
3229 | 0 | tmpHandle = res.getValue(); |
3230 | 0 | CAPTURE_IP_ASSIGN( |
3231 | 0 | auto status, |
3232 | 0 | JSObject::deleteNamed( |
3233 | 0 | Handle<JSObject>::vmcast(tmpHandle), |
3234 | 0 | runtime, |
3235 | 0 | ID(idVal), |
3236 | 0 | defaultPropOpFlags)); |
3237 | 0 | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { |
3238 | 0 | goto exception; |
3239 | 0 | } |
3240 | 0 | O1REG(DelById) = HermesValue::encodeBoolValue(status.getValue()); |
3241 | 0 | tmpHandle.clear(); |
3242 | 0 | } |
3243 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3244 | 0 | ip = nextIP; |
3245 | 0 | DISPATCH; |
3246 | 0 | } |
3247 | | |
3248 | 0 | CASE(DelByVal) { |
3249 | 0 | if (LLVM_LIKELY(O2REG(DelByVal).isObject())) { |
3250 | 0 | CAPTURE_IP_ASSIGN( |
3251 | 0 | auto status, |
3252 | 0 | JSObject::deleteComputed( |
3253 | 0 | Handle<JSObject>::vmcast(&O2REG(DelByVal)), |
3254 | 0 | runtime, |
3255 | 0 | Handle<>(&O3REG(DelByVal)), |
3256 | 0 | defaultPropOpFlags)); |
3257 | 0 | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { |
3258 | 0 | goto exception; |
3259 | 0 | } |
3260 | 0 | O1REG(DelByVal) = HermesValue::encodeBoolValue(status.getValue()); |
3261 | 0 | } else { |
3262 | | // This is the "slow path". |
3263 | 0 | CAPTURE_IP(res = toObject(runtime, Handle<>(&O2REG(DelByVal)))); |
3264 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
3265 | 0 | goto exception; |
3266 | 0 | } |
3267 | 0 | tmpHandle = res.getValue(); |
3268 | 0 | CAPTURE_IP_ASSIGN( |
3269 | 0 | auto status, |
3270 | 0 | JSObject::deleteComputed( |
3271 | 0 | Handle<JSObject>::vmcast(tmpHandle), |
3272 | 0 | runtime, |
3273 | 0 | Handle<>(&O3REG(DelByVal)), |
3274 | 0 | defaultPropOpFlags)); |
3275 | 0 | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { |
3276 | 0 | goto exception; |
3277 | 0 | } |
3278 | 0 | O1REG(DelByVal) = HermesValue::encodeBoolValue(status.getValue()); |
3279 | 0 | } |
3280 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3281 | 0 | tmpHandle.clear(); |
3282 | 0 | ip = NEXTINST(DelByVal); |
3283 | 0 | DISPATCH; |
3284 | 0 | } |
3285 | 3 | CASE(CreateRegExp) { |
3286 | 3 | { |
3287 | | // Create the RegExp object. |
3288 | 3 | CAPTURE_IP( |
3289 | 3 | O1REG(CreateRegExp) = JSRegExp::create(runtime).getHermesValue()); |
3290 | 3 | auto re = Handle<JSRegExp>::vmcast(&O1REG(CreateRegExp)); |
3291 | | // Initialize the regexp. |
3292 | 3 | CAPTURE_IP_ASSIGN( |
3293 | 3 | auto pattern, |
3294 | 3 | runtime.makeHandle(curCodeBlock->getRuntimeModule() |
3295 | 3 | ->getStringPrimFromStringIDMayAllocate( |
3296 | 3 | ip->iCreateRegExp.op2))); |
3297 | 3 | CAPTURE_IP_ASSIGN( |
3298 | 3 | auto flags, |
3299 | 3 | runtime.makeHandle(curCodeBlock->getRuntimeModule() |
3300 | 3 | ->getStringPrimFromStringIDMayAllocate( |
3301 | 3 | ip->iCreateRegExp.op3))); |
3302 | 3 | CAPTURE_IP_ASSIGN( |
3303 | 3 | auto bytecode, |
3304 | 3 | curCodeBlock->getRuntimeModule()->getRegExpBytecodeFromRegExpID( |
3305 | 3 | ip->iCreateRegExp.op4)); |
3306 | 3 | CAPTURE_IP( |
3307 | 3 | JSRegExp::initialize(re, runtime, pattern, flags, bytecode)); |
3308 | 3 | } |
3309 | 3 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3310 | 3 | ip = NEXTINST(CreateRegExp); |
3311 | 15 | DISPATCH; |
3312 | 15 | } |
3313 | | |
3314 | 0 | CASE(SwitchImm) { |
3315 | 0 | if (LLVM_LIKELY(O1REG(SwitchImm).isNumber())) { |
3316 | 0 | double numVal = O1REG(SwitchImm).getNumber(); |
3317 | 0 | uint32_t uintVal = (uint32_t)numVal; |
3318 | 0 | if (LLVM_LIKELY(numVal == uintVal) && // Only integers. |
3319 | 0 | LLVM_LIKELY(uintVal >= ip->iSwitchImm.op4) && // Bounds checking. |
3320 | 0 | LLVM_LIKELY(uintVal <= ip->iSwitchImm.op5)) // Bounds checking. |
3321 | 0 | { |
3322 | | // Calculate the offset into the bytecode where the jump table for |
3323 | | // this SwitchImm starts. |
3324 | 0 | const uint8_t *tablestart = (const uint8_t *)llvh::alignAddr( |
3325 | 0 | (const uint8_t *)ip + ip->iSwitchImm.op2, sizeof(uint32_t)); |
3326 | | |
3327 | | // Read the offset from the table. |
3328 | | // Must be signed to account for backwards branching. |
3329 | 0 | const int32_t *loc = |
3330 | 0 | (const int32_t *)tablestart + uintVal - ip->iSwitchImm.op4; |
3331 | |
|
3332 | 0 | ip = IPADD(*loc); |
3333 | 0 | DISPATCH; |
3334 | 0 | } |
3335 | 0 | } |
3336 | | // Wrong type or out of range, jump to default. |
3337 | 0 | ip = IPADD(ip->iSwitchImm.op3); |
3338 | 0 | DISPATCH; |
3339 | 0 | } |
3340 | 3.39M | LOAD_CONST( |
3341 | 3.39M | LoadConstUInt8, |
3342 | 3.39M | HermesValue::encodeTrustedNumberValue(ip->iLoadConstUInt8.op2)); |
3343 | 3.46M | LOAD_CONST( |
3344 | 3.46M | LoadConstInt, |
3345 | 3.46M | HermesValue::encodeTrustedNumberValue(ip->iLoadConstInt.op2)); |
3346 | 3.46M | LOAD_CONST( |
3347 | 101k | LoadConstDouble, |
3348 | 101k | HermesValue::encodeUntrustedNumberValue(ip->iLoadConstDouble.op2)); |
3349 | 2.49M | LOAD_CONST_CAPTURE_IP( |
3350 | 2.49M | LoadConstString, |
3351 | 2.49M | HermesValue::encodeStringValue( |
3352 | 2.49M | curCodeBlock->getRuntimeModule() |
3353 | 2.49M | ->getStringPrimFromStringIDMayAllocate( |
3354 | 2.49M | ip->iLoadConstString.op2))); |
3355 | 2.49M | LOAD_CONST_CAPTURE_IP( |
3356 | 0 | LoadConstStringLongIndex, |
3357 | 0 | HermesValue::encodeStringValue( |
3358 | 0 | curCodeBlock->getRuntimeModule() |
3359 | 0 | ->getStringPrimFromStringIDMayAllocate( |
3360 | 0 | ip->iLoadConstStringLongIndex.op2))); |
3361 | 0 | LOAD_CONST(LoadConstEmpty, HermesValue::encodeEmptyValue()); |
3362 | 526k | LOAD_CONST(LoadConstUndefined, HermesValue::encodeUndefinedValue()); |
3363 | 526k | LOAD_CONST(LoadConstNull, HermesValue::encodeNullValue()); |
3364 | 515k | LOAD_CONST(LoadConstTrue, HermesValue::encodeBoolValue(true)); |
3365 | 515k | LOAD_CONST(LoadConstFalse, HermesValue::encodeBoolValue(false)); |
3366 | 3.95M | LOAD_CONST(LoadConstZero, HermesValue::encodeTrustedNumberValue(0)); |
3367 | 3.95M | CASE(LoadConstBigInt) { |
3368 | 85.6k | idVal = ip->iLoadConstBigInt.op2; |
3369 | 85.6k | nextIP = NEXTINST(LoadConstBigInt); |
3370 | 85.6k | goto doLoadConstBigInt; |
3371 | 3.95M | } |
3372 | 0 | CASE(LoadConstBigIntLongIndex) { |
3373 | 0 | idVal = ip->iLoadConstBigIntLongIndex.op2; |
3374 | 0 | nextIP = NEXTINST(LoadConstBigIntLongIndex); |
3375 | 0 | goto doLoadConstBigInt; |
3376 | 3.95M | } |
3377 | 85.6k | doLoadConstBigInt: { |
3378 | 85.6k | CAPTURE_IP_ASSIGN( |
3379 | 85.6k | auto res, |
3380 | 85.6k | BigIntPrimitive::fromBytes( |
3381 | 85.6k | runtime, |
3382 | 85.6k | curCodeBlock->getRuntimeModule()->getBigIntBytesFromBigIntId( |
3383 | 85.6k | idVal))); |
3384 | 85.6k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
3385 | 0 | goto exception; |
3386 | 0 | } |
3387 | | // It is safe to access O1REG(LoadConstBigInt) or |
3388 | | // O1REG(LoadConstBigIntLongIndex) here as both instructions' O1 operands |
3389 | | // are the same size and live in the same offset w.r.t. the start of the |
3390 | | // instruction. |
3391 | 85.6k | O1REG(LoadConstBigInt) = std::move(*res); |
3392 | 85.6k | ip = nextIP; |
3393 | 428k | DISPATCH; |
3394 | 428k | } |
3395 | 85.6k | BINOP(Sub); |
3396 | 963 | BINOP(Mul); |
3397 | 0 | BINOP(Div); |
3398 | | // Can't do BINOP(Mod) as there's no ModN opcode. |
3399 | 0 | BITWISEBINOP(BitAnd); |
3400 | 0 | BITWISEBINOP(BitOr); |
3401 | 0 | BITWISEBINOP(BitXor); |
3402 | 0 | SHIFTOP(LShift); |
3403 | 0 | SHIFTOP(RShift); |
3404 | 0 | SHIFTOP(URshift); |
3405 | 0 | CONDOP(Less, <, lessOp_RJS); |
3406 | 0 | CONDOP(LessEq, <=, lessEqualOp_RJS); |
3407 | 0 | CONDOP(Greater, >, greaterOp_RJS); |
3408 | 0 | CONDOP(GreaterEq, >=, greaterEqualOp_RJS); |
3409 | 0 | JCOND(Less, <, lessOp_RJS); |
3410 | 0 | JCOND(LessEqual, <=, lessEqualOp_RJS); |
3411 | 0 | JCOND(Greater, >, greaterOp_RJS); |
3412 | 0 | JCOND(GreaterEqual, >=, greaterEqualOp_RJS); |
3413 | |
|
3414 | 1.45k | JCOND_STRICT_EQ_IMPL( |
3415 | 1.45k | JStrictEqual, , IPADD(ip->iJStrictEqual.op1), NEXTINST(JStrictEqual)); |
3416 | 1.45k | JCOND_STRICT_EQ_IMPL( |
3417 | 0 | JStrictEqual, |
3418 | 0 | Long, |
3419 | 0 | IPADD(ip->iJStrictEqualLong.op1), |
3420 | 0 | NEXTINST(JStrictEqualLong)); |
3421 | 2.90k | JCOND_STRICT_EQ_IMPL( |
3422 | 2.90k | JStrictNotEqual, |
3423 | 2.90k | , |
3424 | 2.90k | NEXTINST(JStrictNotEqual), |
3425 | 2.90k | IPADD(ip->iJStrictNotEqual.op1)); |
3426 | 2.90k | JCOND_STRICT_EQ_IMPL( |
3427 | 0 | JStrictNotEqual, |
3428 | 0 | Long, |
3429 | 0 | NEXTINST(JStrictNotEqualLong), |
3430 | 0 | IPADD(ip->iJStrictNotEqualLong.op1)); |
3431 | |
|
3432 | 1.93k | JCOND_EQ_IMPL(JEqual, , IPADD(ip->iJEqual.op1), NEXTINST(JEqual)); |
3433 | 1.93k | JCOND_EQ_IMPL( |
3434 | 0 | JEqual, Long, IPADD(ip->iJEqualLong.op1), NEXTINST(JEqualLong)); |
3435 | 0 | JCOND_EQ_IMPL( |
3436 | 0 | JNotEqual, , NEXTINST(JNotEqual), IPADD(ip->iJNotEqual.op1)); |
3437 | 0 | JCOND_EQ_IMPL( |
3438 | 0 | JNotEqual, |
3439 | 0 | Long, |
3440 | 0 | NEXTINST(JNotEqualLong), |
3441 | 0 | IPADD(ip->iJNotEqualLong.op1)); |
3442 | |
|
3443 | 147 | CASE_OUTOFLINE(PutOwnByVal); |
3444 | 147 | CASE_OUTOFLINE(PutOwnGetterSetterByVal); |
3445 | 0 | CASE_OUTOFLINE(DirectEval); |
3446 | |
|
3447 | 0 | CASE_OUTOFLINE(IteratorBegin); |
3448 | 0 | CASE_OUTOFLINE(IteratorNext); |
3449 | 0 | CASE(IteratorClose) { |
3450 | 0 | if (LLVM_UNLIKELY(O1REG(IteratorClose).isObject())) { |
3451 | | // The iterator must be closed if it's still an object. |
3452 | | // That means it was never an index and is not done iterating (a state |
3453 | | // which is indicated by `undefined`). |
3454 | 0 | CAPTURE_IP_ASSIGN( |
3455 | 0 | auto res, |
3456 | 0 | iteratorClose( |
3457 | 0 | runtime, |
3458 | 0 | Handle<JSObject>::vmcast(&O1REG(IteratorClose)), |
3459 | 0 | Runtime::getEmptyValue())); |
3460 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { |
3461 | 0 | if (ip->iIteratorClose.op2 && |
3462 | 0 | !isUncatchableError(runtime.thrownValue_)) { |
3463 | | // Ignore inner exception. |
3464 | 0 | runtime.clearThrownValue(); |
3465 | 0 | } else { |
3466 | 0 | goto exception; |
3467 | 0 | } |
3468 | 0 | } |
3469 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3470 | 0 | } |
3471 | 0 | ip = NEXTINST(IteratorClose); |
3472 | 0 | DISPATCH; |
3473 | 0 | } |
3474 | | |
3475 | | #ifdef HERMES_RUN_WASM |
3476 | | // Asm.js/Wasm Intrinsics |
3477 | | CASE(Add32) { |
3478 | | O1REG(Add32) = HermesValue::encodeUntrustedNumberValue( |
3479 | | (int32_t)(int64_t)(O2REG(Add32).getNumber() + |
3480 | | O3REG(Add32).getNumber())); |
3481 | | ip = NEXTINST(Add32); |
3482 | | DISPATCH; |
3483 | | } |
3484 | | CASE(Sub32) { |
3485 | | O1REG(Sub32) = HermesValue::encodeUntrustedNumberValue( |
3486 | | (int32_t)(int64_t)(O2REG(Sub32).getNumber() - |
3487 | | O3REG(Sub32).getNumber())); |
3488 | | ip = NEXTINST(Sub32); |
3489 | | DISPATCH; |
3490 | | } |
3491 | | CASE(Mul32) { |
3492 | | // Signedness matters for multiplication, but low 32 bits are the same |
3493 | | // regardless of signedness. |
3494 | | const uint32_t arg0 = (uint32_t)(int32_t)(O2REG(Mul32).getNumber()); |
3495 | | const uint32_t arg1 = (uint32_t)(int32_t)(O3REG(Mul32).getNumber()); |
3496 | | O1REG(Mul32) = |
3497 | | HermesValue::encodeUntrustedNumberValue((int32_t)(arg0 * arg1)); |
3498 | | ip = NEXTINST(Mul32); |
3499 | | DISPATCH; |
3500 | | } |
3501 | | CASE(Divi32) { |
3502 | | const int32_t arg0 = (int32_t)(O2REG(Divi32).getNumber()); |
3503 | | const int32_t arg1 = (int32_t)(O3REG(Divi32).getNumber()); |
3504 | | O1REG(Divi32) = HermesValue::encodeUntrustedNumberValue(arg0 / arg1); |
3505 | | ip = NEXTINST(Divi32); |
3506 | | DISPATCH; |
3507 | | } |
3508 | | CASE(Divu32) { |
3509 | | const uint32_t arg0 = (uint32_t)(int32_t)(O2REG(Divu32).getNumber()); |
3510 | | const uint32_t arg1 = (uint32_t)(int32_t)(O3REG(Divu32).getNumber()); |
3511 | | O1REG(Divu32) = |
3512 | | HermesValue::encodeUntrustedNumberValue((int32_t)(arg0 / arg1)); |
3513 | | ip = NEXTINST(Divu32); |
3514 | | DISPATCH; |
3515 | | } |
3516 | | |
3517 | | CASE(Loadi8) { |
3518 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadi8)); |
3519 | | int8_t *basePtr = reinterpret_cast<int8_t *>(mem->begin(runtime)); |
3520 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadi8).getNumber()); |
3521 | | O1REG(Loadi8) = HermesValue::encodeUntrustedNumberValue(basePtr[addr]); |
3522 | | ip = NEXTINST(Loadi8); |
3523 | | DISPATCH; |
3524 | | } |
3525 | | CASE(Loadu8) { |
3526 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadu8)); |
3527 | | uint8_t *basePtr = reinterpret_cast<uint8_t *>(mem->begin(runtime)); |
3528 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadu8).getNumber()); |
3529 | | O1REG(Loadu8) = HermesValue::encodeUntrustedNumberValue(basePtr[addr]); |
3530 | | ip = NEXTINST(Loadu8); |
3531 | | DISPATCH; |
3532 | | } |
3533 | | CASE(Loadi16) { |
3534 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadi16)); |
3535 | | int16_t *basePtr = reinterpret_cast<int16_t *>(mem->begin(runtime)); |
3536 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadi16).getNumber()); |
3537 | | O1REG(Loadi16) = |
3538 | | HermesValue::encodeUntrustedNumberValue(basePtr[addr >> 1]); |
3539 | | ip = NEXTINST(Loadi16); |
3540 | | DISPATCH; |
3541 | | } |
3542 | | CASE(Loadu16) { |
3543 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadu16)); |
3544 | | uint16_t *basePtr = reinterpret_cast<uint16_t *>(mem->begin(runtime)); |
3545 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadu16).getNumber()); |
3546 | | O1REG(Loadu16) = |
3547 | | HermesValue::encodeUntrustedNumberValue(basePtr[addr >> 1]); |
3548 | | ip = NEXTINST(Loadu16); |
3549 | | DISPATCH; |
3550 | | } |
3551 | | CASE(Loadi32) { |
3552 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadi32)); |
3553 | | int32_t *basePtr = reinterpret_cast<int32_t *>(mem->begin(runtime)); |
3554 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadi32).getNumber()); |
3555 | | O1REG(Loadi32) = |
3556 | | HermesValue::encodeUntrustedNumberValue(basePtr[addr >> 2]); |
3557 | | ip = NEXTINST(Loadi32); |
3558 | | DISPATCH; |
3559 | | } |
3560 | | CASE(Loadu32) { |
3561 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadu32)); |
3562 | | uint32_t *basePtr = reinterpret_cast<uint32_t *>(mem->begin(runtime)); |
3563 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadu32).getNumber()); |
3564 | | O1REG(Loadu32) = HermesValue::encodeUntrustedNumberValue( |
3565 | | (int32_t)(basePtr[addr >> 2])); |
3566 | | ip = NEXTINST(Loadu32); |
3567 | | DISPATCH; |
3568 | | } |
3569 | | |
3570 | | CASE(Store8) { |
3571 | | auto *mem = vmcast<JSTypedArrayBase>(O1REG(Store8)); |
3572 | | int8_t *basePtr = reinterpret_cast<int8_t *>(mem->begin(runtime)); |
3573 | | const uint32_t addr = (uint32_t)(int32_t)(O2REG(Store8).getNumber()); |
3574 | | basePtr[addr] = (int8_t)(int32_t)(O3REG(Store8).getNumber()); |
3575 | | ip = NEXTINST(Store8); |
3576 | | DISPATCH; |
3577 | | } |
3578 | | CASE(Store16) { |
3579 | | auto *mem = vmcast<JSTypedArrayBase>(O1REG(Store16)); |
3580 | | int16_t *basePtr = reinterpret_cast<int16_t *>(mem->begin(runtime)); |
3581 | | const uint32_t addr = (uint32_t)(int32_t)(O2REG(Store16).getNumber()); |
3582 | | basePtr[addr >> 1] = (int16_t)(int32_t)(O3REG(Store16).getNumber()); |
3583 | | ip = NEXTINST(Store16); |
3584 | | DISPATCH; |
3585 | | } |
3586 | | CASE(Store32) { |
3587 | | auto *mem = vmcast<JSTypedArrayBase>(O1REG(Store32)); |
3588 | | int32_t *basePtr = reinterpret_cast<int32_t *>(mem->begin(runtime)); |
3589 | | const uint32_t addr = (uint32_t)(int32_t)(O2REG(Store32).getNumber()); |
3590 | | basePtr[addr >> 2] = (int32_t)(O3REG(Store32).getNumber()); |
3591 | | // A nop for now. |
3592 | | ip = NEXTINST(Store32); |
3593 | | DISPATCH; |
3594 | | } |
3595 | | #endif |
3596 | | |
3597 | 0 | CASE(_last) { |
3598 | 0 | hermes_fatal("Invalid opcode _last"); |
3599 | 0 | } |
3600 | 0 | } |
3601 | | |
3602 | 0 | hermes_fatal( |
3603 | 0 | "All opcodes should dispatch to the next and not fallthrough " |
3604 | 0 | "to here"); |
3605 | | |
3606 | | // We arrive here if we couldn't allocate the registers for the current frame. |
3607 | 0 | stackOverflow: |
3608 | 0 | CAPTURE_IP(runtime.raiseStackOverflow( |
3609 | 0 | Runtime::StackOverflowKind::JSRegisterStack)); |
3610 | | |
3611 | | // We arrive here when we raised an exception in a callee, but we don't want |
3612 | | // the callee to be able to handle it. |
3613 | 0 | handleExceptionInParent: |
3614 | | // Restore the caller code block and IP. |
3615 | 0 | curCodeBlock = FRAME.getSavedCodeBlock(); |
3616 | 0 | ip = FRAME.getSavedIP(); |
3617 | | |
3618 | | // Pop to the previous frame where technically the error happened. |
3619 | 0 | frameRegs = &runtime.restoreStackAndPreviousFrame(FRAME).getFirstLocalRef(); |
3620 | | |
3621 | | // If we are coming from native code, return. |
3622 | 0 | if (!curCodeBlock) |
3623 | 0 | return ExecutionStatus::EXCEPTION; |
3624 | | |
3625 | | // Handle the exception. |
3626 | 29 | exception: |
3627 | 29 | UPDATE_OPCODE_TIME_SPENT; |
3628 | 29 | assert( |
3629 | 29 | !runtime.thrownValue_.isEmpty() && |
3630 | 29 | "thrownValue unavailable at exception"); |
3631 | | |
3632 | 29 | bool catchable = true; |
3633 | | // If this is an Error object that was thrown internally, it didn't have |
3634 | | // access to the current codeblock and IP, so collect the stack trace here. |
3635 | 29 | if (auto *jsError = dyn_vmcast<JSError>(runtime.thrownValue_)) { |
3636 | 29 | catchable = jsError->catchable(); |
3637 | 29 | if (!jsError->getStackTrace()) { |
3638 | | // Temporarily clear the thrown value for following operations. |
3639 | 27 | CAPTURE_IP_ASSIGN( |
3640 | 27 | auto errorHandle, |
3641 | 27 | runtime.makeHandle(vmcast<JSError>(runtime.thrownValue_))); |
3642 | 27 | runtime.clearThrownValue(); |
3643 | | |
3644 | 27 | CAPTURE_IP(JSError::recordStackTrace( |
3645 | 27 | errorHandle, runtime, false, curCodeBlock, ip)); |
3646 | | |
3647 | | // Restore the thrown value. |
3648 | 27 | runtime.setThrownValue(errorHandle.getHermesValue()); |
3649 | 27 | } |
3650 | 29 | } |
3651 | | |
3652 | 29 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3653 | 29 | tmpHandle.clear(); |
3654 | | |
3655 | 29 | #ifdef HERMES_ENABLE_DEBUGGER |
3656 | 29 | if (SingleStep) { |
3657 | | // If we're single stepping, don't bother with any more checks, |
3658 | | // and simply signal that we should continue execution with an exception. |
3659 | 0 | state.codeBlock = curCodeBlock; |
3660 | 0 | state.offset = CUROFFSET; |
3661 | 0 | return ExecutionStatus::EXCEPTION; |
3662 | 0 | } |
3663 | | |
3664 | 29 | using PauseOnThrowMode = facebook::hermes::debugger::PauseOnThrowMode; |
3665 | 29 | auto mode = runtime.debugger_.getPauseOnThrowMode(); |
3666 | 29 | if (mode != PauseOnThrowMode::None) { |
3667 | 0 | if (!runtime.debugger_.isDebugging()) { |
3668 | | // Determine whether the PauseOnThrowMode requires us to stop here. |
3669 | 0 | bool caught = |
3670 | 0 | runtime.debugger_ |
3671 | 0 | .findCatchTarget(InterpreterState(curCodeBlock, CUROFFSET)) |
3672 | 0 | .hasValue(); |
3673 | 0 | bool shouldStop = mode == PauseOnThrowMode::All || |
3674 | 0 | (mode == PauseOnThrowMode::Uncaught && !caught); |
3675 | 0 | if (shouldStop) { |
3676 | | // When runDebugger is invoked after an exception, |
3677 | | // stepping should never happen internally. |
3678 | | // Any step is a step to an exception handler, which we do |
3679 | | // directly here in the interpreter. |
3680 | | // Thus, the result state should be the same as the input state. |
3681 | 0 | InterpreterState tmpState{curCodeBlock, (uint32_t)CUROFFSET}; |
3682 | 0 | CAPTURE_IP_ASSIGN( |
3683 | 0 | ExecutionStatus resultStatus, |
3684 | 0 | runtime.debugger_.runDebugger( |
3685 | 0 | Debugger::RunReason::Exception, tmpState)); |
3686 | 0 | (void)resultStatus; |
3687 | 0 | assert( |
3688 | 0 | tmpState == InterpreterState(curCodeBlock, CUROFFSET) && |
3689 | 0 | "not allowed to step internally in a pauseOnThrow"); |
3690 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); |
3691 | 0 | } |
3692 | 0 | } |
3693 | 0 | } |
3694 | 29 | #endif |
3695 | | |
3696 | 29 | int32_t handlerOffset = 0; |
3697 | | |
3698 | | // If the exception is not catchable, skip found catch blocks. |
3699 | 29 | while (((handlerOffset = curCodeBlock->findCatchTargetOffset(CUROFFSET)) == |
3700 | 29 | -1) || |
3701 | 29 | !catchable) { |
3702 | 29 | PROFILER_EXIT_FUNCTION(curCodeBlock); |
3703 | | |
3704 | 29 | #ifdef HERMES_MEMORY_INSTRUMENTATION |
3705 | 29 | runtime.popCallStack(); |
3706 | 29 | #endif |
3707 | | |
3708 | | // Restore the code block and IP. |
3709 | 29 | curCodeBlock = FRAME.getSavedCodeBlock(); |
3710 | 29 | ip = FRAME.getSavedIP(); |
3711 | | |
3712 | | // Pop a stack frame. |
3713 | 29 | frameRegs = |
3714 | 29 | &runtime.restoreStackAndPreviousFrame(FRAME).getFirstLocalRef(); |
3715 | | |
3716 | 29 | SLOW_DEBUG( |
3717 | 29 | dbgs() << "function exit with exception: restored stackLevel=" |
3718 | 29 | << runtime.getStackLevel() << "\n"); |
3719 | | |
3720 | | // Are we returning to native code? |
3721 | 29 | if (!curCodeBlock) { |
3722 | 29 | SLOW_DEBUG( |
3723 | 29 | dbgs() |
3724 | 29 | << "function exit with exception: returning to native code\n"); |
3725 | 29 | return ExecutionStatus::EXCEPTION; |
3726 | 29 | } |
3727 | | |
3728 | 0 | assert( |
3729 | 0 | isCallType(ip->opCode) && |
3730 | 0 | "return address is not Call-type instruction"); |
3731 | 0 | } |
3732 | | |
3733 | 0 | INIT_STATE_FOR_CODEBLOCK(curCodeBlock); |
3734 | |
|
3735 | 0 | ip = IPADD(handlerOffset - CUROFFSET); |
3736 | 0 | } |
3737 | 2.03k | } hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::Interpreter::interpretFunction<false, false>(hermes::vm::Runtime&, hermes::vm::InterpreterState&) Line | Count | Source | 850 | 215 | InterpreterState &state) { | 851 | | // The interpreter is re-entrant and also saves/restores its IP via the | 852 | | // runtime whenever a call out is made (see the CAPTURE_IP_* macros). As such, | 853 | | // failure to preserve the IP across calls to interpreterFunction() disrupt | 854 | | // interpreter calls further up the C++ callstack. The RAII utility class | 855 | | // below makes sure we always do this correctly. | 856 | | // | 857 | | // TODO: The IPs stored in the C++ callstack via this holder will generally be | 858 | | // the same as in the JS stack frames via the Saved IP field. We can probably | 859 | | // get rid of one of these redundant stores. Doing this isn't completely | 860 | | // trivial as there are currently cases where we re-enter the interpreter | 861 | | // without calling Runtime::saveCallerIPInStackFrame(), and there are features | 862 | | // (I think mostly the debugger + stack traces) which implicitly rely on | 863 | | // this behavior. At least their tests break if this behavior is not | 864 | | // preserved. | 865 | 215 | struct IPSaver { | 866 | 215 | IPSaver(Runtime &runtime) | 867 | 215 | : ip_(runtime.getCurrentIP()), runtime_(runtime) {} | 868 | | | 869 | 215 | ~IPSaver() { | 870 | 215 | runtime_.setCurrentIP(ip_); | 871 | 215 | } | 872 | | | 873 | 215 | private: | 874 | 215 | const Inst *ip_; | 875 | 215 | Runtime &runtime_; | 876 | 215 | }; | 877 | 215 | IPSaver ipSaver(runtime); | 878 | | | 879 | | #ifndef HERMES_ENABLE_DEBUGGER | 880 | | static_assert(!SingleStep, "can't use single-step mode without the debugger"); | 881 | | #endif | 882 | | // Make sure that the cache can use an optimization by avoiding a branch to | 883 | | // access the property storage. | 884 | 215 | static_assert( | 885 | 215 | HiddenClass::kDictionaryThreshold <= | 886 | 215 | SegmentedArray::kValueToSegmentThreshold, | 887 | 215 | "Cannot avoid branches in cache check if the dictionary " | 888 | 215 | "crossover point is larger than the inline storage"); | 889 | | | 890 | 215 | CodeBlock *curCodeBlock = state.codeBlock; | 891 | 215 | const Inst *ip = nullptr; | 892 | | // Points to the first local register in the current frame. | 893 | | // This eliminates the indirect load from Runtime and the -1 offset. | 894 | 215 | PinnedHermesValue *frameRegs; | 895 | | // Strictness of current function. | 896 | 215 | bool strictMode; | 897 | | // Default flags when accessing properties. | 898 | 215 | PropOpFlags defaultPropOpFlags; | 899 | | | 900 | | // These CAPTURE_IP* macros should wrap around any major calls out of the | 901 | | // interpreter loop. They stash and retrieve the IP via the current Runtime | 902 | | // allowing the IP to be externally observed and even altered to change the flow | 903 | | // of execution. Explicitly saving AND restoring the IP from the Runtime in this | 904 | | // way means the C++ compiler will keep IP in a register within the rest of the | 905 | | // interpreter loop. | 906 | | // | 907 | | // When assertions are enabled we take the extra step of "invalidating" the IP | 908 | | // between captures so we can detect if it's erroneously accessed. | 909 | | // | 910 | | #ifdef NDEBUG | 911 | | | 912 | | #define CAPTURE_IP(expr) \ | 913 | | runtime.setCurrentIP(ip); \ | 914 | | (void)(expr); \ | 915 | | ip = runtime.getCurrentIP(); | 916 | | | 917 | | // Used when we want to declare a new variable and assign the expression to it. | 918 | | #define CAPTURE_IP_ASSIGN(decl, expr) \ | 919 | | runtime.setCurrentIP(ip); \ | 920 | | decl = (expr); \ | 921 | | ip = runtime.getCurrentIP(); | 922 | | | 923 | | #else // !NDEBUG | 924 | | | 925 | 215 | #define CAPTURE_IP(expr) \ | 926 | 215 | runtime.setCurrentIP(ip); \ | 927 | 215 | (void)(expr); \ | 928 | 215 | ip = runtime.getCurrentIP(); \ | 929 | 215 | runtime.invalidateCurrentIP(); | 930 | | | 931 | | // Used when we want to declare a new variable and assign the expression to it. | 932 | 215 | #define CAPTURE_IP_ASSIGN(decl, expr) \ | 933 | 215 | runtime.setCurrentIP(ip); \ | 934 | 215 | decl = (expr); \ | 935 | 215 | ip = runtime.getCurrentIP(); \ | 936 | 215 | runtime.invalidateCurrentIP(); | 937 | | | 938 | 215 | #endif // NDEBUG | 939 | | | 940 | | /// \def DONT_CAPTURE_IP(expr) | 941 | | /// \param expr A call expression to a function external to the interpreter. The | 942 | | /// expression should not make any allocations and the IP should be set | 943 | | /// immediately following this macro. | 944 | 215 | #define DONT_CAPTURE_IP(expr) \ | 945 | 215 | do { \ | 946 | 215 | NoAllocScope noAlloc(runtime); \ | 947 | 215 | (void)expr; \ | 948 | 215 | } while (false) | 949 | | | 950 | | // When performing a tail call, we need to set the runtime IP and leave it set. | 951 | 215 | #define CAPTURE_IP_SET() runtime.setCurrentIP(ip) | 952 | | | 953 | 215 | LLVM_DEBUG(dbgs() << "interpretFunction() called\n"); | 954 | | | 955 | 215 | ScopedNativeDepthTracker depthTracker{runtime}; | 956 | 215 | if (LLVM_UNLIKELY(depthTracker.overflowed())) { | 957 | 0 | return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack); | 958 | 0 | } | 959 | | | 960 | 215 | GCScope gcScope(runtime); | 961 | | // Avoid allocating a handle dynamically by reusing this one. | 962 | 215 | MutableHandle<> tmpHandle(runtime); | 963 | 215 | CallResult<HermesValue> res{ExecutionStatus::EXCEPTION}; | 964 | 215 | CallResult<PseudoHandle<>> resPH{ExecutionStatus::EXCEPTION}; | 965 | 215 | CallResult<Handle<Arguments>> resArgs{ExecutionStatus::EXCEPTION}; | 966 | 215 | CallResult<bool> boolRes{ExecutionStatus::EXCEPTION}; | 967 | | // Start of the bytecode file, used to calculate IP offset in crash traces. | 968 | 215 | const uint8_t *bytecodeFileStart; | 969 | | | 970 | | // Mark the gcScope so we can clear all allocated handles. | 971 | | // Remember how many handles the scope has so we can clear them in the loop. | 972 | 215 | static constexpr unsigned KEEP_HANDLES = 1; | 973 | 215 | assert( | 974 | 215 | gcScope.getHandleCountDbg() == KEEP_HANDLES && | 975 | 215 | "scope has unexpected number of handles"); | 976 | | | 977 | 215 | INIT_OPCODE_PROFILER; | 978 | | | 979 | 2.03k | tailCall: | 980 | 2.03k | PROFILER_ENTER_FUNCTION(curCodeBlock); | 981 | | | 982 | 2.03k | #ifdef HERMES_ENABLE_DEBUGGER | 983 | 2.03k | runtime.getDebugger().willEnterCodeBlock(curCodeBlock); | 984 | 2.03k | #endif | 985 | | | 986 | 2.03k | runtime.getCodeCoverageProfiler().markExecuted(curCodeBlock); | 987 | | | 988 | 2.03k | if (!SingleStep) { | 989 | 2.03k | auto newFrame = runtime.setCurrentFrameToTopOfStack(); | 990 | 2.03k | runtime.saveCallerIPInStackFrame(); | 991 | 2.03k | #ifndef NDEBUG | 992 | 2.03k | runtime.invalidateCurrentIP(); | 993 | 2.03k | #endif | 994 | | | 995 | | // Point frameRegs to the first register in the new frame. Note that at this | 996 | | // moment technically it points above the top of the stack, but we are never | 997 | | // going to access it. | 998 | 2.03k | frameRegs = &newFrame.getFirstLocalRef(); | 999 | | | 1000 | 2.03k | #ifndef NDEBUG | 1001 | 2.03k | LLVM_DEBUG( | 1002 | 2.03k | dbgs() << "function entry: stackLevel=" << runtime.getStackLevel() | 1003 | 2.03k | << ", argCount=" << runtime.getCurrentFrame().getArgCount() | 1004 | 2.03k | << ", frameSize=" << curCodeBlock->getFrameSize() << "\n"); | 1005 | | | 1006 | 2.03k | LLVM_DEBUG( | 1007 | 2.03k | dbgs() << " callee " | 1008 | 2.03k | << DumpHermesValue( | 1009 | 2.03k | runtime.getCurrentFrame().getCalleeClosureOrCBRef()) | 1010 | 2.03k | << "\n"); | 1011 | 2.03k | LLVM_DEBUG( | 1012 | 2.03k | dbgs() << " this " | 1013 | 2.03k | << DumpHermesValue(runtime.getCurrentFrame().getThisArgRef()) | 1014 | 2.03k | << "\n"); | 1015 | 3.48k | for (uint32_t i = 0; i != runtime.getCurrentFrame()->getArgCount(); ++i) { | 1016 | 1.45k | LLVM_DEBUG( | 1017 | 1.45k | dbgs() << " " << llvh::format_decimal(i, 4) << " " | 1018 | 1.45k | << DumpHermesValue(runtime.getCurrentFrame().getArgRef(i)) | 1019 | 1.45k | << "\n"); | 1020 | 1.45k | } | 1021 | 2.03k | #endif | 1022 | | | 1023 | | // Allocate the registers for the new frame. | 1024 | 2.03k | if (LLVM_UNLIKELY(!runtime.checkAndAllocStack( | 1025 | 2.03k | curCodeBlock->getFrameSize() + | 1026 | 2.03k | StackFrameLayout::CalleeExtraRegistersAtStart, | 1027 | 2.03k | HermesValue::encodeUndefinedValue()))) | 1028 | 0 | goto stackOverflow; | 1029 | | | 1030 | 2.03k | ip = (Inst const *)curCodeBlock->begin(); | 1031 | | | 1032 | | // Check for invalid invocation. | 1033 | 2.03k | if (LLVM_UNLIKELY(curCodeBlock->getHeaderFlags().isCallProhibited( | 1034 | 2.03k | newFrame.isConstructorCall()))) { | 1035 | 0 | if (!newFrame.isConstructorCall()) { | 1036 | 0 | CAPTURE_IP( | 1037 | 0 | runtime.raiseTypeError("Class constructor invoked without new")); | 1038 | 0 | } else { | 1039 | 0 | CAPTURE_IP(runtime.raiseTypeError("Function is not a constructor")); | 1040 | 0 | } | 1041 | 0 | goto handleExceptionInParent; | 1042 | 0 | } | 1043 | 2.03k | } else { | 1044 | | // Point frameRegs to the first register in the frame. | 1045 | 0 | frameRegs = &runtime.getCurrentFrame().getFirstLocalRef(); | 1046 | 0 | ip = (Inst const *)(curCodeBlock->begin() + state.offset); | 1047 | 0 | } | 1048 | | | 1049 | 2.03k | assert((const uint8_t *)ip < curCodeBlock->end() && "CodeBlock is empty"); | 1050 | | | 1051 | 2.03k | INIT_STATE_FOR_CODEBLOCK(curCodeBlock); | 1052 | | | 1053 | 2.03k | #define BEFORE_OP_CODE \ | 1054 | 2.03k | { \ | 1055 | 2.03k | UPDATE_OPCODE_TIME_SPENT; \ | 1056 | 2.03k | HERMES_SLOW_ASSERT( \ | 1057 | 2.03k | curCodeBlock->contains(ip) && "curCodeBlock must contain ip"); \ | 1058 | 2.03k | HERMES_SLOW_ASSERT((printDebugInfo(curCodeBlock, frameRegs, ip), true)); \ | 1059 | 2.03k | HERMES_SLOW_ASSERT( \ | 1060 | 2.03k | gcScope.getHandleCountDbg() == KEEP_HANDLES && \ | 1061 | 2.03k | "unaccounted handles were created"); \ | 1062 | 2.03k | HERMES_SLOW_ASSERT(tmpHandle->isUndefined() && "tmpHandle not cleared"); \ | 1063 | 2.03k | RECORD_OPCODE_START_TIME; \ | 1064 | 2.03k | INC_OPCODE_COUNT; \ | 1065 | 2.03k | if (EnableCrashTrace) { \ | 1066 | 2.03k | runtime.crashTrace_.recordInst( \ | 1067 | 2.03k | (uint32_t)((const uint8_t *)ip - bytecodeFileStart), ip->opCode); \ | 1068 | 2.03k | } \ | 1069 | 2.03k | } | 1070 | | | 1071 | 2.03k | #ifdef HERMESVM_INDIRECT_THREADING | 1072 | 2.03k | static void *opcodeDispatch[] = { | 1073 | 2.03k | #define DEFINE_OPCODE(name) &&case_##name, | 1074 | 2.03k | #include "hermes/BCGen/HBC/BytecodeList.def" | 1075 | 2.03k | &&case__last}; | 1076 | | | 1077 | 2.03k | #define CASE(name) case_##name: | 1078 | | // For indirect threading, there is no way to specify a default, leave it as | 1079 | | // an empty label. | 1080 | 2.03k | #define DEFAULT_CASE | 1081 | 2.03k | #define DISPATCH \ | 1082 | 2.03k | BEFORE_OP_CODE; \ | 1083 | 2.03k | if (SingleStep) { \ | 1084 | 2.03k | state.codeBlock = curCodeBlock; \ | 1085 | 2.03k | state.offset = CUROFFSET; \ | 1086 | 2.03k | return HermesValue::encodeUndefinedValue(); \ | 1087 | 2.03k | } \ | 1088 | 2.03k | goto *opcodeDispatch[(unsigned)ip->opCode] | 1089 | | | 1090 | | // Do nothing if we're not in a switch. | 1091 | 2.03k | #define INTERPRETER_FALLTHROUGH | 1092 | | | 1093 | | #else // HERMESVM_INDIRECT_THREADING | 1094 | | | 1095 | | #define CASE(name) case OpCode::name: | 1096 | | #define DEFAULT_CASE default: | 1097 | | #define DISPATCH \ | 1098 | | if (SingleStep) { \ | 1099 | | state.codeBlock = curCodeBlock; \ | 1100 | | state.offset = CUROFFSET; \ | 1101 | | return HermesValue::encodeUndefinedValue(); \ | 1102 | | } \ | 1103 | | continue | 1104 | | | 1105 | | // Fallthrough if we're in a switch. | 1106 | | #define INTERPRETER_FALLTHROUGH [[fallthrough]] | 1107 | | | 1108 | | #endif // HERMESVM_INDIRECT_THREADING | 1109 | | | 1110 | | // This macro is used when we detect that either the Implicit or Explicit | 1111 | | // AsyncBreak flags have been set. It checks to see which one was requested and | 1112 | | // propagate the corresponding RunReason. If both Implicit and Explicit have | 1113 | | // been requested, then we'll propagate the RunReasons for both. Once for | 1114 | | // Implicit and once for Explicit. | 1115 | 2.03k | #define RUN_DEBUGGER_ASYNC_BREAK(flags) \ | 1116 | 2.03k | bool requestedImplicit = (uint8_t)(flags) & \ | 1117 | 2.03k | (uint8_t)Runtime::AsyncBreakReasonBits::DebuggerImplicit; \ | 1118 | 2.03k | bool requestedExplicit = (uint8_t)(flags) & \ | 1119 | 2.03k | (uint8_t)Runtime::AsyncBreakReasonBits::DebuggerExplicit; \ | 1120 | 2.03k | do { \ | 1121 | 2.03k | if (requestedImplicit) { \ | 1122 | 2.03k | CAPTURE_IP_ASSIGN( \ | 1123 | 2.03k | auto dRes, \ | 1124 | 2.03k | runDebuggerUpdatingState( \ | 1125 | 2.03k | Debugger::RunReason::AsyncBreakImplicit, \ | 1126 | 2.03k | runtime, \ | 1127 | 2.03k | curCodeBlock, \ | 1128 | 2.03k | ip, \ | 1129 | 2.03k | frameRegs)); \ | 1130 | 2.03k | if (dRes == ExecutionStatus::EXCEPTION) \ | 1131 | 2.03k | goto exception; \ | 1132 | 2.03k | } \ | 1133 | 2.03k | if (requestedExplicit) { \ | 1134 | 2.03k | CAPTURE_IP_ASSIGN( \ | 1135 | 2.03k | auto dRes, \ | 1136 | 2.03k | runDebuggerUpdatingState( \ | 1137 | 2.03k | Debugger::RunReason::AsyncBreakExplicit, \ | 1138 | 2.03k | runtime, \ | 1139 | 2.03k | curCodeBlock, \ | 1140 | 2.03k | ip, \ | 1141 | 2.03k | frameRegs)); \ | 1142 | 2.03k | if (dRes == ExecutionStatus::EXCEPTION) \ | 1143 | 2.03k | goto exception; \ | 1144 | 2.03k | } \ | 1145 | 2.03k | } while (0) | 1146 | | | 1147 | 2.03k | for (;;) { | 1148 | 8.12k | BEFORE_OP_CODE; | 1149 | | | 1150 | 8.12k | #ifdef HERMESVM_INDIRECT_THREADING | 1151 | 8.12k | goto *opcodeDispatch[(unsigned)ip->opCode]; | 1152 | | #else | 1153 | | switch (ip->opCode) | 1154 | | #endif | 1155 | 8.12k | { | 1156 | 8.12k | const Inst *nextIP; | 1157 | 8.12k | uint32_t idVal; | 1158 | 8.12k | bool tryProp; | 1159 | 8.12k | uint32_t callArgCount; | 1160 | | // This is HermesValue::getRaw(), since HermesValue cannot be assigned | 1161 | | // to. It is meant to be used only for very short durations, in the | 1162 | | // dispatch of call instructions, when there is definitely no possibility | 1163 | | // of a GC. | 1164 | 8.12k | HermesValue::RawType callNewTarget; | 1165 | | | 1166 | | /// Handle an opcode \p name with an out-of-line implementation in a function | 1167 | | /// ExecutionStatus caseName( | 1168 | | /// Runtime &, | 1169 | | /// PinnedHermesValue *frameRegs, | 1170 | | /// Inst *ip) | 1171 | 8.12k | #define CASE_OUTOFLINE(name) \ | 1172 | 8.12k | CASE(name) { \ | 1173 | 8.12k | CAPTURE_IP_ASSIGN(auto res, case##name(runtime, frameRegs, ip)); \ | 1174 | 8.12k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { \ | 1175 | 8.12k | goto exception; \ | 1176 | 8.12k | } \ | 1177 | 8.12k | gcScope.flushToSmallCount(KEEP_HANDLES); \ | 1178 | 8.12k | ip = NEXTINST(name); \ | 1179 | 8.12k | DISPATCH; \ | 1180 | 8.12k | } | 1181 | | | 1182 | | /// Implement a binary arithmetic instruction with a fast path where both | 1183 | | /// operands are numbers. | 1184 | | /// \param name the name of the instruction. The fast path case will have a | 1185 | | /// "n" appended to the name. | 1186 | 8.12k | #define BINOP(name) \ | 1187 | 8.12k | CASE(name) { \ | 1188 | 8.12k | if (LLVM_LIKELY(O2REG(name).isNumber() && O3REG(name).isNumber())) { \ | 1189 | | /* Fast-path. */ \ | 1190 | 8.12k | INTERPRETER_FALLTHROUGH; \ | 1191 | 8.12k | CASE(name##N) { \ | 1192 | 8.12k | O1REG(name) = HermesValue::encodeTrustedNumberValue( \ | 1193 | 8.12k | do##name(O2REG(name).getNumber(), O3REG(name).getNumber())); \ | 1194 | 8.12k | ip = NEXTINST(name); \ | 1195 | 8.12k | DISPATCH; \ | 1196 | 8.12k | } \ | 1197 | 8.12k | } \ | 1198 | 8.12k | CAPTURE_IP( \ | 1199 | 8.12k | res = doOperSlowPath<do##name>( \ | 1200 | 8.12k | runtime, Handle<>(&O2REG(name)), Handle<>(&O3REG(name)))); \ | 1201 | 8.12k | if (res == ExecutionStatus::EXCEPTION) \ | 1202 | 8.12k | goto exception; \ | 1203 | 8.12k | O1REG(name) = *res; \ | 1204 | 8.12k | gcScope.flushToSmallCount(KEEP_HANDLES); \ | 1205 | 8.12k | ip = NEXTINST(name); \ | 1206 | 8.12k | DISPATCH; \ | 1207 | 8.12k | } | 1208 | | | 1209 | 8.12k | #define INCDECOP(name) \ | 1210 | 8.12k | CASE(name) { \ | 1211 | 8.12k | if (LLVM_LIKELY(O2REG(name).isNumber())) { \ | 1212 | 8.12k | O1REG(name) = HermesValue::encodeTrustedNumberValue( \ | 1213 | 8.12k | do##name(O2REG(name).getNumber())); \ | 1214 | 8.12k | ip = NEXTINST(name); \ | 1215 | 8.12k | DISPATCH; \ | 1216 | 8.12k | } \ | 1217 | 8.12k | CAPTURE_IP( \ | 1218 | 8.12k | res = \ | 1219 | 8.12k | doIncDecOperSlowPath<do##name>(runtime, Handle<>(&O2REG(name)))); \ | 1220 | 8.12k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { \ | 1221 | 8.12k | goto exception; \ | 1222 | 8.12k | } \ | 1223 | 8.12k | O1REG(name) = *res; \ | 1224 | 8.12k | gcScope.flushToSmallCount(KEEP_HANDLES); \ | 1225 | 8.12k | ip = NEXTINST(name); \ | 1226 | 8.12k | DISPATCH; \ | 1227 | 8.12k | } | 1228 | | | 1229 | | /// Implement a shift instruction with a fast path where both | 1230 | | /// operands are numbers. | 1231 | | /// \param name the name of the instruction. | 1232 | 8.12k | #define SHIFTOP(name) \ | 1233 | 8.12k | CASE(name) { \ | 1234 | 8.12k | if (LLVM_LIKELY( \ | 1235 | 8.12k | O2REG(name).isNumber() && \ | 1236 | 8.12k | O3REG(name).isNumber())) { /* Fast-path. */ \ | 1237 | 8.12k | auto lnum = hermes::truncateToInt32(O2REG(name).getNumber()); \ | 1238 | 8.12k | uint32_t rnum = hermes::truncateToInt32(O3REG(name).getNumber()) & 0x1f; \ | 1239 | 8.12k | O1REG(name) = \ | 1240 | 8.12k | HermesValue::encodeTrustedNumberValue(do##name(lnum, rnum)); \ | 1241 | 8.12k | ip = NEXTINST(name); \ | 1242 | 8.12k | DISPATCH; \ | 1243 | 8.12k | } \ | 1244 | 8.12k | CAPTURE_IP( \ | 1245 | 8.12k | res = doShiftOperSlowPath<do##name>( \ | 1246 | 8.12k | runtime, Handle<>(&O2REG(name)), Handle<>(&O3REG(name)))); \ | 1247 | 8.12k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { \ | 1248 | 8.12k | goto exception; \ | 1249 | 8.12k | } \ | 1250 | 8.12k | O1REG(name) = *res; \ | 1251 | 8.12k | gcScope.flushToSmallCount(KEEP_HANDLES); \ | 1252 | 8.12k | ip = NEXTINST(name); \ | 1253 | 8.12k | DISPATCH; \ | 1254 | 8.12k | } | 1255 | | | 1256 | | /// Implement a binary bitwise instruction with a fast path where both | 1257 | | /// operands are numbers. | 1258 | | /// \param name the name of the instruction. | 1259 | 8.12k | #define BITWISEBINOP(name) \ | 1260 | 8.12k | CASE(name) { \ | 1261 | 8.12k | if (LLVM_LIKELY(O2REG(name).isNumber() && O3REG(name).isNumber())) { \ | 1262 | | /* Fast-path. */ \ | 1263 | 8.12k | O1REG(name) = HermesValue::encodeTrustedNumberValue(do##name( \ | 1264 | 8.12k | hermes::truncateToInt32(O2REG(name).getNumber()), \ | 1265 | 8.12k | hermes::truncateToInt32(O3REG(name).getNumber()))); \ | 1266 | 8.12k | ip = NEXTINST(name); \ | 1267 | 8.12k | DISPATCH; \ | 1268 | 8.12k | } \ | 1269 | 8.12k | CAPTURE_IP( \ | 1270 | 8.12k | res = doBitOperSlowPath<do##name>( \ | 1271 | 8.12k | runtime, Handle<>(&O2REG(name)), Handle<>(&O3REG(name)))); \ | 1272 | 8.12k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { \ | 1273 | 8.12k | goto exception; \ | 1274 | 8.12k | } \ | 1275 | 8.12k | O1REG(name) = *res; \ | 1276 | 8.12k | gcScope.flushToSmallCount(KEEP_HANDLES); \ | 1277 | 8.12k | ip = NEXTINST(name); \ | 1278 | 8.12k | DISPATCH; \ | 1279 | 8.12k | } | 1280 | | | 1281 | | /// Implement a comparison instruction. | 1282 | | /// \param name the name of the instruction. | 1283 | | /// \param oper the C++ operator to use to actually perform the fast arithmetic | 1284 | | /// comparison. | 1285 | | /// \param operFuncName function to call for the slow-path comparison. | 1286 | 8.12k | #define CONDOP(name, oper, operFuncName) \ | 1287 | 8.12k | CASE(name) { \ | 1288 | 8.12k | if (LLVM_LIKELY(O2REG(name).isNumber() && O3REG(name).isNumber())) { \ | 1289 | | /* Fast-path. */ \ | 1290 | 8.12k | O1REG(name) = HermesValue::encodeBoolValue( \ | 1291 | 8.12k | O2REG(name).getNumber() oper O3REG(name).getNumber()); \ | 1292 | 8.12k | ip = NEXTINST(name); \ | 1293 | 8.12k | DISPATCH; \ | 1294 | 8.12k | } \ | 1295 | 8.12k | CAPTURE_IP( \ | 1296 | 8.12k | boolRes = operFuncName( \ | 1297 | 8.12k | runtime, Handle<>(&O2REG(name)), Handle<>(&O3REG(name)))); \ | 1298 | 8.12k | if (boolRes == ExecutionStatus::EXCEPTION) \ | 1299 | 8.12k | goto exception; \ | 1300 | 8.12k | gcScope.flushToSmallCount(KEEP_HANDLES); \ | 1301 | 8.12k | O1REG(name) = HermesValue::encodeBoolValue(boolRes.getValue()); \ | 1302 | 8.12k | ip = NEXTINST(name); \ | 1303 | 8.12k | DISPATCH; \ | 1304 | 8.12k | } | 1305 | | | 1306 | | /// Implement a comparison conditional jump with a fast path where both | 1307 | | /// operands are numbers. | 1308 | | /// \param name the name of the instruction. The fast path case will have a | 1309 | | /// "N" appended to the name. | 1310 | | /// \param suffix Optional suffix to be added to the end (e.g. Long) | 1311 | | /// \param oper the C++ operator to use to actually perform the fast arithmetic | 1312 | | /// comparison. | 1313 | | /// \param operFuncName function to call for the slow-path comparison. | 1314 | | /// \param trueDest ip value if the conditional evaluates to true | 1315 | | /// \param falseDest ip value if the conditional evaluates to false | 1316 | 8.12k | #define JCOND_IMPL(name, suffix, oper, operFuncName, trueDest, falseDest) \ | 1317 | 8.12k | CASE(name##suffix) { \ | 1318 | 8.12k | if (LLVM_LIKELY( \ | 1319 | 8.12k | O2REG(name##suffix).isNumber() && \ | 1320 | 8.12k | O3REG(name##suffix).isNumber())) { \ | 1321 | | /* Fast-path. */ \ | 1322 | 8.12k | INTERPRETER_FALLTHROUGH; \ | 1323 | 8.12k | CASE(name##N##suffix) { \ | 1324 | 8.12k | if (O2REG(name##N##suffix) \ | 1325 | 8.12k | .getNumber() oper O3REG(name##N##suffix) \ | 1326 | 8.12k | .getNumber()) { \ | 1327 | 8.12k | ip = trueDest; \ | 1328 | 8.12k | DISPATCH; \ | 1329 | 8.12k | } \ | 1330 | 8.12k | ip = falseDest; \ | 1331 | 8.12k | DISPATCH; \ | 1332 | 8.12k | } \ | 1333 | 8.12k | } \ | 1334 | 8.12k | CAPTURE_IP( \ | 1335 | 8.12k | boolRes = operFuncName( \ | 1336 | 8.12k | runtime, \ | 1337 | 8.12k | Handle<>(&O2REG(name##suffix)), \ | 1338 | 8.12k | Handle<>(&O3REG(name##suffix)))); \ | 1339 | 8.12k | if (boolRes == ExecutionStatus::EXCEPTION) \ | 1340 | 8.12k | goto exception; \ | 1341 | 8.12k | gcScope.flushToSmallCount(KEEP_HANDLES); \ | 1342 | 8.12k | if (boolRes.getValue()) { \ | 1343 | 8.12k | ip = trueDest; \ | 1344 | 8.12k | DISPATCH; \ | 1345 | 8.12k | } \ | 1346 | 8.12k | ip = falseDest; \ | 1347 | 8.12k | DISPATCH; \ | 1348 | 8.12k | } | 1349 | | | 1350 | | /// Implement a strict equality conditional jump | 1351 | | /// \param name the name of the instruction. | 1352 | | /// \param suffix Optional suffix to be added to the end (e.g. Long) | 1353 | | /// \param trueDest ip value if the conditional evaluates to true | 1354 | | /// \param falseDest ip value if the conditional evaluates to false | 1355 | 8.12k | #define JCOND_STRICT_EQ_IMPL(name, suffix, trueDest, falseDest) \ | 1356 | 8.12k | CASE(name##suffix) { \ | 1357 | 8.12k | if (strictEqualityTest(O2REG(name##suffix), O3REG(name##suffix))) { \ | 1358 | 8.12k | ip = trueDest; \ | 1359 | 8.12k | DISPATCH; \ | 1360 | 8.12k | } \ | 1361 | 8.12k | ip = falseDest; \ | 1362 | 8.12k | DISPATCH; \ | 1363 | 8.12k | } | 1364 | | | 1365 | | /// Implement an equality conditional jump | 1366 | | /// \param name the name of the instruction. | 1367 | | /// \param suffix Optional suffix to be added to the end (e.g. Long) | 1368 | | /// \param trueDest ip value if the conditional evaluates to true | 1369 | | /// \param falseDest ip value if the conditional evaluates to false | 1370 | 8.12k | #define JCOND_EQ_IMPL(name, suffix, trueDest, falseDest) \ | 1371 | 8.12k | CASE(name##suffix) { \ | 1372 | 8.12k | CAPTURE_IP_ASSIGN( \ | 1373 | 8.12k | auto eqRes, \ | 1374 | 8.12k | abstractEqualityTest_RJS( \ | 1375 | 8.12k | runtime, \ | 1376 | 8.12k | Handle<>(&O2REG(name##suffix)), \ | 1377 | 8.12k | Handle<>(&O3REG(name##suffix)))); \ | 1378 | 8.12k | if (eqRes == ExecutionStatus::EXCEPTION) { \ | 1379 | 8.12k | goto exception; \ | 1380 | 8.12k | } \ | 1381 | 8.12k | gcScope.flushToSmallCount(KEEP_HANDLES); \ | 1382 | 8.12k | if (*eqRes) { \ | 1383 | 8.12k | ip = trueDest; \ | 1384 | 8.12k | DISPATCH; \ | 1385 | 8.12k | } \ | 1386 | 8.12k | ip = falseDest; \ | 1387 | 8.12k | DISPATCH; \ | 1388 | 8.12k | } | 1389 | | | 1390 | | /// Implement the long and short forms of a conditional jump, and its negation. | 1391 | 8.12k | #define JCOND(name, oper, operFuncName) \ | 1392 | 8.12k | JCOND_IMPL( \ | 1393 | 8.12k | J##name, \ | 1394 | 8.12k | , \ | 1395 | 8.12k | oper, \ | 1396 | 8.12k | operFuncName, \ | 1397 | 8.12k | IPADD(ip->iJ##name.op1), \ | 1398 | 8.12k | NEXTINST(J##name)); \ | 1399 | 8.12k | JCOND_IMPL( \ | 1400 | 8.12k | J##name, \ | 1401 | 8.12k | Long, \ | 1402 | 8.12k | oper, \ | 1403 | 8.12k | operFuncName, \ | 1404 | 8.12k | IPADD(ip->iJ##name##Long.op1), \ | 1405 | 8.12k | NEXTINST(J##name##Long)); \ | 1406 | 8.12k | JCOND_IMPL( \ | 1407 | 8.12k | JNot##name, \ | 1408 | 8.12k | , \ | 1409 | 8.12k | oper, \ | 1410 | 8.12k | operFuncName, \ | 1411 | 8.12k | NEXTINST(JNot##name), \ | 1412 | 8.12k | IPADD(ip->iJNot##name.op1)); \ | 1413 | 8.12k | JCOND_IMPL( \ | 1414 | 8.12k | JNot##name, \ | 1415 | 8.12k | Long, \ | 1416 | 8.12k | oper, \ | 1417 | 8.12k | operFuncName, \ | 1418 | 8.12k | NEXTINST(JNot##name##Long), \ | 1419 | 8.12k | IPADD(ip->iJNot##name##Long.op1)); | 1420 | | | 1421 | | /// Load a constant. | 1422 | | /// \param value is the value to store in the output register. | 1423 | 8.12k | #define LOAD_CONST(name, value) \ | 1424 | 8.12k | CASE(name) { \ | 1425 | 8.12k | O1REG(name) = value; \ | 1426 | 8.12k | ip = NEXTINST(name); \ | 1427 | 8.12k | DISPATCH; \ | 1428 | 8.12k | } | 1429 | | | 1430 | 8.12k | #define LOAD_CONST_CAPTURE_IP(name, value) \ | 1431 | 8.12k | CASE(name) { \ | 1432 | 8.12k | CAPTURE_IP(O1REG(name) = value); \ | 1433 | 8.12k | ip = NEXTINST(name); \ | 1434 | 8.12k | DISPATCH; \ | 1435 | 8.12k | } | 1436 | | | 1437 | 1.17M | CASE(Mov) { | 1438 | 1.17M | O1REG(Mov) = O2REG(Mov); | 1439 | 1.17M | ip = NEXTINST(Mov); | 1440 | 5.85M | DISPATCH; | 1441 | 5.85M | } | 1442 | | | 1443 | 1.09M | CASE(MovLong) { | 1444 | 1.09M | O1REG(MovLong) = O2REG(MovLong); | 1445 | 1.09M | ip = NEXTINST(MovLong); | 1446 | 5.46M | DISPATCH; | 1447 | 5.46M | } | 1448 | | | 1449 | 2.17k | CASE(LoadParam) { | 1450 | 2.17k | if (LLVM_LIKELY(ip->iLoadParam.op2 <= FRAME.getArgCount())) { | 1451 | | // index 0 must load 'this'. Index 1 the first argument, etc. | 1452 | 2.17k | O1REG(LoadParam) = FRAME.getArgRef((int32_t)ip->iLoadParam.op2 - 1); | 1453 | 2.17k | ip = NEXTINST(LoadParam); | 1454 | 10.8k | DISPATCH; | 1455 | 10.8k | } | 1456 | 2.17k | O1REG(LoadParam) = HermesValue::encodeUndefinedValue(); | 1457 | 2.17k | ip = NEXTINST(LoadParam); | 1458 | 2.17k | DISPATCH; | 1459 | 0 | } | 1460 | | | 1461 | 0 | CASE(LoadParamLong) { | 1462 | 0 | if (LLVM_LIKELY(ip->iLoadParamLong.op2 <= FRAME.getArgCount())) { | 1463 | | // index 0 must load 'this'. Index 1 the first argument, etc. | 1464 | 0 | O1REG(LoadParamLong) = | 1465 | 0 | FRAME.getArgRef((int32_t)ip->iLoadParamLong.op2 - 1); | 1466 | 0 | ip = NEXTINST(LoadParamLong); | 1467 | 0 | DISPATCH; | 1468 | 0 | } | 1469 | 0 | O1REG(LoadParamLong) = HermesValue::encodeUndefinedValue(); | 1470 | 0 | ip = NEXTINST(LoadParamLong); | 1471 | 0 | DISPATCH; | 1472 | 0 | } | 1473 | | | 1474 | 0 | CASE(CoerceThisNS) { | 1475 | 0 | if (LLVM_LIKELY(O2REG(CoerceThisNS).isObject())) { | 1476 | 0 | O1REG(CoerceThisNS) = O2REG(CoerceThisNS); | 1477 | 0 | } else if ( | 1478 | 0 | O2REG(CoerceThisNS).isNull() || O2REG(CoerceThisNS).isUndefined()) { | 1479 | 0 | O1REG(CoerceThisNS) = runtime.global_; | 1480 | 0 | } else { | 1481 | 0 | tmpHandle = O2REG(CoerceThisNS); | 1482 | 0 | nextIP = NEXTINST(CoerceThisNS); | 1483 | 0 | goto coerceThisSlowPath; | 1484 | 0 | } | 1485 | 0 | ip = NEXTINST(CoerceThisNS); | 1486 | 0 | DISPATCH; | 1487 | 0 | } | 1488 | 6 | CASE(LoadThisNS) { | 1489 | 6 | if (LLVM_LIKELY(FRAME.getThisArgRef().isObject())) { | 1490 | 6 | O1REG(LoadThisNS) = FRAME.getThisArgRef(); | 1491 | 6 | } else if ( | 1492 | 0 | FRAME.getThisArgRef().isNull() || | 1493 | 0 | FRAME.getThisArgRef().isUndefined()) { | 1494 | 0 | O1REG(LoadThisNS) = runtime.global_; | 1495 | 0 | } else { | 1496 | 0 | tmpHandle = FRAME.getThisArgRef(); | 1497 | 0 | nextIP = NEXTINST(LoadThisNS); | 1498 | 0 | goto coerceThisSlowPath; | 1499 | 0 | } | 1500 | 6 | ip = NEXTINST(LoadThisNS); | 1501 | 30 | DISPATCH; | 1502 | 30 | } | 1503 | 0 | coerceThisSlowPath: { | 1504 | 0 | CAPTURE_IP(res = toObject(runtime, tmpHandle)); | 1505 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 1506 | 0 | goto exception; | 1507 | 0 | } | 1508 | 0 | O1REG(CoerceThisNS) = res.getValue(); | 1509 | 0 | tmpHandle.clear(); | 1510 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 1511 | 0 | ip = nextIP; | 1512 | 0 | DISPATCH; | 1513 | 0 | } | 1514 | | | 1515 | 0 | CASE(ConstructLong) { | 1516 | 0 | callArgCount = (uint32_t)ip->iConstructLong.op3; | 1517 | 0 | nextIP = NEXTINST(ConstructLong); | 1518 | 0 | callNewTarget = O2REG(ConstructLong).getRaw(); | 1519 | 0 | goto doCall; | 1520 | 0 | } | 1521 | 14 | CASE(CallLong) { | 1522 | 14 | callArgCount = (uint32_t)ip->iCallLong.op3; | 1523 | 14 | nextIP = NEXTINST(CallLong); | 1524 | 14 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); | 1525 | 14 | goto doCall; | 1526 | 0 | } | 1527 | | | 1528 | | // Note in Call1 through Call4, the first argument is 'this' which has | 1529 | | // argument index -1. | 1530 | | // Also note that we are writing to callNewTarget last, to avoid the | 1531 | | // possibility of it being aliased by the arg writes. | 1532 | 847 | CASE(Call1) { | 1533 | 847 | callArgCount = 1; | 1534 | 847 | nextIP = NEXTINST(Call1); | 1535 | 847 | StackFramePtr fr{runtime.stackPointer_}; | 1536 | 847 | fr.getArgRefUnsafe(-1) = O3REG(Call1); | 1537 | 847 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); | 1538 | 847 | goto doCall; | 1539 | 0 | } | 1540 | | | 1541 | 968 | CASE(Call2) { | 1542 | 968 | callArgCount = 2; | 1543 | 968 | nextIP = NEXTINST(Call2); | 1544 | 968 | StackFramePtr fr{runtime.stackPointer_}; | 1545 | 968 | fr.getArgRefUnsafe(-1) = O3REG(Call2); | 1546 | 968 | fr.getArgRefUnsafe(0) = O4REG(Call2); | 1547 | 968 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); | 1548 | 968 | goto doCall; | 1549 | 0 | } | 1550 | | | 1551 | 0 | CASE(Call3) { | 1552 | 0 | callArgCount = 3; | 1553 | 0 | nextIP = NEXTINST(Call3); | 1554 | 0 | StackFramePtr fr{runtime.stackPointer_}; | 1555 | 0 | fr.getArgRefUnsafe(-1) = O3REG(Call3); | 1556 | 0 | fr.getArgRefUnsafe(0) = O4REG(Call3); | 1557 | 0 | fr.getArgRefUnsafe(1) = O5REG(Call3); | 1558 | 0 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); | 1559 | 0 | goto doCall; | 1560 | 0 | } | 1561 | | | 1562 | 0 | CASE(Call4) { | 1563 | 0 | callArgCount = 4; | 1564 | 0 | nextIP = NEXTINST(Call4); | 1565 | 0 | StackFramePtr fr{runtime.stackPointer_}; | 1566 | 0 | fr.getArgRefUnsafe(-1) = O3REG(Call4); | 1567 | 0 | fr.getArgRefUnsafe(0) = O4REG(Call4); | 1568 | 0 | fr.getArgRefUnsafe(1) = O5REG(Call4); | 1569 | 0 | fr.getArgRefUnsafe(2) = O6REG(Call4); | 1570 | 0 | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); | 1571 | 0 | goto doCall; | 1572 | 0 | } | 1573 | | | 1574 | 726 | CASE(Construct) { | 1575 | 726 | callArgCount = (uint32_t)ip->iConstruct.op3; | 1576 | 726 | nextIP = NEXTINST(Construct); | 1577 | 726 | callNewTarget = O2REG(Construct).getRaw(); | 1578 | 726 | goto doCall; | 1579 | 0 | } | 1580 | 85.7k | CASE(Call) { | 1581 | 85.7k | callArgCount = (uint32_t)ip->iCall.op3; | 1582 | 85.7k | nextIP = NEXTINST(Call); | 1583 | 85.7k | callNewTarget = HermesValue::encodeUndefinedValue().getRaw(); | 1584 | 85.7k | goto doCall; | 1585 | 0 | } | 1586 | | | 1587 | 88.2k | doCall: { | 1588 | 88.2k | #ifdef HERMES_ENABLE_DEBUGGER | 1589 | | // Check for an async debugger request. | 1590 | 88.2k | if (uint8_t asyncFlags = | 1591 | 88.2k | runtime.testAndClearDebuggerAsyncBreakRequest()) { | 1592 | 0 | RUN_DEBUGGER_ASYNC_BREAK(asyncFlags); | 1593 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 1594 | 0 | DISPATCH; | 1595 | 0 | } | 1596 | 88.2k | #endif | 1597 | | | 1598 | | // Subtract 1 from callArgCount as 'this' is considered an argument in the | 1599 | | // instruction, but not in the frame. | 1600 | 88.2k | auto newFrame = StackFramePtr::initFrame( | 1601 | 88.2k | runtime.stackPointer_, | 1602 | 88.2k | FRAME, | 1603 | 88.2k | ip, | 1604 | 88.2k | curCodeBlock, | 1605 | 88.2k | callArgCount - 1, | 1606 | 88.2k | O2REG(Call), | 1607 | 88.2k | HermesValue::fromRaw(callNewTarget)); | 1608 | 88.2k | (void)newFrame; | 1609 | | | 1610 | 88.2k | SLOW_DEBUG(dumpCallArguments(dbgs(), runtime, newFrame)); | 1611 | | | 1612 | 88.2k | if (auto *func = dyn_vmcast<JSFunction>(O2REG(Call))) { | 1613 | 1.81k | assert(!SingleStep && "can't single-step a call"); | 1614 | | | 1615 | 1.81k | #ifdef HERMES_MEMORY_INSTRUMENTATION | 1616 | 1.81k | runtime.pushCallStack(curCodeBlock, ip); | 1617 | 1.81k | #endif | 1618 | | | 1619 | 1.81k | CodeBlock *calleeBlock = func->getCodeBlock(runtime); | 1620 | 1.81k | CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime)); | 1621 | 1.81k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 1622 | 0 | goto exception; | 1623 | 0 | } | 1624 | 1.81k | curCodeBlock = calleeBlock; | 1625 | 1.81k | CAPTURE_IP_SET(); | 1626 | 1.81k | goto tailCall; | 1627 | 1.81k | } | 1628 | 86.4k | CAPTURE_IP( | 1629 | 86.4k | resPH = Interpreter::handleCallSlowPath(runtime, &O2REG(Call))); | 1630 | 86.4k | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 1631 | 2 | goto exception; | 1632 | 2 | } | 1633 | 86.4k | O1REG(Call) = std::move(resPH->get()); | 1634 | 86.4k | SLOW_DEBUG( | 1635 | 86.4k | dbgs() << "native return value r" << (unsigned)ip->iCall.op1 << "=" | 1636 | 86.4k | << DumpHermesValue(O1REG(Call)) << "\n"); | 1637 | 86.4k | gcScope.flushToSmallCount(KEEP_HANDLES); | 1638 | 86.4k | ip = nextIP; | 1639 | 432k | DISPATCH; | 1640 | 432k | } | 1641 | | | 1642 | 0 | CASE(CallDirect) | 1643 | 0 | CASE(CallDirectLongIndex) { | 1644 | 0 | #ifdef HERMES_ENABLE_DEBUGGER | 1645 | | // Check for an async debugger request. | 1646 | 0 | if (uint8_t asyncFlags = | 1647 | 0 | runtime.testAndClearDebuggerAsyncBreakRequest()) { | 1648 | 0 | RUN_DEBUGGER_ASYNC_BREAK(asyncFlags); | 1649 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 1650 | 0 | DISPATCH; | 1651 | 0 | } | 1652 | 0 | #endif | 1653 | | | 1654 | 0 | CAPTURE_IP_ASSIGN( | 1655 | 0 | CodeBlock * calleeBlock, | 1656 | 0 | ip->opCode == OpCode::CallDirect | 1657 | 0 | ? curCodeBlock->getRuntimeModule()->getCodeBlockMayAllocate( | 1658 | 0 | ip->iCallDirect.op3) | 1659 | 0 | : curCodeBlock->getRuntimeModule()->getCodeBlockMayAllocate( | 1660 | 0 | ip->iCallDirectLongIndex.op3)); | 1661 | |
| 1662 | 0 | auto newFrame = StackFramePtr::initFrame( | 1663 | 0 | runtime.stackPointer_, | 1664 | 0 | FRAME, | 1665 | 0 | ip, | 1666 | 0 | curCodeBlock, | 1667 | 0 | (uint32_t)ip->iCallDirect.op2 - 1, | 1668 | 0 | HermesValue::encodeNativePointer(calleeBlock), | 1669 | 0 | HermesValue::encodeUndefinedValue()); | 1670 | 0 | (void)newFrame; | 1671 | |
| 1672 | 0 | LLVM_DEBUG(dumpCallArguments(dbgs(), runtime, newFrame)); | 1673 | |
| 1674 | 0 | assert(!SingleStep && "can't single-step a call"); | 1675 | | | 1676 | 0 | CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime)); | 1677 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 1678 | 0 | goto exception; | 1679 | 0 | } | 1680 | 0 | curCodeBlock = calleeBlock; | 1681 | 0 | CAPTURE_IP_SET(); | 1682 | 0 | goto tailCall; | 1683 | 0 | } | 1684 | | | 1685 | 0 | CASE(GetBuiltinClosure) { | 1686 | 0 | uint8_t methodIndex = ip->iCallBuiltin.op2; | 1687 | 0 | Callable *closure = runtime.getBuiltinCallable(methodIndex); | 1688 | 0 | O1REG(GetBuiltinClosure) = HermesValue::encodeObjectValue(closure); | 1689 | 0 | ip = NEXTINST(GetBuiltinClosure); | 1690 | 0 | DISPATCH; | 1691 | 0 | } | 1692 | | | 1693 | 85.7k | CASE(CallBuiltin) { | 1694 | 85.7k | CAPTURE_IP_ASSIGN( | 1695 | 85.7k | auto cres, | 1696 | 85.7k | implCallBuiltin( | 1697 | 85.7k | runtime, frameRegs, curCodeBlock, ip->iCallBuiltin.op3)); | 1698 | 85.7k | if (LLVM_UNLIKELY(cres == ExecutionStatus::EXCEPTION)) | 1699 | 0 | goto exception; | 1700 | 85.7k | gcScope.flushToSmallCount(KEEP_HANDLES); | 1701 | 85.7k | ip = NEXTINST(CallBuiltin); | 1702 | 428k | DISPATCH; | 1703 | 428k | } | 1704 | 14 | CASE(CallBuiltinLong) { | 1705 | 14 | CAPTURE_IP_ASSIGN( | 1706 | 14 | auto cres, | 1707 | 14 | implCallBuiltin( | 1708 | 14 | runtime, frameRegs, curCodeBlock, ip->iCallBuiltinLong.op3)); | 1709 | 14 | if (LLVM_UNLIKELY(cres == ExecutionStatus::EXCEPTION)) | 1710 | 0 | goto exception; | 1711 | 14 | gcScope.flushToSmallCount(KEEP_HANDLES); | 1712 | 14 | ip = NEXTINST(CallBuiltinLong); | 1713 | 70 | DISPATCH; | 1714 | 70 | } | 1715 | | | 1716 | 0 | CASE(CompleteGenerator) { | 1717 | 0 | auto *innerFn = vmcast<GeneratorInnerFunction>( | 1718 | 0 | runtime.getCurrentFrame().getCalleeClosureUnsafe()); | 1719 | 0 | innerFn->setState(GeneratorInnerFunction::State::Completed); | 1720 | 0 | ip = NEXTINST(CompleteGenerator); | 1721 | 0 | DISPATCH; | 1722 | 0 | } | 1723 | | | 1724 | 0 | CASE(SaveGenerator) { | 1725 | 0 | DONT_CAPTURE_IP( | 1726 | 0 | saveGenerator(runtime, frameRegs, IPADD(ip->iSaveGenerator.op1))); | 1727 | 0 | ip = NEXTINST(SaveGenerator); | 1728 | 0 | DISPATCH; | 1729 | 0 | } | 1730 | 0 | CASE(SaveGeneratorLong) { | 1731 | 0 | DONT_CAPTURE_IP(saveGenerator( | 1732 | 0 | runtime, frameRegs, IPADD(ip->iSaveGeneratorLong.op1))); | 1733 | 0 | ip = NEXTINST(SaveGeneratorLong); | 1734 | 0 | DISPATCH; | 1735 | 0 | } | 1736 | | | 1737 | 0 | CASE(StartGenerator) { | 1738 | 0 | auto *innerFn = vmcast<GeneratorInnerFunction>( | 1739 | 0 | runtime.getCurrentFrame().getCalleeClosureUnsafe()); | 1740 | 0 | if (innerFn->getState() == | 1741 | 0 | GeneratorInnerFunction::State::SuspendedStart) { | 1742 | 0 | nextIP = NEXTINST(StartGenerator); | 1743 | 0 | } else { | 1744 | 0 | nextIP = innerFn->getNextIP(runtime); | 1745 | 0 | innerFn->restoreStack(runtime); | 1746 | 0 | } | 1747 | 0 | innerFn->setState(GeneratorInnerFunction::State::Executing); | 1748 | 0 | ip = nextIP; | 1749 | 0 | DISPATCH; | 1750 | 0 | } | 1751 | | | 1752 | 0 | CASE(ResumeGenerator) { | 1753 | 0 | auto *innerFn = vmcast<GeneratorInnerFunction>( | 1754 | 0 | runtime.getCurrentFrame().getCalleeClosureUnsafe()); | 1755 | 0 | O2REG(ResumeGenerator) = HermesValue::encodeBoolValue( | 1756 | 0 | innerFn->getAction() == GeneratorInnerFunction::Action::Return); | 1757 | | // Write the result last in case it is the same register as O2REG. | 1758 | 0 | O1REG(ResumeGenerator) = innerFn->getResult().unboxToHV(runtime); | 1759 | 0 | innerFn->clearResult(runtime); | 1760 | 0 | if (innerFn->getAction() == GeneratorInnerFunction::Action::Throw) { | 1761 | 0 | runtime.setThrownValue(O1REG(ResumeGenerator)); | 1762 | 0 | goto exception; | 1763 | 0 | } | 1764 | 0 | ip = NEXTINST(ResumeGenerator); | 1765 | 0 | DISPATCH; | 1766 | 0 | } | 1767 | | | 1768 | 2.00k | CASE(Ret) { | 1769 | 2.00k | #ifdef HERMES_ENABLE_DEBUGGER | 1770 | | // Check for an async debugger request, but skip it if we're single | 1771 | | // stepping. The only case where we'd be single stepping a Ret is if it | 1772 | | // was replaced with Debugger OpCode and we're coming here from | 1773 | | // stepFunction(). This does take away a chance to handle AsyncBreak. An | 1774 | | // AsyncBreak request could be either Explicit or Implicit. The Explicit | 1775 | | // case is to have the program being executed to pause. There isn't a | 1776 | | // need to pause at a particular location. Also, since we just came from | 1777 | | // a breakpoint, handling Explicit AsyncBreak for single step isn't so | 1778 | | // important. The other possible kind is an Implicit AsyncBreak, which | 1779 | | // is used for debug clients to interrupt the runtime to execute their | 1780 | | // own code. Not processing AsyncBreak just means that the Implicit | 1781 | | // AsyncBreak needs to wait for the next opportunity to interrupt the | 1782 | | // runtime, which should be fine. There is no contract for when the | 1783 | | // interrupt should happen. | 1784 | 2.00k | if (!SingleStep) { | 1785 | 2.00k | if (uint8_t asyncFlags = | 1786 | 2.00k | runtime.testAndClearDebuggerAsyncBreakRequest()) { | 1787 | 0 | RUN_DEBUGGER_ASYNC_BREAK(asyncFlags); | 1788 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 1789 | 0 | DISPATCH; | 1790 | 0 | } | 1791 | 2.00k | } | 1792 | 2.00k | #endif | 1793 | | | 1794 | 2.00k | PROFILER_EXIT_FUNCTION(curCodeBlock); | 1795 | | | 1796 | 2.00k | #ifdef HERMES_MEMORY_INSTRUMENTATION | 1797 | 2.00k | runtime.popCallStack(); | 1798 | 2.00k | #endif | 1799 | | | 1800 | | // Store the return value. | 1801 | 2.00k | res = O1REG(Ret); | 1802 | | | 1803 | 2.00k | ip = FRAME.getSavedIP(); | 1804 | 2.00k | curCodeBlock = FRAME.getSavedCodeBlock(); | 1805 | | | 1806 | 2.00k | frameRegs = | 1807 | 2.00k | &runtime.restoreStackAndPreviousFrame(FRAME).getFirstLocalRef(); | 1808 | | | 1809 | 2.00k | SLOW_DEBUG( | 1810 | 2.00k | dbgs() << "function exit: restored stackLevel=" | 1811 | 2.00k | << runtime.getStackLevel() << "\n"); | 1812 | | | 1813 | | // Are we returning to native code? | 1814 | 2.00k | if (!curCodeBlock) { | 1815 | 186 | SLOW_DEBUG(dbgs() << "function exit: returning to native code\n"); | 1816 | 186 | return res; | 1817 | 186 | } | 1818 | | | 1819 | 1.81k | INIT_STATE_FOR_CODEBLOCK(curCodeBlock); | 1820 | 1.81k | O1REG(Call) = res.getValue(); | 1821 | 1.81k | ip = nextInstCall(ip); | 1822 | 9.07k | DISPATCH; | 1823 | 9.07k | } | 1824 | | | 1825 | 0 | CASE(Catch) { | 1826 | 0 | assert(!runtime.thrownValue_.isEmpty() && "Invalid thrown value"); | 1827 | 0 | assert( | 1828 | 0 | !isUncatchableError(runtime.thrownValue_) && | 1829 | 0 | "Uncatchable thrown value was caught"); | 1830 | 0 | O1REG(Catch) = runtime.thrownValue_; | 1831 | 0 | runtime.clearThrownValue(); | 1832 | 0 | #ifdef HERMES_ENABLE_DEBUGGER | 1833 | | // Signal to the debugger that we're done unwinding an exception, | 1834 | | // and we can resume normal debugging flow. | 1835 | 0 | runtime.debugger_.finishedUnwindingException(); | 1836 | 0 | #endif | 1837 | 0 | ip = NEXTINST(Catch); | 1838 | 0 | DISPATCH; | 1839 | 0 | } | 1840 | | | 1841 | 0 | CASE(Throw) { | 1842 | 0 | runtime.thrownValue_ = O1REG(Throw); | 1843 | 0 | SLOW_DEBUG( | 1844 | 0 | dbgs() << "Exception thrown: " | 1845 | 0 | << DumpHermesValue(runtime.thrownValue_) << "\n"); | 1846 | 0 | goto exception; | 1847 | 0 | } | 1848 | | | 1849 | 0 | CASE(ThrowIfEmpty) { | 1850 | 0 | if (LLVM_UNLIKELY(O2REG(ThrowIfEmpty).isEmpty())) { | 1851 | 0 | SLOW_DEBUG(dbgs() << "Throwing ReferenceError for empty variable"); | 1852 | 0 | CAPTURE_IP(runtime.raiseReferenceError( | 1853 | 0 | "accessing an uninitialized variable")); | 1854 | 0 | goto exception; | 1855 | 0 | } | 1856 | 0 | O1REG(ThrowIfEmpty) = O2REG(ThrowIfEmpty); | 1857 | 0 | ip = NEXTINST(ThrowIfEmpty); | 1858 | 0 | DISPATCH; | 1859 | 0 | } | 1860 | | | 1861 | 0 | CASE(Debugger) { | 1862 | 0 | SLOW_DEBUG(dbgs() << "debugger statement executed\n"); | 1863 | 0 | #ifdef HERMES_ENABLE_DEBUGGER | 1864 | 0 | { | 1865 | 0 | if (!runtime.debugger_.isDebugging()) { | 1866 | | // Only run the debugger if we're not already debugging. | 1867 | | // Don't want to call it again and mess with its state. | 1868 | 0 | CAPTURE_IP_ASSIGN( | 1869 | 0 | auto res, | 1870 | 0 | runDebuggerUpdatingState( | 1871 | 0 | Debugger::RunReason::Opcode, | 1872 | 0 | runtime, | 1873 | 0 | curCodeBlock, | 1874 | 0 | ip, | 1875 | 0 | frameRegs)); | 1876 | 0 | if (res == ExecutionStatus::EXCEPTION) { | 1877 | | // If one of the internal steps threw, | 1878 | | // then handle that here by jumping to where we're supposed to go. | 1879 | | // If we're in mid-step, the breakpoint at the catch point | 1880 | | // will have been set by the debugger. | 1881 | | // We don't want to execute this instruction because it's already | 1882 | | // thrown. | 1883 | 0 | goto exception; | 1884 | 0 | } | 1885 | 0 | } | 1886 | 0 | auto breakpointOpt = runtime.debugger_.getBreakpointLocation(ip); | 1887 | 0 | if (breakpointOpt.hasValue()) { | 1888 | | // We're on a breakpoint but we're supposed to continue. | 1889 | 0 | curCodeBlock->uninstallBreakpointAtOffset( | 1890 | 0 | CUROFFSET, breakpointOpt->opCode); | 1891 | 0 | if (ip->opCode == OpCode::Debugger) { | 1892 | | // Breakpointed a debugger instruction, so move past it | 1893 | | // since we've already called the debugger on this instruction. | 1894 | 0 | ip = NEXTINST(Debugger); | 1895 | 0 | } else { | 1896 | 0 | InterpreterState newState{curCodeBlock, (uint32_t)CUROFFSET}; | 1897 | 0 | CAPTURE_IP_ASSIGN( | 1898 | 0 | ExecutionStatus status, runtime.stepFunction(newState)); | 1899 | 0 | curCodeBlock->installBreakpointAtOffset(CUROFFSET); | 1900 | 0 | if (status == ExecutionStatus::EXCEPTION) { | 1901 | 0 | goto exception; | 1902 | 0 | } | 1903 | 0 | curCodeBlock = newState.codeBlock; | 1904 | 0 | ip = newState.codeBlock->getOffsetPtr(newState.offset); | 1905 | 0 | INIT_STATE_FOR_CODEBLOCK(curCodeBlock); | 1906 | | // Single-stepping should handle call stack management for us. | 1907 | 0 | frameRegs = &runtime.getCurrentFrame().getFirstLocalRef(); | 1908 | 0 | } | 1909 | 0 | } else if (ip->opCode == OpCode::Debugger) { | 1910 | | // No breakpoint here and we've already run the debugger, | 1911 | | // just continue on. | 1912 | | // If the current instruction is no longer a debugger instruction, | 1913 | | // we're just going to keep executing from the current IP. | 1914 | 0 | ip = NEXTINST(Debugger); | 1915 | 0 | } | 1916 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 1917 | 0 | } | 1918 | 0 | DISPATCH; | 1919 | | #else | 1920 | | ip = NEXTINST(Debugger); | 1921 | | DISPATCH; | 1922 | | #endif | 1923 | 0 | } | 1924 | | | 1925 | 357k | CASE(AsyncBreakCheck) { | 1926 | 357k | if (LLVM_UNLIKELY(runtime.hasAsyncBreak())) { | 1927 | 0 | #ifdef HERMES_ENABLE_DEBUGGER | 1928 | 0 | if (uint8_t asyncFlags = | 1929 | 0 | runtime.testAndClearDebuggerAsyncBreakRequest()) { | 1930 | 0 | RUN_DEBUGGER_ASYNC_BREAK(asyncFlags); | 1931 | 0 | } | 1932 | 0 | #endif | 1933 | 0 | if (runtime.testAndClearTimeoutAsyncBreakRequest()) { | 1934 | 0 | CAPTURE_IP_ASSIGN(auto nRes, runtime.notifyTimeout()); | 1935 | 0 | if (nRes == ExecutionStatus::EXCEPTION) { | 1936 | 0 | goto exception; | 1937 | 0 | } | 1938 | 0 | } | 1939 | 0 | } | 1940 | 357k | gcScope.flushToSmallCount(KEEP_HANDLES); | 1941 | | | 1942 | 357k | ip = NEXTINST(AsyncBreakCheck); | 1943 | 1.78M | DISPATCH; | 1944 | 1.78M | } | 1945 | | | 1946 | 0 | CASE(ProfilePoint) { | 1947 | | #ifdef HERMESVM_PROFILER_BB | 1948 | | auto pointIndex = ip->iProfilePoint.op1; | 1949 | | SLOW_DEBUG(llvh::dbgs() << "ProfilePoint: " << pointIndex << "\n"); | 1950 | | CAPTURE_IP(runtime.getBasicBlockExecutionInfo().executeBlock( | 1951 | | curCodeBlock, pointIndex)); | 1952 | | #endif | 1953 | 0 | ip = NEXTINST(ProfilePoint); | 1954 | 0 | DISPATCH; | 1955 | 0 | } | 1956 | | | 1957 | | // Use a macro here to avoid clang-format issues with a literal default: | 1958 | | // label. | 1959 | 0 | DEFAULT_CASE | 1960 | 0 | CASE(Unreachable) { | 1961 | 0 | hermes_fatal("Unreachable instruction encountered"); | 1962 | | // The fatal call doesn't return, no need to set the IP differently and | 1963 | | // dispatch. | 1964 | 0 | } | 1965 | | | 1966 | 4.37k | CASE(CreateClosure) { | 1967 | 4.37k | idVal = ip->iCreateClosure.op3; | 1968 | 4.37k | nextIP = NEXTINST(CreateClosure); | 1969 | 4.37k | goto createClosure; | 1970 | 0 | } | 1971 | 0 | CASE(CreateClosureLongIndex) { | 1972 | 0 | idVal = ip->iCreateClosureLongIndex.op3; | 1973 | 0 | nextIP = NEXTINST(CreateClosureLongIndex); | 1974 | 0 | goto createClosure; | 1975 | 0 | } | 1976 | 4.37k | createClosure: { | 1977 | 4.37k | auto *runtimeModule = curCodeBlock->getRuntimeModule(); | 1978 | 4.37k | CAPTURE_IP( | 1979 | 4.37k | O1REG(CreateClosure) = | 1980 | 4.37k | JSFunction::create( | 1981 | 4.37k | runtime, | 1982 | 4.37k | runtimeModule->getDomain(runtime), | 1983 | 4.37k | Handle<JSObject>::vmcast(&runtime.functionPrototype), | 1984 | 4.37k | Handle<Environment>::vmcast(&O2REG(CreateClosure)), | 1985 | 4.37k | runtimeModule->getCodeBlockMayAllocate(idVal)) | 1986 | 4.37k | .getHermesValue()); | 1987 | 4.37k | gcScope.flushToSmallCount(KEEP_HANDLES); | 1988 | 4.37k | ip = nextIP; | 1989 | 21.8k | DISPATCH; | 1990 | 21.8k | } | 1991 | | | 1992 | 0 | CASE(CreateAsyncClosure) { | 1993 | 0 | idVal = ip->iCreateAsyncClosure.op3; | 1994 | 0 | nextIP = NEXTINST(CreateAsyncClosure); | 1995 | 0 | goto createAsyncClosure; | 1996 | 21.8k | } | 1997 | 0 | CASE(CreateAsyncClosureLongIndex) { | 1998 | 0 | idVal = ip->iCreateAsyncClosureLongIndex.op3; | 1999 | 0 | nextIP = NEXTINST(CreateAsyncClosureLongIndex); | 2000 | 0 | goto createAsyncClosure; | 2001 | 21.8k | } | 2002 | 0 | createAsyncClosure: { | 2003 | 0 | auto *runtimeModule = curCodeBlock->getRuntimeModule(); | 2004 | 0 | CAPTURE_IP_ASSIGN( | 2005 | 0 | O1REG(CreateAsyncClosure), | 2006 | 0 | JSAsyncFunction::create( | 2007 | 0 | runtime, | 2008 | 0 | runtimeModule->getDomain(runtime), | 2009 | 0 | Handle<JSObject>::vmcast(&runtime.asyncFunctionPrototype), | 2010 | 0 | Handle<Environment>::vmcast(&O2REG(CreateAsyncClosure)), | 2011 | 0 | runtimeModule->getCodeBlockMayAllocate(idVal)) | 2012 | 0 | .getHermesValue()); | 2013 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2014 | 0 | ip = nextIP; | 2015 | 0 | DISPATCH; | 2016 | 0 | } | 2017 | | | 2018 | 0 | CASE(CreateGeneratorClosure) { | 2019 | 0 | idVal = ip->iCreateGeneratorClosure.op3; | 2020 | 0 | nextIP = NEXTINST(CreateGeneratorClosure); | 2021 | 0 | goto createGeneratorClosure; | 2022 | 0 | } | 2023 | 0 | CASE(CreateGeneratorClosureLongIndex) { | 2024 | 0 | idVal = ip->iCreateGeneratorClosureLongIndex.op3; | 2025 | 0 | nextIP = NEXTINST(CreateGeneratorClosureLongIndex); | 2026 | 0 | goto createGeneratorClosure; | 2027 | 0 | } | 2028 | 0 | createGeneratorClosure: { | 2029 | 0 | auto *runtimeModule = curCodeBlock->getRuntimeModule(); | 2030 | 0 | CAPTURE_IP_ASSIGN( | 2031 | 0 | O1REG(CreateGeneratorClosure), | 2032 | 0 | JSGeneratorFunction::create( | 2033 | 0 | runtime, | 2034 | 0 | runtimeModule->getDomain(runtime), | 2035 | 0 | Handle<JSObject>::vmcast(&runtime.generatorFunctionPrototype), | 2036 | 0 | Handle<Environment>::vmcast(&O2REG(CreateGeneratorClosure)), | 2037 | 0 | runtimeModule->getCodeBlockMayAllocate(idVal)) | 2038 | 0 | .getHermesValue()); | 2039 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2040 | 0 | ip = nextIP; | 2041 | 0 | DISPATCH; | 2042 | 0 | } | 2043 | | | 2044 | 0 | CASE(CreateGenerator) { | 2045 | 0 | CAPTURE_IP_ASSIGN( | 2046 | 0 | auto res, | 2047 | 0 | createGenerator_RJS( | 2048 | 0 | runtime, | 2049 | 0 | curCodeBlock->getRuntimeModule(), | 2050 | 0 | ip->iCreateGenerator.op3, | 2051 | 0 | Handle<Environment>::vmcast(&O2REG(CreateGenerator)), | 2052 | 0 | FRAME.getNativeArgs())); | 2053 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 2054 | 0 | goto exception; | 2055 | 0 | } | 2056 | 0 | O1REG(CreateGenerator) = res->getHermesValue(); | 2057 | 0 | res->invalidate(); | 2058 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2059 | 0 | ip = NEXTINST(CreateGenerator); | 2060 | 0 | DISPATCH; | 2061 | 0 | } | 2062 | 0 | CASE(CreateGeneratorLongIndex) { | 2063 | 0 | CAPTURE_IP_ASSIGN( | 2064 | 0 | auto res, | 2065 | 0 | createGenerator_RJS( | 2066 | 0 | runtime, | 2067 | 0 | curCodeBlock->getRuntimeModule(), | 2068 | 0 | ip->iCreateGeneratorLongIndex.op3, | 2069 | 0 | Handle<Environment>::vmcast(&O2REG(CreateGeneratorLongIndex)), | 2070 | 0 | FRAME.getNativeArgs())); | 2071 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 2072 | 0 | goto exception; | 2073 | 0 | } | 2074 | 0 | O1REG(CreateGeneratorLongIndex) = res->getHermesValue(); | 2075 | 0 | res->invalidate(); | 2076 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2077 | 0 | ip = NEXTINST(CreateGeneratorLongIndex); | 2078 | 0 | DISPATCH; | 2079 | 0 | } | 2080 | | | 2081 | 1.57k | CASE(GetEnvironment) { | 2082 | | // The currently executing function must exist, so get the environment. | 2083 | 1.57k | Environment *curEnv = | 2084 | 1.57k | FRAME.getCalleeClosureUnsafe()->getEnvironment(runtime); | 2085 | 1.57k | for (unsigned level = ip->iGetEnvironment.op2; level; --level) { | 2086 | 0 | assert(curEnv && "invalid environment relative level"); | 2087 | 0 | curEnv = curEnv->getParentEnvironment(runtime); | 2088 | 0 | } | 2089 | 1.57k | O1REG(GetEnvironment) = HermesValue::encodeObjectValue(curEnv); | 2090 | 1.57k | ip = NEXTINST(GetEnvironment); | 2091 | 7.86k | DISPATCH; | 2092 | 7.86k | } | 2093 | | | 2094 | 0 | CASE(CreateInnerEnvironment) { | 2095 | 0 | CAPTURE_IP( | 2096 | 0 | O1REG(CreateInnerEnvironment) = Environment::create( | 2097 | 0 | runtime, | 2098 | 0 | Handle<Environment>::vmcast(&O2REG(CreateInnerEnvironment)), | 2099 | 0 | ip->iCreateInnerEnvironment.op3)); | 2100 | 0 | ip = NEXTINST(CreateInnerEnvironment); | 2101 | 0 | DISPATCH; | 2102 | 0 | } | 2103 | | | 2104 | 578 | CASE(CreateEnvironment) { | 2105 | 578 | tmpHandle = HermesValue::encodeObjectValueUnsafe( | 2106 | 578 | FRAME.getCalleeClosureUnsafe()->getEnvironment(runtime)); | 2107 | | | 2108 | 578 | CAPTURE_IP( | 2109 | 578 | O1REG(CreateEnvironment) = Environment::create( | 2110 | 578 | runtime, | 2111 | 578 | Handle<Environment>::vmcast_or_null(tmpHandle), | 2112 | 578 | curCodeBlock->getEnvironmentSize())); | 2113 | | | 2114 | 578 | tmpHandle = HermesValue::encodeUndefinedValue(); | 2115 | 578 | ip = NEXTINST(CreateEnvironment); | 2116 | 2.89k | DISPATCH; | 2117 | 2.89k | } | 2118 | | | 2119 | 4.24k | CASE(StoreToEnvironment) { | 2120 | 4.24k | vmcast<Environment>(O1REG(StoreToEnvironment)) | 2121 | 4.24k | ->slot(ip->iStoreToEnvironment.op2) | 2122 | 4.24k | .set(O3REG(StoreToEnvironment), runtime.getHeap()); | 2123 | 4.24k | ip = NEXTINST(StoreToEnvironment); | 2124 | 21.2k | DISPATCH; | 2125 | 21.2k | } | 2126 | 0 | CASE(StoreToEnvironmentL) { | 2127 | 0 | vmcast<Environment>(O1REG(StoreToEnvironmentL)) | 2128 | 0 | ->slot(ip->iStoreToEnvironmentL.op2) | 2129 | 0 | .set(O3REG(StoreToEnvironmentL), runtime.getHeap()); | 2130 | 0 | ip = NEXTINST(StoreToEnvironmentL); | 2131 | 0 | DISPATCH; | 2132 | 0 | } | 2133 | | | 2134 | 250 | CASE(StoreNPToEnvironment) { | 2135 | 250 | vmcast<Environment>(O1REG(StoreNPToEnvironment)) | 2136 | 250 | ->slot(ip->iStoreNPToEnvironment.op2) | 2137 | 250 | .setNonPtr(O3REG(StoreNPToEnvironment), runtime.getHeap()); | 2138 | 250 | ip = NEXTINST(StoreNPToEnvironment); | 2139 | 1.25k | DISPATCH; | 2140 | 1.25k | } | 2141 | 0 | CASE(StoreNPToEnvironmentL) { | 2142 | 0 | vmcast<Environment>(O1REG(StoreNPToEnvironmentL)) | 2143 | 0 | ->slot(ip->iStoreNPToEnvironmentL.op2) | 2144 | 0 | .setNonPtr(O3REG(StoreNPToEnvironmentL), runtime.getHeap()); | 2145 | 0 | ip = NEXTINST(StoreNPToEnvironmentL); | 2146 | 0 | DISPATCH; | 2147 | 0 | } | 2148 | | | 2149 | 1.57k | CASE(LoadFromEnvironment) { | 2150 | 1.57k | O1REG(LoadFromEnvironment) = | 2151 | 1.57k | vmcast<Environment>(O2REG(LoadFromEnvironment)) | 2152 | 1.57k | ->slot(ip->iLoadFromEnvironment.op3); | 2153 | 1.57k | ip = NEXTINST(LoadFromEnvironment); | 2154 | 7.86k | DISPATCH; | 2155 | 7.86k | } | 2156 | | | 2157 | 0 | CASE(LoadFromEnvironmentL) { | 2158 | 0 | O1REG(LoadFromEnvironmentL) = | 2159 | 0 | vmcast<Environment>(O2REG(LoadFromEnvironmentL)) | 2160 | 0 | ->slot(ip->iLoadFromEnvironmentL.op3); | 2161 | 0 | ip = NEXTINST(LoadFromEnvironmentL); | 2162 | 0 | DISPATCH; | 2163 | 0 | } | 2164 | | | 2165 | 529k | CASE(GetGlobalObject) { | 2166 | 529k | O1REG(GetGlobalObject) = runtime.global_; | 2167 | 529k | ip = NEXTINST(GetGlobalObject); | 2168 | 2.64M | DISPATCH; | 2169 | 2.64M | } | 2170 | | | 2171 | 6 | CASE(GetNewTarget) { | 2172 | 6 | O1REG(GetNewTarget) = FRAME.getNewTargetRef(); | 2173 | 6 | ip = NEXTINST(GetNewTarget); | 2174 | 30 | DISPATCH; | 2175 | 30 | } | 2176 | | | 2177 | 0 | CASE(DeclareGlobalVar) { | 2178 | 0 | CAPTURE_IP_ASSIGN( | 2179 | 0 | auto res, declareGlobalVarImpl(runtime, curCodeBlock, ip)); | 2180 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 2181 | 0 | goto exception; | 2182 | 0 | } | 2183 | 0 | ip = NEXTINST(DeclareGlobalVar); | 2184 | 0 | DISPATCH; | 2185 | 0 | } | 2186 | | | 2187 | 0 | CASE(ThrowIfHasRestrictedGlobalProperty) { | 2188 | 0 | CAPTURE_IP_ASSIGN( | 2189 | 0 | auto res, | 2190 | 0 | throwIfHasRestrictedGlobalPropertyImpl(runtime, curCodeBlock, ip)); | 2191 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 2192 | 0 | goto exception; | 2193 | 0 | } | 2194 | 0 | ip = NEXTINST(ThrowIfHasRestrictedGlobalProperty); | 2195 | 0 | DISPATCH; | 2196 | 0 | } | 2197 | | | 2198 | 0 | CASE(TryGetByIdLong) { | 2199 | 0 | tryProp = true; | 2200 | 0 | idVal = ip->iTryGetByIdLong.op4; | 2201 | 0 | nextIP = NEXTINST(TryGetByIdLong); | 2202 | 0 | goto getById; | 2203 | 0 | } | 2204 | 0 | CASE(GetByIdLong) { | 2205 | 0 | tryProp = false; | 2206 | 0 | idVal = ip->iGetByIdLong.op4; | 2207 | 0 | nextIP = NEXTINST(GetByIdLong); | 2208 | 0 | goto getById; | 2209 | 0 | } | 2210 | 2.90k | CASE(GetByIdShort) { | 2211 | 2.90k | tryProp = false; | 2212 | 2.90k | idVal = ip->iGetByIdShort.op4; | 2213 | 2.90k | nextIP = NEXTINST(GetByIdShort); | 2214 | 2.90k | goto getById; | 2215 | 0 | } | 2216 | 172k | CASE(TryGetById) { | 2217 | 172k | tryProp = true; | 2218 | 172k | idVal = ip->iTryGetById.op4; | 2219 | 172k | nextIP = NEXTINST(TryGetById); | 2220 | 172k | goto getById; | 2221 | 0 | } | 2222 | 0 | CASE(GetById) { | 2223 | 0 | tryProp = false; | 2224 | 0 | idVal = ip->iGetById.op4; | 2225 | 0 | nextIP = NEXTINST(GetById); | 2226 | 0 | } | 2227 | 175k | getById: { | 2228 | 175k | ++NumGetById; | 2229 | | // NOTE: it is safe to use OnREG(GetById) here because all instructions | 2230 | | // have the same layout: opcode, registers, non-register operands, i.e. | 2231 | | // they only differ in the width of the last "identifier" field. | 2232 | 175k | if (LLVM_LIKELY(O2REG(GetById).isObject())) { | 2233 | 175k | auto *obj = vmcast<JSObject>(O2REG(GetById)); | 2234 | 175k | auto cacheIdx = ip->iGetById.op3; | 2235 | 175k | auto *cacheEntry = curCodeBlock->getReadCacheEntry(cacheIdx); | 2236 | | | 2237 | | #ifdef HERMESVM_PROFILER_BB | 2238 | | { | 2239 | | HERMES_SLOW_ASSERT( | 2240 | | gcScope.getHandleCountDbg() == KEEP_HANDLES && | 2241 | | "unaccounted handles were created"); | 2242 | | auto objHandle = runtime.makeHandle(obj); | 2243 | | auto cacheHCPtr = vmcast_or_null<HiddenClass>(static_cast<GCCell *>( | 2244 | | cacheEntry->clazz.get(runtime, runtime.getHeap()))); | 2245 | | CAPTURE_IP(runtime.recordHiddenClass( | 2246 | | curCodeBlock, ip, ID(idVal), obj->getClass(runtime), cacheHCPtr)); | 2247 | | // obj may be moved by GC due to recordHiddenClass | 2248 | | obj = objHandle.get(); | 2249 | | } | 2250 | | gcScope.flushToSmallCount(KEEP_HANDLES); | 2251 | | #endif | 2252 | 175k | CompressedPointer clazzPtr{obj->getClassGCPtr()}; | 2253 | 175k | #ifndef NDEBUG | 2254 | 175k | if (vmcast<HiddenClass>(clazzPtr.getNonNull(runtime))->isDictionary()) | 2255 | 171k | ++NumGetByIdDict; | 2256 | | #else | 2257 | | (void)NumGetByIdDict; | 2258 | | #endif | 2259 | | | 2260 | | // If we have a cache hit, reuse the cached offset and immediately | 2261 | | // return the property. | 2262 | 175k | if (LLVM_LIKELY(cacheEntry->clazz == clazzPtr)) { | 2263 | 172k | ++NumGetByIdCacheHits; | 2264 | 172k | CAPTURE_IP( | 2265 | 172k | O1REG(GetById) = | 2266 | 172k | JSObject::getNamedSlotValueUnsafe<PropStorage::Inline::Yes>( | 2267 | 172k | obj, runtime, cacheEntry->slot) | 2268 | 172k | .unboxToHV(runtime)); | 2269 | 172k | ip = nextIP; | 2270 | 864k | DISPATCH; | 2271 | 864k | } | 2272 | 175k | auto id = ID(idVal); | 2273 | 175k | NamedPropertyDescriptor desc; | 2274 | 175k | CAPTURE_IP_ASSIGN( | 2275 | 175k | OptValue<bool> fastPathResult, | 2276 | 175k | JSObject::tryGetOwnNamedDescriptorFast(obj, runtime, id, desc)); | 2277 | 175k | if (LLVM_LIKELY( | 2278 | 175k | fastPathResult.hasValue() && fastPathResult.getValue()) && | 2279 | 175k | !desc.flags.accessor) { | 2280 | 2.44k | ++NumGetByIdFastPaths; | 2281 | | | 2282 | | // cacheIdx == 0 indicates no caching so don't update the cache in | 2283 | | // those cases. | 2284 | 2.44k | HiddenClass *clazz = | 2285 | 2.44k | vmcast<HiddenClass>(clazzPtr.getNonNull(runtime)); | 2286 | 2.44k | if (LLVM_LIKELY(!clazz->isDictionaryNoCache()) && | 2287 | 2.44k | LLVM_LIKELY(cacheIdx != hbc::PROPERTY_CACHING_DISABLED)) { | 2288 | 2.44k | #ifdef HERMES_SLOW_DEBUG | 2289 | 2.44k | if (cacheEntry->clazz && cacheEntry->clazz != clazzPtr) | 2290 | 484 | ++NumGetByIdCacheEvicts; | 2291 | | #else | 2292 | | (void)NumGetByIdCacheEvicts; | 2293 | | #endif | 2294 | | // Cache the class, id and property slot. | 2295 | 2.44k | cacheEntry->clazz = clazzPtr; | 2296 | 2.44k | cacheEntry->slot = desc.slot; | 2297 | 2.44k | } | 2298 | | | 2299 | 2.44k | assert( | 2300 | 2.44k | !obj->isProxyObject() && | 2301 | 2.44k | "tryGetOwnNamedDescriptorFast returned true on Proxy"); | 2302 | 2.44k | CAPTURE_IP( | 2303 | 2.44k | O1REG(GetById) = | 2304 | 2.44k | JSObject::getNamedSlotValueUnsafe(obj, runtime, desc) | 2305 | 2.44k | .unboxToHV(runtime)); | 2306 | 2.44k | ip = nextIP; | 2307 | 12.2k | DISPATCH; | 2308 | 12.2k | } | 2309 | | | 2310 | | // The cache may also be populated via the prototype of the object. | 2311 | | // This value is only reliable if the fast path was a definite | 2312 | | // not-found. | 2313 | 175k | if (fastPathResult.hasValue() && !fastPathResult.getValue() && | 2314 | 175k | LLVM_LIKELY(!obj->isProxyObject())) { | 2315 | 27 | CAPTURE_IP_ASSIGN(JSObject * parent, obj->getParent(runtime)); | 2316 | | // TODO: This isLazy check is because a lazy object is reported as | 2317 | | // having no properties and therefore cannot contain the property. | 2318 | | // This check does not belong here, it should be merged into | 2319 | | // tryGetOwnNamedDescriptorFast(). | 2320 | 27 | if (parent && cacheEntry->clazz == parent->getClassGCPtr() && | 2321 | 27 | LLVM_LIKELY(!obj->isLazy())) { | 2322 | 0 | ++NumGetByIdProtoHits; | 2323 | | // We've already checked that this isn't a Proxy. | 2324 | 0 | CAPTURE_IP( | 2325 | 0 | O1REG(GetById) = JSObject::getNamedSlotValueUnsafe( | 2326 | 0 | parent, runtime, cacheEntry->slot) | 2327 | 0 | .unboxToHV(runtime)); | 2328 | 0 | ip = nextIP; | 2329 | 0 | DISPATCH; | 2330 | 0 | } | 2331 | 27 | } | 2332 | | | 2333 | 175k | #ifdef HERMES_SLOW_DEBUG | 2334 | | // Call to getNamedDescriptorUnsafe is safe because `id` is kept alive | 2335 | | // by the IdentifierTable. | 2336 | 175k | CAPTURE_IP_ASSIGN( | 2337 | 175k | JSObject * propObj, | 2338 | 175k | JSObject::getNamedDescriptorUnsafe( | 2339 | 175k | Handle<JSObject>::vmcast(&O2REG(GetById)), runtime, id, desc)); | 2340 | 175k | if (propObj) { | 2341 | 244 | if (desc.flags.accessor) | 2342 | 0 | ++NumGetByIdAccessor; | 2343 | 244 | else if (propObj != vmcast<JSObject>(O2REG(GetById))) | 2344 | 122 | ++NumGetByIdProto; | 2345 | 175k | } else { | 2346 | 175k | ++NumGetByIdNotFound; | 2347 | 175k | } | 2348 | | #else | 2349 | | (void)NumGetByIdAccessor; | 2350 | | (void)NumGetByIdProto; | 2351 | | (void)NumGetByIdNotFound; | 2352 | | #endif | 2353 | 175k | #ifdef HERMES_SLOW_DEBUG | 2354 | 175k | auto *savedClass = cacheIdx != hbc::PROPERTY_CACHING_DISABLED | 2355 | 175k | ? cacheEntry->clazz.get(runtime, runtime.getHeap()) | 2356 | 175k | : nullptr; | 2357 | 175k | #endif | 2358 | 175k | ++NumGetByIdSlow; | 2359 | 175k | CAPTURE_IP( | 2360 | 175k | resPH = JSObject::getNamed_RJS( | 2361 | 175k | Handle<JSObject>::vmcast(&O2REG(GetById)), | 2362 | 175k | runtime, | 2363 | 175k | id, | 2364 | 175k | !tryProp ? defaultPropOpFlags | 2365 | 175k | : defaultPropOpFlags.plusMustExist(), | 2366 | 175k | cacheIdx != hbc::PROPERTY_CACHING_DISABLED ? cacheEntry | 2367 | 175k | : nullptr)); | 2368 | 175k | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 2369 | 27 | goto exception; | 2370 | 27 | } | 2371 | 175k | #ifdef HERMES_SLOW_DEBUG | 2372 | 175k | if (cacheIdx != hbc::PROPERTY_CACHING_DISABLED && savedClass && | 2373 | 175k | cacheEntry->clazz.get(runtime, runtime.getHeap()) != savedClass) { | 2374 | 0 | ++NumGetByIdCacheEvicts; | 2375 | 0 | } | 2376 | 175k | #endif | 2377 | 175k | } else { | 2378 | 3 | ++NumGetByIdTransient; | 2379 | 3 | assert(!tryProp && "TryGetById can only be used on the global object"); | 2380 | | /* Slow path. */ | 2381 | 3 | CAPTURE_IP( | 2382 | 3 | resPH = Interpreter::getByIdTransient_RJS( | 2383 | 3 | runtime, Handle<>(&O2REG(GetById)), ID(idVal))); | 2384 | 3 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 2385 | 0 | goto exception; | 2386 | 0 | } | 2387 | 3 | } | 2388 | 175k | O1REG(GetById) = resPH->get(); | 2389 | 175k | gcScope.flushToSmallCount(KEEP_HANDLES); | 2390 | 175k | ip = nextIP; | 2391 | 175k | DISPATCH; | 2392 | 1.23k | } | 2393 | | | 2394 | 0 | CASE(TryPutByIdLong) { | 2395 | 0 | tryProp = true; | 2396 | 0 | idVal = ip->iTryPutByIdLong.op4; | 2397 | 0 | nextIP = NEXTINST(TryPutByIdLong); | 2398 | 0 | goto putById; | 2399 | 1.23k | } | 2400 | 0 | CASE(PutByIdLong) { | 2401 | 0 | tryProp = false; | 2402 | 0 | idVal = ip->iPutByIdLong.op4; | 2403 | 0 | nextIP = NEXTINST(PutByIdLong); | 2404 | 0 | goto putById; | 2405 | 1.23k | } | 2406 | 0 | CASE(TryPutById) { | 2407 | 0 | tryProp = true; | 2408 | 0 | idVal = ip->iTryPutById.op4; | 2409 | 0 | nextIP = NEXTINST(TryPutById); | 2410 | 0 | goto putById; | 2411 | 1.23k | } | 2412 | 635k | CASE(PutById) { | 2413 | 635k | tryProp = false; | 2414 | 635k | idVal = ip->iPutById.op4; | 2415 | 635k | nextIP = NEXTINST(PutById); | 2416 | 635k | } | 2417 | 635k | putById: { | 2418 | 635k | ++NumPutById; | 2419 | 635k | if (LLVM_LIKELY(O1REG(PutById).isObject())) { | 2420 | 635k | CAPTURE_IP_ASSIGN( | 2421 | 635k | SmallHermesValue shv, | 2422 | 635k | SmallHermesValue::encodeHermesValue(O2REG(PutById), runtime)); | 2423 | 635k | auto *obj = vmcast<JSObject>(O1REG(PutById)); | 2424 | 635k | auto cacheIdx = ip->iPutById.op3; | 2425 | 635k | auto *cacheEntry = curCodeBlock->getWriteCacheEntry(cacheIdx); | 2426 | | | 2427 | | #ifdef HERMESVM_PROFILER_BB | 2428 | | { | 2429 | | HERMES_SLOW_ASSERT( | 2430 | | gcScope.getHandleCountDbg() == KEEP_HANDLES && | 2431 | | "unaccounted handles were created"); | 2432 | | auto shvHandle = runtime.makeHandle(shv.toHV(runtime)); | 2433 | | auto objHandle = runtime.makeHandle(obj); | 2434 | | auto cacheHCPtr = vmcast_or_null<HiddenClass>(static_cast<GCCell *>( | 2435 | | cacheEntry->clazz.get(runtime, runtime.getHeap()))); | 2436 | | CAPTURE_IP(runtime.recordHiddenClass( | 2437 | | curCodeBlock, ip, ID(idVal), obj->getClass(runtime), cacheHCPtr)); | 2438 | | // shv/obj may be invalidated by recordHiddenClass | 2439 | | if (shv.isPointer()) | 2440 | | shv.unsafeUpdatePointer( | 2441 | | static_cast<GCCell *>(shvHandle->getPointer()), runtime); | 2442 | | obj = objHandle.get(); | 2443 | | } | 2444 | | gcScope.flushToSmallCount(KEEP_HANDLES); | 2445 | | #endif | 2446 | 635k | CompressedPointer clazzPtr{obj->getClassGCPtr()}; | 2447 | | // If we have a cache hit, reuse the cached offset and immediately | 2448 | | // return the property. | 2449 | 635k | if (LLVM_LIKELY(cacheEntry->clazz == clazzPtr)) { | 2450 | 1.21k | ++NumPutByIdCacheHits; | 2451 | 1.21k | CAPTURE_IP( | 2452 | 1.21k | JSObject::setNamedSlotValueUnsafe<PropStorage::Inline::Yes>( | 2453 | 1.21k | obj, runtime, cacheEntry->slot, shv)); | 2454 | 1.21k | ip = nextIP; | 2455 | 6.05k | DISPATCH; | 2456 | 6.05k | } | 2457 | 635k | auto id = ID(idVal); | 2458 | 635k | NamedPropertyDescriptor desc; | 2459 | 635k | CAPTURE_IP_ASSIGN( | 2460 | 635k | OptValue<bool> hasOwnProp, | 2461 | 635k | JSObject::tryGetOwnNamedDescriptorFast(obj, runtime, id, desc)); | 2462 | 635k | if (LLVM_LIKELY(hasOwnProp.hasValue() && hasOwnProp.getValue()) && | 2463 | 635k | !desc.flags.accessor && desc.flags.writable && | 2464 | 635k | !desc.flags.internalSetter) { | 2465 | 357k | ++NumPutByIdFastPaths; | 2466 | | | 2467 | | // cacheIdx == 0 indicates no caching so don't update the cache in | 2468 | | // those cases. | 2469 | 357k | HiddenClass *clazz = | 2470 | 357k | vmcast<HiddenClass>(clazzPtr.getNonNull(runtime)); | 2471 | 357k | if (LLVM_LIKELY(!clazz->isDictionary()) && | 2472 | 357k | LLVM_LIKELY(cacheIdx != hbc::PROPERTY_CACHING_DISABLED)) { | 2473 | 242 | #ifdef HERMES_SLOW_DEBUG | 2474 | 242 | if (cacheEntry->clazz && cacheEntry->clazz != clazzPtr) | 2475 | 0 | ++NumPutByIdCacheEvicts; | 2476 | | #else | 2477 | | (void)NumPutByIdCacheEvicts; | 2478 | | #endif | 2479 | | // Cache the class and property slot. | 2480 | 242 | cacheEntry->clazz = clazzPtr; | 2481 | 242 | cacheEntry->slot = desc.slot; | 2482 | 242 | } | 2483 | | | 2484 | | // This must be valid because an own property was already found. | 2485 | 357k | CAPTURE_IP( | 2486 | 357k | JSObject::setNamedSlotValueUnsafe(obj, runtime, desc.slot, shv)); | 2487 | 357k | ip = nextIP; | 2488 | 1.78M | DISPATCH; | 2489 | 1.78M | } | 2490 | | | 2491 | 635k | CAPTURE_IP_ASSIGN( | 2492 | 635k | auto putRes, | 2493 | 635k | JSObject::putNamed_RJS( | 2494 | 635k | Handle<JSObject>::vmcast(&O1REG(PutById)), | 2495 | 635k | runtime, | 2496 | 635k | id, | 2497 | 635k | Handle<>(&O2REG(PutById)), | 2498 | 635k | !tryProp ? defaultPropOpFlags | 2499 | 635k | : defaultPropOpFlags.plusMustExist())); | 2500 | 635k | if (LLVM_UNLIKELY(putRes == ExecutionStatus::EXCEPTION)) { | 2501 | 0 | goto exception; | 2502 | 0 | } | 2503 | 635k | } else { | 2504 | 0 | ++NumPutByIdTransient; | 2505 | 0 | assert(!tryProp && "TryPutById can only be used on the global object"); | 2506 | 0 | CAPTURE_IP_ASSIGN( | 2507 | 0 | auto retStatus, | 2508 | 0 | Interpreter::putByIdTransient_RJS( | 2509 | 0 | runtime, | 2510 | 0 | Handle<>(&O1REG(PutById)), | 2511 | 0 | ID(idVal), | 2512 | 0 | Handle<>(&O2REG(PutById)), | 2513 | 0 | strictMode)); | 2514 | 0 | if (retStatus == ExecutionStatus::EXCEPTION) { | 2515 | 0 | goto exception; | 2516 | 0 | } | 2517 | 0 | } | 2518 | 635k | gcScope.flushToSmallCount(KEEP_HANDLES); | 2519 | 635k | ip = nextIP; | 2520 | 1.38M | DISPATCH; | 2521 | 1.38M | } | 2522 | | | 2523 | 357k | CASE(GetByVal) { | 2524 | 357k | if (LLVM_LIKELY(O2REG(GetByVal).isObject())) { | 2525 | 357k | CAPTURE_IP( | 2526 | 357k | resPH = JSObject::getComputed_RJS( | 2527 | 357k | Handle<JSObject>::vmcast(&O2REG(GetByVal)), | 2528 | 357k | runtime, | 2529 | 357k | Handle<>(&O3REG(GetByVal)))); | 2530 | 357k | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 2531 | 0 | goto exception; | 2532 | 0 | } | 2533 | 357k | } else { | 2534 | | // This is the "slow path". | 2535 | 0 | CAPTURE_IP( | 2536 | 0 | resPH = Interpreter::getByValTransient_RJS( | 2537 | 0 | runtime, | 2538 | 0 | Handle<>(&O2REG(GetByVal)), | 2539 | 0 | Handle<>(&O3REG(GetByVal)))); | 2540 | 0 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 2541 | 0 | goto exception; | 2542 | 0 | } | 2543 | 0 | } | 2544 | 357k | gcScope.flushToSmallCount(KEEP_HANDLES); | 2545 | 357k | O1REG(GetByVal) = resPH->get(); | 2546 | 357k | ip = NEXTINST(GetByVal); | 2547 | 1.78M | DISPATCH; | 2548 | 1.78M | } | 2549 | | | 2550 | 271k | CASE(PutByVal) { | 2551 | 271k | if (LLVM_LIKELY(O1REG(PutByVal).isObject())) { | 2552 | 271k | CAPTURE_IP_ASSIGN( | 2553 | 271k | auto putRes, | 2554 | 271k | JSObject::putComputed_RJS( | 2555 | 271k | Handle<JSObject>::vmcast(&O1REG(PutByVal)), | 2556 | 271k | runtime, | 2557 | 271k | Handle<>(&O2REG(PutByVal)), | 2558 | 271k | Handle<>(&O3REG(PutByVal)), | 2559 | 271k | defaultPropOpFlags)); | 2560 | 271k | if (LLVM_UNLIKELY(putRes == ExecutionStatus::EXCEPTION)) { | 2561 | 0 | goto exception; | 2562 | 0 | } | 2563 | 271k | } else { | 2564 | | // This is the "slow path". | 2565 | 0 | CAPTURE_IP_ASSIGN( | 2566 | 0 | auto retStatus, | 2567 | 0 | Interpreter::putByValTransient_RJS( | 2568 | 0 | runtime, | 2569 | 0 | Handle<>(&O1REG(PutByVal)), | 2570 | 0 | Handle<>(&O2REG(PutByVal)), | 2571 | 0 | Handle<>(&O3REG(PutByVal)), | 2572 | 0 | strictMode)); | 2573 | 0 | if (LLVM_UNLIKELY(retStatus == ExecutionStatus::EXCEPTION)) { | 2574 | 0 | goto exception; | 2575 | 0 | } | 2576 | 0 | } | 2577 | 271k | gcScope.flushToSmallCount(KEEP_HANDLES); | 2578 | 271k | ip = NEXTINST(PutByVal); | 2579 | 1.35M | DISPATCH; | 2580 | 1.35M | } | 2581 | | | 2582 | 639k | CASE(PutOwnByIndexL) { | 2583 | 639k | nextIP = NEXTINST(PutOwnByIndexL); | 2584 | 639k | idVal = ip->iPutOwnByIndexL.op3; | 2585 | 639k | goto putOwnByIndex; | 2586 | 1.35M | } | 2587 | 402 | CASE(PutOwnByIndex) { | 2588 | 402 | nextIP = NEXTINST(PutOwnByIndex); | 2589 | 402 | idVal = ip->iPutOwnByIndex.op3; | 2590 | 402 | } | 2591 | 639k | putOwnByIndex: { | 2592 | 639k | tmpHandle = HermesValue::encodeUntrustedNumberValue(idVal); | 2593 | 639k | CAPTURE_IP(JSObject::defineOwnComputedPrimitive( | 2594 | 639k | Handle<JSObject>::vmcast(&O1REG(PutOwnByIndex)), | 2595 | 639k | runtime, | 2596 | 639k | tmpHandle, | 2597 | 639k | DefinePropertyFlags::getDefaultNewPropertyFlags(), | 2598 | 639k | Handle<>(&O2REG(PutOwnByIndex)))); | 2599 | 639k | gcScope.flushToSmallCount(KEEP_HANDLES); | 2600 | 639k | tmpHandle.clear(); | 2601 | 639k | ip = nextIP; | 2602 | 3.19M | DISPATCH; | 2603 | 3.19M | } | 2604 | | | 2605 | 639k | CASE_OUTOFLINE(GetPNameList); | 2606 | | | 2607 | 357k | CASE(GetNextPName) { | 2608 | 357k | { | 2609 | 357k | assert( | 2610 | 357k | vmisa<BigStorage>(O2REG(GetNextPName)) && | 2611 | 357k | "GetNextPName's second op must be BigStorage"); | 2612 | 357k | auto obj = Handle<JSObject>::vmcast(&O3REG(GetNextPName)); | 2613 | 357k | auto arr = Handle<BigStorage>::vmcast(&O2REG(GetNextPName)); | 2614 | 357k | uint32_t idx = O4REG(GetNextPName).getNumber(); | 2615 | 357k | uint32_t size = O5REG(GetNextPName).getNumber(); | 2616 | 357k | MutableHandle<JSObject> propObj{runtime}; | 2617 | 357k | MutableHandle<SymbolID> tmpPropNameStorage{runtime}; | 2618 | | // Loop until we find a property which is present. | 2619 | 357k | while (idx < size) { | 2620 | 357k | tmpHandle = arr->at(runtime, idx); | 2621 | 357k | ComputedPropertyDescriptor desc; | 2622 | 357k | CAPTURE_IP_ASSIGN( | 2623 | 357k | ExecutionStatus status, | 2624 | 357k | JSObject::getComputedPrimitiveDescriptor( | 2625 | 357k | obj, | 2626 | 357k | runtime, | 2627 | 357k | tmpHandle, | 2628 | 357k | propObj, | 2629 | 357k | tmpPropNameStorage, | 2630 | 357k | desc)); | 2631 | 357k | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { | 2632 | 0 | goto exception; | 2633 | 0 | } | 2634 | 357k | if (LLVM_LIKELY(propObj)) | 2635 | 357k | break; | 2636 | 0 | ++idx; | 2637 | 0 | } | 2638 | 357k | if (idx < size) { | 2639 | | // We must return the property as a string | 2640 | 357k | if (tmpHandle->isNumber()) { | 2641 | 357k | CAPTURE_IP_ASSIGN(auto status, toString_RJS(runtime, tmpHandle)); | 2642 | 357k | assert( | 2643 | 357k | status == ExecutionStatus::RETURNED && | 2644 | 357k | "toString on number cannot fail"); | 2645 | 357k | tmpHandle = status->getHermesValue(); | 2646 | 357k | } | 2647 | 357k | O4REG(GetNextPName) = | 2648 | 357k | HermesValue::encodeUntrustedNumberValue(idx + 1); | 2649 | | // Write the result last in case it is the same register as O4REG. | 2650 | 357k | O1REG(GetNextPName) = tmpHandle.get(); | 2651 | 357k | } else { | 2652 | 3 | O1REG(GetNextPName) = HermesValue::encodeUndefinedValue(); | 2653 | 3 | } | 2654 | 357k | } | 2655 | 357k | gcScope.flushToSmallCount(KEEP_HANDLES); | 2656 | 357k | tmpHandle.clear(); | 2657 | 357k | ip = NEXTINST(GetNextPName); | 2658 | 1.78M | DISPATCH; | 2659 | 1.78M | } | 2660 | | | 2661 | 4 | CASE(ToNumber) { | 2662 | 4 | if (LLVM_LIKELY(O2REG(ToNumber).isNumber())) { | 2663 | 0 | O1REG(ToNumber) = O2REG(ToNumber); | 2664 | 0 | ip = NEXTINST(ToNumber); | 2665 | 4 | } else { | 2666 | 4 | CAPTURE_IP(res = toNumber_RJS(runtime, Handle<>(&O2REG(ToNumber)))); | 2667 | 4 | if (res == ExecutionStatus::EXCEPTION) | 2668 | 0 | goto exception; | 2669 | 4 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2670 | 4 | O1REG(ToNumber) = res.getValue(); | 2671 | 4 | ip = NEXTINST(ToNumber); | 2672 | 4 | } | 2673 | 24 | DISPATCH; | 2674 | 24 | } | 2675 | | | 2676 | 0 | CASE(ToNumeric) { | 2677 | 0 | if (LLVM_LIKELY(O2REG(ToNumeric).isNumber())) { | 2678 | 0 | O1REG(ToNumeric) = O2REG(ToNumeric); | 2679 | 0 | ip = NEXTINST(ToNumeric); | 2680 | 0 | } else { | 2681 | 0 | CAPTURE_IP(res = toNumeric_RJS(runtime, Handle<>(&O2REG(ToNumeric)))); | 2682 | 0 | if (res == ExecutionStatus::EXCEPTION) | 2683 | 0 | goto exception; | 2684 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2685 | 0 | O1REG(ToNumeric) = res.getValue(); | 2686 | 0 | ip = NEXTINST(ToNumeric); | 2687 | 0 | } | 2688 | 0 | DISPATCH; | 2689 | 0 | } | 2690 | | | 2691 | 0 | CASE(ToInt32) { | 2692 | 0 | CAPTURE_IP(res = toInt32_RJS(runtime, Handle<>(&O2REG(ToInt32)))); | 2693 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) | 2694 | 0 | goto exception; | 2695 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2696 | 0 | O1REG(ToInt32) = res.getValue(); | 2697 | 0 | ip = NEXTINST(ToInt32); | 2698 | 0 | DISPATCH; | 2699 | 0 | } | 2700 | | | 2701 | 0 | CASE(AddEmptyString) { | 2702 | 0 | if (LLVM_LIKELY(O2REG(AddEmptyString).isString())) { | 2703 | 0 | O1REG(AddEmptyString) = O2REG(AddEmptyString); | 2704 | 0 | ip = NEXTINST(AddEmptyString); | 2705 | 0 | } else { | 2706 | 0 | CAPTURE_IP( | 2707 | 0 | res = toPrimitive_RJS( | 2708 | 0 | runtime, | 2709 | 0 | Handle<>(&O2REG(AddEmptyString)), | 2710 | 0 | PreferredType::NONE)); | 2711 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) | 2712 | 0 | goto exception; | 2713 | 0 | tmpHandle = res.getValue(); | 2714 | 0 | CAPTURE_IP_ASSIGN(auto strRes, toString_RJS(runtime, tmpHandle)); | 2715 | 0 | if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) | 2716 | 0 | goto exception; | 2717 | 0 | tmpHandle.clear(); | 2718 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2719 | 0 | O1REG(AddEmptyString) = strRes->getHermesValue(); | 2720 | 0 | ip = NEXTINST(AddEmptyString); | 2721 | 0 | } | 2722 | 0 | DISPATCH; | 2723 | 0 | } | 2724 | | | 2725 | 357k | CASE(Jmp) { | 2726 | 357k | ip = IPADD(ip->iJmp.op1); | 2727 | 1.78M | DISPATCH; | 2728 | 1.78M | } | 2729 | 0 | CASE(JmpLong) { | 2730 | 0 | ip = IPADD(ip->iJmpLong.op1); | 2731 | 0 | DISPATCH; | 2732 | 0 | } | 2733 | 847 | CASE(JmpTrue) { | 2734 | 847 | if (toBoolean(O2REG(JmpTrue))) | 2735 | 121 | ip = IPADD(ip->iJmpTrue.op1); | 2736 | 726 | else | 2737 | 726 | ip = NEXTINST(JmpTrue); | 2738 | 4.23k | DISPATCH; | 2739 | 4.23k | } | 2740 | 0 | CASE(JmpTrueLong) { | 2741 | 0 | if (toBoolean(O2REG(JmpTrueLong))) | 2742 | 0 | ip = IPADD(ip->iJmpTrueLong.op1); | 2743 | 0 | else | 2744 | 0 | ip = NEXTINST(JmpTrueLong); | 2745 | 0 | DISPATCH; | 2746 | 0 | } | 2747 | 242 | CASE(JmpFalse) { | 2748 | 242 | if (!toBoolean(O2REG(JmpFalse))) | 2749 | 121 | ip = IPADD(ip->iJmpFalse.op1); | 2750 | 121 | else | 2751 | 121 | ip = NEXTINST(JmpFalse); | 2752 | 1.21k | DISPATCH; | 2753 | 1.21k | } | 2754 | 0 | CASE(JmpFalseLong) { | 2755 | 0 | if (!toBoolean(O2REG(JmpFalseLong))) | 2756 | 0 | ip = IPADD(ip->iJmpFalseLong.op1); | 2757 | 0 | else | 2758 | 0 | ip = NEXTINST(JmpFalseLong); | 2759 | 0 | DISPATCH; | 2760 | 0 | } | 2761 | 357k | CASE(JmpUndefined) { | 2762 | 357k | if (O2REG(JmpUndefined).isUndefined()) | 2763 | 3 | ip = IPADD(ip->iJmpUndefined.op1); | 2764 | 357k | else | 2765 | 357k | ip = NEXTINST(JmpUndefined); | 2766 | 1.78M | DISPATCH; | 2767 | 1.78M | } | 2768 | 2 | CASE(JmpUndefinedLong) { | 2769 | 2 | if (O2REG(JmpUndefinedLong).isUndefined()) | 2770 | 0 | ip = IPADD(ip->iJmpUndefinedLong.op1); | 2771 | 2 | else | 2772 | 2 | ip = NEXTINST(JmpUndefinedLong); | 2773 | 10 | DISPATCH; | 2774 | 10 | } | 2775 | 2.17M | INCDECOP(Inc) | 2776 | 271k | INCDECOP(Dec) | 2777 | 199k | CASE(Add) { | 2778 | 199k | if (LLVM_LIKELY( | 2779 | 199k | O2REG(Add).isNumber() && | 2780 | 199k | O3REG(Add).isNumber())) { /* Fast-path. */ | 2781 | 0 | INTERPRETER_FALLTHROUGH; | 2782 | 0 | CASE(AddN) { | 2783 | 0 | O1REG(Add) = HermesValue::encodeTrustedNumberValue( | 2784 | 0 | O2REG(Add).getNumber() + O3REG(Add).getNumber()); | 2785 | 0 | ip = NEXTINST(Add); | 2786 | 0 | DISPATCH; | 2787 | 0 | } | 2788 | 0 | } | 2789 | 199k | CAPTURE_IP( | 2790 | 199k | res = addOp_RJS( | 2791 | 199k | runtime, Handle<>(&O2REG(Add)), Handle<>(&O3REG(Add)))); | 2792 | 199k | if (res == ExecutionStatus::EXCEPTION) { | 2793 | 0 | goto exception; | 2794 | 0 | } | 2795 | 199k | gcScope.flushToSmallCount(KEEP_HANDLES); | 2796 | 199k | O1REG(Add) = res.getValue(); | 2797 | 199k | ip = NEXTINST(Add); | 2798 | 995k | DISPATCH; | 2799 | 995k | } | 2800 | | | 2801 | 0 | CASE(BitNot) { | 2802 | 0 | if (LLVM_LIKELY(O2REG(BitNot).isNumber())) { /* Fast-path. */ | 2803 | 0 | O1REG(BitNot) = HermesValue::encodeUntrustedNumberValue( | 2804 | 0 | ~hermes::truncateToInt32(O2REG(BitNot).getNumber())); | 2805 | 0 | ip = NEXTINST(BitNot); | 2806 | 0 | DISPATCH; | 2807 | 0 | } | 2808 | 0 | CAPTURE_IP(res = doBitNotSlowPath(runtime, Handle<>(&O2REG(BitNot)))); | 2809 | 0 | if (res == ExecutionStatus::EXCEPTION) { | 2810 | 0 | goto exception; | 2811 | 0 | } | 2812 | 0 | O1REG(BitNot) = *res; | 2813 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2814 | 0 | ip = NEXTINST(BitNot); | 2815 | 0 | DISPATCH; | 2816 | 0 | } | 2817 | | | 2818 | 0 | CASE(GetArgumentsLength) { | 2819 | | // If the arguments object hasn't been created yet. | 2820 | 0 | if (O2REG(GetArgumentsLength).isUndefined()) { | 2821 | 0 | O1REG(GetArgumentsLength) = | 2822 | 0 | HermesValue::encodeUntrustedNumberValue(FRAME.getArgCount()); | 2823 | 0 | ip = NEXTINST(GetArgumentsLength); | 2824 | 0 | DISPATCH; | 2825 | 0 | } | 2826 | | // The arguments object has been created, so this is a regular property | 2827 | | // get. | 2828 | 0 | assert( | 2829 | 0 | O2REG(GetArgumentsLength).isObject() && | 2830 | 0 | "arguments lazy register is not an object"); | 2831 | 0 | CAPTURE_IP( | 2832 | 0 | resPH = JSObject::getNamed_RJS( | 2833 | 0 | Handle<JSObject>::vmcast(&O2REG(GetArgumentsLength)), | 2834 | 0 | runtime, | 2835 | 0 | Predefined::getSymbolID(Predefined::length))); | 2836 | 0 | if (resPH == ExecutionStatus::EXCEPTION) { | 2837 | 0 | goto exception; | 2838 | 0 | } | 2839 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2840 | 0 | O1REG(GetArgumentsLength) = resPH->get(); | 2841 | 0 | ip = NEXTINST(GetArgumentsLength); | 2842 | 0 | DISPATCH; | 2843 | 0 | } | 2844 | | | 2845 | 0 | CASE(GetArgumentsPropByVal) { | 2846 | | // If the arguments object hasn't been created yet and we have a | 2847 | | // valid integer index, we use the fast path. | 2848 | 0 | if (O3REG(GetArgumentsPropByVal).isUndefined()) { | 2849 | | // If this is an integer index. | 2850 | 0 | if (auto index = toArrayIndexFastPath(O2REG(GetArgumentsPropByVal))) { | 2851 | | // Is this an existing argument? | 2852 | 0 | if (*index < FRAME.getArgCount()) { | 2853 | 0 | O1REG(GetArgumentsPropByVal) = FRAME.getArgRef(*index); | 2854 | 0 | ip = NEXTINST(GetArgumentsPropByVal); | 2855 | 0 | DISPATCH; | 2856 | 0 | } | 2857 | 0 | } | 2858 | 0 | } | 2859 | | // Slow path. | 2860 | 0 | CAPTURE_IP_ASSIGN( | 2861 | 0 | auto res, | 2862 | 0 | getArgumentsPropByValSlowPath_RJS( | 2863 | 0 | runtime, | 2864 | 0 | &O3REG(GetArgumentsPropByVal), | 2865 | 0 | &O2REG(GetArgumentsPropByVal), | 2866 | 0 | FRAME.getCalleeClosureHandleUnsafe(), | 2867 | 0 | strictMode)); | 2868 | 0 | if (res == ExecutionStatus::EXCEPTION) { | 2869 | 0 | goto exception; | 2870 | 0 | } | 2871 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2872 | 0 | O1REG(GetArgumentsPropByVal) = res->getHermesValue(); | 2873 | 0 | ip = NEXTINST(GetArgumentsPropByVal); | 2874 | 0 | DISPATCH; | 2875 | 0 | } | 2876 | | | 2877 | 2 | CASE(ReifyArguments) { | 2878 | | // If the arguments object was already created, do nothing. | 2879 | 2 | if (!O1REG(ReifyArguments).isUndefined()) { | 2880 | 0 | assert( | 2881 | 0 | O1REG(ReifyArguments).isObject() && | 2882 | 0 | "arguments lazy register is not an object"); | 2883 | 0 | ip = NEXTINST(ReifyArguments); | 2884 | 0 | DISPATCH; | 2885 | 0 | } | 2886 | 2 | CAPTURE_IP( | 2887 | 2 | resArgs = reifyArgumentsSlowPath( | 2888 | 2 | runtime, FRAME.getCalleeClosureHandleUnsafe(), strictMode)); | 2889 | 2 | if (LLVM_UNLIKELY(resArgs == ExecutionStatus::EXCEPTION)) { | 2890 | 0 | goto exception; | 2891 | 0 | } | 2892 | 2 | O1REG(ReifyArguments) = resArgs->getHermesValue(); | 2893 | 2 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2894 | 2 | ip = NEXTINST(ReifyArguments); | 2895 | 10 | DISPATCH; | 2896 | 10 | } | 2897 | | | 2898 | 508 | CASE(NewObject) { | 2899 | | // Create a new object using the built-in constructor. Note that the | 2900 | | // built-in constructor is empty, so we don't actually need to call | 2901 | | // it. | 2902 | 508 | CAPTURE_IP( | 2903 | 508 | O1REG(NewObject) = JSObject::create(runtime).getHermesValue()); | 2904 | 508 | assert( | 2905 | 508 | gcScope.getHandleCountDbg() == KEEP_HANDLES && | 2906 | 508 | "Should not create handles."); | 2907 | 508 | ip = NEXTINST(NewObject); | 2908 | 2.54k | DISPATCH; | 2909 | 2.54k | } | 2910 | 0 | CASE(NewObjectWithParent) { | 2911 | 0 | CAPTURE_IP( | 2912 | 0 | O1REG(NewObjectWithParent) = | 2913 | 0 | JSObject::create( | 2914 | 0 | runtime, | 2915 | 0 | O2REG(NewObjectWithParent).isObject() | 2916 | 0 | ? Handle<JSObject>::vmcast(&O2REG(NewObjectWithParent)) | 2917 | 0 | : O2REG(NewObjectWithParent).isNull() | 2918 | 0 | ? Runtime::makeNullHandle<JSObject>() | 2919 | 0 | : Handle<JSObject>::vmcast(&runtime.objectPrototype)) | 2920 | 0 | .getHermesValue()); | 2921 | 0 | assert( | 2922 | 0 | gcScope.getHandleCountDbg() == KEEP_HANDLES && | 2923 | 0 | "Should not create handles."); | 2924 | 0 | ip = NEXTINST(NewObjectWithParent); | 2925 | 0 | DISPATCH; | 2926 | 0 | } | 2927 | | | 2928 | 0 | CASE(NewObjectWithBuffer) { | 2929 | 0 | CAPTURE_IP( | 2930 | 0 | resPH = Interpreter::createObjectFromBuffer( | 2931 | 0 | runtime, | 2932 | 0 | curCodeBlock, | 2933 | 0 | ip->iNewObjectWithBuffer.op3, | 2934 | 0 | ip->iNewObjectWithBuffer.op4, | 2935 | 0 | ip->iNewObjectWithBuffer.op5)); | 2936 | 0 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 2937 | 0 | goto exception; | 2938 | 0 | } | 2939 | 0 | O1REG(NewObjectWithBuffer) = resPH->get(); | 2940 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2941 | 0 | ip = NEXTINST(NewObjectWithBuffer); | 2942 | 0 | DISPATCH; | 2943 | 0 | } | 2944 | | | 2945 | 0 | CASE(NewObjectWithBufferLong) { | 2946 | 0 | CAPTURE_IP( | 2947 | 0 | resPH = Interpreter::createObjectFromBuffer( | 2948 | 0 | runtime, | 2949 | 0 | curCodeBlock, | 2950 | 0 | ip->iNewObjectWithBufferLong.op3, | 2951 | 0 | ip->iNewObjectWithBufferLong.op4, | 2952 | 0 | ip->iNewObjectWithBufferLong.op5)); | 2953 | 0 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 2954 | 0 | goto exception; | 2955 | 0 | } | 2956 | 0 | O1REG(NewObjectWithBufferLong) = resPH->get(); | 2957 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2958 | 0 | ip = NEXTINST(NewObjectWithBufferLong); | 2959 | 0 | DISPATCH; | 2960 | 0 | } | 2961 | | | 2962 | 357k | CASE(NewArray) { | 2963 | | // Create a new array using the built-in constructor. Note that the | 2964 | | // built-in constructor is empty, so we don't actually need to call | 2965 | | // it. | 2966 | 357k | { | 2967 | 357k | CAPTURE_IP_ASSIGN( | 2968 | 357k | auto createRes, | 2969 | 357k | JSArray::create(runtime, ip->iNewArray.op2, ip->iNewArray.op2)); | 2970 | 357k | if (createRes == ExecutionStatus::EXCEPTION) { | 2971 | 0 | goto exception; | 2972 | 0 | } | 2973 | 357k | O1REG(NewArray) = createRes->getHermesValue(); | 2974 | 357k | } | 2975 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2976 | 357k | ip = NEXTINST(NewArray); | 2977 | 1.78M | DISPATCH; | 2978 | 1.78M | } | 2979 | | | 2980 | 12 | CASE(NewArrayWithBuffer) { | 2981 | 12 | CAPTURE_IP( | 2982 | 12 | resPH = Interpreter::createArrayFromBuffer( | 2983 | 12 | runtime, | 2984 | 12 | curCodeBlock, | 2985 | 12 | ip->iNewArrayWithBuffer.op2, | 2986 | 12 | ip->iNewArrayWithBuffer.op3, | 2987 | 12 | ip->iNewArrayWithBuffer.op4)); | 2988 | 12 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 2989 | 0 | goto exception; | 2990 | 0 | } | 2991 | 12 | O1REG(NewArrayWithBuffer) = resPH->get(); | 2992 | 12 | gcScope.flushToSmallCount(KEEP_HANDLES); | 2993 | 12 | tmpHandle.clear(); | 2994 | 12 | ip = NEXTINST(NewArrayWithBuffer); | 2995 | 60 | DISPATCH; | 2996 | 60 | } | 2997 | | | 2998 | 0 | CASE(NewArrayWithBufferLong) { | 2999 | 0 | CAPTURE_IP( | 3000 | 0 | resPH = Interpreter::createArrayFromBuffer( | 3001 | 0 | runtime, | 3002 | 0 | curCodeBlock, | 3003 | 0 | ip->iNewArrayWithBufferLong.op2, | 3004 | 0 | ip->iNewArrayWithBufferLong.op3, | 3005 | 0 | ip->iNewArrayWithBufferLong.op4)); | 3006 | 0 | if (LLVM_UNLIKELY(resPH == ExecutionStatus::EXCEPTION)) { | 3007 | 0 | goto exception; | 3008 | 0 | } | 3009 | 0 | O1REG(NewArrayWithBufferLong) = resPH->get(); | 3010 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3011 | 0 | tmpHandle.clear(); | 3012 | 0 | ip = NEXTINST(NewArrayWithBufferLong); | 3013 | 0 | DISPATCH; | 3014 | 0 | } | 3015 | | | 3016 | 726 | CASE(CreateThis) { | 3017 | | // Registers: output, prototype, closure. | 3018 | 726 | if (LLVM_UNLIKELY(!vmisa<Callable>(O3REG(CreateThis)))) { | 3019 | 0 | CAPTURE_IP(runtime.raiseTypeError("constructor is not callable")); | 3020 | 0 | goto exception; | 3021 | 0 | } | 3022 | 726 | CAPTURE_IP_ASSIGN( | 3023 | 726 | auto res, | 3024 | 726 | Callable::newObject( | 3025 | 726 | Handle<Callable>::vmcast(&O3REG(CreateThis)), | 3026 | 726 | runtime, | 3027 | 726 | Handle<JSObject>::vmcast( | 3028 | 726 | O2REG(CreateThis).isObject() ? &O2REG(CreateThis) | 3029 | 726 | : &runtime.objectPrototype))); | 3030 | 726 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 3031 | 0 | goto exception; | 3032 | 0 | } | 3033 | 726 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3034 | 726 | O1REG(CreateThis) = res->getHermesValue(); | 3035 | 726 | ip = NEXTINST(CreateThis); | 3036 | 3.63k | DISPATCH; | 3037 | 3.63k | } | 3038 | | | 3039 | 726 | CASE(SelectObject) { | 3040 | | // Registers: output, thisObject, constructorReturnValue. | 3041 | 726 | O1REG(SelectObject) = O3REG(SelectObject).isObject() | 3042 | 726 | ? O3REG(SelectObject) | 3043 | 726 | : O2REG(SelectObject); | 3044 | 726 | ip = NEXTINST(SelectObject); | 3045 | 3.63k | DISPATCH; | 3046 | 3.63k | } | 3047 | | | 3048 | 726 | CASE(Eq) | 3049 | 726 | CASE(Neq) { | 3050 | 726 | CAPTURE_IP_ASSIGN( | 3051 | 726 | auto eqRes, | 3052 | 726 | abstractEqualityTest_RJS( | 3053 | 726 | runtime, Handle<>(&O2REG(Eq)), Handle<>(&O3REG(Eq)))); | 3054 | 726 | if (eqRes == ExecutionStatus::EXCEPTION) { | 3055 | 0 | goto exception; | 3056 | 0 | } | 3057 | 726 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3058 | 726 | O1REG(Eq) = HermesValue::encodeBoolValue( | 3059 | 726 | ip->opCode == OpCode::Eq ? *eqRes : !*eqRes); | 3060 | 726 | ip = NEXTINST(Eq); | 3061 | 3.63k | DISPATCH; | 3062 | 3.63k | } | 3063 | 0 | CASE(StrictEq) { | 3064 | 0 | O1REG(StrictEq) = HermesValue::encodeBoolValue( | 3065 | 0 | strictEqualityTest(O2REG(StrictEq), O3REG(StrictEq))); | 3066 | 0 | ip = NEXTINST(StrictEq); | 3067 | 0 | DISPATCH; | 3068 | 0 | } | 3069 | 0 | CASE(StrictNeq) { | 3070 | 0 | O1REG(StrictNeq) = HermesValue::encodeBoolValue( | 3071 | 0 | !strictEqualityTest(O2REG(StrictNeq), O3REG(StrictNeq))); | 3072 | 0 | ip = NEXTINST(StrictNeq); | 3073 | 0 | DISPATCH; | 3074 | 0 | } | 3075 | 1 | CASE(Not) { | 3076 | 1 | O1REG(Not) = HermesValue::encodeBoolValue(!toBoolean(O2REG(Not))); | 3077 | 1 | ip = NEXTINST(Not); | 3078 | 5 | DISPATCH; | 3079 | 5 | } | 3080 | 17.0k | CASE(Negate) { | 3081 | 17.0k | if (LLVM_LIKELY(O2REG(Negate).isNumber())) { | 3082 | 17.0k | O1REG(Negate) = HermesValue::encodeUntrustedNumberValue( | 3083 | 17.0k | -O2REG(Negate).getNumber()); | 3084 | 17.0k | ip = NEXTINST(Negate); | 3085 | 85.3k | DISPATCH; | 3086 | 85.3k | } | 3087 | 17.0k | CAPTURE_IP(res = doNegateSlowPath(runtime, Handle<>(&O2REG(Negate)))); | 3088 | 17.0k | if (res == ExecutionStatus::EXCEPTION) | 3089 | 0 | goto exception; | 3090 | 17.0k | O1REG(Negate) = *res; | 3091 | 17.0k | gcScope.flushToSmallCount(KEEP_HANDLES); | 3092 | 17.0k | ip = NEXTINST(Negate); | 3093 | 17.0k | DISPATCH; | 3094 | 110 | } | 3095 | 1.45k | CASE(TypeOf) { | 3096 | 1.45k | CAPTURE_IP(O1REG(TypeOf) = typeOf(runtime, Handle<>(&O2REG(TypeOf)))); | 3097 | 1.45k | ip = NEXTINST(TypeOf); | 3098 | 7.26k | DISPATCH; | 3099 | 7.26k | } | 3100 | 0 | CASE(Mod) { | 3101 | 0 | if (LLVM_LIKELY(O2REG(Mod).isNumber() && O3REG(Mod).isNumber())) { | 3102 | | /* Fast-path. */ | 3103 | 0 | O1REG(Mod) = HermesValue::encodeUntrustedNumberValue( | 3104 | 0 | doMod(O2REG(Mod).getNumber(), O3REG(Mod).getNumber())); | 3105 | 0 | ip = NEXTINST(Mod); | 3106 | 0 | DISPATCH; | 3107 | 0 | } | 3108 | 0 | CAPTURE_IP( | 3109 | 0 | res = doOperSlowPath<doMod>( | 3110 | 0 | runtime, Handle<>(&O2REG(Mod)), Handle<>(&O3REG(Mod)))); | 3111 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 3112 | 0 | goto exception; | 3113 | 0 | } | 3114 | 0 | O1REG(Mod) = *res; | 3115 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3116 | 0 | ip = NEXTINST(Mod); | 3117 | 0 | DISPATCH; | 3118 | 0 | } | 3119 | 0 | CASE(InstanceOf) { | 3120 | 0 | CAPTURE_IP_ASSIGN( | 3121 | 0 | auto result, | 3122 | 0 | instanceOfOperator_RJS( | 3123 | 0 | runtime, | 3124 | 0 | Handle<>(&O2REG(InstanceOf)), | 3125 | 0 | Handle<>(&O3REG(InstanceOf)))); | 3126 | 0 | if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) { | 3127 | 0 | goto exception; | 3128 | 0 | } | 3129 | 0 | O1REG(InstanceOf) = HermesValue::encodeBoolValue(*result); | 3130 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3131 | 0 | ip = NEXTINST(InstanceOf); | 3132 | 0 | DISPATCH; | 3133 | 0 | } | 3134 | 0 | CASE(IsIn) { | 3135 | 0 | { | 3136 | 0 | if (LLVM_UNLIKELY(!O3REG(IsIn).isObject())) { | 3137 | 0 | CAPTURE_IP(runtime.raiseTypeError( | 3138 | 0 | "right operand of 'in' is not an object")); | 3139 | 0 | goto exception; | 3140 | 0 | } | 3141 | 0 | CAPTURE_IP_ASSIGN( | 3142 | 0 | auto cr, | 3143 | 0 | JSObject::hasComputed( | 3144 | 0 | Handle<JSObject>::vmcast(&O3REG(IsIn)), | 3145 | 0 | runtime, | 3146 | 0 | Handle<>(&O2REG(IsIn)))); | 3147 | 0 | if (cr == ExecutionStatus::EXCEPTION) { | 3148 | 0 | goto exception; | 3149 | 0 | } | 3150 | 0 | O1REG(IsIn) = HermesValue::encodeBoolValue(*cr); | 3151 | 0 | } | 3152 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3153 | 0 | ip = NEXTINST(IsIn); | 3154 | 0 | DISPATCH; | 3155 | 0 | } | 3156 | | | 3157 | 316 | CASE(PutNewOwnByIdShort) { | 3158 | 316 | nextIP = NEXTINST(PutNewOwnByIdShort); | 3159 | 316 | idVal = ip->iPutNewOwnByIdShort.op3; | 3160 | 316 | goto putOwnById; | 3161 | 0 | } | 3162 | 0 | CASE(PutNewOwnNEByIdLong) | 3163 | 0 | CASE(PutNewOwnByIdLong) { | 3164 | 0 | nextIP = NEXTINST(PutNewOwnByIdLong); | 3165 | 0 | idVal = ip->iPutNewOwnByIdLong.op3; | 3166 | 0 | goto putOwnById; | 3167 | 0 | } | 3168 | 0 | CASE(PutNewOwnNEById) | 3169 | 0 | CASE(PutNewOwnById) { | 3170 | 0 | nextIP = NEXTINST(PutNewOwnById); | 3171 | 0 | idVal = ip->iPutNewOwnById.op3; | 3172 | 0 | } | 3173 | 316 | putOwnById: { | 3174 | 316 | assert( | 3175 | 316 | O1REG(PutNewOwnById).isObject() && | 3176 | 316 | "Object argument of PutNewOwnById must be an object"); | 3177 | 316 | CAPTURE_IP_ASSIGN( | 3178 | 316 | auto res, | 3179 | 316 | JSObject::defineNewOwnProperty( | 3180 | 316 | Handle<JSObject>::vmcast(&O1REG(PutNewOwnById)), | 3181 | 316 | runtime, | 3182 | 316 | ID(idVal), | 3183 | 316 | ip->opCode <= OpCode::PutNewOwnByIdLong | 3184 | 316 | ? PropertyFlags::defaultNewNamedPropertyFlags() | 3185 | 316 | : PropertyFlags::nonEnumerablePropertyFlags(), | 3186 | 316 | Handle<>(&O2REG(PutNewOwnById)))); | 3187 | 316 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 3188 | 0 | goto exception; | 3189 | 0 | } | 3190 | 316 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3191 | 316 | ip = nextIP; | 3192 | 1.58k | DISPATCH; | 3193 | 1.58k | } | 3194 | | | 3195 | 0 | CASE(DelByIdLong) { | 3196 | 0 | idVal = ip->iDelByIdLong.op3; | 3197 | 0 | nextIP = NEXTINST(DelByIdLong); | 3198 | 0 | goto DelById; | 3199 | 1.58k | } | 3200 | | | 3201 | 0 | CASE(DelById) { | 3202 | 0 | idVal = ip->iDelById.op3; | 3203 | 0 | nextIP = NEXTINST(DelById); | 3204 | 0 | } | 3205 | 0 | DelById: { | 3206 | 0 | if (LLVM_LIKELY(O2REG(DelById).isObject())) { | 3207 | 0 | CAPTURE_IP_ASSIGN( | 3208 | 0 | auto status, | 3209 | 0 | JSObject::deleteNamed( | 3210 | 0 | Handle<JSObject>::vmcast(&O2REG(DelById)), | 3211 | 0 | runtime, | 3212 | 0 | ID(idVal), | 3213 | 0 | defaultPropOpFlags)); | 3214 | 0 | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { | 3215 | 0 | goto exception; | 3216 | 0 | } | 3217 | 0 | O1REG(DelById) = HermesValue::encodeBoolValue(status.getValue()); | 3218 | 0 | } else { | 3219 | | // This is the "slow path". | 3220 | 0 | CAPTURE_IP(res = toObject(runtime, Handle<>(&O2REG(DelById)))); | 3221 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 3222 | | // If an exception is thrown, likely we are trying to convert | 3223 | | // undefined/null to an object. Passing over the name of the property | 3224 | | // so that we could emit more meaningful error messages. | 3225 | 0 | CAPTURE_IP(amendPropAccessErrorMsgWithPropName( | 3226 | 0 | runtime, Handle<>(&O2REG(DelById)), "delete", ID(idVal))); | 3227 | 0 | goto exception; | 3228 | 0 | } | 3229 | 0 | tmpHandle = res.getValue(); | 3230 | 0 | CAPTURE_IP_ASSIGN( | 3231 | 0 | auto status, | 3232 | 0 | JSObject::deleteNamed( | 3233 | 0 | Handle<JSObject>::vmcast(tmpHandle), | 3234 | 0 | runtime, | 3235 | 0 | ID(idVal), | 3236 | 0 | defaultPropOpFlags)); | 3237 | 0 | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { | 3238 | 0 | goto exception; | 3239 | 0 | } | 3240 | 0 | O1REG(DelById) = HermesValue::encodeBoolValue(status.getValue()); | 3241 | 0 | tmpHandle.clear(); | 3242 | 0 | } | 3243 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3244 | 0 | ip = nextIP; | 3245 | 0 | DISPATCH; | 3246 | 0 | } | 3247 | | | 3248 | 0 | CASE(DelByVal) { | 3249 | 0 | if (LLVM_LIKELY(O2REG(DelByVal).isObject())) { | 3250 | 0 | CAPTURE_IP_ASSIGN( | 3251 | 0 | auto status, | 3252 | 0 | JSObject::deleteComputed( | 3253 | 0 | Handle<JSObject>::vmcast(&O2REG(DelByVal)), | 3254 | 0 | runtime, | 3255 | 0 | Handle<>(&O3REG(DelByVal)), | 3256 | 0 | defaultPropOpFlags)); | 3257 | 0 | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { | 3258 | 0 | goto exception; | 3259 | 0 | } | 3260 | 0 | O1REG(DelByVal) = HermesValue::encodeBoolValue(status.getValue()); | 3261 | 0 | } else { | 3262 | | // This is the "slow path". | 3263 | 0 | CAPTURE_IP(res = toObject(runtime, Handle<>(&O2REG(DelByVal)))); | 3264 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 3265 | 0 | goto exception; | 3266 | 0 | } | 3267 | 0 | tmpHandle = res.getValue(); | 3268 | 0 | CAPTURE_IP_ASSIGN( | 3269 | 0 | auto status, | 3270 | 0 | JSObject::deleteComputed( | 3271 | 0 | Handle<JSObject>::vmcast(tmpHandle), | 3272 | 0 | runtime, | 3273 | 0 | Handle<>(&O3REG(DelByVal)), | 3274 | 0 | defaultPropOpFlags)); | 3275 | 0 | if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { | 3276 | 0 | goto exception; | 3277 | 0 | } | 3278 | 0 | O1REG(DelByVal) = HermesValue::encodeBoolValue(status.getValue()); | 3279 | 0 | } | 3280 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3281 | 0 | tmpHandle.clear(); | 3282 | 0 | ip = NEXTINST(DelByVal); | 3283 | 0 | DISPATCH; | 3284 | 0 | } | 3285 | 3 | CASE(CreateRegExp) { | 3286 | 3 | { | 3287 | | // Create the RegExp object. | 3288 | 3 | CAPTURE_IP( | 3289 | 3 | O1REG(CreateRegExp) = JSRegExp::create(runtime).getHermesValue()); | 3290 | 3 | auto re = Handle<JSRegExp>::vmcast(&O1REG(CreateRegExp)); | 3291 | | // Initialize the regexp. | 3292 | 3 | CAPTURE_IP_ASSIGN( | 3293 | 3 | auto pattern, | 3294 | 3 | runtime.makeHandle(curCodeBlock->getRuntimeModule() | 3295 | 3 | ->getStringPrimFromStringIDMayAllocate( | 3296 | 3 | ip->iCreateRegExp.op2))); | 3297 | 3 | CAPTURE_IP_ASSIGN( | 3298 | 3 | auto flags, | 3299 | 3 | runtime.makeHandle(curCodeBlock->getRuntimeModule() | 3300 | 3 | ->getStringPrimFromStringIDMayAllocate( | 3301 | 3 | ip->iCreateRegExp.op3))); | 3302 | 3 | CAPTURE_IP_ASSIGN( | 3303 | 3 | auto bytecode, | 3304 | 3 | curCodeBlock->getRuntimeModule()->getRegExpBytecodeFromRegExpID( | 3305 | 3 | ip->iCreateRegExp.op4)); | 3306 | 3 | CAPTURE_IP( | 3307 | 3 | JSRegExp::initialize(re, runtime, pattern, flags, bytecode)); | 3308 | 3 | } | 3309 | 3 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3310 | 3 | ip = NEXTINST(CreateRegExp); | 3311 | 15 | DISPATCH; | 3312 | 15 | } | 3313 | | | 3314 | 0 | CASE(SwitchImm) { | 3315 | 0 | if (LLVM_LIKELY(O1REG(SwitchImm).isNumber())) { | 3316 | 0 | double numVal = O1REG(SwitchImm).getNumber(); | 3317 | 0 | uint32_t uintVal = (uint32_t)numVal; | 3318 | 0 | if (LLVM_LIKELY(numVal == uintVal) && // Only integers. | 3319 | 0 | LLVM_LIKELY(uintVal >= ip->iSwitchImm.op4) && // Bounds checking. | 3320 | 0 | LLVM_LIKELY(uintVal <= ip->iSwitchImm.op5)) // Bounds checking. | 3321 | 0 | { | 3322 | | // Calculate the offset into the bytecode where the jump table for | 3323 | | // this SwitchImm starts. | 3324 | 0 | const uint8_t *tablestart = (const uint8_t *)llvh::alignAddr( | 3325 | 0 | (const uint8_t *)ip + ip->iSwitchImm.op2, sizeof(uint32_t)); | 3326 | | | 3327 | | // Read the offset from the table. | 3328 | | // Must be signed to account for backwards branching. | 3329 | 0 | const int32_t *loc = | 3330 | 0 | (const int32_t *)tablestart + uintVal - ip->iSwitchImm.op4; | 3331 | |
| 3332 | 0 | ip = IPADD(*loc); | 3333 | 0 | DISPATCH; | 3334 | 0 | } | 3335 | 0 | } | 3336 | | // Wrong type or out of range, jump to default. | 3337 | 0 | ip = IPADD(ip->iSwitchImm.op3); | 3338 | 0 | DISPATCH; | 3339 | 0 | } | 3340 | 3.39M | LOAD_CONST( | 3341 | 3.39M | LoadConstUInt8, | 3342 | 3.39M | HermesValue::encodeTrustedNumberValue(ip->iLoadConstUInt8.op2)); | 3343 | 3.46M | LOAD_CONST( | 3344 | 3.46M | LoadConstInt, | 3345 | 3.46M | HermesValue::encodeTrustedNumberValue(ip->iLoadConstInt.op2)); | 3346 | 3.46M | LOAD_CONST( | 3347 | 101k | LoadConstDouble, | 3348 | 101k | HermesValue::encodeUntrustedNumberValue(ip->iLoadConstDouble.op2)); | 3349 | 2.49M | LOAD_CONST_CAPTURE_IP( | 3350 | 2.49M | LoadConstString, | 3351 | 2.49M | HermesValue::encodeStringValue( | 3352 | 2.49M | curCodeBlock->getRuntimeModule() | 3353 | 2.49M | ->getStringPrimFromStringIDMayAllocate( | 3354 | 2.49M | ip->iLoadConstString.op2))); | 3355 | 2.49M | LOAD_CONST_CAPTURE_IP( | 3356 | 0 | LoadConstStringLongIndex, | 3357 | 0 | HermesValue::encodeStringValue( | 3358 | 0 | curCodeBlock->getRuntimeModule() | 3359 | 0 | ->getStringPrimFromStringIDMayAllocate( | 3360 | 0 | ip->iLoadConstStringLongIndex.op2))); | 3361 | 0 | LOAD_CONST(LoadConstEmpty, HermesValue::encodeEmptyValue()); | 3362 | 526k | LOAD_CONST(LoadConstUndefined, HermesValue::encodeUndefinedValue()); | 3363 | 526k | LOAD_CONST(LoadConstNull, HermesValue::encodeNullValue()); | 3364 | 515k | LOAD_CONST(LoadConstTrue, HermesValue::encodeBoolValue(true)); | 3365 | 515k | LOAD_CONST(LoadConstFalse, HermesValue::encodeBoolValue(false)); | 3366 | 3.95M | LOAD_CONST(LoadConstZero, HermesValue::encodeTrustedNumberValue(0)); | 3367 | 3.95M | CASE(LoadConstBigInt) { | 3368 | 85.6k | idVal = ip->iLoadConstBigInt.op2; | 3369 | 85.6k | nextIP = NEXTINST(LoadConstBigInt); | 3370 | 85.6k | goto doLoadConstBigInt; | 3371 | 3.95M | } | 3372 | 0 | CASE(LoadConstBigIntLongIndex) { | 3373 | 0 | idVal = ip->iLoadConstBigIntLongIndex.op2; | 3374 | 0 | nextIP = NEXTINST(LoadConstBigIntLongIndex); | 3375 | 0 | goto doLoadConstBigInt; | 3376 | 3.95M | } | 3377 | 85.6k | doLoadConstBigInt: { | 3378 | 85.6k | CAPTURE_IP_ASSIGN( | 3379 | 85.6k | auto res, | 3380 | 85.6k | BigIntPrimitive::fromBytes( | 3381 | 85.6k | runtime, | 3382 | 85.6k | curCodeBlock->getRuntimeModule()->getBigIntBytesFromBigIntId( | 3383 | 85.6k | idVal))); | 3384 | 85.6k | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 3385 | 0 | goto exception; | 3386 | 0 | } | 3387 | | // It is safe to access O1REG(LoadConstBigInt) or | 3388 | | // O1REG(LoadConstBigIntLongIndex) here as both instructions' O1 operands | 3389 | | // are the same size and live in the same offset w.r.t. the start of the | 3390 | | // instruction. | 3391 | 85.6k | O1REG(LoadConstBigInt) = std::move(*res); | 3392 | 85.6k | ip = nextIP; | 3393 | 428k | DISPATCH; | 3394 | 428k | } | 3395 | 85.6k | BINOP(Sub); | 3396 | 963 | BINOP(Mul); | 3397 | 0 | BINOP(Div); | 3398 | | // Can't do BINOP(Mod) as there's no ModN opcode. | 3399 | 0 | BITWISEBINOP(BitAnd); | 3400 | 0 | BITWISEBINOP(BitOr); | 3401 | 0 | BITWISEBINOP(BitXor); | 3402 | 0 | SHIFTOP(LShift); | 3403 | 0 | SHIFTOP(RShift); | 3404 | 0 | SHIFTOP(URshift); | 3405 | 0 | CONDOP(Less, <, lessOp_RJS); | 3406 | 0 | CONDOP(LessEq, <=, lessEqualOp_RJS); | 3407 | 0 | CONDOP(Greater, >, greaterOp_RJS); | 3408 | 0 | CONDOP(GreaterEq, >=, greaterEqualOp_RJS); | 3409 | 0 | JCOND(Less, <, lessOp_RJS); | 3410 | 0 | JCOND(LessEqual, <=, lessEqualOp_RJS); | 3411 | 0 | JCOND(Greater, >, greaterOp_RJS); | 3412 | 0 | JCOND(GreaterEqual, >=, greaterEqualOp_RJS); | 3413 | |
| 3414 | 1.45k | JCOND_STRICT_EQ_IMPL( | 3415 | 1.45k | JStrictEqual, , IPADD(ip->iJStrictEqual.op1), NEXTINST(JStrictEqual)); | 3416 | 1.45k | JCOND_STRICT_EQ_IMPL( | 3417 | 0 | JStrictEqual, | 3418 | 0 | Long, | 3419 | 0 | IPADD(ip->iJStrictEqualLong.op1), | 3420 | 0 | NEXTINST(JStrictEqualLong)); | 3421 | 2.90k | JCOND_STRICT_EQ_IMPL( | 3422 | 2.90k | JStrictNotEqual, | 3423 | 2.90k | , | 3424 | 2.90k | NEXTINST(JStrictNotEqual), | 3425 | 2.90k | IPADD(ip->iJStrictNotEqual.op1)); | 3426 | 2.90k | JCOND_STRICT_EQ_IMPL( | 3427 | 0 | JStrictNotEqual, | 3428 | 0 | Long, | 3429 | 0 | NEXTINST(JStrictNotEqualLong), | 3430 | 0 | IPADD(ip->iJStrictNotEqualLong.op1)); | 3431 | |
| 3432 | 1.93k | JCOND_EQ_IMPL(JEqual, , IPADD(ip->iJEqual.op1), NEXTINST(JEqual)); | 3433 | 1.93k | JCOND_EQ_IMPL( | 3434 | 0 | JEqual, Long, IPADD(ip->iJEqualLong.op1), NEXTINST(JEqualLong)); | 3435 | 0 | JCOND_EQ_IMPL( | 3436 | 0 | JNotEqual, , NEXTINST(JNotEqual), IPADD(ip->iJNotEqual.op1)); | 3437 | 0 | JCOND_EQ_IMPL( | 3438 | 0 | JNotEqual, | 3439 | 0 | Long, | 3440 | 0 | NEXTINST(JNotEqualLong), | 3441 | 0 | IPADD(ip->iJNotEqualLong.op1)); | 3442 | |
| 3443 | 147 | CASE_OUTOFLINE(PutOwnByVal); | 3444 | 147 | CASE_OUTOFLINE(PutOwnGetterSetterByVal); | 3445 | 0 | CASE_OUTOFLINE(DirectEval); | 3446 | |
| 3447 | 0 | CASE_OUTOFLINE(IteratorBegin); | 3448 | 0 | CASE_OUTOFLINE(IteratorNext); | 3449 | 0 | CASE(IteratorClose) { | 3450 | 0 | if (LLVM_UNLIKELY(O1REG(IteratorClose).isObject())) { | 3451 | | // The iterator must be closed if it's still an object. | 3452 | | // That means it was never an index and is not done iterating (a state | 3453 | | // which is indicated by `undefined`). | 3454 | 0 | CAPTURE_IP_ASSIGN( | 3455 | 0 | auto res, | 3456 | 0 | iteratorClose( | 3457 | 0 | runtime, | 3458 | 0 | Handle<JSObject>::vmcast(&O1REG(IteratorClose)), | 3459 | 0 | Runtime::getEmptyValue())); | 3460 | 0 | if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { | 3461 | 0 | if (ip->iIteratorClose.op2 && | 3462 | 0 | !isUncatchableError(runtime.thrownValue_)) { | 3463 | | // Ignore inner exception. | 3464 | 0 | runtime.clearThrownValue(); | 3465 | 0 | } else { | 3466 | 0 | goto exception; | 3467 | 0 | } | 3468 | 0 | } | 3469 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3470 | 0 | } | 3471 | 0 | ip = NEXTINST(IteratorClose); | 3472 | 0 | DISPATCH; | 3473 | 0 | } | 3474 | | | 3475 | | #ifdef HERMES_RUN_WASM | 3476 | | // Asm.js/Wasm Intrinsics | 3477 | | CASE(Add32) { | 3478 | | O1REG(Add32) = HermesValue::encodeUntrustedNumberValue( | 3479 | | (int32_t)(int64_t)(O2REG(Add32).getNumber() + | 3480 | | O3REG(Add32).getNumber())); | 3481 | | ip = NEXTINST(Add32); | 3482 | | DISPATCH; | 3483 | | } | 3484 | | CASE(Sub32) { | 3485 | | O1REG(Sub32) = HermesValue::encodeUntrustedNumberValue( | 3486 | | (int32_t)(int64_t)(O2REG(Sub32).getNumber() - | 3487 | | O3REG(Sub32).getNumber())); | 3488 | | ip = NEXTINST(Sub32); | 3489 | | DISPATCH; | 3490 | | } | 3491 | | CASE(Mul32) { | 3492 | | // Signedness matters for multiplication, but low 32 bits are the same | 3493 | | // regardless of signedness. | 3494 | | const uint32_t arg0 = (uint32_t)(int32_t)(O2REG(Mul32).getNumber()); | 3495 | | const uint32_t arg1 = (uint32_t)(int32_t)(O3REG(Mul32).getNumber()); | 3496 | | O1REG(Mul32) = | 3497 | | HermesValue::encodeUntrustedNumberValue((int32_t)(arg0 * arg1)); | 3498 | | ip = NEXTINST(Mul32); | 3499 | | DISPATCH; | 3500 | | } | 3501 | | CASE(Divi32) { | 3502 | | const int32_t arg0 = (int32_t)(O2REG(Divi32).getNumber()); | 3503 | | const int32_t arg1 = (int32_t)(O3REG(Divi32).getNumber()); | 3504 | | O1REG(Divi32) = HermesValue::encodeUntrustedNumberValue(arg0 / arg1); | 3505 | | ip = NEXTINST(Divi32); | 3506 | | DISPATCH; | 3507 | | } | 3508 | | CASE(Divu32) { | 3509 | | const uint32_t arg0 = (uint32_t)(int32_t)(O2REG(Divu32).getNumber()); | 3510 | | const uint32_t arg1 = (uint32_t)(int32_t)(O3REG(Divu32).getNumber()); | 3511 | | O1REG(Divu32) = | 3512 | | HermesValue::encodeUntrustedNumberValue((int32_t)(arg0 / arg1)); | 3513 | | ip = NEXTINST(Divu32); | 3514 | | DISPATCH; | 3515 | | } | 3516 | | | 3517 | | CASE(Loadi8) { | 3518 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadi8)); | 3519 | | int8_t *basePtr = reinterpret_cast<int8_t *>(mem->begin(runtime)); | 3520 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadi8).getNumber()); | 3521 | | O1REG(Loadi8) = HermesValue::encodeUntrustedNumberValue(basePtr[addr]); | 3522 | | ip = NEXTINST(Loadi8); | 3523 | | DISPATCH; | 3524 | | } | 3525 | | CASE(Loadu8) { | 3526 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadu8)); | 3527 | | uint8_t *basePtr = reinterpret_cast<uint8_t *>(mem->begin(runtime)); | 3528 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadu8).getNumber()); | 3529 | | O1REG(Loadu8) = HermesValue::encodeUntrustedNumberValue(basePtr[addr]); | 3530 | | ip = NEXTINST(Loadu8); | 3531 | | DISPATCH; | 3532 | | } | 3533 | | CASE(Loadi16) { | 3534 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadi16)); | 3535 | | int16_t *basePtr = reinterpret_cast<int16_t *>(mem->begin(runtime)); | 3536 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadi16).getNumber()); | 3537 | | O1REG(Loadi16) = | 3538 | | HermesValue::encodeUntrustedNumberValue(basePtr[addr >> 1]); | 3539 | | ip = NEXTINST(Loadi16); | 3540 | | DISPATCH; | 3541 | | } | 3542 | | CASE(Loadu16) { | 3543 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadu16)); | 3544 | | uint16_t *basePtr = reinterpret_cast<uint16_t *>(mem->begin(runtime)); | 3545 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadu16).getNumber()); | 3546 | | O1REG(Loadu16) = | 3547 | | HermesValue::encodeUntrustedNumberValue(basePtr[addr >> 1]); | 3548 | | ip = NEXTINST(Loadu16); | 3549 | | DISPATCH; | 3550 | | } | 3551 | | CASE(Loadi32) { | 3552 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadi32)); | 3553 | | int32_t *basePtr = reinterpret_cast<int32_t *>(mem->begin(runtime)); | 3554 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadi32).getNumber()); | 3555 | | O1REG(Loadi32) = | 3556 | | HermesValue::encodeUntrustedNumberValue(basePtr[addr >> 2]); | 3557 | | ip = NEXTINST(Loadi32); | 3558 | | DISPATCH; | 3559 | | } | 3560 | | CASE(Loadu32) { | 3561 | | auto *mem = vmcast<JSTypedArrayBase>(O2REG(Loadu32)); | 3562 | | uint32_t *basePtr = reinterpret_cast<uint32_t *>(mem->begin(runtime)); | 3563 | | const uint32_t addr = (uint32_t)(int32_t)(O3REG(Loadu32).getNumber()); | 3564 | | O1REG(Loadu32) = HermesValue::encodeUntrustedNumberValue( | 3565 | | (int32_t)(basePtr[addr >> 2])); | 3566 | | ip = NEXTINST(Loadu32); | 3567 | | DISPATCH; | 3568 | | } | 3569 | | | 3570 | | CASE(Store8) { | 3571 | | auto *mem = vmcast<JSTypedArrayBase>(O1REG(Store8)); | 3572 | | int8_t *basePtr = reinterpret_cast<int8_t *>(mem->begin(runtime)); | 3573 | | const uint32_t addr = (uint32_t)(int32_t)(O2REG(Store8).getNumber()); | 3574 | | basePtr[addr] = (int8_t)(int32_t)(O3REG(Store8).getNumber()); | 3575 | | ip = NEXTINST(Store8); | 3576 | | DISPATCH; | 3577 | | } | 3578 | | CASE(Store16) { | 3579 | | auto *mem = vmcast<JSTypedArrayBase>(O1REG(Store16)); | 3580 | | int16_t *basePtr = reinterpret_cast<int16_t *>(mem->begin(runtime)); | 3581 | | const uint32_t addr = (uint32_t)(int32_t)(O2REG(Store16).getNumber()); | 3582 | | basePtr[addr >> 1] = (int16_t)(int32_t)(O3REG(Store16).getNumber()); | 3583 | | ip = NEXTINST(Store16); | 3584 | | DISPATCH; | 3585 | | } | 3586 | | CASE(Store32) { | 3587 | | auto *mem = vmcast<JSTypedArrayBase>(O1REG(Store32)); | 3588 | | int32_t *basePtr = reinterpret_cast<int32_t *>(mem->begin(runtime)); | 3589 | | const uint32_t addr = (uint32_t)(int32_t)(O2REG(Store32).getNumber()); | 3590 | | basePtr[addr >> 2] = (int32_t)(O3REG(Store32).getNumber()); | 3591 | | // A nop for now. | 3592 | | ip = NEXTINST(Store32); | 3593 | | DISPATCH; | 3594 | | } | 3595 | | #endif | 3596 | | | 3597 | 0 | CASE(_last) { | 3598 | 0 | hermes_fatal("Invalid opcode _last"); | 3599 | 0 | } | 3600 | 0 | } | 3601 | | | 3602 | 0 | hermes_fatal( | 3603 | 0 | "All opcodes should dispatch to the next and not fallthrough " | 3604 | 0 | "to here"); | 3605 | | | 3606 | | // We arrive here if we couldn't allocate the registers for the current frame. | 3607 | 0 | stackOverflow: | 3608 | 0 | CAPTURE_IP(runtime.raiseStackOverflow( | 3609 | 0 | Runtime::StackOverflowKind::JSRegisterStack)); | 3610 | | | 3611 | | // We arrive here when we raised an exception in a callee, but we don't want | 3612 | | // the callee to be able to handle it. | 3613 | 0 | handleExceptionInParent: | 3614 | | // Restore the caller code block and IP. | 3615 | 0 | curCodeBlock = FRAME.getSavedCodeBlock(); | 3616 | 0 | ip = FRAME.getSavedIP(); | 3617 | | | 3618 | | // Pop to the previous frame where technically the error happened. | 3619 | 0 | frameRegs = &runtime.restoreStackAndPreviousFrame(FRAME).getFirstLocalRef(); | 3620 | | | 3621 | | // If we are coming from native code, return. | 3622 | 0 | if (!curCodeBlock) | 3623 | 0 | return ExecutionStatus::EXCEPTION; | 3624 | | | 3625 | | // Handle the exception. | 3626 | 29 | exception: | 3627 | 29 | UPDATE_OPCODE_TIME_SPENT; | 3628 | 29 | assert( | 3629 | 29 | !runtime.thrownValue_.isEmpty() && | 3630 | 29 | "thrownValue unavailable at exception"); | 3631 | | | 3632 | 29 | bool catchable = true; | 3633 | | // If this is an Error object that was thrown internally, it didn't have | 3634 | | // access to the current codeblock and IP, so collect the stack trace here. | 3635 | 29 | if (auto *jsError = dyn_vmcast<JSError>(runtime.thrownValue_)) { | 3636 | 29 | catchable = jsError->catchable(); | 3637 | 29 | if (!jsError->getStackTrace()) { | 3638 | | // Temporarily clear the thrown value for following operations. | 3639 | 27 | CAPTURE_IP_ASSIGN( | 3640 | 27 | auto errorHandle, | 3641 | 27 | runtime.makeHandle(vmcast<JSError>(runtime.thrownValue_))); | 3642 | 27 | runtime.clearThrownValue(); | 3643 | | | 3644 | 27 | CAPTURE_IP(JSError::recordStackTrace( | 3645 | 27 | errorHandle, runtime, false, curCodeBlock, ip)); | 3646 | | | 3647 | | // Restore the thrown value. | 3648 | 27 | runtime.setThrownValue(errorHandle.getHermesValue()); | 3649 | 27 | } | 3650 | 29 | } | 3651 | | | 3652 | 29 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3653 | 29 | tmpHandle.clear(); | 3654 | | | 3655 | 29 | #ifdef HERMES_ENABLE_DEBUGGER | 3656 | 29 | if (SingleStep) { | 3657 | | // If we're single stepping, don't bother with any more checks, | 3658 | | // and simply signal that we should continue execution with an exception. | 3659 | 0 | state.codeBlock = curCodeBlock; | 3660 | 0 | state.offset = CUROFFSET; | 3661 | 0 | return ExecutionStatus::EXCEPTION; | 3662 | 0 | } | 3663 | | | 3664 | 29 | using PauseOnThrowMode = facebook::hermes::debugger::PauseOnThrowMode; | 3665 | 29 | auto mode = runtime.debugger_.getPauseOnThrowMode(); | 3666 | 29 | if (mode != PauseOnThrowMode::None) { | 3667 | 0 | if (!runtime.debugger_.isDebugging()) { | 3668 | | // Determine whether the PauseOnThrowMode requires us to stop here. | 3669 | 0 | bool caught = | 3670 | 0 | runtime.debugger_ | 3671 | 0 | .findCatchTarget(InterpreterState(curCodeBlock, CUROFFSET)) | 3672 | 0 | .hasValue(); | 3673 | 0 | bool shouldStop = mode == PauseOnThrowMode::All || | 3674 | 0 | (mode == PauseOnThrowMode::Uncaught && !caught); | 3675 | 0 | if (shouldStop) { | 3676 | | // When runDebugger is invoked after an exception, | 3677 | | // stepping should never happen internally. | 3678 | | // Any step is a step to an exception handler, which we do | 3679 | | // directly here in the interpreter. | 3680 | | // Thus, the result state should be the same as the input state. | 3681 | 0 | InterpreterState tmpState{curCodeBlock, (uint32_t)CUROFFSET}; | 3682 | 0 | CAPTURE_IP_ASSIGN( | 3683 | 0 | ExecutionStatus resultStatus, | 3684 | 0 | runtime.debugger_.runDebugger( | 3685 | 0 | Debugger::RunReason::Exception, tmpState)); | 3686 | 0 | (void)resultStatus; | 3687 | 0 | assert( | 3688 | 0 | tmpState == InterpreterState(curCodeBlock, CUROFFSET) && | 3689 | 0 | "not allowed to step internally in a pauseOnThrow"); | 3690 | 0 | gcScope.flushToSmallCount(KEEP_HANDLES); | 3691 | 0 | } | 3692 | 0 | } | 3693 | 0 | } | 3694 | 29 | #endif | 3695 | | | 3696 | 29 | int32_t handlerOffset = 0; | 3697 | | | 3698 | | // If the exception is not catchable, skip found catch blocks. | 3699 | 29 | while (((handlerOffset = curCodeBlock->findCatchTargetOffset(CUROFFSET)) == | 3700 | 29 | -1) || | 3701 | 29 | !catchable) { | 3702 | 29 | PROFILER_EXIT_FUNCTION(curCodeBlock); | 3703 | | | 3704 | 29 | #ifdef HERMES_MEMORY_INSTRUMENTATION | 3705 | 29 | runtime.popCallStack(); | 3706 | 29 | #endif | 3707 | | | 3708 | | // Restore the code block and IP. | 3709 | 29 | curCodeBlock = FRAME.getSavedCodeBlock(); | 3710 | 29 | ip = FRAME.getSavedIP(); | 3711 | | | 3712 | | // Pop a stack frame. | 3713 | 29 | frameRegs = | 3714 | 29 | &runtime.restoreStackAndPreviousFrame(FRAME).getFirstLocalRef(); | 3715 | | | 3716 | 29 | SLOW_DEBUG( | 3717 | 29 | dbgs() << "function exit with exception: restored stackLevel=" | 3718 | 29 | << runtime.getStackLevel() << "\n"); | 3719 | | | 3720 | | // Are we returning to native code? | 3721 | 29 | if (!curCodeBlock) { | 3722 | 29 | SLOW_DEBUG( | 3723 | 29 | dbgs() | 3724 | 29 | << "function exit with exception: returning to native code\n"); | 3725 | 29 | return ExecutionStatus::EXCEPTION; | 3726 | 29 | } | 3727 | | | 3728 | 0 | assert( | 3729 | 0 | isCallType(ip->opCode) && | 3730 | 0 | "return address is not Call-type instruction"); | 3731 | 0 | } | 3732 | | | 3733 | 0 | INIT_STATE_FOR_CODEBLOCK(curCodeBlock); | 3734 | |
| 3735 | 0 | ip = IPADD(handlerOffset - CUROFFSET); | 3736 | 0 | } | 3737 | 2.03k | } |
Unexecuted instantiation: hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::Interpreter::interpretFunction<true, false>(hermes::vm::Runtime&, hermes::vm::InterpreterState&) Unexecuted instantiation: hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::Interpreter::interpretFunction<false, true>(hermes::vm::Runtime&, hermes::vm::InterpreterState&) Unexecuted instantiation: hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::Interpreter::interpretFunction<true, true>(hermes::vm::Runtime&, hermes::vm::InterpreterState&) |
3738 | | |
3739 | | } // namespace vm |
3740 | | } // namespace hermes |
3741 | | |
3742 | | #undef DEBUG_TYPE |