1 | /* |
2 | * Copyright (C) 2011-2019 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "CommonSlowPaths.h" |
28 | |
29 | #include "ArithProfile.h" |
30 | #include "ArrayConstructor.h" |
31 | #include "BuiltinNames.h" |
32 | #include "BytecodeStructs.h" |
33 | #include "CallFrame.h" |
34 | #include "ClonedArguments.h" |
35 | #include "CodeProfiling.h" |
36 | #include "DefinePropertyAttributes.h" |
37 | #include "DirectArguments.h" |
38 | #include "Error.h" |
39 | #include "ErrorHandlingScope.h" |
40 | #include "ExceptionFuzz.h" |
41 | #include "FrameTracers.h" |
42 | #include "GetterSetter.h" |
43 | #include "HostCallReturnValue.h" |
44 | #include "ICStats.h" |
45 | #include "Interpreter.h" |
46 | #include "IteratorOperations.h" |
47 | #include "JIT.h" |
48 | #include "JSArrayInlines.h" |
49 | #include "JSCInlines.h" |
50 | #include "JSCJSValue.h" |
51 | #include "JSFixedArray.h" |
52 | #include "JSGlobalObjectFunctions.h" |
53 | #include "JSImmutableButterfly.h" |
54 | #include "JSLexicalEnvironment.h" |
55 | #include "JSPropertyNameEnumerator.h" |
56 | #include "JSString.h" |
57 | #include "JSWithScope.h" |
58 | #include "LLIntCommon.h" |
59 | #include "LLIntExceptions.h" |
60 | #include "LowLevelInterpreter.h" |
61 | #include "MathCommon.h" |
62 | #include "ObjectConstructor.h" |
63 | #include "OpcodeInlines.h" |
64 | #include "ScopedArguments.h" |
65 | #include "StructureRareDataInlines.h" |
66 | #include "ThunkGenerators.h" |
67 | #include "TypeProfilerLog.h" |
68 | #include <wtf/StringPrintStream.h> |
69 | #include <wtf/Variant.h> |
70 | |
71 | namespace JSC { |
72 | |
73 | #define BEGIN_NO_SET_PC() \ |
74 | VM& vm = exec->vm(); \ |
75 | NativeCallFrameTracer tracer(&vm, exec); \ |
76 | auto throwScope = DECLARE_THROW_SCOPE(vm); \ |
77 | UNUSED_PARAM(throwScope) |
78 | |
79 | #ifndef NDEBUG |
80 | #define SET_PC_FOR_STUBS() do { \ |
81 | exec->codeBlock()->bytecodeOffset(pc); \ |
82 | exec->setCurrentVPC(pc); \ |
83 | } while (false) |
84 | #else |
85 | #define SET_PC_FOR_STUBS() do { \ |
86 | exec->setCurrentVPC(pc); \ |
87 | } while (false) |
88 | #endif |
89 | |
90 | #define RETURN_TO_THROW(exec, pc) pc = LLInt::returnToThrow(exec) |
91 | |
92 | #define BEGIN() \ |
93 | BEGIN_NO_SET_PC(); \ |
94 | SET_PC_FOR_STUBS() |
95 | |
96 | #define GET(operand) (exec->uncheckedR(operand.offset())) |
97 | #define GET_C(operand) (exec->r(operand.offset())) |
98 | |
99 | #define RETURN_TWO(first, second) do { \ |
100 | return encodeResult(first, second); \ |
101 | } while (false) |
102 | |
103 | #define END_IMPL() RETURN_TWO(pc, exec) |
104 | |
105 | #define THROW(exceptionToThrow) do { \ |
106 | throwException(exec, throwScope, exceptionToThrow); \ |
107 | RETURN_TO_THROW(exec, pc); \ |
108 | END_IMPL(); \ |
109 | } while (false) |
110 | |
111 | #define CHECK_EXCEPTION() do { \ |
112 | doExceptionFuzzingIfEnabled(exec, throwScope, "CommonSlowPaths", pc); \ |
113 | if (UNLIKELY(throwScope.exception())) { \ |
114 | RETURN_TO_THROW(exec, pc); \ |
115 | END_IMPL(); \ |
116 | } \ |
117 | } while (false) |
118 | |
119 | #define END() do { \ |
120 | CHECK_EXCEPTION(); \ |
121 | END_IMPL(); \ |
122 | } while (false) |
123 | |
124 | #define BRANCH(condition) do { \ |
125 | bool bCondition = (condition); \ |
126 | CHECK_EXCEPTION(); \ |
127 | if (bCondition) \ |
128 | pc = bytecode.m_targetLabel \ |
129 | ? reinterpret_cast<const Instruction*>(reinterpret_cast<const uint8_t*>(pc) + bytecode.m_targetLabel) \ |
130 | : exec->codeBlock()->outOfLineJumpTarget(pc); \ |
131 | else \ |
132 | pc = reinterpret_cast<const Instruction*>(reinterpret_cast<const uint8_t*>(pc) + pc->size()); \ |
133 | END_IMPL(); \ |
134 | } while (false) |
135 | |
136 | #define RETURN_WITH_PROFILING_CUSTOM(result__, value__, profilingAction__) do { \ |
137 | JSValue returnValue__ = (value__); \ |
138 | CHECK_EXCEPTION(); \ |
139 | GET(result__) = returnValue__; \ |
140 | profilingAction__; \ |
141 | END_IMPL(); \ |
142 | } while (false) |
143 | |
144 | #define RETURN_WITH_PROFILING(value__, profilingAction__) RETURN_WITH_PROFILING_CUSTOM(bytecode.m_dst, value__, profilingAction__) |
145 | |
146 | #define RETURN(value) \ |
147 | RETURN_WITH_PROFILING(value, { }) |
148 | |
149 | #define RETURN_PROFILED(value__) \ |
150 | RETURN_WITH_PROFILING(value__, PROFILE_VALUE(returnValue__)) |
151 | |
152 | #define PROFILE_VALUE(value) do { \ |
153 | bytecode.metadata(exec).m_profile.m_buckets[0] = JSValue::encode(value); \ |
154 | } while (false) |
155 | |
156 | #define CALL_END_IMPL(exec, callTarget, callTargetTag) \ |
157 | RETURN_TWO(retagCodePtr((callTarget), callTargetTag, SlowPathPtrTag), (exec)) |
158 | |
159 | #define CALL_CHECK_EXCEPTION(exec, pc) do { \ |
160 | ExecState* cceExec = (exec); \ |
161 | Instruction* ccePC = (pc); \ |
162 | if (UNLIKELY(throwScope.exception())) \ |
163 | CALL_END_IMPL(cceExec, LLInt::callToThrow(cceExec), ExceptionHandlerPtrTag); \ |
164 | } while (false) |
165 | |
166 | static void throwArityCheckStackOverflowError(ExecState* exec, ThrowScope& scope) |
167 | { |
168 | JSObject* error = createStackOverflowError(exec); |
169 | throwException(exec, scope, error); |
170 | #if LLINT_TRACING |
171 | if (UNLIKELY(Options::traceLLIntSlowPath())) |
172 | dataLog("Throwing exception " , JSValue(scope.exception()), ".\n" ); |
173 | #endif |
174 | } |
175 | |
176 | SLOW_PATH_DECL(slow_path_call_arityCheck) |
177 | { |
178 | BEGIN(); |
179 | int slotsToAdd = CommonSlowPaths::arityCheckFor(exec, vm, CodeForCall); |
180 | if (UNLIKELY(slotsToAdd < 0)) { |
181 | CodeBlock* codeBlock = CommonSlowPaths::codeBlockFromCallFrameCallee(exec, CodeForCall); |
182 | exec->convertToStackOverflowFrame(vm, codeBlock); |
183 | NativeCallFrameTracer tracer(&vm, exec); |
184 | ErrorHandlingScope errorScope(vm); |
185 | throwScope.release(); |
186 | throwArityCheckStackOverflowError(exec, throwScope); |
187 | RETURN_TWO(bitwise_cast<void*>(static_cast<uintptr_t>(1)), exec); |
188 | } |
189 | RETURN_TWO(0, bitwise_cast<void*>(static_cast<uintptr_t>(slotsToAdd))); |
190 | } |
191 | |
192 | SLOW_PATH_DECL(slow_path_construct_arityCheck) |
193 | { |
194 | BEGIN(); |
195 | int slotsToAdd = CommonSlowPaths::arityCheckFor(exec, vm, CodeForConstruct); |
196 | if (UNLIKELY(slotsToAdd < 0)) { |
197 | CodeBlock* codeBlock = CommonSlowPaths::codeBlockFromCallFrameCallee(exec, CodeForConstruct); |
198 | exec->convertToStackOverflowFrame(vm, codeBlock); |
199 | NativeCallFrameTracer tracer(&vm, exec); |
200 | ErrorHandlingScope errorScope(vm); |
201 | throwArityCheckStackOverflowError(exec, throwScope); |
202 | RETURN_TWO(bitwise_cast<void*>(static_cast<uintptr_t>(1)), exec); |
203 | } |
204 | RETURN_TWO(0, bitwise_cast<void*>(static_cast<uintptr_t>(slotsToAdd))); |
205 | } |
206 | |
207 | SLOW_PATH_DECL(slow_path_create_direct_arguments) |
208 | { |
209 | BEGIN(); |
210 | auto bytecode = pc->as<OpCreateDirectArguments>(); |
211 | RETURN(DirectArguments::createByCopying(exec)); |
212 | } |
213 | |
214 | SLOW_PATH_DECL(slow_path_create_scoped_arguments) |
215 | { |
216 | BEGIN(); |
217 | auto bytecode = pc->as<OpCreateScopedArguments>(); |
218 | JSLexicalEnvironment* scope = jsCast<JSLexicalEnvironment*>(GET(bytecode.m_scope).jsValue()); |
219 | ScopedArgumentsTable* table = scope->symbolTable()->arguments(); |
220 | RETURN(ScopedArguments::createByCopying(exec, table, scope)); |
221 | } |
222 | |
223 | SLOW_PATH_DECL(slow_path_create_cloned_arguments) |
224 | { |
225 | BEGIN(); |
226 | auto bytecode = pc->as<OpCreateClonedArguments>(); |
227 | RETURN(ClonedArguments::createWithMachineFrame(exec, exec, ArgumentsMode::Cloned)); |
228 | } |
229 | |
230 | SLOW_PATH_DECL(slow_path_create_this) |
231 | { |
232 | BEGIN(); |
233 | auto bytecode = pc->as<OpCreateThis>(); |
234 | JSObject* result; |
235 | JSObject* constructorAsObject = asObject(GET(bytecode.m_callee).jsValue()); |
236 | if (constructorAsObject->type() == JSFunctionType && jsCast<JSFunction*>(constructorAsObject)->canUseAllocationProfile()) { |
237 | JSFunction* constructor = jsCast<JSFunction*>(constructorAsObject); |
238 | WriteBarrier<JSCell>& cachedCallee = bytecode.metadata(exec).m_cachedCallee; |
239 | if (!cachedCallee) |
240 | cachedCallee.set(vm, exec->codeBlock(), constructor); |
241 | else if (cachedCallee.unvalidatedGet() != JSCell::seenMultipleCalleeObjects() && cachedCallee.get() != constructor) |
242 | cachedCallee.setWithoutWriteBarrier(JSCell::seenMultipleCalleeObjects()); |
243 | |
244 | size_t inlineCapacity = bytecode.m_inlineCapacity; |
245 | ObjectAllocationProfileWithPrototype* allocationProfile = constructor->ensureRareDataAndAllocationProfile(exec, inlineCapacity)->objectAllocationProfile(); |
246 | throwScope.releaseAssertNoException(); |
247 | Structure* structure = allocationProfile->structure(); |
248 | result = constructEmptyObject(exec, structure); |
249 | if (structure->hasPolyProto()) { |
250 | JSObject* prototype = allocationProfile->prototype(); |
251 | ASSERT(prototype == constructor->prototypeForConstruction(vm, exec)); |
252 | result->putDirect(vm, knownPolyProtoOffset, prototype); |
253 | prototype->didBecomePrototype(); |
254 | ASSERT_WITH_MESSAGE(!hasIndexedProperties(result->indexingType()), "We rely on JSFinalObject not starting out with an indexing type otherwise we would potentially need to convert to slow put storage" ); |
255 | } |
256 | } else { |
257 | // http://ecma-international.org/ecma-262/6.0/#sec-ordinarycreatefromconstructor |
258 | JSValue proto = constructorAsObject->get(exec, vm.propertyNames->prototype); |
259 | CHECK_EXCEPTION(); |
260 | if (proto.isObject()) |
261 | result = constructEmptyObject(exec, asObject(proto)); |
262 | else |
263 | result = constructEmptyObject(exec); |
264 | } |
265 | RETURN(result); |
266 | } |
267 | |
268 | SLOW_PATH_DECL(slow_path_to_this) |
269 | { |
270 | BEGIN(); |
271 | auto bytecode = pc->as<OpToThis>(); |
272 | auto& metadata = bytecode.metadata(exec); |
273 | JSValue v1 = GET(bytecode.m_srcDst).jsValue(); |
274 | if (v1.isCell()) { |
275 | StructureID myStructureID = v1.asCell()->structureID(); |
276 | StructureID otherStructureID = metadata.m_cachedStructureID; |
277 | if (myStructureID != otherStructureID) { |
278 | if (otherStructureID) |
279 | metadata.m_toThisStatus = ToThisConflicted; |
280 | metadata.m_cachedStructureID = myStructureID; |
281 | vm.heap.writeBarrier(exec->codeBlock(), vm.getStructure(myStructureID)); |
282 | } |
283 | } else { |
284 | metadata.m_toThisStatus = ToThisConflicted; |
285 | metadata.m_cachedStructureID = 0; |
286 | } |
287 | // Note: We only need to do this value profiling here on the slow path. The fast path |
288 | // just returns the input to to_this if the structure check succeeds. If the structure |
289 | // check succeeds, doing value profiling here is equivalent to doing it with a potentially |
290 | // different object that still has the same structure on the fast path since it'll produce |
291 | // the same SpeculatedType. Therefore, we don't need to worry about value profiling on the |
292 | // fast path. |
293 | auto value = v1.toThis(exec, exec->codeBlock()->isStrictMode() ? StrictMode : NotStrictMode); |
294 | RETURN_WITH_PROFILING_CUSTOM(bytecode.m_srcDst, value, PROFILE_VALUE(value)); |
295 | } |
296 | |
297 | SLOW_PATH_DECL(slow_path_throw_tdz_error) |
298 | { |
299 | BEGIN(); |
300 | THROW(createTDZError(exec)); |
301 | } |
302 | |
303 | SLOW_PATH_DECL(slow_path_check_tdz) |
304 | { |
305 | BEGIN(); |
306 | THROW(createTDZError(exec)); |
307 | } |
308 | |
309 | SLOW_PATH_DECL(slow_path_throw_strict_mode_readonly_property_write_error) |
310 | { |
311 | BEGIN(); |
312 | THROW(createTypeError(exec, ReadonlyPropertyWriteError)); |
313 | } |
314 | |
315 | SLOW_PATH_DECL(slow_path_not) |
316 | { |
317 | BEGIN(); |
318 | auto bytecode = pc->as<OpNot>(); |
319 | RETURN(jsBoolean(!GET_C(bytecode.m_operand).jsValue().toBoolean(exec))); |
320 | } |
321 | |
322 | SLOW_PATH_DECL(slow_path_eq) |
323 | { |
324 | BEGIN(); |
325 | auto bytecode = pc->as<OpEq>(); |
326 | RETURN(jsBoolean(JSValue::equal(exec, GET_C(bytecode.m_lhs).jsValue(), GET_C(bytecode.m_rhs).jsValue()))); |
327 | } |
328 | |
329 | SLOW_PATH_DECL(slow_path_neq) |
330 | { |
331 | BEGIN(); |
332 | auto bytecode = pc->as<OpNeq>(); |
333 | RETURN(jsBoolean(!JSValue::equal(exec, GET_C(bytecode.m_lhs).jsValue(), GET_C(bytecode.m_rhs).jsValue()))); |
334 | } |
335 | |
336 | SLOW_PATH_DECL(slow_path_stricteq) |
337 | { |
338 | BEGIN(); |
339 | auto bytecode = pc->as<OpStricteq>(); |
340 | RETURN(jsBoolean(JSValue::strictEqual(exec, GET_C(bytecode.m_lhs).jsValue(), GET_C(bytecode.m_rhs).jsValue()))); |
341 | } |
342 | |
343 | SLOW_PATH_DECL(slow_path_nstricteq) |
344 | { |
345 | BEGIN(); |
346 | auto bytecode = pc->as<OpNstricteq>(); |
347 | RETURN(jsBoolean(!JSValue::strictEqual(exec, GET_C(bytecode.m_lhs).jsValue(), GET_C(bytecode.m_rhs).jsValue()))); |
348 | } |
349 | |
350 | SLOW_PATH_DECL(slow_path_less) |
351 | { |
352 | BEGIN(); |
353 | auto bytecode = pc->as<OpLess>(); |
354 | RETURN(jsBoolean(jsLess<true>(exec, GET_C(bytecode.m_lhs).jsValue(), GET_C(bytecode.m_rhs).jsValue()))); |
355 | } |
356 | |
357 | SLOW_PATH_DECL(slow_path_lesseq) |
358 | { |
359 | BEGIN(); |
360 | auto bytecode = pc->as<OpLesseq>(); |
361 | RETURN(jsBoolean(jsLessEq<true>(exec, GET_C(bytecode.m_lhs).jsValue(), GET_C(bytecode.m_rhs).jsValue()))); |
362 | } |
363 | |
364 | SLOW_PATH_DECL(slow_path_greater) |
365 | { |
366 | BEGIN(); |
367 | auto bytecode = pc->as<OpGreater>(); |
368 | RETURN(jsBoolean(jsLess<false>(exec, GET_C(bytecode.m_rhs).jsValue(), GET_C(bytecode.m_lhs).jsValue()))); |
369 | } |
370 | |
371 | SLOW_PATH_DECL(slow_path_greatereq) |
372 | { |
373 | BEGIN(); |
374 | auto bytecode = pc->as<OpGreatereq>(); |
375 | RETURN(jsBoolean(jsLessEq<false>(exec, GET_C(bytecode.m_rhs).jsValue(), GET_C(bytecode.m_lhs).jsValue()))); |
376 | } |
377 | |
378 | SLOW_PATH_DECL(slow_path_inc) |
379 | { |
380 | BEGIN(); |
381 | auto bytecode = pc->as<OpInc>(); |
382 | RETURN_WITH_PROFILING_CUSTOM(bytecode.m_srcDst, jsNumber(GET(bytecode.m_srcDst).jsValue().toNumber(exec) + 1), { }); |
383 | } |
384 | |
385 | SLOW_PATH_DECL(slow_path_dec) |
386 | { |
387 | BEGIN(); |
388 | auto bytecode = pc->as<OpDec>(); |
389 | RETURN_WITH_PROFILING_CUSTOM(bytecode.m_srcDst, jsNumber(GET(bytecode.m_srcDst).jsValue().toNumber(exec) - 1), { }); |
390 | } |
391 | |
392 | SLOW_PATH_DECL(slow_path_to_string) |
393 | { |
394 | BEGIN(); |
395 | auto bytecode = pc->as<OpToString>(); |
396 | RETURN(GET_C(bytecode.m_operand).jsValue().toString(exec)); |
397 | } |
398 | |
399 | #if ENABLE(JIT) |
400 | static void updateArithProfileForUnaryArithOp(OpNegate::Metadata& metadata, JSValue result, JSValue operand) |
401 | { |
402 | ArithProfile& profile = metadata.m_arithProfile; |
403 | profile.observeLHS(operand); |
404 | ASSERT(result.isNumber() || result.isBigInt()); |
405 | if (result.isNumber()) { |
406 | if (!result.isInt32()) { |
407 | if (operand.isInt32()) |
408 | profile.setObservedInt32Overflow(); |
409 | |
410 | double doubleVal = result.asNumber(); |
411 | if (!doubleVal && std::signbit(doubleVal)) |
412 | profile.setObservedNegZeroDouble(); |
413 | else { |
414 | profile.setObservedNonNegZeroDouble(); |
415 | |
416 | // The Int52 overflow check here intentionally omits 1ll << 51 as a valid negative Int52 value. |
417 | // Therefore, we will get a false positive if the result is that value. This is intentionally |
418 | // done to simplify the checking algorithm. |
419 | static const int64_t int52OverflowPoint = (1ll << 51); |
420 | int64_t int64Val = static_cast<int64_t>(std::abs(doubleVal)); |
421 | if (int64Val >= int52OverflowPoint) |
422 | profile.setObservedInt52Overflow(); |
423 | } |
424 | } |
425 | } else if (result.isBigInt()) |
426 | profile.setObservedBigInt(); |
427 | else |
428 | profile.setObservedNonNumeric(); |
429 | } |
430 | #else |
431 | static void updateArithProfileForUnaryArithOp(OpNegate::Metadata&, JSValue, JSValue) { } |
432 | #endif |
433 | |
434 | SLOW_PATH_DECL(slow_path_negate) |
435 | { |
436 | BEGIN(); |
437 | auto bytecode = pc->as<OpNegate>(); |
438 | auto& metadata = bytecode.metadata(exec); |
439 | JSValue operand = GET_C(bytecode.m_operand).jsValue(); |
440 | JSValue primValue = operand.toPrimitive(exec, PreferNumber); |
441 | CHECK_EXCEPTION(); |
442 | |
443 | if (primValue.isBigInt()) { |
444 | JSBigInt* result = JSBigInt::unaryMinus(vm, asBigInt(primValue)); |
445 | RETURN_WITH_PROFILING(result, { |
446 | updateArithProfileForUnaryArithOp(metadata, result, operand); |
447 | }); |
448 | } |
449 | |
450 | JSValue result = jsNumber(-primValue.toNumber(exec)); |
451 | CHECK_EXCEPTION(); |
452 | RETURN_WITH_PROFILING(result, { |
453 | updateArithProfileForUnaryArithOp(metadata, result, operand); |
454 | }); |
455 | } |
456 | |
457 | #if ENABLE(DFG_JIT) |
458 | static void updateArithProfileForBinaryArithOp(ExecState* exec, const Instruction* pc, JSValue result, JSValue left, JSValue right) |
459 | { |
460 | CodeBlock* codeBlock = exec->codeBlock(); |
461 | ArithProfile& profile = *codeBlock->arithProfileForPC(pc); |
462 | |
463 | if (result.isNumber()) { |
464 | if (!result.isInt32()) { |
465 | if (left.isInt32() && right.isInt32()) |
466 | profile.setObservedInt32Overflow(); |
467 | |
468 | double doubleVal = result.asNumber(); |
469 | if (!doubleVal && std::signbit(doubleVal)) |
470 | profile.setObservedNegZeroDouble(); |
471 | else { |
472 | profile.setObservedNonNegZeroDouble(); |
473 | |
474 | // The Int52 overflow check here intentionally omits 1ll << 51 as a valid negative Int52 value. |
475 | // Therefore, we will get a false positive if the result is that value. This is intentionally |
476 | // done to simplify the checking algorithm. |
477 | static const int64_t int52OverflowPoint = (1ll << 51); |
478 | int64_t int64Val = static_cast<int64_t>(std::abs(doubleVal)); |
479 | if (int64Val >= int52OverflowPoint) |
480 | profile.setObservedInt52Overflow(); |
481 | } |
482 | } |
483 | } else if (result.isBigInt()) |
484 | profile.setObservedBigInt(); |
485 | else |
486 | profile.setObservedNonNumeric(); |
487 | } |
488 | #else |
489 | static void updateArithProfileForBinaryArithOp(ExecState*, const Instruction*, JSValue, JSValue, JSValue) { } |
490 | #endif |
491 | |
492 | SLOW_PATH_DECL(slow_path_to_number) |
493 | { |
494 | BEGIN(); |
495 | auto bytecode = pc->as<OpToNumber>(); |
496 | JSValue argument = GET_C(bytecode.m_operand).jsValue(); |
497 | JSValue result = jsNumber(argument.toNumber(exec)); |
498 | RETURN_PROFILED(result); |
499 | } |
500 | |
501 | SLOW_PATH_DECL(slow_path_to_object) |
502 | { |
503 | BEGIN(); |
504 | auto bytecode = pc->as<OpToObject>(); |
505 | JSValue argument = GET_C(bytecode.m_operand).jsValue(); |
506 | if (UNLIKELY(argument.isUndefinedOrNull())) { |
507 | const Identifier& ident = exec->codeBlock()->identifier(bytecode.m_message); |
508 | if (!ident.isEmpty()) |
509 | THROW(createTypeError(exec, ident.impl())); |
510 | } |
511 | JSObject* result = argument.toObject(exec); |
512 | RETURN_PROFILED(result); |
513 | } |
514 | |
515 | SLOW_PATH_DECL(slow_path_add) |
516 | { |
517 | BEGIN(); |
518 | auto bytecode = pc->as<OpAdd>(); |
519 | JSValue v1 = GET_C(bytecode.m_lhs).jsValue(); |
520 | JSValue v2 = GET_C(bytecode.m_rhs).jsValue(); |
521 | |
522 | ArithProfile& arithProfile = *exec->codeBlock()->arithProfileForPC(pc); |
523 | arithProfile.observeLHSAndRHS(v1, v2); |
524 | |
525 | JSValue result = jsAdd(exec, v1, v2); |
526 | |
527 | RETURN_WITH_PROFILING(result, { |
528 | updateArithProfileForBinaryArithOp(exec, pc, result, v1, v2); |
529 | }); |
530 | } |
531 | |
532 | // The following arithmetic and bitwise operations need to be sure to run |
533 | // toNumber() on their operands in order. (A call to toNumber() is idempotent |
534 | // if an exception is already set on the ExecState.) |
535 | |
536 | SLOW_PATH_DECL(slow_path_mul) |
537 | { |
538 | BEGIN(); |
539 | auto bytecode = pc->as<OpMul>(); |
540 | JSValue left = GET_C(bytecode.m_lhs).jsValue(); |
541 | JSValue right = GET_C(bytecode.m_rhs).jsValue(); |
542 | JSValue result = jsMul(exec, left, right); |
543 | CHECK_EXCEPTION(); |
544 | RETURN_WITH_PROFILING(result, { |
545 | updateArithProfileForBinaryArithOp(exec, pc, result, left, right); |
546 | }); |
547 | } |
548 | |
549 | SLOW_PATH_DECL(slow_path_sub) |
550 | { |
551 | BEGIN(); |
552 | auto bytecode = pc->as<OpSub>(); |
553 | JSValue left = GET_C(bytecode.m_lhs).jsValue(); |
554 | JSValue right = GET_C(bytecode.m_rhs).jsValue(); |
555 | auto leftNumeric = left.toNumeric(exec); |
556 | CHECK_EXCEPTION(); |
557 | auto rightNumeric = right.toNumeric(exec); |
558 | CHECK_EXCEPTION(); |
559 | |
560 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
561 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
562 | JSBigInt* result = JSBigInt::sub(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
563 | CHECK_EXCEPTION(); |
564 | RETURN_WITH_PROFILING(result, { |
565 | updateArithProfileForBinaryArithOp(exec, pc, result, left, right); |
566 | }); |
567 | } |
568 | |
569 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in subtraction." )); |
570 | } |
571 | |
572 | JSValue result = jsNumber(WTF::get<double>(leftNumeric) - WTF::get<double>(rightNumeric)); |
573 | RETURN_WITH_PROFILING(result, { |
574 | updateArithProfileForBinaryArithOp(exec, pc, result, left, right); |
575 | }); |
576 | } |
577 | |
578 | SLOW_PATH_DECL(slow_path_div) |
579 | { |
580 | BEGIN(); |
581 | auto bytecode = pc->as<OpDiv>(); |
582 | JSValue left = GET_C(bytecode.m_lhs).jsValue(); |
583 | JSValue right = GET_C(bytecode.m_rhs).jsValue(); |
584 | auto leftNumeric = left.toNumeric(exec); |
585 | CHECK_EXCEPTION(); |
586 | auto rightNumeric = right.toNumeric(exec); |
587 | CHECK_EXCEPTION(); |
588 | |
589 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
590 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
591 | JSBigInt* result = JSBigInt::divide(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
592 | CHECK_EXCEPTION(); |
593 | RETURN_WITH_PROFILING(result, { |
594 | updateArithProfileForBinaryArithOp(exec, pc, result, left, right); |
595 | }); |
596 | } |
597 | |
598 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in division." )); |
599 | } |
600 | |
601 | double a = WTF::get<double>(leftNumeric); |
602 | double b = WTF::get<double>(rightNumeric); |
603 | JSValue result = jsNumber(a / b); |
604 | RETURN_WITH_PROFILING(result, { |
605 | updateArithProfileForBinaryArithOp(exec, pc, result, left, right); |
606 | }); |
607 | } |
608 | |
609 | SLOW_PATH_DECL(slow_path_mod) |
610 | { |
611 | BEGIN(); |
612 | auto bytecode = pc->as<OpMod>(); |
613 | JSValue left = GET_C(bytecode.m_lhs).jsValue(); |
614 | JSValue right = GET_C(bytecode.m_rhs).jsValue(); |
615 | auto leftNumeric = left.toNumeric(exec); |
616 | CHECK_EXCEPTION(); |
617 | auto rightNumeric = right.toNumeric(exec); |
618 | CHECK_EXCEPTION(); |
619 | |
620 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
621 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
622 | JSBigInt* result = JSBigInt::remainder(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
623 | CHECK_EXCEPTION(); |
624 | RETURN(result); |
625 | } |
626 | |
627 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in remainder operation." )); |
628 | } |
629 | |
630 | double a = WTF::get<double>(leftNumeric); |
631 | double b = WTF::get<double>(rightNumeric); |
632 | RETURN(jsNumber(jsMod(a, b))); |
633 | } |
634 | |
635 | SLOW_PATH_DECL(slow_path_pow) |
636 | { |
637 | BEGIN(); |
638 | auto bytecode = pc->as<OpPow>(); |
639 | JSValue left = GET_C(bytecode.m_lhs).jsValue(); |
640 | JSValue right = GET_C(bytecode.m_rhs).jsValue(); |
641 | auto leftNumeric = left.toNumeric(exec); |
642 | CHECK_EXCEPTION(); |
643 | auto rightNumeric = right.toNumeric(exec); |
644 | CHECK_EXCEPTION(); |
645 | |
646 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
647 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
648 | JSBigInt* result = JSBigInt::exponentiate(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
649 | CHECK_EXCEPTION(); |
650 | RETURN(result); |
651 | } |
652 | |
653 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in exponentiation operation." )); |
654 | } |
655 | |
656 | double a = WTF::get<double>(leftNumeric); |
657 | double b = WTF::get<double>(rightNumeric); |
658 | |
659 | RETURN(jsNumber(operationMathPow(a, b))); |
660 | } |
661 | |
662 | SLOW_PATH_DECL(slow_path_lshift) |
663 | { |
664 | BEGIN(); |
665 | auto bytecode = pc->as<OpLshift>(); |
666 | JSValue left = GET_C(bytecode.m_lhs).jsValue(); |
667 | JSValue right = GET_C(bytecode.m_rhs).jsValue(); |
668 | auto leftNumeric = left.toBigIntOrInt32(exec); |
669 | CHECK_EXCEPTION(); |
670 | auto rightNumeric = right.toBigIntOrInt32(exec); |
671 | CHECK_EXCEPTION(); |
672 | |
673 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
674 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
675 | JSBigInt* result = JSBigInt::leftShift(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
676 | CHECK_EXCEPTION(); |
677 | RETURN(result); |
678 | } |
679 | |
680 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in left shift operation." )); |
681 | } |
682 | |
683 | RETURN(jsNumber(WTF::get<int32_t>(leftNumeric) << (WTF::get<int32_t>(rightNumeric) & 31))); |
684 | } |
685 | |
686 | SLOW_PATH_DECL(slow_path_rshift) |
687 | { |
688 | BEGIN(); |
689 | auto bytecode = pc->as<OpRshift>(); |
690 | JSValue left = GET_C(bytecode.m_lhs).jsValue(); |
691 | JSValue right = GET_C(bytecode.m_rhs).jsValue(); |
692 | auto leftNumeric = left.toBigIntOrInt32(exec); |
693 | CHECK_EXCEPTION(); |
694 | auto rightNumeric = right.toBigIntOrInt32(exec); |
695 | CHECK_EXCEPTION(); |
696 | |
697 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
698 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
699 | JSBigInt* result = JSBigInt::signedRightShift(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
700 | CHECK_EXCEPTION(); |
701 | RETURN(result); |
702 | } |
703 | |
704 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in signed right shift operation." )); |
705 | } |
706 | |
707 | RETURN(jsNumber(WTF::get<int32_t>(leftNumeric) >> (WTF::get<int32_t>(rightNumeric) & 31))); |
708 | } |
709 | |
710 | SLOW_PATH_DECL(slow_path_urshift) |
711 | { |
712 | BEGIN(); |
713 | auto bytecode = pc->as<OpUrshift>(); |
714 | uint32_t a = GET_C(bytecode.m_lhs).jsValue().toUInt32(exec); |
715 | if (UNLIKELY(throwScope.exception())) |
716 | RETURN(JSValue()); |
717 | uint32_t b = GET_C(bytecode.m_rhs).jsValue().toUInt32(exec); |
718 | RETURN(jsNumber(static_cast<int32_t>(a >> (b & 31)))); |
719 | } |
720 | |
721 | SLOW_PATH_DECL(slow_path_unsigned) |
722 | { |
723 | BEGIN(); |
724 | auto bytecode = pc->as<OpUnsigned>(); |
725 | uint32_t a = GET_C(bytecode.m_operand).jsValue().toUInt32(exec); |
726 | RETURN(jsNumber(a)); |
727 | } |
728 | |
729 | SLOW_PATH_DECL(slow_path_bitnot) |
730 | { |
731 | BEGIN(); |
732 | auto bytecode = pc->as<OpBitnot>(); |
733 | auto operandNumeric = GET_C(bytecode.m_operand).jsValue().toBigIntOrInt32(exec); |
734 | CHECK_EXCEPTION(); |
735 | |
736 | if (WTF::holds_alternative<JSBigInt*>(operandNumeric)) { |
737 | JSBigInt* result = JSBigInt::bitwiseNot(exec, WTF::get<JSBigInt*>(operandNumeric)); |
738 | CHECK_EXCEPTION(); |
739 | RETURN_PROFILED(result); |
740 | } |
741 | |
742 | RETURN_PROFILED(jsNumber(~WTF::get<int32_t>(operandNumeric))); |
743 | } |
744 | |
745 | SLOW_PATH_DECL(slow_path_bitand) |
746 | { |
747 | BEGIN(); |
748 | auto bytecode = pc->as<OpBitand>(); |
749 | auto leftNumeric = GET_C(bytecode.m_lhs).jsValue().toBigIntOrInt32(exec); |
750 | CHECK_EXCEPTION(); |
751 | auto rightNumeric = GET_C(bytecode.m_rhs).jsValue().toBigIntOrInt32(exec); |
752 | CHECK_EXCEPTION(); |
753 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
754 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
755 | JSBigInt* result = JSBigInt::bitwiseAnd(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
756 | CHECK_EXCEPTION(); |
757 | RETURN_PROFILED(result); |
758 | } |
759 | |
760 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in bitwise 'and' operation." )); |
761 | } |
762 | |
763 | RETURN_PROFILED(jsNumber(WTF::get<int32_t>(leftNumeric) & WTF::get<int32_t>(rightNumeric))); |
764 | } |
765 | |
766 | SLOW_PATH_DECL(slow_path_bitor) |
767 | { |
768 | BEGIN(); |
769 | auto bytecode = pc->as<OpBitor>(); |
770 | auto leftNumeric = GET_C(bytecode.m_lhs).jsValue().toBigIntOrInt32(exec); |
771 | CHECK_EXCEPTION(); |
772 | auto rightNumeric = GET_C(bytecode.m_rhs).jsValue().toBigIntOrInt32(exec); |
773 | CHECK_EXCEPTION(); |
774 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
775 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
776 | JSBigInt* result = JSBigInt::bitwiseOr(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
777 | CHECK_EXCEPTION(); |
778 | RETURN_PROFILED(result); |
779 | } |
780 | |
781 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in bitwise 'or' operation." )); |
782 | } |
783 | |
784 | RETURN_PROFILED(jsNumber(WTF::get<int32_t>(leftNumeric) | WTF::get<int32_t>(rightNumeric))); |
785 | } |
786 | |
787 | SLOW_PATH_DECL(slow_path_bitxor) |
788 | { |
789 | BEGIN(); |
790 | auto bytecode = pc->as<OpBitxor>(); |
791 | auto leftNumeric = GET_C(bytecode.m_lhs).jsValue().toBigIntOrInt32(exec); |
792 | CHECK_EXCEPTION(); |
793 | auto rightNumeric = GET_C(bytecode.m_rhs).jsValue().toBigIntOrInt32(exec); |
794 | CHECK_EXCEPTION(); |
795 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
796 | if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { |
797 | JSBigInt* result = JSBigInt::bitwiseXor(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); |
798 | CHECK_EXCEPTION(); |
799 | RETURN_PROFILED(result); |
800 | } |
801 | |
802 | THROW(createTypeError(exec, "Invalid mix of BigInt and other type in bitwise 'xor' operation." )); |
803 | } |
804 | |
805 | RETURN_PROFILED(jsNumber(WTF::get<int32_t>(leftNumeric) ^ WTF::get<int32_t>(rightNumeric))); |
806 | } |
807 | |
808 | SLOW_PATH_DECL(slow_path_typeof) |
809 | { |
810 | BEGIN(); |
811 | auto bytecode = pc->as<OpTypeof>(); |
812 | RETURN(jsTypeStringForValue(exec, GET_C(bytecode.m_value).jsValue())); |
813 | } |
814 | |
815 | SLOW_PATH_DECL(slow_path_is_object_or_null) |
816 | { |
817 | BEGIN(); |
818 | auto bytecode = pc->as<OpIsObjectOrNull>(); |
819 | RETURN(jsBoolean(jsIsObjectTypeOrNull(exec, GET_C(bytecode.m_operand).jsValue()))); |
820 | } |
821 | |
822 | SLOW_PATH_DECL(slow_path_is_function) |
823 | { |
824 | BEGIN(); |
825 | auto bytecode = pc->as<OpIsFunction>(); |
826 | RETURN(jsBoolean(GET_C(bytecode.m_operand).jsValue().isFunction(vm))); |
827 | } |
828 | |
829 | SLOW_PATH_DECL(slow_path_in_by_val) |
830 | { |
831 | BEGIN(); |
832 | auto bytecode = pc->as<OpInByVal>(); |
833 | auto& metadata = bytecode.metadata(exec); |
834 | RETURN(jsBoolean(CommonSlowPaths::opInByVal(exec, GET_C(bytecode.m_base).jsValue(), GET_C(bytecode.m_property).jsValue(), &metadata.m_arrayProfile))); |
835 | } |
836 | |
837 | SLOW_PATH_DECL(slow_path_in_by_id) |
838 | { |
839 | BEGIN(); |
840 | |
841 | auto bytecode = pc->as<OpInById>(); |
842 | JSValue baseValue = GET_C(bytecode.m_base).jsValue(); |
843 | if (!baseValue.isObject()) |
844 | THROW(createInvalidInParameterError(exec, baseValue)); |
845 | |
846 | RETURN(jsBoolean(asObject(baseValue)->hasProperty(exec, exec->codeBlock()->identifier(bytecode.m_property)))); |
847 | } |
848 | |
849 | SLOW_PATH_DECL(slow_path_del_by_val) |
850 | { |
851 | BEGIN(); |
852 | auto bytecode = pc->as<OpDelByVal>(); |
853 | JSValue baseValue = GET_C(bytecode.m_base).jsValue(); |
854 | JSObject* baseObject = baseValue.toObject(exec); |
855 | CHECK_EXCEPTION(); |
856 | |
857 | JSValue subscript = GET_C(bytecode.m_property).jsValue(); |
858 | |
859 | bool couldDelete; |
860 | |
861 | uint32_t i; |
862 | if (subscript.getUInt32(i)) |
863 | couldDelete = baseObject->methodTable(vm)->deletePropertyByIndex(baseObject, exec, i); |
864 | else { |
865 | CHECK_EXCEPTION(); |
866 | auto property = subscript.toPropertyKey(exec); |
867 | CHECK_EXCEPTION(); |
868 | couldDelete = baseObject->methodTable(vm)->deleteProperty(baseObject, exec, property); |
869 | } |
870 | |
871 | if (!couldDelete && exec->codeBlock()->isStrictMode()) |
872 | THROW(createTypeError(exec, UnableToDeletePropertyError)); |
873 | |
874 | RETURN(jsBoolean(couldDelete)); |
875 | } |
876 | |
877 | SLOW_PATH_DECL(slow_path_strcat) |
878 | { |
879 | BEGIN(); |
880 | auto bytecode = pc->as<OpStrcat>(); |
881 | RETURN(jsStringFromRegisterArray(exec, &GET(bytecode.m_src), bytecode.m_count)); |
882 | } |
883 | |
884 | SLOW_PATH_DECL(slow_path_to_primitive) |
885 | { |
886 | BEGIN(); |
887 | auto bytecode = pc->as<OpToPrimitive>(); |
888 | RETURN(GET_C(bytecode.m_src).jsValue().toPrimitive(exec)); |
889 | } |
890 | |
891 | SLOW_PATH_DECL(slow_path_enter) |
892 | { |
893 | BEGIN(); |
894 | CodeBlock* codeBlock = exec->codeBlock(); |
895 | Heap::heap(codeBlock)->writeBarrier(codeBlock); |
896 | END(); |
897 | } |
898 | |
899 | SLOW_PATH_DECL(slow_path_get_enumerable_length) |
900 | { |
901 | BEGIN(); |
902 | auto bytecode = pc->as<OpGetEnumerableLength>(); |
903 | JSValue enumeratorValue = GET(bytecode.m_base).jsValue(); |
904 | if (enumeratorValue.isUndefinedOrNull()) |
905 | RETURN(jsNumber(0)); |
906 | |
907 | JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(enumeratorValue.asCell()); |
908 | |
909 | RETURN(jsNumber(enumerator->indexedLength())); |
910 | } |
911 | |
912 | SLOW_PATH_DECL(slow_path_has_indexed_property) |
913 | { |
914 | BEGIN(); |
915 | auto bytecode = pc->as<OpHasIndexedProperty>(); |
916 | auto& metadata = bytecode.metadata(exec); |
917 | JSObject* base = GET(bytecode.m_base).jsValue().toObject(exec); |
918 | CHECK_EXCEPTION(); |
919 | JSValue property = GET(bytecode.m_property).jsValue(); |
920 | metadata.m_arrayProfile.observeStructure(base->structure(vm)); |
921 | ASSERT(property.isUInt32AsAnyInt()); |
922 | RETURN(jsBoolean(base->hasPropertyGeneric(exec, property.asUInt32AsAnyInt(), PropertySlot::InternalMethodType::GetOwnProperty))); |
923 | } |
924 | |
925 | SLOW_PATH_DECL(slow_path_has_structure_property) |
926 | { |
927 | BEGIN(); |
928 | auto bytecode = pc->as<OpHasStructureProperty>(); |
929 | JSObject* base = GET(bytecode.m_base).jsValue().toObject(exec); |
930 | CHECK_EXCEPTION(); |
931 | JSValue property = GET(bytecode.m_property).jsValue(); |
932 | ASSERT(property.isString()); |
933 | JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue().asCell()); |
934 | if (base->structure(vm)->id() == enumerator->cachedStructureID()) |
935 | RETURN(jsBoolean(true)); |
936 | JSString* string = asString(property); |
937 | auto propertyName = string->toIdentifier(exec); |
938 | CHECK_EXCEPTION(); |
939 | RETURN(jsBoolean(base->hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::GetOwnProperty))); |
940 | } |
941 | |
942 | SLOW_PATH_DECL(slow_path_has_generic_property) |
943 | { |
944 | BEGIN(); |
945 | auto bytecode = pc->as<OpHasGenericProperty>(); |
946 | JSObject* base = GET(bytecode.m_base).jsValue().toObject(exec); |
947 | CHECK_EXCEPTION(); |
948 | JSValue property = GET(bytecode.m_property).jsValue(); |
949 | ASSERT(property.isString()); |
950 | JSString* string = asString(property); |
951 | auto propertyName = string->toIdentifier(exec); |
952 | CHECK_EXCEPTION(); |
953 | RETURN(jsBoolean(base->hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::GetOwnProperty))); |
954 | } |
955 | |
956 | SLOW_PATH_DECL(slow_path_get_direct_pname) |
957 | { |
958 | BEGIN(); |
959 | auto bytecode = pc->as<OpGetDirectPname>(); |
960 | JSValue baseValue = GET_C(bytecode.m_base).jsValue(); |
961 | JSValue property = GET(bytecode.m_property).jsValue(); |
962 | ASSERT(property.isString()); |
963 | JSString* string = asString(property); |
964 | auto propertyName = string->toIdentifier(exec); |
965 | CHECK_EXCEPTION(); |
966 | RETURN(baseValue.get(exec, propertyName)); |
967 | } |
968 | |
969 | SLOW_PATH_DECL(slow_path_get_property_enumerator) |
970 | { |
971 | BEGIN(); |
972 | auto bytecode = pc->as<OpGetPropertyEnumerator>(); |
973 | JSValue baseValue = GET(bytecode.m_base).jsValue(); |
974 | if (baseValue.isUndefinedOrNull()) |
975 | RETURN(JSPropertyNameEnumerator::create(vm)); |
976 | |
977 | JSObject* base = baseValue.toObject(exec); |
978 | CHECK_EXCEPTION(); |
979 | |
980 | RETURN(propertyNameEnumerator(exec, base)); |
981 | } |
982 | |
983 | SLOW_PATH_DECL(slow_path_enumerator_structure_pname) |
984 | { |
985 | BEGIN(); |
986 | auto bytecode = pc->as<OpEnumeratorStructurePname>(); |
987 | JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue().asCell()); |
988 | uint32_t index = GET(bytecode.m_index).jsValue().asUInt32(); |
989 | |
990 | JSString* propertyName = nullptr; |
991 | if (index < enumerator->endStructurePropertyIndex()) |
992 | propertyName = enumerator->propertyNameAtIndex(index); |
993 | RETURN(propertyName ? propertyName : jsNull()); |
994 | } |
995 | |
996 | SLOW_PATH_DECL(slow_path_enumerator_generic_pname) |
997 | { |
998 | BEGIN(); |
999 | auto bytecode = pc->as<OpEnumeratorGenericPname>(); |
1000 | JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue().asCell()); |
1001 | uint32_t index = GET(bytecode.m_index).jsValue().asUInt32(); |
1002 | |
1003 | JSString* propertyName = nullptr; |
1004 | if (enumerator->endStructurePropertyIndex() <= index && index < enumerator->endGenericPropertyIndex()) |
1005 | propertyName = enumerator->propertyNameAtIndex(index); |
1006 | RETURN(propertyName ? propertyName : jsNull()); |
1007 | } |
1008 | |
1009 | SLOW_PATH_DECL(slow_path_to_index_string) |
1010 | { |
1011 | BEGIN(); |
1012 | auto bytecode = pc->as<OpToIndexString>(); |
1013 | JSValue indexValue = GET(bytecode.m_index).jsValue(); |
1014 | ASSERT(indexValue.isUInt32AsAnyInt()); |
1015 | RETURN(jsString(exec, Identifier::from(exec, indexValue.asUInt32AsAnyInt()).string())); |
1016 | } |
1017 | |
1018 | SLOW_PATH_DECL(slow_path_profile_type_clear_log) |
1019 | { |
1020 | BEGIN(); |
1021 | vm.typeProfilerLog()->processLogEntries(vm, "LLInt log full."_s ); |
1022 | END(); |
1023 | } |
1024 | |
1025 | SLOW_PATH_DECL(slow_path_unreachable) |
1026 | { |
1027 | BEGIN(); |
1028 | UNREACHABLE_FOR_PLATFORM(); |
1029 | END(); |
1030 | } |
1031 | |
1032 | SLOW_PATH_DECL(slow_path_create_lexical_environment) |
1033 | { |
1034 | BEGIN(); |
1035 | auto bytecode = pc->as<OpCreateLexicalEnvironment>(); |
1036 | int scopeReg = bytecode.m_scope.offset(); |
1037 | JSScope* currentScope = exec->uncheckedR(scopeReg).Register::scope(); |
1038 | SymbolTable* symbolTable = jsCast<SymbolTable*>(GET_C(bytecode.m_symbolTable).jsValue()); |
1039 | JSValue initialValue = GET_C(bytecode.m_initialValue).jsValue(); |
1040 | ASSERT(initialValue == jsUndefined() || initialValue == jsTDZValue()); |
1041 | JSScope* newScope = JSLexicalEnvironment::create(vm, exec->lexicalGlobalObject(), currentScope, symbolTable, initialValue); |
1042 | RETURN(newScope); |
1043 | } |
1044 | |
1045 | SLOW_PATH_DECL(slow_path_push_with_scope) |
1046 | { |
1047 | BEGIN(); |
1048 | auto bytecode = pc->as<OpPushWithScope>(); |
1049 | JSObject* newScope = GET_C(bytecode.m_newScope).jsValue().toObject(exec); |
1050 | CHECK_EXCEPTION(); |
1051 | |
1052 | int scopeReg = bytecode.m_currentScope.offset(); |
1053 | JSScope* currentScope = exec->uncheckedR(scopeReg).Register::scope(); |
1054 | RETURN(JSWithScope::create(vm, exec->lexicalGlobalObject(), currentScope, newScope)); |
1055 | } |
1056 | |
1057 | SLOW_PATH_DECL(slow_path_resolve_scope_for_hoisting_func_decl_in_eval) |
1058 | { |
1059 | BEGIN(); |
1060 | auto bytecode = pc->as<OpResolveScopeForHoistingFuncDeclInEval>(); |
1061 | const Identifier& ident = exec->codeBlock()->identifier(bytecode.m_property); |
1062 | JSScope* scope = exec->uncheckedR(bytecode.m_scope.offset()).Register::scope(); |
1063 | JSValue resolvedScope = JSScope::resolveScopeForHoistingFuncDeclInEval(exec, scope, ident); |
1064 | |
1065 | CHECK_EXCEPTION(); |
1066 | |
1067 | RETURN(resolvedScope); |
1068 | } |
1069 | |
1070 | SLOW_PATH_DECL(slow_path_resolve_scope) |
1071 | { |
1072 | BEGIN(); |
1073 | auto bytecode = pc->as<OpResolveScope>(); |
1074 | auto& metadata = bytecode.metadata(exec); |
1075 | const Identifier& ident = exec->codeBlock()->identifier(bytecode.m_var); |
1076 | JSScope* scope = exec->uncheckedR(bytecode.m_scope.offset()).Register::scope(); |
1077 | JSObject* resolvedScope = JSScope::resolve(exec, scope, ident); |
1078 | // Proxy can throw an error here, e.g. Proxy in with statement's @unscopables. |
1079 | CHECK_EXCEPTION(); |
1080 | |
1081 | ResolveType resolveType = metadata.m_resolveType; |
1082 | |
1083 | // ModuleVar does not keep the scope register value alive in DFG. |
1084 | ASSERT(resolveType != ModuleVar); |
1085 | |
1086 | switch (resolveType) { |
1087 | case GlobalProperty: |
1088 | case GlobalPropertyWithVarInjectionChecks: |
1089 | case UnresolvedProperty: |
1090 | case UnresolvedPropertyWithVarInjectionChecks: { |
1091 | if (resolvedScope->isGlobalObject()) { |
1092 | JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(resolvedScope); |
1093 | bool hasProperty = globalObject->hasProperty(exec, ident); |
1094 | CHECK_EXCEPTION(); |
1095 | if (hasProperty) { |
1096 | ConcurrentJSLocker locker(exec->codeBlock()->m_lock); |
1097 | metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalPropertyWithVarInjectionChecks : GlobalProperty; |
1098 | metadata.m_globalObject = globalObject; |
1099 | metadata.m_globalLexicalBindingEpoch = globalObject->globalLexicalBindingEpoch(); |
1100 | } |
1101 | } else if (resolvedScope->isGlobalLexicalEnvironment()) { |
1102 | JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(resolvedScope); |
1103 | ConcurrentJSLocker locker(exec->codeBlock()->m_lock); |
1104 | metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; |
1105 | metadata.m_globalLexicalEnvironment = globalLexicalEnvironment; |
1106 | } |
1107 | break; |
1108 | } |
1109 | default: |
1110 | break; |
1111 | } |
1112 | |
1113 | RETURN(resolvedScope); |
1114 | } |
1115 | |
1116 | SLOW_PATH_DECL(slow_path_create_rest) |
1117 | { |
1118 | BEGIN(); |
1119 | auto bytecode = pc->as<OpCreateRest>(); |
1120 | unsigned arraySize = GET_C(bytecode.m_arraySize).jsValue().asUInt32(); |
1121 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
1122 | Structure* structure = globalObject->restParameterStructure(); |
1123 | unsigned numParamsToSkip = bytecode.m_numParametersToSkip; |
1124 | JSValue* argumentsToCopyRegion = exec->addressOfArgumentsStart() + numParamsToSkip; |
1125 | RETURN(constructArray(exec, structure, argumentsToCopyRegion, arraySize)); |
1126 | } |
1127 | |
1128 | SLOW_PATH_DECL(slow_path_get_by_id_with_this) |
1129 | { |
1130 | BEGIN(); |
1131 | auto bytecode = pc->as<OpGetByIdWithThis>(); |
1132 | const Identifier& ident = exec->codeBlock()->identifier(bytecode.m_property); |
1133 | JSValue baseValue = GET_C(bytecode.m_base).jsValue(); |
1134 | JSValue thisVal = GET_C(bytecode.m_thisValue).jsValue(); |
1135 | PropertySlot slot(thisVal, PropertySlot::PropertySlot::InternalMethodType::Get); |
1136 | JSValue result = baseValue.get(exec, ident, slot); |
1137 | RETURN_PROFILED(result); |
1138 | } |
1139 | |
1140 | SLOW_PATH_DECL(slow_path_get_by_val_with_this) |
1141 | { |
1142 | BEGIN(); |
1143 | |
1144 | auto bytecode = pc->as<OpGetByValWithThis>(); |
1145 | JSValue baseValue = GET_C(bytecode.m_base).jsValue(); |
1146 | JSValue thisValue = GET_C(bytecode.m_thisValue).jsValue(); |
1147 | JSValue subscript = GET_C(bytecode.m_property).jsValue(); |
1148 | |
1149 | if (LIKELY(baseValue.isCell() && subscript.isString())) { |
1150 | Structure& structure = *baseValue.asCell()->structure(vm); |
1151 | if (JSCell::canUseFastGetOwnProperty(structure)) { |
1152 | RefPtr<AtomStringImpl> existingAtomString = asString(subscript)->toExistingAtomString(exec); |
1153 | CHECK_EXCEPTION(); |
1154 | if (existingAtomString) { |
1155 | if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.get())) |
1156 | RETURN_PROFILED(result); |
1157 | } |
1158 | } |
1159 | } |
1160 | |
1161 | PropertySlot slot(thisValue, PropertySlot::PropertySlot::InternalMethodType::Get); |
1162 | if (subscript.isUInt32()) { |
1163 | uint32_t i = subscript.asUInt32(); |
1164 | if (isJSString(baseValue) && asString(baseValue)->canGetIndex(i)) |
1165 | RETURN_PROFILED(asString(baseValue)->getIndex(exec, i)); |
1166 | |
1167 | RETURN_PROFILED(baseValue.get(exec, i, slot)); |
1168 | } |
1169 | |
1170 | baseValue.requireObjectCoercible(exec); |
1171 | CHECK_EXCEPTION(); |
1172 | auto property = subscript.toPropertyKey(exec); |
1173 | CHECK_EXCEPTION(); |
1174 | RETURN_PROFILED(baseValue.get(exec, property, slot)); |
1175 | } |
1176 | |
1177 | SLOW_PATH_DECL(slow_path_put_by_id_with_this) |
1178 | { |
1179 | BEGIN(); |
1180 | auto bytecode = pc->as<OpPutByIdWithThis>(); |
1181 | CodeBlock* codeBlock = exec->codeBlock(); |
1182 | const Identifier& ident = codeBlock->identifier(bytecode.m_property); |
1183 | JSValue baseValue = GET_C(bytecode.m_base).jsValue(); |
1184 | JSValue thisVal = GET_C(bytecode.m_thisValue).jsValue(); |
1185 | JSValue putValue = GET_C(bytecode.m_value).jsValue(); |
1186 | PutPropertySlot slot(thisVal, codeBlock->isStrictMode(), codeBlock->putByIdContext()); |
1187 | baseValue.putInline(exec, ident, putValue, slot); |
1188 | END(); |
1189 | } |
1190 | |
1191 | SLOW_PATH_DECL(slow_path_put_by_val_with_this) |
1192 | { |
1193 | BEGIN(); |
1194 | auto bytecode = pc->as<OpPutByValWithThis>(); |
1195 | JSValue baseValue = GET_C(bytecode.m_base).jsValue(); |
1196 | JSValue thisValue = GET_C(bytecode.m_thisValue).jsValue(); |
1197 | JSValue subscript = GET_C(bytecode.m_property).jsValue(); |
1198 | JSValue value = GET_C(bytecode.m_value).jsValue(); |
1199 | |
1200 | auto property = subscript.toPropertyKey(exec); |
1201 | CHECK_EXCEPTION(); |
1202 | PutPropertySlot slot(thisValue, exec->codeBlock()->isStrictMode()); |
1203 | baseValue.put(exec, property, value, slot); |
1204 | END(); |
1205 | } |
1206 | |
1207 | SLOW_PATH_DECL(slow_path_define_data_property) |
1208 | { |
1209 | BEGIN(); |
1210 | auto bytecode = pc->as<OpDefineDataProperty>(); |
1211 | JSObject* base = asObject(GET_C(bytecode.m_base).jsValue()); |
1212 | JSValue property = GET_C(bytecode.m_property).jsValue(); |
1213 | JSValue value = GET_C(bytecode.m_value).jsValue(); |
1214 | JSValue attributes = GET_C(bytecode.m_attributes).jsValue(); |
1215 | ASSERT(attributes.isInt32()); |
1216 | |
1217 | auto propertyName = property.toPropertyKey(exec); |
1218 | CHECK_EXCEPTION(); |
1219 | PropertyDescriptor descriptor = toPropertyDescriptor(value, jsUndefined(), jsUndefined(), DefinePropertyAttributes(attributes.asInt32())); |
1220 | ASSERT((descriptor.attributes() & PropertyAttribute::Accessor) || (!descriptor.isAccessorDescriptor())); |
1221 | base->methodTable(vm)->defineOwnProperty(base, exec, propertyName, descriptor, true); |
1222 | END(); |
1223 | } |
1224 | |
1225 | SLOW_PATH_DECL(slow_path_define_accessor_property) |
1226 | { |
1227 | BEGIN(); |
1228 | auto bytecode = pc->as<OpDefineAccessorProperty>(); |
1229 | JSObject* base = asObject(GET_C(bytecode.m_base).jsValue()); |
1230 | JSValue property = GET_C(bytecode.m_property).jsValue(); |
1231 | JSValue getter = GET_C(bytecode.m_getter).jsValue(); |
1232 | JSValue setter = GET_C(bytecode.m_setter).jsValue(); |
1233 | JSValue attributes = GET_C(bytecode.m_attributes).jsValue(); |
1234 | ASSERT(attributes.isInt32()); |
1235 | |
1236 | auto propertyName = property.toPropertyKey(exec); |
1237 | CHECK_EXCEPTION(); |
1238 | PropertyDescriptor descriptor = toPropertyDescriptor(jsUndefined(), getter, setter, DefinePropertyAttributes(attributes.asInt32())); |
1239 | ASSERT((descriptor.attributes() & PropertyAttribute::Accessor) || (!descriptor.isAccessorDescriptor())); |
1240 | base->methodTable(vm)->defineOwnProperty(base, exec, propertyName, descriptor, true); |
1241 | END(); |
1242 | } |
1243 | |
1244 | SLOW_PATH_DECL(slow_path_throw_static_error) |
1245 | { |
1246 | BEGIN(); |
1247 | auto bytecode = pc->as<OpThrowStaticError>(); |
1248 | JSValue errorMessageValue = GET_C(bytecode.m_message).jsValue(); |
1249 | RELEASE_ASSERT(errorMessageValue.isString()); |
1250 | String errorMessage = asString(errorMessageValue)->value(exec); |
1251 | ErrorType errorType = bytecode.m_errorType; |
1252 | THROW(createError(exec, errorType, errorMessage)); |
1253 | } |
1254 | |
1255 | SLOW_PATH_DECL(slow_path_new_array_with_spread) |
1256 | { |
1257 | BEGIN(); |
1258 | auto bytecode = pc->as<OpNewArrayWithSpread>(); |
1259 | int numItems = bytecode.m_argc; |
1260 | ASSERT(numItems >= 0); |
1261 | const BitVector& bitVector = exec->codeBlock()->unlinkedCodeBlock()->bitVector(bytecode.m_bitVector); |
1262 | |
1263 | JSValue* values = bitwise_cast<JSValue*>(&GET(bytecode.m_argv)); |
1264 | |
1265 | Checked<unsigned, RecordOverflow> checkedArraySize = 0; |
1266 | for (int i = 0; i < numItems; i++) { |
1267 | if (bitVector.get(i)) { |
1268 | JSValue value = values[-i]; |
1269 | JSFixedArray* array = jsCast<JSFixedArray*>(value); |
1270 | checkedArraySize += array->size(); |
1271 | } else |
1272 | checkedArraySize += 1; |
1273 | } |
1274 | if (UNLIKELY(checkedArraySize.hasOverflowed())) |
1275 | THROW(createOutOfMemoryError(exec)); |
1276 | |
1277 | unsigned arraySize = checkedArraySize.unsafeGet(); |
1278 | if (UNLIKELY(arraySize >= MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)) |
1279 | THROW(createOutOfMemoryError(exec)); |
1280 | |
1281 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
1282 | Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous); |
1283 | |
1284 | JSArray* result = JSArray::tryCreate(vm, structure, arraySize); |
1285 | if (UNLIKELY(!result)) |
1286 | THROW(createOutOfMemoryError(exec)); |
1287 | CHECK_EXCEPTION(); |
1288 | |
1289 | unsigned index = 0; |
1290 | for (int i = 0; i < numItems; i++) { |
1291 | JSValue value = values[-i]; |
1292 | if (bitVector.get(i)) { |
1293 | // We are spreading. |
1294 | JSFixedArray* array = jsCast<JSFixedArray*>(value); |
1295 | for (unsigned i = 0; i < array->size(); i++) { |
1296 | RELEASE_ASSERT(array->get(i)); |
1297 | result->putDirectIndex(exec, index, array->get(i)); |
1298 | CHECK_EXCEPTION(); |
1299 | ++index; |
1300 | } |
1301 | } else { |
1302 | // We are not spreading. |
1303 | result->putDirectIndex(exec, index, value); |
1304 | CHECK_EXCEPTION(); |
1305 | ++index; |
1306 | } |
1307 | } |
1308 | |
1309 | RETURN(result); |
1310 | } |
1311 | |
1312 | SLOW_PATH_DECL(slow_path_new_array_buffer) |
1313 | { |
1314 | BEGIN(); |
1315 | auto bytecode = pc->as<OpNewArrayBuffer>(); |
1316 | ASSERT(exec->codeBlock()->isConstantRegisterIndex(bytecode.m_immutableButterfly.offset())); |
1317 | JSImmutableButterfly* immutableButterfly = bitwise_cast<JSImmutableButterfly*>(GET_C(bytecode.m_immutableButterfly).jsValue().asCell()); |
1318 | auto& profile = bytecode.metadata(exec).m_arrayAllocationProfile; |
1319 | |
1320 | IndexingType indexingMode = profile.selectIndexingType(); |
1321 | Structure* structure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(indexingMode); |
1322 | ASSERT(isCopyOnWrite(indexingMode)); |
1323 | ASSERT(!structure->outOfLineCapacity()); |
1324 | |
1325 | if (UNLIKELY(immutableButterfly->indexingMode() != indexingMode)) { |
1326 | auto* newButterfly = JSImmutableButterfly::create(vm, indexingMode, immutableButterfly->length()); |
1327 | for (unsigned i = 0; i < immutableButterfly->length(); ++i) |
1328 | newButterfly->setIndex(vm, i, immutableButterfly->get(i)); |
1329 | immutableButterfly = newButterfly; |
1330 | CodeBlock* codeBlock = exec->codeBlock(); |
1331 | |
1332 | // FIXME: This is kinda gross and only works because we can't inline new_array_bufffer in the baseline. |
1333 | // We also cannot allocate a new butterfly from compilation threads since it's invalid to allocate cells from |
1334 | // a compilation thread. |
1335 | WTF::storeStoreFence(); |
1336 | codeBlock->constantRegister(bytecode.m_immutableButterfly.offset()).set(vm, codeBlock, immutableButterfly); |
1337 | WTF::storeStoreFence(); |
1338 | } |
1339 | |
1340 | JSArray* result = CommonSlowPaths::allocateNewArrayBuffer(vm, structure, immutableButterfly); |
1341 | ASSERT(isCopyOnWrite(result->indexingMode()) || exec->lexicalGlobalObject()->isHavingABadTime()); |
1342 | ArrayAllocationProfile::updateLastAllocationFor(&profile, result); |
1343 | RETURN(result); |
1344 | } |
1345 | |
1346 | SLOW_PATH_DECL(slow_path_spread) |
1347 | { |
1348 | BEGIN(); |
1349 | |
1350 | auto bytecode = pc->as<OpSpread>(); |
1351 | JSValue iterable = GET_C(bytecode.m_argument).jsValue(); |
1352 | |
1353 | if (iterable.isCell() && isJSArray(iterable.asCell())) { |
1354 | JSArray* array = jsCast<JSArray*>(iterable); |
1355 | if (array->isIteratorProtocolFastAndNonObservable()) { |
1356 | // JSFixedArray::createFromArray does not consult the prototype chain, |
1357 | // so we must be sure that not consulting the prototype chain would |
1358 | // produce the same value during iteration. |
1359 | RETURN(JSFixedArray::createFromArray(exec, vm, array)); |
1360 | } |
1361 | } |
1362 | |
1363 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
1364 | |
1365 | JSArray* array; |
1366 | { |
1367 | JSFunction* iterationFunction = globalObject->iteratorProtocolFunction(); |
1368 | CallData callData; |
1369 | CallType callType = JSC::getCallData(vm, iterationFunction, callData); |
1370 | ASSERT(callType != CallType::None); |
1371 | |
1372 | MarkedArgumentBuffer arguments; |
1373 | arguments.append(iterable); |
1374 | ASSERT(!arguments.hasOverflowed()); |
1375 | JSValue arrayResult = call(exec, iterationFunction, callType, callData, jsNull(), arguments); |
1376 | CHECK_EXCEPTION(); |
1377 | array = jsCast<JSArray*>(arrayResult); |
1378 | } |
1379 | |
1380 | RETURN(JSFixedArray::createFromArray(exec, vm, array)); |
1381 | } |
1382 | |
1383 | } // namespace JSC |
1384 | |