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