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