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