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 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 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 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 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 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 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 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 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 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 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