OLD | NEW |
(Empty) | |
| 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/v8.h" |
| 6 |
| 7 #include "src/allocation-site-scopes.h" |
| 8 #include "src/arguments.h" |
| 9 #include "src/ast.h" |
| 10 #include "src/parser.h" |
| 11 #include "src/runtime/runtime.h" |
| 12 #include "src/runtime/runtime-utils.h" |
| 13 |
| 14 namespace v8 { |
| 15 namespace internal { |
| 16 |
| 17 static Handle<Map> ComputeObjectLiteralMap( |
| 18 Handle<Context> context, Handle<FixedArray> constant_properties, |
| 19 bool* is_result_from_cache) { |
| 20 Isolate* isolate = context->GetIsolate(); |
| 21 int properties_length = constant_properties->length(); |
| 22 int number_of_properties = properties_length / 2; |
| 23 // Check that there are only internal strings and array indices among keys. |
| 24 int number_of_string_keys = 0; |
| 25 for (int p = 0; p != properties_length; p += 2) { |
| 26 Object* key = constant_properties->get(p); |
| 27 uint32_t element_index = 0; |
| 28 if (key->IsInternalizedString()) { |
| 29 number_of_string_keys++; |
| 30 } else if (key->ToArrayIndex(&element_index)) { |
| 31 // An index key does not require space in the property backing store. |
| 32 number_of_properties--; |
| 33 } else { |
| 34 // Bail out as a non-internalized-string non-index key makes caching |
| 35 // impossible. |
| 36 // DCHECK to make sure that the if condition after the loop is false. |
| 37 DCHECK(number_of_string_keys != number_of_properties); |
| 38 break; |
| 39 } |
| 40 } |
| 41 // If we only have internalized strings and array indices among keys then we |
| 42 // can use the map cache in the native context. |
| 43 const int kMaxKeys = 10; |
| 44 if ((number_of_string_keys == number_of_properties) && |
| 45 (number_of_string_keys < kMaxKeys)) { |
| 46 // Create the fixed array with the key. |
| 47 Handle<FixedArray> keys = |
| 48 isolate->factory()->NewFixedArray(number_of_string_keys); |
| 49 if (number_of_string_keys > 0) { |
| 50 int index = 0; |
| 51 for (int p = 0; p < properties_length; p += 2) { |
| 52 Object* key = constant_properties->get(p); |
| 53 if (key->IsInternalizedString()) { |
| 54 keys->set(index++, key); |
| 55 } |
| 56 } |
| 57 DCHECK(index == number_of_string_keys); |
| 58 } |
| 59 *is_result_from_cache = true; |
| 60 return isolate->factory()->ObjectLiteralMapFromCache(context, keys); |
| 61 } |
| 62 *is_result_from_cache = false; |
| 63 return Map::Create(isolate, number_of_properties); |
| 64 } |
| 65 |
| 66 |
| 67 MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate( |
| 68 Isolate* isolate, Handle<FixedArray> literals, |
| 69 Handle<FixedArray> constant_properties); |
| 70 |
| 71 |
| 72 MUST_USE_RESULT static MaybeHandle<Object> CreateObjectLiteralBoilerplate( |
| 73 Isolate* isolate, Handle<FixedArray> literals, |
| 74 Handle<FixedArray> constant_properties, bool should_have_fast_elements, |
| 75 bool has_function_literal) { |
| 76 // Get the native context from the literals array. This is the |
| 77 // context in which the function was created and we use the object |
| 78 // function from this context to create the object literal. We do |
| 79 // not use the object function from the current native context |
| 80 // because this might be the object function from another context |
| 81 // which we should not have access to. |
| 82 Handle<Context> context = |
| 83 Handle<Context>(JSFunction::NativeContextFromLiterals(*literals)); |
| 84 |
| 85 // In case we have function literals, we want the object to be in |
| 86 // slow properties mode for now. We don't go in the map cache because |
| 87 // maps with constant functions can't be shared if the functions are |
| 88 // not the same (which is the common case). |
| 89 bool is_result_from_cache = false; |
| 90 Handle<Map> map = has_function_literal |
| 91 ? Handle<Map>(context->object_function()->initial_map()) |
| 92 : ComputeObjectLiteralMap(context, constant_properties, |
| 93 &is_result_from_cache); |
| 94 |
| 95 PretenureFlag pretenure_flag = |
| 96 isolate->heap()->InNewSpace(*literals) ? NOT_TENURED : TENURED; |
| 97 |
| 98 Handle<JSObject> boilerplate = |
| 99 isolate->factory()->NewJSObjectFromMap(map, pretenure_flag); |
| 100 |
| 101 // Normalize the elements of the boilerplate to save space if needed. |
| 102 if (!should_have_fast_elements) JSObject::NormalizeElements(boilerplate); |
| 103 |
| 104 // Add the constant properties to the boilerplate. |
| 105 int length = constant_properties->length(); |
| 106 bool should_transform = |
| 107 !is_result_from_cache && boilerplate->HasFastProperties(); |
| 108 bool should_normalize = should_transform || has_function_literal; |
| 109 if (should_normalize) { |
| 110 // TODO(verwaest): We might not want to ever normalize here. |
| 111 JSObject::NormalizeProperties(boilerplate, KEEP_INOBJECT_PROPERTIES, |
| 112 length / 2); |
| 113 } |
| 114 // TODO(verwaest): Support tracking representations in the boilerplate. |
| 115 for (int index = 0; index < length; index += 2) { |
| 116 Handle<Object> key(constant_properties->get(index + 0), isolate); |
| 117 Handle<Object> value(constant_properties->get(index + 1), isolate); |
| 118 if (value->IsFixedArray()) { |
| 119 // The value contains the constant_properties of a |
| 120 // simple object or array literal. |
| 121 Handle<FixedArray> array = Handle<FixedArray>::cast(value); |
| 122 ASSIGN_RETURN_ON_EXCEPTION( |
| 123 isolate, value, CreateLiteralBoilerplate(isolate, literals, array), |
| 124 Object); |
| 125 } |
| 126 MaybeHandle<Object> maybe_result; |
| 127 uint32_t element_index = 0; |
| 128 if (key->IsInternalizedString()) { |
| 129 if (Handle<String>::cast(key)->AsArrayIndex(&element_index)) { |
| 130 // Array index as string (uint32). |
| 131 if (value->IsUninitialized()) value = handle(Smi::FromInt(0), isolate); |
| 132 maybe_result = |
| 133 JSObject::SetOwnElement(boilerplate, element_index, value, SLOPPY); |
| 134 } else { |
| 135 Handle<String> name(String::cast(*key)); |
| 136 DCHECK(!name->AsArrayIndex(&element_index)); |
| 137 maybe_result = JSObject::SetOwnPropertyIgnoreAttributes( |
| 138 boilerplate, name, value, NONE); |
| 139 } |
| 140 } else if (key->ToArrayIndex(&element_index)) { |
| 141 // Array index (uint32). |
| 142 if (value->IsUninitialized()) value = handle(Smi::FromInt(0), isolate); |
| 143 maybe_result = |
| 144 JSObject::SetOwnElement(boilerplate, element_index, value, SLOPPY); |
| 145 } else { |
| 146 // Non-uint32 number. |
| 147 DCHECK(key->IsNumber()); |
| 148 double num = key->Number(); |
| 149 char arr[100]; |
| 150 Vector<char> buffer(arr, arraysize(arr)); |
| 151 const char* str = DoubleToCString(num, buffer); |
| 152 Handle<String> name = isolate->factory()->NewStringFromAsciiChecked(str); |
| 153 maybe_result = JSObject::SetOwnPropertyIgnoreAttributes(boilerplate, name, |
| 154 value, NONE); |
| 155 } |
| 156 // If setting the property on the boilerplate throws an |
| 157 // exception, the exception is converted to an empty handle in |
| 158 // the handle based operations. In that case, we need to |
| 159 // convert back to an exception. |
| 160 RETURN_ON_EXCEPTION(isolate, maybe_result, Object); |
| 161 } |
| 162 |
| 163 // Transform to fast properties if necessary. For object literals with |
| 164 // containing function literals we defer this operation until after all |
| 165 // computed properties have been assigned so that we can generate |
| 166 // constant function properties. |
| 167 if (should_transform && !has_function_literal) { |
| 168 JSObject::MigrateSlowToFast(boilerplate, |
| 169 boilerplate->map()->unused_property_fields()); |
| 170 } |
| 171 |
| 172 return boilerplate; |
| 173 } |
| 174 |
| 175 |
| 176 MaybeHandle<Object> Runtime::CreateArrayLiteralBoilerplate( |
| 177 Isolate* isolate, Handle<FixedArray> literals, |
| 178 Handle<FixedArray> elements) { |
| 179 // Create the JSArray. |
| 180 Handle<JSFunction> constructor( |
| 181 JSFunction::NativeContextFromLiterals(*literals)->array_function()); |
| 182 |
| 183 PretenureFlag pretenure_flag = |
| 184 isolate->heap()->InNewSpace(*literals) ? NOT_TENURED : TENURED; |
| 185 |
| 186 Handle<JSArray> object = Handle<JSArray>::cast( |
| 187 isolate->factory()->NewJSObject(constructor, pretenure_flag)); |
| 188 |
| 189 ElementsKind constant_elements_kind = |
| 190 static_cast<ElementsKind>(Smi::cast(elements->get(0))->value()); |
| 191 Handle<FixedArrayBase> constant_elements_values( |
| 192 FixedArrayBase::cast(elements->get(1))); |
| 193 |
| 194 { |
| 195 DisallowHeapAllocation no_gc; |
| 196 DCHECK(IsFastElementsKind(constant_elements_kind)); |
| 197 Context* native_context = isolate->context()->native_context(); |
| 198 Object* maps_array = native_context->js_array_maps(); |
| 199 DCHECK(!maps_array->IsUndefined()); |
| 200 Object* map = FixedArray::cast(maps_array)->get(constant_elements_kind); |
| 201 object->set_map(Map::cast(map)); |
| 202 } |
| 203 |
| 204 Handle<FixedArrayBase> copied_elements_values; |
| 205 if (IsFastDoubleElementsKind(constant_elements_kind)) { |
| 206 copied_elements_values = isolate->factory()->CopyFixedDoubleArray( |
| 207 Handle<FixedDoubleArray>::cast(constant_elements_values)); |
| 208 } else { |
| 209 DCHECK(IsFastSmiOrObjectElementsKind(constant_elements_kind)); |
| 210 const bool is_cow = (constant_elements_values->map() == |
| 211 isolate->heap()->fixed_cow_array_map()); |
| 212 if (is_cow) { |
| 213 copied_elements_values = constant_elements_values; |
| 214 #if DEBUG |
| 215 Handle<FixedArray> fixed_array_values = |
| 216 Handle<FixedArray>::cast(copied_elements_values); |
| 217 for (int i = 0; i < fixed_array_values->length(); i++) { |
| 218 DCHECK(!fixed_array_values->get(i)->IsFixedArray()); |
| 219 } |
| 220 #endif |
| 221 } else { |
| 222 Handle<FixedArray> fixed_array_values = |
| 223 Handle<FixedArray>::cast(constant_elements_values); |
| 224 Handle<FixedArray> fixed_array_values_copy = |
| 225 isolate->factory()->CopyFixedArray(fixed_array_values); |
| 226 copied_elements_values = fixed_array_values_copy; |
| 227 for (int i = 0; i < fixed_array_values->length(); i++) { |
| 228 if (fixed_array_values->get(i)->IsFixedArray()) { |
| 229 // The value contains the constant_properties of a |
| 230 // simple object or array literal. |
| 231 Handle<FixedArray> fa(FixedArray::cast(fixed_array_values->get(i))); |
| 232 Handle<Object> result; |
| 233 ASSIGN_RETURN_ON_EXCEPTION( |
| 234 isolate, result, CreateLiteralBoilerplate(isolate, literals, fa), |
| 235 Object); |
| 236 fixed_array_values_copy->set(i, *result); |
| 237 } |
| 238 } |
| 239 } |
| 240 } |
| 241 object->set_elements(*copied_elements_values); |
| 242 object->set_length(Smi::FromInt(copied_elements_values->length())); |
| 243 |
| 244 JSObject::ValidateElements(object); |
| 245 return object; |
| 246 } |
| 247 |
| 248 |
| 249 MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate( |
| 250 Isolate* isolate, Handle<FixedArray> literals, Handle<FixedArray> array) { |
| 251 Handle<FixedArray> elements = CompileTimeValue::GetElements(array); |
| 252 const bool kHasNoFunctionLiteral = false; |
| 253 switch (CompileTimeValue::GetLiteralType(array)) { |
| 254 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS: |
| 255 return CreateObjectLiteralBoilerplate(isolate, literals, elements, true, |
| 256 kHasNoFunctionLiteral); |
| 257 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS: |
| 258 return CreateObjectLiteralBoilerplate(isolate, literals, elements, false, |
| 259 kHasNoFunctionLiteral); |
| 260 case CompileTimeValue::ARRAY_LITERAL: |
| 261 return Runtime::CreateArrayLiteralBoilerplate(isolate, literals, |
| 262 elements); |
| 263 default: |
| 264 UNREACHABLE(); |
| 265 return MaybeHandle<Object>(); |
| 266 } |
| 267 } |
| 268 |
| 269 |
| 270 RUNTIME_FUNCTION(Runtime_CreateObjectLiteral) { |
| 271 HandleScope scope(isolate); |
| 272 DCHECK(args.length() == 4); |
| 273 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); |
| 274 CONVERT_SMI_ARG_CHECKED(literals_index, 1); |
| 275 CONVERT_ARG_HANDLE_CHECKED(FixedArray, constant_properties, 2); |
| 276 CONVERT_SMI_ARG_CHECKED(flags, 3); |
| 277 bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0; |
| 278 bool has_function_literal = (flags & ObjectLiteral::kHasFunction) != 0; |
| 279 |
| 280 RUNTIME_ASSERT(literals_index >= 0 && literals_index < literals->length()); |
| 281 |
| 282 // Check if boilerplate exists. If not, create it first. |
| 283 Handle<Object> literal_site(literals->get(literals_index), isolate); |
| 284 Handle<AllocationSite> site; |
| 285 Handle<JSObject> boilerplate; |
| 286 if (*literal_site == isolate->heap()->undefined_value()) { |
| 287 Handle<Object> raw_boilerplate; |
| 288 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| 289 isolate, raw_boilerplate, |
| 290 CreateObjectLiteralBoilerplate(isolate, literals, constant_properties, |
| 291 should_have_fast_elements, |
| 292 has_function_literal)); |
| 293 boilerplate = Handle<JSObject>::cast(raw_boilerplate); |
| 294 |
| 295 AllocationSiteCreationContext creation_context(isolate); |
| 296 site = creation_context.EnterNewScope(); |
| 297 RETURN_FAILURE_ON_EXCEPTION( |
| 298 isolate, JSObject::DeepWalk(boilerplate, &creation_context)); |
| 299 creation_context.ExitScope(site, boilerplate); |
| 300 |
| 301 // Update the functions literal and return the boilerplate. |
| 302 literals->set(literals_index, *site); |
| 303 } else { |
| 304 site = Handle<AllocationSite>::cast(literal_site); |
| 305 boilerplate = |
| 306 Handle<JSObject>(JSObject::cast(site->transition_info()), isolate); |
| 307 } |
| 308 |
| 309 AllocationSiteUsageContext usage_context(isolate, site, true); |
| 310 usage_context.EnterNewScope(); |
| 311 MaybeHandle<Object> maybe_copy = |
| 312 JSObject::DeepCopy(boilerplate, &usage_context); |
| 313 usage_context.ExitScope(site, boilerplate); |
| 314 Handle<Object> copy; |
| 315 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, copy, maybe_copy); |
| 316 return *copy; |
| 317 } |
| 318 |
| 319 |
| 320 MUST_USE_RESULT static MaybeHandle<AllocationSite> GetLiteralAllocationSite( |
| 321 Isolate* isolate, Handle<FixedArray> literals, int literals_index, |
| 322 Handle<FixedArray> elements) { |
| 323 // Check if boilerplate exists. If not, create it first. |
| 324 Handle<Object> literal_site(literals->get(literals_index), isolate); |
| 325 Handle<AllocationSite> site; |
| 326 if (*literal_site == isolate->heap()->undefined_value()) { |
| 327 DCHECK(*elements != isolate->heap()->empty_fixed_array()); |
| 328 Handle<Object> boilerplate; |
| 329 ASSIGN_RETURN_ON_EXCEPTION( |
| 330 isolate, boilerplate, |
| 331 Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements), |
| 332 AllocationSite); |
| 333 |
| 334 AllocationSiteCreationContext creation_context(isolate); |
| 335 site = creation_context.EnterNewScope(); |
| 336 if (JSObject::DeepWalk(Handle<JSObject>::cast(boilerplate), |
| 337 &creation_context).is_null()) { |
| 338 return Handle<AllocationSite>::null(); |
| 339 } |
| 340 creation_context.ExitScope(site, Handle<JSObject>::cast(boilerplate)); |
| 341 |
| 342 literals->set(literals_index, *site); |
| 343 } else { |
| 344 site = Handle<AllocationSite>::cast(literal_site); |
| 345 } |
| 346 |
| 347 return site; |
| 348 } |
| 349 |
| 350 |
| 351 static MaybeHandle<JSObject> CreateArrayLiteralImpl(Isolate* isolate, |
| 352 Handle<FixedArray> literals, |
| 353 int literals_index, |
| 354 Handle<FixedArray> elements, |
| 355 int flags) { |
| 356 RUNTIME_ASSERT_HANDLIFIED( |
| 357 literals_index >= 0 && literals_index < literals->length(), JSObject); |
| 358 Handle<AllocationSite> site; |
| 359 ASSIGN_RETURN_ON_EXCEPTION( |
| 360 isolate, site, |
| 361 GetLiteralAllocationSite(isolate, literals, literals_index, elements), |
| 362 JSObject); |
| 363 |
| 364 bool enable_mementos = (flags & ArrayLiteral::kDisableMementos) == 0; |
| 365 Handle<JSObject> boilerplate(JSObject::cast(site->transition_info())); |
| 366 AllocationSiteUsageContext usage_context(isolate, site, enable_mementos); |
| 367 usage_context.EnterNewScope(); |
| 368 JSObject::DeepCopyHints hints = (flags & ArrayLiteral::kShallowElements) == 0 |
| 369 ? JSObject::kNoHints |
| 370 : JSObject::kObjectIsShallow; |
| 371 MaybeHandle<JSObject> copy = |
| 372 JSObject::DeepCopy(boilerplate, &usage_context, hints); |
| 373 usage_context.ExitScope(site, boilerplate); |
| 374 return copy; |
| 375 } |
| 376 |
| 377 |
| 378 RUNTIME_FUNCTION(Runtime_CreateArrayLiteral) { |
| 379 HandleScope scope(isolate); |
| 380 DCHECK(args.length() == 4); |
| 381 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); |
| 382 CONVERT_SMI_ARG_CHECKED(literals_index, 1); |
| 383 CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2); |
| 384 CONVERT_SMI_ARG_CHECKED(flags, 3); |
| 385 |
| 386 Handle<JSObject> result; |
| 387 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| 388 isolate, result, CreateArrayLiteralImpl(isolate, literals, literals_index, |
| 389 elements, flags)); |
| 390 return *result; |
| 391 } |
| 392 |
| 393 |
| 394 RUNTIME_FUNCTION(Runtime_CreateArrayLiteralStubBailout) { |
| 395 HandleScope scope(isolate); |
| 396 DCHECK(args.length() == 3); |
| 397 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); |
| 398 CONVERT_SMI_ARG_CHECKED(literals_index, 1); |
| 399 CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2); |
| 400 |
| 401 Handle<JSObject> result; |
| 402 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| 403 isolate, result, |
| 404 CreateArrayLiteralImpl(isolate, literals, literals_index, elements, |
| 405 ArrayLiteral::kShallowElements)); |
| 406 return *result; |
| 407 } |
| 408 |
| 409 |
| 410 RUNTIME_FUNCTION(Runtime_StoreArrayLiteralElement) { |
| 411 HandleScope scope(isolate); |
| 412 RUNTIME_ASSERT(args.length() == 5); |
| 413 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); |
| 414 CONVERT_SMI_ARG_CHECKED(store_index, 1); |
| 415 CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); |
| 416 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 3); |
| 417 CONVERT_SMI_ARG_CHECKED(literal_index, 4); |
| 418 |
| 419 Object* raw_literal_cell = literals->get(literal_index); |
| 420 JSArray* boilerplate = NULL; |
| 421 if (raw_literal_cell->IsAllocationSite()) { |
| 422 AllocationSite* site = AllocationSite::cast(raw_literal_cell); |
| 423 boilerplate = JSArray::cast(site->transition_info()); |
| 424 } else { |
| 425 boilerplate = JSArray::cast(raw_literal_cell); |
| 426 } |
| 427 Handle<JSArray> boilerplate_object(boilerplate); |
| 428 ElementsKind elements_kind = object->GetElementsKind(); |
| 429 DCHECK(IsFastElementsKind(elements_kind)); |
| 430 // Smis should never trigger transitions. |
| 431 DCHECK(!value->IsSmi()); |
| 432 |
| 433 if (value->IsNumber()) { |
| 434 DCHECK(IsFastSmiElementsKind(elements_kind)); |
| 435 ElementsKind transitioned_kind = IsFastHoleyElementsKind(elements_kind) |
| 436 ? FAST_HOLEY_DOUBLE_ELEMENTS |
| 437 : FAST_DOUBLE_ELEMENTS; |
| 438 if (IsMoreGeneralElementsKindTransition( |
| 439 boilerplate_object->GetElementsKind(), transitioned_kind)) { |
| 440 JSObject::TransitionElementsKind(boilerplate_object, transitioned_kind); |
| 441 } |
| 442 JSObject::TransitionElementsKind(object, transitioned_kind); |
| 443 DCHECK(IsFastDoubleElementsKind(object->GetElementsKind())); |
| 444 FixedDoubleArray* double_array = FixedDoubleArray::cast(object->elements()); |
| 445 HeapNumber* number = HeapNumber::cast(*value); |
| 446 double_array->set(store_index, number->Number()); |
| 447 } else { |
| 448 if (!IsFastObjectElementsKind(elements_kind)) { |
| 449 ElementsKind transitioned_kind = IsFastHoleyElementsKind(elements_kind) |
| 450 ? FAST_HOLEY_ELEMENTS |
| 451 : FAST_ELEMENTS; |
| 452 JSObject::TransitionElementsKind(object, transitioned_kind); |
| 453 ElementsKind boilerplate_elements_kind = |
| 454 boilerplate_object->GetElementsKind(); |
| 455 if (IsMoreGeneralElementsKindTransition(boilerplate_elements_kind, |
| 456 transitioned_kind)) { |
| 457 JSObject::TransitionElementsKind(boilerplate_object, transitioned_kind); |
| 458 } |
| 459 } |
| 460 FixedArray* object_array = FixedArray::cast(object->elements()); |
| 461 object_array->set(store_index, *value); |
| 462 } |
| 463 return *object; |
| 464 } |
| 465 } |
| 466 } // namespace v8::internal |
OLD | NEW |