1/*
2 * Copyright (C) 2014-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 "FTLOperations.h"
28
29#if ENABLE(FTL_JIT)
30
31#include "BytecodeStructs.h"
32#include "ClonedArguments.h"
33#include "CommonSlowPaths.h"
34#include "DirectArguments.h"
35#include "FTLJITCode.h"
36#include "FTLLazySlowPath.h"
37#include "FrameTracers.h"
38#include "InlineCallFrame.h"
39#include "Interpreter.h"
40#include "JSAsyncFunction.h"
41#include "JSAsyncGeneratorFunction.h"
42#include "JSCInlines.h"
43#include "JSFixedArray.h"
44#include "JSGeneratorFunction.h"
45#include "JSImmutableButterfly.h"
46#include "JSLexicalEnvironment.h"
47#include "RegExpObject.h"
48
49IGNORE_WARNINGS_BEGIN("frame-address")
50
51namespace JSC { namespace FTL {
52
53extern "C" void JIT_OPERATION operationPopulateObjectInOSR(JSGlobalObject* globalObject, ExitTimeObjectMaterialization* materialization, EncodedJSValue* encodedValue, EncodedJSValue* values)
54{
55 using namespace DFG;
56 VM& vm = globalObject->vm();
57 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
58 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
59
60 CodeBlock* codeBlock = callFrame->codeBlock();
61
62 // We cannot GC. We've got pointers in evil places.
63 // FIXME: We are not doing anything that can GC here, and this is
64 // probably unnecessary.
65 DeferGCForAWhile deferGC(vm.heap);
66
67 switch (materialization->type()) {
68 case PhantomNewObject: {
69 JSFinalObject* object = jsCast<JSFinalObject*>(JSValue::decode(*encodedValue));
70 Structure* structure = object->structure(vm);
71
72 // Figure out what the heck to populate the object with. Use
73 // getPropertiesConcurrently() because that happens to be
74 // lower-level and more convenient. It doesn't change the
75 // materialization of the property table. We want to have
76 // minimal visible effects on the system. Also, don't mind
77 // that this is O(n^2). It doesn't matter. We only get here
78 // from OSR exit.
79 for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) {
80 for (unsigned i = materialization->properties().size(); i--;) {
81 const ExitPropertyValue& property = materialization->properties()[i];
82 if (property.location().kind() != NamedPropertyPLoc)
83 continue;
84 if (codeBlock->identifier(property.location().info()).impl() != entry.key)
85 continue;
86
87 object->putDirect(vm, entry.offset, JSValue::decode(values[i]));
88 }
89 }
90 break;
91 }
92
93 case PhantomNewFunction:
94 case PhantomNewGeneratorFunction:
95 case PhantomNewAsyncFunction:
96 case PhantomNewAsyncGeneratorFunction:
97 case PhantomDirectArguments:
98 case PhantomClonedArguments:
99 case PhantomCreateRest:
100 case PhantomSpread:
101 case PhantomNewArrayWithSpread:
102 case PhantomNewArrayBuffer:
103 // Those are completely handled by operationMaterializeObjectInOSR
104 break;
105
106 case PhantomCreateActivation: {
107 JSLexicalEnvironment* activation = jsCast<JSLexicalEnvironment*>(JSValue::decode(*encodedValue));
108
109 // Figure out what to populate the activation with
110 for (unsigned i = materialization->properties().size(); i--;) {
111 const ExitPropertyValue& property = materialization->properties()[i];
112 if (property.location().kind() != ClosureVarPLoc)
113 continue;
114
115 activation->variableAt(ScopeOffset(property.location().info())).set(vm, activation, JSValue::decode(values[i]));
116 }
117
118 break;
119 }
120
121 case PhantomNewRegexp: {
122 RegExpObject* regExpObject = jsCast<RegExpObject*>(JSValue::decode(*encodedValue));
123
124 for (unsigned i = materialization->properties().size(); i--;) {
125 const ExitPropertyValue& property = materialization->properties()[i];
126 if (property.location().kind() != RegExpObjectLastIndexPLoc)
127 continue;
128
129 regExpObject->setLastIndex(globalObject, JSValue::decode(values[i]), false /* shouldThrow */);
130 break;
131 }
132 break;
133 }
134
135 default:
136 RELEASE_ASSERT_NOT_REACHED();
137 break;
138
139 }
140}
141
142extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR(JSGlobalObject* globalObject, ExitTimeObjectMaterialization* materialization, EncodedJSValue* values)
143{
144 using namespace DFG;
145 VM& vm = globalObject->vm();
146 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
147 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
148
149 // We cannot GC. We've got pointers in evil places.
150 DeferGCForAWhile deferGC(vm.heap);
151
152 switch (materialization->type()) {
153 case PhantomNewObject: {
154 // Figure out what the structure is
155 Structure* structure = nullptr;
156 for (unsigned i = materialization->properties().size(); i--;) {
157 const ExitPropertyValue& property = materialization->properties()[i];
158 if (property.location() != PromotedLocationDescriptor(StructurePLoc))
159 continue;
160
161 RELEASE_ASSERT(JSValue::decode(values[i]).asCell()->inherits<Structure>(vm));
162 structure = jsCast<Structure*>(JSValue::decode(values[i]));
163 break;
164 }
165 RELEASE_ASSERT(structure);
166
167 JSFinalObject* result = JSFinalObject::create(vm, structure);
168
169 // The real values will be put subsequently by
170 // operationPopulateNewObjectInOSR. We can't fill them in
171 // now, because they may not be available yet (typically
172 // because we have a cyclic dependency graph).
173
174 // We put a dummy value here in order to avoid super-subtle
175 // GC-and-OSR-exit crashes in case we have a bug and some
176 // field is, for any reason, not filled later.
177 // We use a random-ish number instead of a sensible value like
178 // undefined to make possible bugs easier to track.
179 for (PropertyMapEntry entry : structure->getPropertiesConcurrently())
180 result->putDirect(vm, entry.offset, jsNumber(19723));
181
182 return result;
183 }
184
185 case PhantomNewFunction:
186 case PhantomNewGeneratorFunction:
187 case PhantomNewAsyncGeneratorFunction:
188 case PhantomNewAsyncFunction: {
189 // Figure out what the executable and activation are
190 FunctionExecutable* executable = nullptr;
191 JSScope* activation = nullptr;
192 for (unsigned i = materialization->properties().size(); i--;) {
193 const ExitPropertyValue& property = materialization->properties()[i];
194 if (property.location() == PromotedLocationDescriptor(FunctionExecutablePLoc)) {
195 RELEASE_ASSERT(JSValue::decode(values[i]).asCell()->inherits<FunctionExecutable>(vm));
196 executable = jsCast<FunctionExecutable*>(JSValue::decode(values[i]));
197 }
198 if (property.location() == PromotedLocationDescriptor(FunctionActivationPLoc)) {
199 RELEASE_ASSERT(JSValue::decode(values[i]).asCell()->inherits<JSScope>(vm));
200 activation = jsCast<JSScope*>(JSValue::decode(values[i]));
201 }
202 }
203 RELEASE_ASSERT(executable && activation);
204
205 if (materialization->type() == PhantomNewFunction)
206 return JSFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
207 else if (materialization->type() == PhantomNewGeneratorFunction)
208 return JSGeneratorFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
209 else if (materialization->type() == PhantomNewAsyncGeneratorFunction)
210 return JSAsyncGeneratorFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
211 ASSERT(materialization->type() == PhantomNewAsyncFunction);
212 return JSAsyncFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
213 }
214
215 case PhantomCreateActivation: {
216 // Figure out what the scope and symbol table are
217 JSScope* scope = nullptr;
218 SymbolTable* table = nullptr;
219 for (unsigned i = materialization->properties().size(); i--;) {
220 const ExitPropertyValue& property = materialization->properties()[i];
221 if (property.location() == PromotedLocationDescriptor(ActivationScopePLoc)) {
222 RELEASE_ASSERT(JSValue::decode(values[i]).asCell()->inherits<JSScope>(vm));
223 scope = jsCast<JSScope*>(JSValue::decode(values[i]));
224 } else if (property.location() == PromotedLocationDescriptor(ActivationSymbolTablePLoc)) {
225 RELEASE_ASSERT(JSValue::decode(values[i]).asCell()->inherits<SymbolTable>(vm));
226 table = jsCast<SymbolTable*>(JSValue::decode(values[i]));
227 }
228 }
229 RELEASE_ASSERT(scope);
230 RELEASE_ASSERT(table);
231
232 CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
233 materialization->origin(), callFrame->codeBlock()->baselineAlternative());
234 Structure* structure = codeBlock->globalObject()->activationStructure();
235
236 // It doesn't matter what values we initialize as bottom values inside the activation constructor because
237 // activation sinking will set bottom values for each slot.
238 // FIXME: Slight optimization would be to create a constructor that doesn't initialize all slots.
239 JSLexicalEnvironment* result = JSLexicalEnvironment::create(vm, structure, scope, table, jsUndefined());
240
241 RELEASE_ASSERT(materialization->properties().size() - 2 == table->scopeSize());
242
243 // The real values will be put subsequently by
244 // operationPopulateNewObjectInOSR. See the PhantomNewObject
245 // case for details.
246 for (unsigned i = materialization->properties().size(); i--;) {
247 const ExitPropertyValue& property = materialization->properties()[i];
248 if (property.location().kind() != ClosureVarPLoc)
249 continue;
250
251 result->variableAt(ScopeOffset(property.location().info())).set(
252 vm, result, jsNumber(29834));
253 }
254
255 if (validationEnabled()) {
256 // Validate to make sure every slot in the scope has one value.
257 ConcurrentJSLocker locker(table->m_lock);
258 for (auto iter = table->begin(locker), end = table->end(locker); iter != end; ++iter) {
259 bool found = false;
260 for (unsigned i = materialization->properties().size(); i--;) {
261 const ExitPropertyValue& property = materialization->properties()[i];
262 if (property.location().kind() != ClosureVarPLoc)
263 continue;
264 if (ScopeOffset(property.location().info()) == iter->value.scopeOffset()) {
265 found = true;
266 break;
267 }
268 }
269 ASSERT_UNUSED(found, found);
270 }
271 unsigned numberOfClosureVarPloc = 0;
272 for (unsigned i = materialization->properties().size(); i--;) {
273 const ExitPropertyValue& property = materialization->properties()[i];
274 if (property.location().kind() == ClosureVarPLoc)
275 numberOfClosureVarPloc++;
276 }
277 ASSERT(numberOfClosureVarPloc == table->scopeSize());
278 }
279
280 return result;
281 }
282
283 case PhantomCreateRest:
284 case PhantomDirectArguments:
285 case PhantomClonedArguments: {
286 if (!materialization->origin().inlineCallFrame()) {
287 switch (materialization->type()) {
288 case PhantomDirectArguments:
289 return DirectArguments::createByCopying(globalObject, callFrame);
290 case PhantomClonedArguments:
291 return ClonedArguments::createWithMachineFrame(globalObject, callFrame, ArgumentsMode::Cloned);
292 case PhantomCreateRest: {
293 CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
294 materialization->origin(), callFrame->codeBlock()->baselineAlternative());
295
296 unsigned numberOfArgumentsToSkip = codeBlock->numberOfArgumentsToSkip();
297 JSGlobalObject* globalObject = codeBlock->globalObject();
298 Structure* structure = globalObject->restParameterStructure();
299 JSValue* argumentsToCopyRegion = callFrame->addressOfArgumentsStart() + numberOfArgumentsToSkip;
300 unsigned arraySize = callFrame->argumentCount() > numberOfArgumentsToSkip ? callFrame->argumentCount() - numberOfArgumentsToSkip : 0;
301 return constructArray(globalObject, structure, argumentsToCopyRegion, arraySize);
302 }
303 default:
304 RELEASE_ASSERT_NOT_REACHED();
305 return nullptr;
306 }
307 }
308
309 // First figure out the argument count. If there isn't one then we represent the machine frame.
310 unsigned argumentCount = 0;
311 if (materialization->origin().inlineCallFrame()->isVarargs()) {
312 for (unsigned i = materialization->properties().size(); i--;) {
313 const ExitPropertyValue& property = materialization->properties()[i];
314 if (property.location() != PromotedLocationDescriptor(ArgumentCountPLoc))
315 continue;
316 argumentCount = JSValue::decode(values[i]).asUInt32();
317 break;
318 }
319 } else
320 argumentCount = materialization->origin().inlineCallFrame()->argumentCountIncludingThis;
321 RELEASE_ASSERT(argumentCount);
322
323 JSFunction* callee = nullptr;
324 if (materialization->origin().inlineCallFrame()->isClosureCall) {
325 for (unsigned i = materialization->properties().size(); i--;) {
326 const ExitPropertyValue& property = materialization->properties()[i];
327 if (property.location() != PromotedLocationDescriptor(ArgumentsCalleePLoc))
328 continue;
329
330 callee = jsCast<JSFunction*>(JSValue::decode(values[i]));
331 break;
332 }
333 } else
334 callee = materialization->origin().inlineCallFrame()->calleeConstant();
335 RELEASE_ASSERT(callee);
336
337 CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
338 materialization->origin(), callFrame->codeBlock()->baselineAlternative());
339
340 // We have an inline frame and we have all of the data we need to recreate it.
341 switch (materialization->type()) {
342 case PhantomDirectArguments: {
343 unsigned length = argumentCount - 1;
344 unsigned capacity = std::max(length, static_cast<unsigned>(codeBlock->numParameters() - 1));
345 DirectArguments* result = DirectArguments::create(
346 vm, codeBlock->globalObject()->directArgumentsStructure(), length, capacity);
347 result->setCallee(vm, callee);
348 for (unsigned i = materialization->properties().size(); i--;) {
349 const ExitPropertyValue& property = materialization->properties()[i];
350 if (property.location().kind() != ArgumentPLoc)
351 continue;
352
353 unsigned index = property.location().info();
354 if (index >= capacity)
355 continue;
356
357 // We don't want to use setIndexQuickly(), since that's only for the passed-in
358 // arguments but sometimes the number of named arguments is greater. For
359 // example:
360 //
361 // function foo(a, b, c) { ... }
362 // foo();
363 //
364 // setIndexQuickly() would fail for indices 0, 1, 2 - but we need to recover
365 // those here.
366 result->argument(DirectArgumentsOffset(index)).set(
367 vm, result, JSValue::decode(values[i]));
368 }
369 return result;
370 }
371 case PhantomClonedArguments: {
372 unsigned length = argumentCount - 1;
373 ClonedArguments* result = ClonedArguments::createEmpty(
374 vm, codeBlock->globalObject()->clonedArgumentsStructure(), callee, length);
375
376 for (unsigned i = materialization->properties().size(); i--;) {
377 const ExitPropertyValue& property = materialization->properties()[i];
378 if (property.location().kind() != ArgumentPLoc)
379 continue;
380
381 unsigned index = property.location().info();
382 if (index >= length)
383 continue;
384 result->putDirectIndex(globalObject, index, JSValue::decode(values[i]));
385 }
386
387 return result;
388 }
389 case PhantomCreateRest: {
390 unsigned numberOfArgumentsToSkip = codeBlock->numberOfArgumentsToSkip();
391 JSGlobalObject* globalObject = codeBlock->globalObject();
392 Structure* structure = globalObject->restParameterStructure();
393 ASSERT(argumentCount > 0);
394 unsigned arraySize = (argumentCount - 1) > numberOfArgumentsToSkip ? argumentCount - 1 - numberOfArgumentsToSkip : 0;
395
396 // FIXME: we should throw an out of memory error here if tryCreate() fails.
397 // https://bugs.webkit.org/show_bug.cgi?id=169784
398 JSArray* array = JSArray::tryCreate(vm, structure, arraySize);
399 RELEASE_ASSERT(array);
400
401 for (unsigned i = materialization->properties().size(); i--;) {
402 const ExitPropertyValue& property = materialization->properties()[i];
403 if (property.location().kind() != ArgumentPLoc)
404 continue;
405
406 unsigned argIndex = property.location().info();
407 if (numberOfArgumentsToSkip > argIndex)
408 continue;
409 unsigned arrayIndex = argIndex - numberOfArgumentsToSkip;
410 if (arrayIndex >= arraySize)
411 continue;
412 array->putDirectIndex(globalObject, arrayIndex, JSValue::decode(values[i]));
413 }
414
415#if !ASSERT_DISABLED
416 // We avoid this O(n^2) loop when asserts are disabled, but the condition checked here
417 // must hold to ensure the correctness of the above loop because of how we allocate the array.
418 for (unsigned targetIndex = 0; targetIndex < arraySize; ++targetIndex) {
419 bool found = false;
420 for (unsigned i = materialization->properties().size(); i--;) {
421 const ExitPropertyValue& property = materialization->properties()[i];
422 if (property.location().kind() != ArgumentPLoc)
423 continue;
424
425 unsigned argIndex = property.location().info();
426 if (numberOfArgumentsToSkip > argIndex)
427 continue;
428 unsigned arrayIndex = argIndex - numberOfArgumentsToSkip;
429 if (arrayIndex >= arraySize)
430 continue;
431 if (arrayIndex == targetIndex) {
432 found = true;
433 break;
434 }
435 }
436 ASSERT(found);
437 }
438#endif
439 return array;
440 }
441
442 default:
443 RELEASE_ASSERT_NOT_REACHED();
444 return nullptr;
445 }
446 }
447
448 case PhantomSpread: {
449 JSArray* array = nullptr;
450 for (unsigned i = materialization->properties().size(); i--;) {
451 const ExitPropertyValue& property = materialization->properties()[i];
452 if (property.location().kind() == SpreadPLoc) {
453 array = jsCast<JSArray*>(JSValue::decode(values[i]));
454 break;
455 }
456 }
457 RELEASE_ASSERT(array);
458
459 // Note: it is sound for JSFixedArray::createFromArray to call getDirectIndex here
460 // because we're guaranteed we won't be calling any getters. The reason for this is
461 // that we only support PhantomSpread over CreateRest, which is an array we create.
462 // Any attempts to put a getter on any indices on the rest array will escape the array.
463 JSFixedArray* fixedArray = JSFixedArray::createFromArray(globalObject, vm, array);
464 RELEASE_ASSERT(fixedArray);
465 return fixedArray;
466 }
467
468 case PhantomNewArrayBuffer: {
469 JSImmutableButterfly* immutableButterfly = nullptr;
470 for (unsigned i = materialization->properties().size(); i--;) {
471 const ExitPropertyValue& property = materialization->properties()[i];
472 if (property.location().kind() == NewArrayBufferPLoc) {
473 immutableButterfly = jsCast<JSImmutableButterfly*>(JSValue::decode(values[i]));
474 break;
475 }
476 }
477 RELEASE_ASSERT(immutableButterfly);
478
479 // For now, we use array allocation profile in the actual CodeBlock. It is OK since current NewArrayBuffer
480 // and PhantomNewArrayBuffer are always bound to a specific op_new_array_buffer.
481 CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(materialization->origin(), callFrame->codeBlock()->baselineAlternative());
482 const Instruction* currentInstruction = codeBlock->instructions().at(materialization->origin().bytecodeIndex()).ptr();
483 if (!currentInstruction->is<OpNewArrayBuffer>()) {
484 // This case can happen if Object.keys, an OpCall is first converted into a NewArrayBuffer which is then converted into a PhantomNewArrayBuffer.
485 // There is no need to update the array allocation profile in that case.
486 RELEASE_ASSERT(currentInstruction->is<OpCall>());
487 Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(immutableButterfly->indexingMode());
488 return CommonSlowPaths::allocateNewArrayBuffer(vm, structure, immutableButterfly);
489 }
490 auto newArrayBuffer = currentInstruction->as<OpNewArrayBuffer>();
491 ArrayAllocationProfile* profile = &newArrayBuffer.metadata(codeBlock).m_arrayAllocationProfile;
492
493 // FIXME: Share the code with CommonSlowPaths. Currently, codeBlock etc. are slightly different.
494 IndexingType indexingMode = profile->selectIndexingType();
495 Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingMode);
496 ASSERT(isCopyOnWrite(indexingMode));
497 ASSERT(!structure->outOfLineCapacity());
498
499 if (UNLIKELY(immutableButterfly->indexingMode() != indexingMode)) {
500 auto* newButterfly = JSImmutableButterfly::create(vm, indexingMode, immutableButterfly->length());
501 for (unsigned i = 0; i < immutableButterfly->length(); ++i)
502 newButterfly->setIndex(vm, i, immutableButterfly->get(i));
503 immutableButterfly = newButterfly;
504
505 // FIXME: This is kinda gross and only works because we can't inline new_array_bufffer in the baseline.
506 // We also cannot allocate a new butterfly from compilation threads since it's invalid to allocate cells from
507 // a compilation thread.
508 WTF::storeStoreFence();
509 codeBlock->constantRegister(newArrayBuffer.m_immutableButterfly.offset()).set(vm, codeBlock, immutableButterfly);
510 WTF::storeStoreFence();
511 }
512
513 JSArray* result = CommonSlowPaths::allocateNewArrayBuffer(vm, structure, immutableButterfly);
514 ArrayAllocationProfile::updateLastAllocationFor(profile, result);
515 return result;
516 }
517
518 case PhantomNewArrayWithSpread: {
519 CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
520 materialization->origin(), callFrame->codeBlock()->baselineAlternative());
521 JSGlobalObject* globalObject = codeBlock->globalObject();
522 Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous);
523
524 Checked<unsigned, RecordOverflow> checkedArraySize = 0;
525 unsigned numProperties = 0;
526 for (unsigned i = materialization->properties().size(); i--;) {
527 const ExitPropertyValue& property = materialization->properties()[i];
528 if (property.location().kind() == NewArrayWithSpreadArgumentPLoc) {
529 ++numProperties;
530 JSValue value = JSValue::decode(values[i]);
531 if (JSFixedArray* fixedArray = jsDynamicCast<JSFixedArray*>(vm, value))
532 checkedArraySize += fixedArray->size();
533 else
534 checkedArraySize += 1;
535 }
536 }
537
538 // FIXME: we should throw an out of memory error here if checkedArraySize has hasOverflowed() or tryCreate() fails.
539 // https://bugs.webkit.org/show_bug.cgi?id=169784
540 unsigned arraySize = checkedArraySize.unsafeGet(); // Crashes if overflowed.
541 JSArray* result = JSArray::tryCreate(vm, structure, arraySize);
542 RELEASE_ASSERT(result);
543
544#if !ASSERT_DISABLED
545 // Ensure we see indices for everything in the range: [0, numProperties)
546 for (unsigned i = 0; i < numProperties; ++i) {
547 bool found = false;
548 for (unsigned j = 0; j < materialization->properties().size(); ++j) {
549 const ExitPropertyValue& property = materialization->properties()[j];
550 if (property.location().kind() == NewArrayWithSpreadArgumentPLoc && property.location().info() == i) {
551 found = true;
552 break;
553 }
554 }
555 ASSERT(found);
556 }
557#endif
558
559 Vector<JSValue, 8> arguments;
560 arguments.grow(numProperties);
561
562 for (unsigned i = materialization->properties().size(); i--;) {
563 const ExitPropertyValue& property = materialization->properties()[i];
564 if (property.location().kind() == NewArrayWithSpreadArgumentPLoc) {
565 JSValue value = JSValue::decode(values[i]);
566 RELEASE_ASSERT(property.location().info() < numProperties);
567 arguments[property.location().info()] = value;
568 }
569 }
570
571 unsigned arrayIndex = 0;
572 for (JSValue value : arguments) {
573 if (JSFixedArray* fixedArray = jsDynamicCast<JSFixedArray*>(vm, value)) {
574 for (unsigned i = 0; i < fixedArray->size(); i++) {
575 ASSERT(fixedArray->get(i));
576 result->putDirectIndex(globalObject, arrayIndex, fixedArray->get(i));
577 ++arrayIndex;
578 }
579 } else {
580 // We are not spreading.
581 result->putDirectIndex(globalObject, arrayIndex, value);
582 ++arrayIndex;
583 }
584 }
585
586 return result;
587 }
588
589 case PhantomNewRegexp: {
590 RegExp* regExp = nullptr;
591 for (unsigned i = materialization->properties().size(); i--;) {
592 const ExitPropertyValue& property = materialization->properties()[i];
593 if (property.location() == PromotedLocationDescriptor(RegExpObjectRegExpPLoc)) {
594 RELEASE_ASSERT(JSValue::decode(values[i]).asCell()->inherits<RegExp>(vm));
595 regExp = jsCast<RegExp*>(JSValue::decode(values[i]));
596 }
597 }
598 RELEASE_ASSERT(regExp);
599 CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(materialization->origin(), callFrame->codeBlock()->baselineAlternative());
600 Structure* structure = codeBlock->globalObject()->regExpStructure();
601 return RegExpObject::create(vm, structure, regExp);
602 }
603
604 default:
605 RELEASE_ASSERT_NOT_REACHED();
606 return nullptr;
607 }
608}
609
610extern "C" void* JIT_OPERATION operationCompileFTLLazySlowPath(CallFrame* callFrame, unsigned index)
611{
612 VM& vm = callFrame->deprecatedVM();
613
614 // We cannot GC. We've got pointers in evil places.
615 DeferGCForAWhile deferGC(vm.heap);
616
617 CodeBlock* codeBlock = callFrame->codeBlock();
618 JITCode* jitCode = codeBlock->jitCode()->ftl();
619
620 LazySlowPath& lazySlowPath = *jitCode->lazySlowPaths[index];
621 lazySlowPath.generate(codeBlock);
622
623 return lazySlowPath.stub().code().executableAddress();
624}
625
626} } // namespace JSC::FTL
627
628IGNORE_WARNINGS_END
629
630#endif // ENABLE(FTL_JIT)
631
632