1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/runtime/runtime-utils.h"
6
7#include "src/allocation-site-scopes.h"
8#include "src/arguments.h"
9#include "src/ast/ast.h"
10#include "src/ast/compile-time-value.h"
11#include "src/isolate-inl.h"
12#include "src/runtime/runtime.h"
13
14namespace v8 {
15namespace internal {
16
17static Handle<Map> ComputeObjectLiteralMap(
18    Handle<Context> context, Handle<FixedArray> constant_properties,
19    bool* is_result_from_cache) {
20  int properties_length = constant_properties->length();
21  int number_of_properties = properties_length / 2;
22
23  for (int p = 0; p != properties_length; p += 2) {
24    Object* key = constant_properties->get(p);
25    uint32_t element_index = 0;
26    if (key->ToArrayIndex(&element_index)) {
27      // An index key does not require space in the property backing store.
28      number_of_properties--;
29    }
30  }
31  Isolate* isolate = context->GetIsolate();
32  return isolate->factory()->ObjectLiteralMapFromCache(
33      context, number_of_properties, is_result_from_cache);
34}
35
36MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
37    Isolate* isolate, Handle<LiteralsArray> literals,
38    Handle<FixedArray> constant_properties);
39
40MUST_USE_RESULT static MaybeHandle<Object> CreateObjectLiteralBoilerplate(
41    Isolate* isolate, Handle<LiteralsArray> literals,
42    Handle<FixedArray> constant_properties, bool should_have_fast_elements) {
43  Handle<Context> context = isolate->native_context();
44
45  // In case we have function literals, we want the object to be in
46  // slow properties mode for now. We don't go in the map cache because
47  // maps with constant functions can't be shared if the functions are
48  // not the same (which is the common case).
49  bool is_result_from_cache = false;
50  Handle<Map> map = ComputeObjectLiteralMap(context, constant_properties,
51                                            &is_result_from_cache);
52
53  PretenureFlag pretenure_flag =
54      isolate->heap()->InNewSpace(*literals) ? NOT_TENURED : TENURED;
55
56  Handle<JSObject> boilerplate =
57      isolate->factory()->NewJSObjectFromMap(map, pretenure_flag);
58
59  // Normalize the elements of the boilerplate to save space if needed.
60  if (!should_have_fast_elements) JSObject::NormalizeElements(boilerplate);
61
62  // Add the constant properties to the boilerplate.
63  int length = constant_properties->length();
64  bool should_transform =
65      !is_result_from_cache && boilerplate->HasFastProperties();
66  bool should_normalize = should_transform;
67  if (should_normalize) {
68    // TODO(verwaest): We might not want to ever normalize here.
69    JSObject::NormalizeProperties(boilerplate, KEEP_INOBJECT_PROPERTIES,
70                                  length / 2, "Boilerplate");
71  }
72  // TODO(verwaest): Support tracking representations in the boilerplate.
73  for (int index = 0; index < length; index += 2) {
74    Handle<Object> key(constant_properties->get(index + 0), isolate);
75    Handle<Object> value(constant_properties->get(index + 1), isolate);
76    if (value->IsFixedArray()) {
77      // The value contains the constant_properties of a
78      // simple object or array literal.
79      Handle<FixedArray> array = Handle<FixedArray>::cast(value);
80      ASSIGN_RETURN_ON_EXCEPTION(
81          isolate, value, CreateLiteralBoilerplate(isolate, literals, array),
82          Object);
83    }
84    MaybeHandle<Object> maybe_result;
85    uint32_t element_index = 0;
86    if (key->ToArrayIndex(&element_index)) {
87      // Array index (uint32).
88      if (value->IsUninitialized(isolate)) {
89        value = handle(Smi::kZero, isolate);
90      }
91      maybe_result = JSObject::SetOwnElementIgnoreAttributes(
92          boilerplate, element_index, value, NONE);
93    } else {
94      Handle<String> name = Handle<String>::cast(key);
95      DCHECK(!name->AsArrayIndex(&element_index));
96      maybe_result = JSObject::SetOwnPropertyIgnoreAttributes(boilerplate, name,
97                                                              value, NONE);
98    }
99    RETURN_ON_EXCEPTION(isolate, maybe_result, Object);
100  }
101
102  // Transform to fast properties if necessary. For object literals with
103  // containing function literals we defer this operation until after all
104  // computed properties have been assigned so that we can generate
105  // constant function properties.
106  if (should_transform) {
107    JSObject::MigrateSlowToFast(boilerplate,
108                                boilerplate->map()->unused_property_fields(),
109                                "FastLiteral");
110  }
111  return boilerplate;
112}
113
114static MaybeHandle<Object> CreateArrayLiteralBoilerplate(
115    Isolate* isolate, Handle<LiteralsArray> literals,
116    Handle<FixedArray> elements) {
117  // Create the JSArray.
118  Handle<JSFunction> constructor = isolate->array_function();
119
120  PretenureFlag pretenure_flag =
121      isolate->heap()->InNewSpace(*literals) ? NOT_TENURED : TENURED;
122
123  Handle<JSArray> object = Handle<JSArray>::cast(
124      isolate->factory()->NewJSObject(constructor, pretenure_flag));
125
126  ElementsKind constant_elements_kind =
127      static_cast<ElementsKind>(Smi::cast(elements->get(0))->value());
128  Handle<FixedArrayBase> constant_elements_values(
129      FixedArrayBase::cast(elements->get(1)));
130
131  {
132    DisallowHeapAllocation no_gc;
133    DCHECK(IsFastElementsKind(constant_elements_kind));
134    Context* native_context = isolate->context()->native_context();
135    Object* map =
136        native_context->get(Context::ArrayMapIndex(constant_elements_kind));
137    object->set_map(Map::cast(map));
138  }
139
140  Handle<FixedArrayBase> copied_elements_values;
141  if (IsFastDoubleElementsKind(constant_elements_kind)) {
142    copied_elements_values = isolate->factory()->CopyFixedDoubleArray(
143        Handle<FixedDoubleArray>::cast(constant_elements_values));
144  } else {
145    DCHECK(IsFastSmiOrObjectElementsKind(constant_elements_kind));
146    const bool is_cow = (constant_elements_values->map() ==
147                         isolate->heap()->fixed_cow_array_map());
148    if (is_cow) {
149      copied_elements_values = constant_elements_values;
150#if DEBUG
151      Handle<FixedArray> fixed_array_values =
152          Handle<FixedArray>::cast(copied_elements_values);
153      for (int i = 0; i < fixed_array_values->length(); i++) {
154        DCHECK(!fixed_array_values->get(i)->IsFixedArray());
155      }
156#endif
157    } else {
158      Handle<FixedArray> fixed_array_values =
159          Handle<FixedArray>::cast(constant_elements_values);
160      Handle<FixedArray> fixed_array_values_copy =
161          isolate->factory()->CopyFixedArray(fixed_array_values);
162      copied_elements_values = fixed_array_values_copy;
163      FOR_WITH_HANDLE_SCOPE(
164          isolate, int, i = 0, i, i < fixed_array_values->length(), i++, {
165            if (fixed_array_values->get(i)->IsFixedArray()) {
166              // The value contains the constant_properties of a
167              // simple object or array literal.
168              Handle<FixedArray> fa(
169                  FixedArray::cast(fixed_array_values->get(i)));
170              Handle<Object> result;
171              ASSIGN_RETURN_ON_EXCEPTION(
172                  isolate, result,
173                  CreateLiteralBoilerplate(isolate, literals, fa), Object);
174              fixed_array_values_copy->set(i, *result);
175            }
176          });
177    }
178  }
179  object->set_elements(*copied_elements_values);
180  object->set_length(Smi::FromInt(copied_elements_values->length()));
181
182  JSObject::ValidateElements(object);
183  return object;
184}
185
186MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
187    Isolate* isolate, Handle<LiteralsArray> literals,
188    Handle<FixedArray> array) {
189  Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
190  switch (CompileTimeValue::GetLiteralType(array)) {
191    case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
192      return CreateObjectLiteralBoilerplate(isolate, literals, elements, true);
193    case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
194      return CreateObjectLiteralBoilerplate(isolate, literals, elements, false);
195    case CompileTimeValue::ARRAY_LITERAL:
196      return CreateArrayLiteralBoilerplate(isolate, literals, elements);
197    default:
198      UNREACHABLE();
199      return MaybeHandle<Object>();
200  }
201}
202
203
204RUNTIME_FUNCTION(Runtime_CreateRegExpLiteral) {
205  HandleScope scope(isolate);
206  DCHECK_EQ(4, args.length());
207  CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
208  CONVERT_SMI_ARG_CHECKED(index, 1);
209  CONVERT_ARG_HANDLE_CHECKED(String, pattern, 2);
210  CONVERT_SMI_ARG_CHECKED(flags, 3);
211
212  // Check if boilerplate exists. If not, create it first.
213  Handle<Object> boilerplate(closure->literals()->literal(index), isolate);
214  if (boilerplate->IsUndefined(isolate)) {
215    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
216        isolate, boilerplate, JSRegExp::New(pattern, JSRegExp::Flags(flags)));
217    closure->literals()->set_literal(index, *boilerplate);
218  }
219  return *JSRegExp::Copy(Handle<JSRegExp>::cast(boilerplate));
220}
221
222
223RUNTIME_FUNCTION(Runtime_CreateObjectLiteral) {
224  HandleScope scope(isolate);
225  DCHECK_EQ(4, args.length());
226  CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
227  CONVERT_SMI_ARG_CHECKED(literals_index, 1);
228  CONVERT_ARG_HANDLE_CHECKED(FixedArray, constant_properties, 2);
229  CONVERT_SMI_ARG_CHECKED(flags, 3);
230  Handle<LiteralsArray> literals(closure->literals(), isolate);
231  bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0;
232  bool enable_mementos = (flags & ObjectLiteral::kDisableMementos) == 0;
233
234  CHECK(literals_index >= 0);
235  CHECK(literals_index < literals->literals_count());
236
237  // Check if boilerplate exists. If not, create it first.
238  Handle<Object> literal_site(literals->literal(literals_index), isolate);
239  Handle<AllocationSite> site;
240  Handle<JSObject> boilerplate;
241  if (literal_site->IsUndefined(isolate)) {
242    Handle<Object> raw_boilerplate;
243    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
244        isolate, raw_boilerplate,
245        CreateObjectLiteralBoilerplate(isolate, literals, constant_properties,
246                                       should_have_fast_elements));
247    boilerplate = Handle<JSObject>::cast(raw_boilerplate);
248
249    AllocationSiteCreationContext creation_context(isolate);
250    site = creation_context.EnterNewScope();
251    RETURN_FAILURE_ON_EXCEPTION(
252        isolate, JSObject::DeepWalk(boilerplate, &creation_context));
253    creation_context.ExitScope(site, boilerplate);
254
255    // Update the functions literal and return the boilerplate.
256    literals->set_literal(literals_index, *site);
257  } else {
258    site = Handle<AllocationSite>::cast(literal_site);
259    boilerplate =
260        Handle<JSObject>(JSObject::cast(site->transition_info()), isolate);
261  }
262
263  AllocationSiteUsageContext usage_context(isolate, site, enable_mementos);
264  usage_context.EnterNewScope();
265  MaybeHandle<Object> maybe_copy =
266      JSObject::DeepCopy(boilerplate, &usage_context);
267  usage_context.ExitScope(site, boilerplate);
268  RETURN_RESULT_OR_FAILURE(isolate, maybe_copy);
269}
270
271MUST_USE_RESULT static MaybeHandle<AllocationSite> GetLiteralAllocationSite(
272    Isolate* isolate, Handle<LiteralsArray> literals, int literals_index,
273    Handle<FixedArray> elements) {
274  // Check if boilerplate exists. If not, create it first.
275  Handle<Object> literal_site(literals->literal(literals_index), isolate);
276  Handle<AllocationSite> site;
277  if (literal_site->IsUndefined(isolate)) {
278    DCHECK(*elements != isolate->heap()->empty_fixed_array());
279    Handle<Object> boilerplate;
280    ASSIGN_RETURN_ON_EXCEPTION(
281        isolate, boilerplate,
282        CreateArrayLiteralBoilerplate(isolate, literals, elements),
283        AllocationSite);
284
285    AllocationSiteCreationContext creation_context(isolate);
286    site = creation_context.EnterNewScope();
287    if (JSObject::DeepWalk(Handle<JSObject>::cast(boilerplate),
288                           &creation_context).is_null()) {
289      return Handle<AllocationSite>::null();
290    }
291    creation_context.ExitScope(site, Handle<JSObject>::cast(boilerplate));
292
293    literals->set_literal(literals_index, *site);
294  } else {
295    site = Handle<AllocationSite>::cast(literal_site);
296  }
297
298  return site;
299}
300
301
302static MaybeHandle<JSObject> CreateArrayLiteralImpl(
303    Isolate* isolate, Handle<LiteralsArray> literals, int literals_index,
304    Handle<FixedArray> elements, int flags) {
305  CHECK(literals_index >= 0 && literals_index < literals->literals_count());
306  Handle<AllocationSite> site;
307  ASSIGN_RETURN_ON_EXCEPTION(
308      isolate, site,
309      GetLiteralAllocationSite(isolate, literals, literals_index, elements),
310      JSObject);
311
312  bool enable_mementos = (flags & ArrayLiteral::kDisableMementos) == 0;
313  Handle<JSObject> boilerplate(JSObject::cast(site->transition_info()));
314  AllocationSiteUsageContext usage_context(isolate, site, enable_mementos);
315  usage_context.EnterNewScope();
316  JSObject::DeepCopyHints hints = (flags & ArrayLiteral::kShallowElements) == 0
317                                      ? JSObject::kNoHints
318                                      : JSObject::kObjectIsShallow;
319  MaybeHandle<JSObject> copy =
320      JSObject::DeepCopy(boilerplate, &usage_context, hints);
321  usage_context.ExitScope(site, boilerplate);
322  return copy;
323}
324
325
326RUNTIME_FUNCTION(Runtime_CreateArrayLiteral) {
327  HandleScope scope(isolate);
328  DCHECK_EQ(4, args.length());
329  CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
330  CONVERT_SMI_ARG_CHECKED(literals_index, 1);
331  CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2);
332  CONVERT_SMI_ARG_CHECKED(flags, 3);
333
334  Handle<LiteralsArray> literals(closure->literals(), isolate);
335  RETURN_RESULT_OR_FAILURE(
336      isolate, CreateArrayLiteralImpl(isolate, literals, literals_index,
337                                      elements, flags));
338}
339
340
341RUNTIME_FUNCTION(Runtime_CreateArrayLiteralStubBailout) {
342  HandleScope scope(isolate);
343  DCHECK_EQ(3, args.length());
344  CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
345  CONVERT_SMI_ARG_CHECKED(literals_index, 1);
346  CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2);
347
348  Handle<LiteralsArray> literals(closure->literals(), isolate);
349  RETURN_RESULT_OR_FAILURE(
350      isolate,
351      CreateArrayLiteralImpl(isolate, literals, literals_index, elements,
352                             ArrayLiteral::kShallowElements));
353}
354
355}  // namespace internal
356}  // namespace v8
357