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