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