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 
14 namespace v8 {
15 namespace internal {
16 
ComputeObjectLiteralMap(Handle<Context> context,Handle<FixedArray> constant_properties,bool * is_result_from_cache)17 static 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 
36 MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
37     Isolate* isolate, Handle<LiteralsArray> literals,
38     Handle<FixedArray> constant_properties);
39 
CreateObjectLiteralBoilerplate(Isolate * isolate,Handle<LiteralsArray> literals,Handle<FixedArray> constant_properties,bool should_have_fast_elements)40 MUST_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 
CreateArrayLiteralBoilerplate(Isolate * isolate,Handle<LiteralsArray> literals,Handle<FixedArray> elements)114 static 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 
CreateLiteralBoilerplate(Isolate * isolate,Handle<LiteralsArray> literals,Handle<FixedArray> array)186 MUST_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 
RUNTIME_FUNCTION(Runtime_CreateRegExpLiteral)204 RUNTIME_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 
RUNTIME_FUNCTION(Runtime_CreateObjectLiteral)223 RUNTIME_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 
GetLiteralAllocationSite(Isolate * isolate,Handle<LiteralsArray> literals,int literals_index,Handle<FixedArray> elements)271 MUST_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 
CreateArrayLiteralImpl(Isolate * isolate,Handle<LiteralsArray> literals,int literals_index,Handle<FixedArray> elements,int flags)302 static 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 
RUNTIME_FUNCTION(Runtime_CreateArrayLiteral)326 RUNTIME_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 
RUNTIME_FUNCTION(Runtime_CreateArrayLiteralStubBailout)341 RUNTIME_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