1// Copyright 2017 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/builtins/builtins-utils-gen.h" 6#include "src/builtins/builtins.h" 7#include "src/codegen/code-stub-assembler.h" 8#include "src/common/globals.h" 9#include "src/heap/factory-inl.h" 10#include "src/ic/accessor-assembler.h" 11#include "src/ic/keyed-store-generic.h" 12#include "src/objects/js-generator.h" 13#include "src/objects/js-objects.h" 14#include "src/objects/property-descriptor-object.h" 15#include "src/objects/property-details.h" 16#include "src/objects/shared-function-info.h" 17 18namespace v8 { 19namespace internal { 20 21// ----------------------------------------------------------------------------- 22// ES6 section 19.1 Object Objects 23 24class ObjectBuiltinsAssembler : public CodeStubAssembler { 25 public: 26 explicit ObjectBuiltinsAssembler(compiler::CodeAssemblerState* state) 27 : CodeStubAssembler(state) {} 28 29 protected: 30 void ReturnToStringFormat(TNode<Context> context, TNode<String> string); 31 32 // TODO(v8:11167) remove |context| and |object| once OrderedNameDictionary 33 // supported. 34 void AddToDictionaryIf(TNode<BoolT> condition, TNode<Context> context, 35 TNode<Object> object, 36 TNode<HeapObject> name_dictionary, Handle<Name> name, 37 TNode<Object> value, Label* bailout); 38 TNode<JSObject> FromPropertyDescriptor(TNode<Context> context, 39 TNode<PropertyDescriptorObject> desc); 40 TNode<JSObject> FromPropertyDetails(TNode<Context> context, 41 TNode<Object> raw_value, 42 TNode<Word32T> details, 43 Label* if_bailout); 44 TNode<JSObject> ConstructAccessorDescriptor(TNode<Context> context, 45 TNode<Object> getter, 46 TNode<Object> setter, 47 TNode<BoolT> enumerable, 48 TNode<BoolT> configurable); 49 TNode<JSObject> ConstructDataDescriptor(TNode<Context> context, 50 TNode<Object> value, 51 TNode<BoolT> writable, 52 TNode<BoolT> enumerable, 53 TNode<BoolT> configurable); 54 TNode<HeapObject> GetAccessorOrUndefined(TNode<HeapObject> accessor, 55 Label* if_bailout); 56}; 57 58class ObjectEntriesValuesBuiltinsAssembler : public ObjectBuiltinsAssembler { 59 public: 60 explicit ObjectEntriesValuesBuiltinsAssembler( 61 compiler::CodeAssemblerState* state) 62 : ObjectBuiltinsAssembler(state) {} 63 64 protected: 65 enum CollectType { kEntries, kValues }; 66 67 TNode<BoolT> IsPropertyEnumerable(TNode<Uint32T> details); 68 69 TNode<BoolT> IsPropertyKindAccessor(TNode<Uint32T> kind); 70 71 TNode<BoolT> IsPropertyKindData(TNode<Uint32T> kind); 72 73 TNode<Uint32T> LoadPropertyKind(TNode<Uint32T> details) { 74 return DecodeWord32<PropertyDetails::KindField>(details); 75 } 76 77 void GetOwnValuesOrEntries(TNode<Context> context, TNode<Object> maybe_object, 78 CollectType collect_type); 79 80 TNode<JSArray> FastGetOwnValuesOrEntries( 81 TNode<Context> context, TNode<JSObject> object, 82 Label* if_call_runtime_with_fast_path, Label* if_no_properties, 83 CollectType collect_type); 84 85 TNode<JSArray> FinalizeValuesOrEntriesJSArray( 86 TNode<Context> context, TNode<FixedArray> values_or_entries, 87 TNode<IntPtrT> size, TNode<Map> array_map, Label* if_empty); 88}; 89 90void ObjectBuiltinsAssembler::ReturnToStringFormat(TNode<Context> context, 91 TNode<String> string) { 92 TNode<String> lhs = StringConstant("[object "); 93 TNode<String> rhs = StringConstant("]"); 94 95 Callable callable = CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE); 96 97 Return(CallStub(callable, context, CallStub(callable, context, lhs, string), 98 rhs)); 99} 100 101TNode<JSObject> ObjectBuiltinsAssembler::ConstructAccessorDescriptor( 102 TNode<Context> context, TNode<Object> getter, TNode<Object> setter, 103 TNode<BoolT> enumerable, TNode<BoolT> configurable) { 104 TNode<NativeContext> native_context = LoadNativeContext(context); 105 TNode<Map> map = CAST(LoadContextElement( 106 native_context, Context::ACCESSOR_PROPERTY_DESCRIPTOR_MAP_INDEX)); 107 TNode<JSObject> js_desc = AllocateJSObjectFromMap(map); 108 109 StoreObjectFieldNoWriteBarrier( 110 js_desc, JSAccessorPropertyDescriptor::kGetOffset, getter); 111 StoreObjectFieldNoWriteBarrier( 112 js_desc, JSAccessorPropertyDescriptor::kSetOffset, setter); 113 StoreObjectFieldNoWriteBarrier( 114 js_desc, JSAccessorPropertyDescriptor::kEnumerableOffset, 115 SelectBooleanConstant(enumerable)); 116 StoreObjectFieldNoWriteBarrier( 117 js_desc, JSAccessorPropertyDescriptor::kConfigurableOffset, 118 SelectBooleanConstant(configurable)); 119 120 return js_desc; 121} 122 123TNode<JSObject> ObjectBuiltinsAssembler::ConstructDataDescriptor( 124 TNode<Context> context, TNode<Object> value, TNode<BoolT> writable, 125 TNode<BoolT> enumerable, TNode<BoolT> configurable) { 126 TNode<NativeContext> native_context = LoadNativeContext(context); 127 TNode<Map> map = CAST(LoadContextElement( 128 native_context, Context::DATA_PROPERTY_DESCRIPTOR_MAP_INDEX)); 129 TNode<JSObject> js_desc = AllocateJSObjectFromMap(map); 130 131 StoreObjectFieldNoWriteBarrier(js_desc, 132 JSDataPropertyDescriptor::kValueOffset, value); 133 StoreObjectFieldNoWriteBarrier(js_desc, 134 JSDataPropertyDescriptor::kWritableOffset, 135 SelectBooleanConstant(writable)); 136 StoreObjectFieldNoWriteBarrier(js_desc, 137 JSDataPropertyDescriptor::kEnumerableOffset, 138 SelectBooleanConstant(enumerable)); 139 StoreObjectFieldNoWriteBarrier(js_desc, 140 JSDataPropertyDescriptor::kConfigurableOffset, 141 SelectBooleanConstant(configurable)); 142 143 return js_desc; 144} 145 146TNode<BoolT> ObjectEntriesValuesBuiltinsAssembler::IsPropertyEnumerable( 147 TNode<Uint32T> details) { 148 TNode<Uint32T> attributes = 149 DecodeWord32<PropertyDetails::AttributesField>(details); 150 return IsNotSetWord32(attributes, PropertyAttributes::DONT_ENUM); 151} 152 153TNode<BoolT> ObjectEntriesValuesBuiltinsAssembler::IsPropertyKindAccessor( 154 TNode<Uint32T> kind) { 155 return Word32Equal(kind, 156 Int32Constant(static_cast<int>(PropertyKind::kAccessor))); 157} 158 159TNode<BoolT> ObjectEntriesValuesBuiltinsAssembler::IsPropertyKindData( 160 TNode<Uint32T> kind) { 161 return Word32Equal(kind, 162 Int32Constant(static_cast<int>(PropertyKind::kData))); 163} 164 165void ObjectEntriesValuesBuiltinsAssembler::GetOwnValuesOrEntries( 166 TNode<Context> context, TNode<Object> maybe_object, 167 CollectType collect_type) { 168 TNode<JSReceiver> receiver = ToObject_Inline(context, maybe_object); 169 170 Label if_call_runtime_with_fast_path(this, Label::kDeferred), 171 if_call_runtime(this, Label::kDeferred), 172 if_no_properties(this, Label::kDeferred); 173 174 TNode<Map> map = LoadMap(receiver); 175 GotoIfNot(IsJSObjectMap(map), &if_call_runtime); 176 GotoIfMapHasSlowProperties(map, &if_call_runtime); 177 178 TNode<JSObject> object = CAST(receiver); 179 TNode<FixedArrayBase> elements = LoadElements(object); 180 // If the object has elements, we treat it as slow case. 181 // So, we go to runtime call. 182 GotoIfNot(IsEmptyFixedArray(elements), &if_call_runtime_with_fast_path); 183 184 TNode<JSArray> result = FastGetOwnValuesOrEntries( 185 context, object, &if_call_runtime_with_fast_path, &if_no_properties, 186 collect_type); 187 Return(result); 188 189 BIND(&if_no_properties); 190 { 191 TNode<NativeContext> native_context = LoadNativeContext(context); 192 TNode<Map> array_map = 193 LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context); 194 TNode<JSArray> empty_array = AllocateJSArray( 195 PACKED_ELEMENTS, array_map, IntPtrConstant(0), SmiConstant(0)); 196 Return(empty_array); 197 } 198 199 BIND(&if_call_runtime_with_fast_path); 200 { 201 // In slow case, we simply call runtime. 202 if (collect_type == CollectType::kEntries) { 203 Return(CallRuntime(Runtime::kObjectEntries, context, object)); 204 } else { 205 DCHECK(collect_type == CollectType::kValues); 206 Return(CallRuntime(Runtime::kObjectValues, context, object)); 207 } 208 } 209 210 BIND(&if_call_runtime); 211 { 212 // In slow case, we simply call runtime. 213 if (collect_type == CollectType::kEntries) { 214 Return( 215 CallRuntime(Runtime::kObjectEntriesSkipFastPath, context, receiver)); 216 } else { 217 DCHECK(collect_type == CollectType::kValues); 218 Return( 219 CallRuntime(Runtime::kObjectValuesSkipFastPath, context, receiver)); 220 } 221 } 222} 223 224TNode<JSArray> ObjectEntriesValuesBuiltinsAssembler::FastGetOwnValuesOrEntries( 225 TNode<Context> context, TNode<JSObject> object, 226 Label* if_call_runtime_with_fast_path, Label* if_no_properties, 227 CollectType collect_type) { 228 TNode<NativeContext> native_context = LoadNativeContext(context); 229 TNode<Map> array_map = 230 LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context); 231 TNode<Map> map = LoadMap(object); 232 TNode<Uint32T> bit_field3 = LoadMapBitField3(map); 233 234 Label if_has_enum_cache(this), if_not_has_enum_cache(this), 235 collect_entries(this); 236 TNode<IntPtrT> object_enum_length = 237 Signed(DecodeWordFromWord32<Map::Bits3::EnumLengthBits>(bit_field3)); 238 TNode<BoolT> has_enum_cache = WordNotEqual( 239 object_enum_length, IntPtrConstant(kInvalidEnumCacheSentinel)); 240 241 // In case, we found enum_cache in object, 242 // we use it as array_length because it has same size for 243 // Object.(entries/values) result array object length. 244 // So object_enum_length use less memory space than 245 // NumberOfOwnDescriptorsBits value. 246 // And in case, if enum_cache_not_found, 247 // we call runtime and initialize enum_cache for subsequent call of 248 // CSA fast path. 249 Branch(has_enum_cache, &if_has_enum_cache, if_call_runtime_with_fast_path); 250 251 BIND(&if_has_enum_cache); 252 { 253 GotoIf(WordEqual(object_enum_length, IntPtrConstant(0)), if_no_properties); 254 TNode<FixedArray> values_or_entries = 255 CAST(AllocateFixedArray(PACKED_ELEMENTS, object_enum_length, 256 AllocationFlag::kAllowLargeObjectAllocation)); 257 258 // If in case we have enum_cache, 259 // we can't detect accessor of object until loop through descriptors. 260 // So if object might have accessor, 261 // we will remain invalid addresses of FixedArray. 262 // Because in that case, we need to jump to runtime call. 263 // So the array filled by the-hole even if enum_cache exists. 264 FillFixedArrayWithValue(PACKED_ELEMENTS, values_or_entries, 265 IntPtrConstant(0), object_enum_length, 266 RootIndex::kTheHoleValue); 267 268 TVARIABLE(IntPtrT, var_result_index, IntPtrConstant(0)); 269 TVARIABLE(IntPtrT, var_descriptor_number, IntPtrConstant(0)); 270 // Let desc be ? O.[[GetOwnProperty]](key). 271 TNode<DescriptorArray> descriptors = LoadMapDescriptors(map); 272 Label loop(this, {&var_descriptor_number, &var_result_index}), 273 after_loop(this), next_descriptor(this); 274 Branch(IntPtrEqual(var_descriptor_number.value(), object_enum_length), 275 &after_loop, &loop); 276 277 // We dont use BuildFastLoop. 278 // Instead, we use hand-written loop 279 // because of we need to use 'continue' functionality. 280 BIND(&loop); 281 { 282 // Currently, we will not invoke getters, 283 // so, map will not be changed. 284 CSA_DCHECK(this, TaggedEqual(map, LoadMap(object))); 285 TNode<IntPtrT> descriptor_entry = var_descriptor_number.value(); 286 TNode<Name> next_key = 287 LoadKeyByDescriptorEntry(descriptors, descriptor_entry); 288 289 // Skip Symbols. 290 GotoIf(IsSymbol(next_key), &next_descriptor); 291 292 TNode<Uint32T> details = 293 LoadDetailsByDescriptorEntry(descriptors, descriptor_entry); 294 295 TNode<Uint32T> kind = LoadPropertyKind(details); 296 297 // If property is accessor, we escape fast path and call runtime. 298 GotoIf(IsPropertyKindAccessor(kind), if_call_runtime_with_fast_path); 299 CSA_DCHECK(this, IsPropertyKindData(kind)); 300 301 // If desc is not undefined and desc.[[Enumerable]] is true, then skip to 302 // the next descriptor. 303 GotoIfNot(IsPropertyEnumerable(details), &next_descriptor); 304 305 TVARIABLE(Object, var_property_value, UndefinedConstant()); 306 TNode<IntPtrT> descriptor_name_index = ToKeyIndex<DescriptorArray>( 307 Unsigned(TruncateIntPtrToInt32(var_descriptor_number.value()))); 308 309 // Let value be ? Get(O, key). 310 LoadPropertyFromFastObject(object, map, descriptors, 311 descriptor_name_index, details, 312 &var_property_value); 313 314 // If kind is "value", append value to properties. 315 TNode<Object> value = var_property_value.value(); 316 317 if (collect_type == CollectType::kEntries) { 318 // Let entry be CreateArrayFromList(« key, value »). 319 TNode<JSArray> array; 320 TNode<FixedArrayBase> elements; 321 std::tie(array, elements) = AllocateUninitializedJSArrayWithElements( 322 PACKED_ELEMENTS, array_map, SmiConstant(2), base::nullopt, 323 IntPtrConstant(2)); 324 StoreFixedArrayElement(CAST(elements), 0, next_key, SKIP_WRITE_BARRIER); 325 StoreFixedArrayElement(CAST(elements), 1, value, SKIP_WRITE_BARRIER); 326 value = array; 327 } 328 329 StoreFixedArrayElement(values_or_entries, var_result_index.value(), 330 value); 331 Increment(&var_result_index); 332 Goto(&next_descriptor); 333 334 BIND(&next_descriptor); 335 { 336 Increment(&var_descriptor_number); 337 Branch(IntPtrEqual(var_result_index.value(), object_enum_length), 338 &after_loop, &loop); 339 } 340 } 341 BIND(&after_loop); 342 return FinalizeValuesOrEntriesJSArray(context, values_or_entries, 343 var_result_index.value(), array_map, 344 if_no_properties); 345 } 346} 347 348TNode<JSArray> 349ObjectEntriesValuesBuiltinsAssembler::FinalizeValuesOrEntriesJSArray( 350 TNode<Context> context, TNode<FixedArray> result, TNode<IntPtrT> size, 351 TNode<Map> array_map, Label* if_empty) { 352 CSA_DCHECK(this, IsJSArrayMap(array_map)); 353 354 GotoIf(IntPtrEqual(size, IntPtrConstant(0)), if_empty); 355 TNode<JSArray> array = AllocateJSArray(array_map, result, SmiTag(size)); 356 return array; 357} 358 359TF_BUILTIN(ObjectPrototypeHasOwnProperty, ObjectBuiltinsAssembler) { 360 auto object = Parameter<Object>(Descriptor::kReceiver); 361 auto key = Parameter<Object>(Descriptor::kKey); 362 auto context = Parameter<Context>(Descriptor::kContext); 363 364 Label call_runtime(this), return_true(this), return_false(this), 365 to_primitive(this); 366 367 // Smi receivers do not have own properties, just perform ToPrimitive on the 368 // key. 369 Label if_objectisnotsmi(this); 370 Branch(TaggedIsSmi(object), &to_primitive, &if_objectisnotsmi); 371 BIND(&if_objectisnotsmi); 372 373 TNode<HeapObject> heap_object = CAST(object); 374 375 TNode<Map> map = LoadMap(heap_object); 376 TNode<Uint16T> instance_type = LoadMapInstanceType(map); 377 378 { 379 TVARIABLE(IntPtrT, var_index); 380 TVARIABLE(Name, var_unique); 381 382 Label if_index(this, &var_index), if_unique_name(this), 383 if_notunique_name(this); 384 TryToName(key, &if_index, &var_index, &if_unique_name, &var_unique, 385 &call_runtime, &if_notunique_name); 386 387 BIND(&if_unique_name); 388 TryHasOwnProperty(heap_object, map, instance_type, var_unique.value(), 389 &return_true, &return_false, &call_runtime); 390 391 BIND(&if_index); 392 { 393 TryLookupElement(heap_object, map, instance_type, var_index.value(), 394 &return_true, &return_false, &return_false, 395 &call_runtime); 396 } 397 398 BIND(&if_notunique_name); 399 { 400 Label not_in_string_table(this); 401 TryInternalizeString(CAST(key), &if_index, &var_index, &if_unique_name, 402 &var_unique, ¬_in_string_table, &call_runtime); 403 404 BIND(¬_in_string_table); 405 { 406 // If the string was not found in the string table, then no regular 407 // object can have a property with that name, so return |false|. 408 // "Special API objects" with interceptors must take the slow path. 409 Branch(IsSpecialReceiverInstanceType(instance_type), &call_runtime, 410 &return_false); 411 } 412 } 413 } 414 BIND(&to_primitive); 415 GotoIf(IsNumber(key), &return_false); 416 Branch(IsName(CAST(key)), &return_false, &call_runtime); 417 418 BIND(&return_true); 419 Return(TrueConstant()); 420 421 BIND(&return_false); 422 Return(FalseConstant()); 423 424 BIND(&call_runtime); 425 Return(CallRuntime(Runtime::kObjectHasOwnProperty, context, object, key)); 426} 427 428// ES #sec-object.assign 429TF_BUILTIN(ObjectAssign, ObjectBuiltinsAssembler) { 430 TNode<IntPtrT> argc = ChangeInt32ToIntPtr( 431 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount)); 432 CodeStubArguments args(this, argc); 433 434 auto context = Parameter<Context>(Descriptor::kContext); 435 TNode<Object> target = args.GetOptionalArgumentValue(0); 436 437 // 1. Let to be ? ToObject(target). 438 TNode<JSReceiver> to = ToObject_Inline(context, target); 439 440 Label done(this); 441 // 2. If only one argument was passed, return to. 442 GotoIf(UintPtrLessThanOrEqual(args.GetLengthWithoutReceiver(), 443 IntPtrConstant(1)), 444 &done); 445 446 // 3. Let sources be the List of argument values starting with the 447 // second argument. 448 // 4. For each element nextSource of sources, in ascending index order, 449 args.ForEach( 450 [=](TNode<Object> next_source) { 451 CallBuiltin(Builtin::kSetDataProperties, context, to, next_source); 452 }, 453 IntPtrConstant(1)); 454 Goto(&done); 455 456 // 5. Return to. 457 BIND(&done); 458 args.PopAndReturn(to); 459} 460 461// ES #sec-object.keys 462TF_BUILTIN(ObjectKeys, ObjectBuiltinsAssembler) { 463 auto object = Parameter<Object>(Descriptor::kObject); 464 auto context = Parameter<Context>(Descriptor::kContext); 465 466 TVARIABLE(Smi, var_length); 467 TVARIABLE(FixedArrayBase, var_elements); 468 Label if_empty(this, Label::kDeferred), if_empty_elements(this), 469 if_fast(this), if_slow(this, Label::kDeferred), if_join(this); 470 471 // Check if the {object} has a usable enum cache. 472 GotoIf(TaggedIsSmi(object), &if_slow); 473 474 TNode<Map> object_map = LoadMap(CAST(object)); 475 TNode<Uint32T> object_bit_field3 = LoadMapBitField3(object_map); 476 TNode<UintPtrT> object_enum_length = 477 DecodeWordFromWord32<Map::Bits3::EnumLengthBits>(object_bit_field3); 478 GotoIf( 479 WordEqual(object_enum_length, IntPtrConstant(kInvalidEnumCacheSentinel)), 480 &if_slow); 481 482 // Ensure that the {object} doesn't have any elements. 483 CSA_DCHECK(this, IsJSObjectMap(object_map)); 484 TNode<FixedArrayBase> object_elements = LoadElements(CAST(object)); 485 GotoIf(IsEmptyFixedArray(object_elements), &if_empty_elements); 486 Branch(IsEmptySlowElementDictionary(object_elements), &if_empty_elements, 487 &if_slow); 488 489 // Check whether there are enumerable properties. 490 BIND(&if_empty_elements); 491 Branch(WordEqual(object_enum_length, IntPtrConstant(0)), &if_empty, &if_fast); 492 493 // TODO(solanes): These if_xxx here and below seem to be quite similar for 494 // ObjectKeys and for ObjectGetOwnPropertyNames. In particular, if_fast seem 495 // to be the exact same. 496 BIND(&if_fast); 497 { 498 // The {object} has a usable enum cache, use that. 499 TNode<DescriptorArray> object_descriptors = LoadMapDescriptors(object_map); 500 TNode<EnumCache> object_enum_cache = LoadObjectField<EnumCache>( 501 object_descriptors, DescriptorArray::kEnumCacheOffset); 502 auto object_enum_keys = LoadObjectField<FixedArrayBase>( 503 object_enum_cache, EnumCache::kKeysOffset); 504 505 // Allocate a JSArray and copy the elements from the {object_enum_keys}. 506 TNode<JSArray> array; 507 TNode<FixedArrayBase> elements; 508 TNode<NativeContext> native_context = LoadNativeContext(context); 509 TNode<Map> array_map = 510 LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context); 511 TNode<IntPtrT> object_enum_length_intptr = Signed(object_enum_length); 512 TNode<Smi> array_length = SmiTag(object_enum_length_intptr); 513 std::tie(array, elements) = AllocateUninitializedJSArrayWithElements( 514 PACKED_ELEMENTS, array_map, array_length, base::nullopt, 515 object_enum_length_intptr); 516 CopyFixedArrayElements(PACKED_ELEMENTS, object_enum_keys, elements, 517 object_enum_length_intptr, SKIP_WRITE_BARRIER); 518 Return(array); 519 } 520 521 BIND(&if_empty); 522 { 523 // The {object} doesn't have any enumerable keys. 524 var_length = SmiConstant(0); 525 var_elements = EmptyFixedArrayConstant(); 526 Goto(&if_join); 527 } 528 529 BIND(&if_slow); 530 { 531 // Let the runtime compute the elements. 532 TNode<FixedArray> elements = 533 CAST(CallRuntime(Runtime::kObjectKeys, context, object)); 534 var_length = LoadObjectField<Smi>(elements, FixedArray::kLengthOffset); 535 var_elements = elements; 536 Goto(&if_join); 537 } 538 539 BIND(&if_join); 540 { 541 // Wrap the elements into a proper JSArray and return that. 542 TNode<NativeContext> native_context = LoadNativeContext(context); 543 TNode<Map> array_map = 544 LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context); 545 TNode<JSArray> array = 546 AllocateJSArray(array_map, var_elements.value(), var_length.value()); 547 Return(array); 548 } 549} 550 551// https://github.com/tc39/proposal-accessible-object-hasownproperty 552TF_BUILTIN(ObjectHasOwn, ObjectBuiltinsAssembler) { 553 // Object.prototype.hasOwnProperty() 554 // 1. Let obj be ? ToObject(O). 555 // 2. Let key be ? ToPropertyKey(P). 556 // 3. Return ? HasOwnProperty(obj, key). 557 // 558 // ObjectPrototypeHasOwnProperty has similar semantics with steps 1 and 2 559 // swapped. We check if ToObject can fail and delegate the rest of the 560 // execution to ObjectPrototypeHasOwnProperty. 561 562 auto target = Parameter<Object>(Descriptor::kJSTarget); 563 auto new_target = Parameter<Object>(Descriptor::kJSNewTarget); 564 auto object = Parameter<Object>(Descriptor::kObject); 565 auto key = Parameter<Object>(Descriptor::kKey); 566 auto context = Parameter<Context>(Descriptor::kContext); 567 568 // ToObject can only fail when object is undefined or null. 569 Label undefined_or_null(this), not_undefined_nor_null(this); 570 Branch(IsNullOrUndefined(object), &undefined_or_null, 571 ¬_undefined_nor_null); 572 573 BIND(&undefined_or_null); 574 ThrowTypeError(context, MessageTemplate::kUndefinedOrNullToObject); 575 576 BIND(¬_undefined_nor_null); 577 Return(CallBuiltin(Builtin::kObjectPrototypeHasOwnProperty, context, target, 578 new_target, JSParameterCount(1), object, key)); 579} 580 581// ES #sec-object.getOwnPropertyNames 582TF_BUILTIN(ObjectGetOwnPropertyNames, ObjectBuiltinsAssembler) { 583 auto object = Parameter<Object>(Descriptor::kObject); 584 auto context = Parameter<Context>(Descriptor::kContext); 585 586 TVARIABLE(Smi, var_length); 587 TVARIABLE(FixedArrayBase, var_elements); 588 Label if_empty(this, Label::kDeferred), if_empty_elements(this), 589 if_fast(this), try_fast(this, Label::kDeferred), 590 if_slow(this, Label::kDeferred), if_join(this); 591 592 // Take the slow path if the {object} IsCustomElementsReceiverInstanceType or 593 // has any elements. 594 GotoIf(TaggedIsSmi(object), &if_slow); 595 596 TNode<Map> object_map = LoadMap(CAST(object)); 597 TNode<Uint16T> instance_type = LoadMapInstanceType(object_map); 598 GotoIf(IsCustomElementsReceiverInstanceType(instance_type), &if_slow); 599 TNode<FixedArrayBase> object_elements = LoadElements(CAST(object)); 600 GotoIf(IsEmptyFixedArray(object_elements), &if_empty_elements); 601 Branch(IsEmptySlowElementDictionary(object_elements), &if_empty_elements, 602 &if_slow); 603 604 // Check if the {object} has a usable enum cache. 605 BIND(&if_empty_elements); 606 TNode<Uint32T> object_bit_field3 = LoadMapBitField3(object_map); 607 TNode<UintPtrT> object_enum_length = 608 DecodeWordFromWord32<Map::Bits3::EnumLengthBits>(object_bit_field3); 609 GotoIf( 610 WordEqual(object_enum_length, IntPtrConstant(kInvalidEnumCacheSentinel)), 611 &try_fast); 612 613 // Check whether all own properties are enumerable. 614 TNode<UintPtrT> number_descriptors = 615 DecodeWordFromWord32<Map::Bits3::NumberOfOwnDescriptorsBits>( 616 object_bit_field3); 617 GotoIfNot(WordEqual(object_enum_length, number_descriptors), &if_slow); 618 619 // Check whether there are enumerable properties. 620 Branch(WordEqual(object_enum_length, IntPtrConstant(0)), &if_empty, &if_fast); 621 622 // TODO(solanes): These if_xxx here and below seem to be quite similar for 623 // ObjectKeys and for ObjectGetOwnPropertyNames. In particular, if_fast seem 624 // to be the exact same. 625 BIND(&if_fast); 626 { 627 // The {object} has a usable enum cache and all own properties are 628 // enumerable, use that. 629 TNode<DescriptorArray> object_descriptors = LoadMapDescriptors(object_map); 630 TNode<EnumCache> object_enum_cache = LoadObjectField<EnumCache>( 631 object_descriptors, DescriptorArray::kEnumCacheOffset); 632 auto object_enum_keys = LoadObjectField<FixedArrayBase>( 633 object_enum_cache, EnumCache::kKeysOffset); 634 635 // Allocate a JSArray and copy the elements from the {object_enum_keys}. 636 TNode<JSArray> array; 637 TNode<FixedArrayBase> elements; 638 TNode<NativeContext> native_context = LoadNativeContext(context); 639 TNode<Map> array_map = 640 LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context); 641 TNode<IntPtrT> object_enum_length_intptr = Signed(object_enum_length); 642 TNode<Smi> array_length = SmiTag(object_enum_length_intptr); 643 std::tie(array, elements) = AllocateUninitializedJSArrayWithElements( 644 PACKED_ELEMENTS, array_map, array_length, base::nullopt, 645 object_enum_length_intptr); 646 CopyFixedArrayElements(PACKED_ELEMENTS, object_enum_keys, elements, 647 object_enum_length_intptr, SKIP_WRITE_BARRIER); 648 Return(array); 649 } 650 651 BIND(&try_fast); 652 { 653 // Let the runtime compute the elements and try initializing enum cache. 654 TNode<FixedArray> elements = CAST(CallRuntime( 655 Runtime::kObjectGetOwnPropertyNamesTryFast, context, object)); 656 var_length = LoadObjectField<Smi>(elements, FixedArray::kLengthOffset); 657 var_elements = elements; 658 Goto(&if_join); 659 } 660 661 BIND(&if_empty); 662 { 663 // The {object} doesn't have any enumerable keys. 664 var_length = SmiConstant(0); 665 var_elements = EmptyFixedArrayConstant(); 666 Goto(&if_join); 667 } 668 669 BIND(&if_slow); 670 { 671 // Let the runtime compute the elements. 672 TNode<FixedArray> elements = 673 CAST(CallRuntime(Runtime::kObjectGetOwnPropertyNames, context, object)); 674 var_length = LoadObjectField<Smi>(elements, FixedArray::kLengthOffset); 675 var_elements = elements; 676 Goto(&if_join); 677 } 678 679 BIND(&if_join); 680 { 681 // Wrap the elements into a proper JSArray and return that. 682 TNode<NativeContext> native_context = LoadNativeContext(context); 683 TNode<Map> array_map = 684 LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context); 685 TNode<JSArray> array = 686 AllocateJSArray(array_map, var_elements.value(), var_length.value()); 687 Return(array); 688 } 689} 690 691TF_BUILTIN(ObjectValues, ObjectEntriesValuesBuiltinsAssembler) { 692 auto object = UncheckedParameter<JSObject>(Descriptor::kObject); 693 auto context = UncheckedParameter<Context>(Descriptor::kContext); 694 GetOwnValuesOrEntries(context, object, CollectType::kValues); 695} 696 697TF_BUILTIN(ObjectEntries, ObjectEntriesValuesBuiltinsAssembler) { 698 auto object = UncheckedParameter<JSObject>(Descriptor::kObject); 699 auto context = UncheckedParameter<Context>(Descriptor::kContext); 700 GetOwnValuesOrEntries(context, object, CollectType::kEntries); 701} 702 703// ES #sec-object.prototype.isprototypeof 704TF_BUILTIN(ObjectPrototypeIsPrototypeOf, ObjectBuiltinsAssembler) { 705 auto receiver = Parameter<Object>(Descriptor::kReceiver); 706 auto value = Parameter<Object>(Descriptor::kValue); 707 auto context = Parameter<Context>(Descriptor::kContext); 708 Label if_receiverisnullorundefined(this, Label::kDeferred), 709 if_valueisnotreceiver(this, Label::kDeferred); 710 711 // We only check whether {value} is a Smi here, so that the 712 // prototype chain walk below can safely access the {value}s 713 // map. We don't rule out Primitive {value}s, since all of 714 // them have null as their prototype, so the chain walk below 715 // immediately aborts and returns false anyways. 716 GotoIf(TaggedIsSmi(value), &if_valueisnotreceiver); 717 718 { 719 TNode<HeapObject> value_heap_object = CAST(value); 720 721 // Check if {receiver} is either null or undefined and in that case, 722 // invoke the ToObject builtin, which raises the appropriate error. 723 // Otherwise we don't need to invoke ToObject, since {receiver} is 724 // either already a JSReceiver, in which case ToObject is a no-op, 725 // or it's a Primitive and ToObject would allocate a fresh 726 // JSPrimitiveWrapper wrapper, which wouldn't be identical to any existing 727 // JSReceiver found in the prototype chain of {value}, hence it will return 728 // false no matter if we search for the Primitive {receiver} or 729 // a newly allocated JSPrimitiveWrapper wrapper for {receiver}. 730 GotoIf(IsNull(receiver), &if_receiverisnullorundefined); 731 GotoIf(IsUndefined(receiver), &if_receiverisnullorundefined); 732 733 // Loop through the prototype chain looking for the {receiver}. 734 Return(HasInPrototypeChain(context, value_heap_object, receiver)); 735 736 BIND(&if_receiverisnullorundefined); 737 { 738 // If {value} is a primitive HeapObject, we need to return 739 // false instead of throwing an exception per order of the 740 // steps in the specification, so check that first here. 741 GotoIfNot(IsJSReceiver(value_heap_object), &if_valueisnotreceiver); 742 743 // Simulate the ToObject invocation on {receiver}. 744 ToObject(context, receiver); 745 Unreachable(); 746 } 747 } 748 749 BIND(&if_valueisnotreceiver); 750 Return(FalseConstant()); 751} 752 753TF_BUILTIN(ObjectToString, ObjectBuiltinsAssembler) { 754 Label checkstringtag(this), if_arguments(this), if_array(this), 755 if_boolean(this), if_date(this), if_error(this), if_function(this), 756 if_number(this, Label::kDeferred), if_object(this), if_primitive(this), 757 if_proxy(this, Label::kDeferred), if_regexp(this), if_string(this), 758 if_symbol(this, Label::kDeferred), if_value(this), 759 if_bigint(this, Label::kDeferred); 760 761 auto receiver = Parameter<Object>(Descriptor::kReceiver); 762 auto context = Parameter<Context>(Descriptor::kContext); 763 764 TVARIABLE(String, var_default); 765 TVARIABLE(HeapObject, var_holder); 766 767 // This is arranged to check the likely cases first. 768 GotoIf(TaggedIsSmi(receiver), &if_number); 769 770 TNode<HeapObject> receiver_heap_object = CAST(receiver); 771 TNode<Map> receiver_map = LoadMap(receiver_heap_object); 772 var_holder = receiver_heap_object; 773 TNode<Uint16T> receiver_instance_type = LoadMapInstanceType(receiver_map); 774 GotoIf(IsPrimitiveInstanceType(receiver_instance_type), &if_primitive); 775 GotoIf(IsFunctionInstanceType(receiver_instance_type), &if_function); 776 const struct { 777 InstanceType value; 778 Label* label; 779 } kJumpTable[] = {{JS_OBJECT_TYPE, &if_object}, 780 {JS_ARRAY_TYPE, &if_array}, 781 {JS_REG_EXP_TYPE, &if_regexp}, 782 {JS_ARGUMENTS_OBJECT_TYPE, &if_arguments}, 783 {JS_DATE_TYPE, &if_date}, 784 {JS_API_OBJECT_TYPE, &if_object}, 785 {JS_SPECIAL_API_OBJECT_TYPE, &if_object}, 786 {JS_PROXY_TYPE, &if_proxy}, 787 {JS_ERROR_TYPE, &if_error}, 788 {JS_PRIMITIVE_WRAPPER_TYPE, &if_value}}; 789 size_t const kNumCases = arraysize(kJumpTable); 790 Label* case_labels[kNumCases]; 791 int32_t case_values[kNumCases]; 792 for (size_t i = 0; i < kNumCases; ++i) { 793 case_labels[i] = kJumpTable[i].label; 794 case_values[i] = kJumpTable[i].value; 795 } 796 Switch(receiver_instance_type, &if_object, case_values, case_labels, 797 arraysize(case_values)); 798 799 BIND(&if_arguments); 800 { 801 var_default = ArgumentsToStringConstant(); 802 Goto(&checkstringtag); 803 } 804 805 BIND(&if_array); 806 { 807 var_default = ArrayToStringConstant(); 808 Goto(&checkstringtag); 809 } 810 811 BIND(&if_boolean); 812 { 813 TNode<NativeContext> native_context = LoadNativeContext(context); 814 TNode<JSFunction> boolean_constructor = CAST( 815 LoadContextElement(native_context, Context::BOOLEAN_FUNCTION_INDEX)); 816 TNode<Map> boolean_initial_map = LoadObjectField<Map>( 817 boolean_constructor, JSFunction::kPrototypeOrInitialMapOffset); 818 TNode<HeapObject> boolean_prototype = 819 LoadObjectField<HeapObject>(boolean_initial_map, Map::kPrototypeOffset); 820 var_default = BooleanToStringConstant(); 821 var_holder = boolean_prototype; 822 Goto(&checkstringtag); 823 } 824 825 BIND(&if_date); 826 { 827 var_default = DateToStringConstant(); 828 Goto(&checkstringtag); 829 } 830 831 BIND(&if_error); 832 { 833 var_default = ErrorToStringConstant(); 834 Goto(&checkstringtag); 835 } 836 837 BIND(&if_function); 838 { 839 var_default = FunctionToStringConstant(); 840 Goto(&checkstringtag); 841 } 842 843 BIND(&if_number); 844 { 845 TNode<NativeContext> native_context = LoadNativeContext(context); 846 TNode<JSFunction> number_constructor = CAST( 847 LoadContextElement(native_context, Context::NUMBER_FUNCTION_INDEX)); 848 TNode<Map> number_initial_map = LoadObjectField<Map>( 849 number_constructor, JSFunction::kPrototypeOrInitialMapOffset); 850 TNode<HeapObject> number_prototype = 851 LoadObjectField<HeapObject>(number_initial_map, Map::kPrototypeOffset); 852 var_default = NumberToStringConstant(); 853 var_holder = number_prototype; 854 Goto(&checkstringtag); 855 } 856 857 BIND(&if_object); 858 { 859 CSA_DCHECK(this, IsJSReceiver(CAST(receiver))); 860 var_default = ObjectToStringConstant(); 861 Goto(&checkstringtag); 862 } 863 864 BIND(&if_primitive); 865 { 866 Label return_undefined(this); 867 868 GotoIf(IsStringInstanceType(receiver_instance_type), &if_string); 869 GotoIf(IsBigIntInstanceType(receiver_instance_type), &if_bigint); 870 GotoIf(IsBooleanMap(receiver_map), &if_boolean); 871 GotoIf(IsHeapNumberMap(receiver_map), &if_number); 872 GotoIf(IsSymbolMap(receiver_map), &if_symbol); 873 GotoIf(IsUndefined(receiver), &return_undefined); 874 CSA_DCHECK(this, IsNull(receiver)); 875 Return(NullToStringConstant()); 876 877 BIND(&return_undefined); 878 Return(UndefinedToStringConstant()); 879 } 880 881 BIND(&if_proxy); 882 { 883 // If {receiver} is a proxy for a JSArray, we default to "[object Array]", 884 // otherwise we default to "[object Object]" or "[object Function]" here, 885 // depending on whether the {receiver} is callable. The order matters here, 886 // i.e. we need to execute the %ArrayIsArray check before the [[Get]] below, 887 // as the exception is observable. 888 TNode<Object> receiver_is_array = 889 CallRuntime(Runtime::kArrayIsArray, context, receiver); 890 TNode<String> builtin_tag = Select<String>( 891 IsTrue(receiver_is_array), [=] { return ArrayStringConstant(); }, 892 [=] { 893 return Select<String>( 894 IsCallableMap(receiver_map), 895 [=] { return FunctionStringConstant(); }, 896 [=] { return ObjectStringConstant(); }); 897 }); 898 899 // Lookup the @@toStringTag property on the {receiver}. 900 TVARIABLE(Object, var_tag, 901 GetProperty(context, receiver, 902 isolate()->factory()->to_string_tag_symbol())); 903 Label if_tagisnotstring(this), if_tagisstring(this); 904 GotoIf(TaggedIsSmi(var_tag.value()), &if_tagisnotstring); 905 Branch(IsString(CAST(var_tag.value())), &if_tagisstring, 906 &if_tagisnotstring); 907 BIND(&if_tagisnotstring); 908 { 909 var_tag = builtin_tag; 910 Goto(&if_tagisstring); 911 } 912 BIND(&if_tagisstring); 913 ReturnToStringFormat(context, CAST(var_tag.value())); 914 } 915 916 BIND(&if_regexp); 917 { 918 var_default = RegexpToStringConstant(); 919 Goto(&checkstringtag); 920 } 921 922 BIND(&if_string); 923 { 924 TNode<NativeContext> native_context = LoadNativeContext(context); 925 TNode<JSFunction> string_constructor = CAST( 926 LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX)); 927 TNode<Map> string_initial_map = LoadObjectField<Map>( 928 string_constructor, JSFunction::kPrototypeOrInitialMapOffset); 929 TNode<HeapObject> string_prototype = 930 LoadObjectField<HeapObject>(string_initial_map, Map::kPrototypeOffset); 931 var_default = StringToStringConstant(); 932 var_holder = string_prototype; 933 Goto(&checkstringtag); 934 } 935 936 BIND(&if_symbol); 937 { 938 TNode<NativeContext> native_context = LoadNativeContext(context); 939 TNode<JSFunction> symbol_constructor = CAST( 940 LoadContextElement(native_context, Context::SYMBOL_FUNCTION_INDEX)); 941 TNode<Map> symbol_initial_map = LoadObjectField<Map>( 942 symbol_constructor, JSFunction::kPrototypeOrInitialMapOffset); 943 TNode<HeapObject> symbol_prototype = 944 LoadObjectField<HeapObject>(symbol_initial_map, Map::kPrototypeOffset); 945 var_default = ObjectToStringConstant(); 946 var_holder = symbol_prototype; 947 Goto(&checkstringtag); 948 } 949 950 BIND(&if_bigint); 951 { 952 TNode<NativeContext> native_context = LoadNativeContext(context); 953 TNode<JSFunction> bigint_constructor = CAST( 954 LoadContextElement(native_context, Context::BIGINT_FUNCTION_INDEX)); 955 TNode<Map> bigint_initial_map = LoadObjectField<Map>( 956 bigint_constructor, JSFunction::kPrototypeOrInitialMapOffset); 957 TNode<HeapObject> bigint_prototype = 958 LoadObjectField<HeapObject>(bigint_initial_map, Map::kPrototypeOffset); 959 var_default = ObjectToStringConstant(); 960 var_holder = bigint_prototype; 961 Goto(&checkstringtag); 962 } 963 964 BIND(&if_value); 965 { 966 Label if_value_is_number(this, Label::kDeferred), 967 if_value_is_boolean(this, Label::kDeferred), 968 if_value_is_symbol(this, Label::kDeferred), 969 if_value_is_bigint(this, Label::kDeferred), 970 if_value_is_string(this, Label::kDeferred); 971 972 TNode<Object> receiver_value = 973 LoadJSPrimitiveWrapperValue(CAST(receiver_heap_object)); 974 // We need to start with the object to see if the value was a subclass 975 // which might have interesting properties. 976 var_holder = receiver_heap_object; 977 GotoIf(TaggedIsSmi(receiver_value), &if_value_is_number); 978 TNode<Map> receiver_value_map = LoadMap(CAST(receiver_value)); 979 GotoIf(IsHeapNumberMap(receiver_value_map), &if_value_is_number); 980 GotoIf(IsBooleanMap(receiver_value_map), &if_value_is_boolean); 981 GotoIf(IsSymbolMap(receiver_value_map), &if_value_is_symbol); 982 TNode<Uint16T> receiver_value_instance_type = 983 LoadMapInstanceType(receiver_value_map); 984 GotoIf(IsBigIntInstanceType(receiver_value_instance_type), 985 &if_value_is_bigint); 986 CSA_DCHECK(this, IsStringInstanceType(receiver_value_instance_type)); 987 Goto(&if_value_is_string); 988 989 BIND(&if_value_is_number); 990 { 991 var_default = NumberToStringConstant(); 992 Goto(&checkstringtag); 993 } 994 995 BIND(&if_value_is_boolean); 996 { 997 var_default = BooleanToStringConstant(); 998 Goto(&checkstringtag); 999 } 1000 1001 BIND(&if_value_is_string); 1002 { 1003 var_default = StringToStringConstant(); 1004 Goto(&checkstringtag); 1005 } 1006 1007 BIND(&if_value_is_bigint); 1008 { 1009 var_default = ObjectToStringConstant(); 1010 Goto(&checkstringtag); 1011 } 1012 1013 BIND(&if_value_is_symbol); 1014 { 1015 var_default = ObjectToStringConstant(); 1016 Goto(&checkstringtag); 1017 } 1018 } 1019 1020 BIND(&checkstringtag); 1021 { 1022 // Check if all relevant maps (including the prototype maps) don't 1023 // have any interesting symbols (i.e. that none of them have the 1024 // @@toStringTag property). 1025 Label loop(this, &var_holder), return_default(this), 1026 return_generic(this, Label::kDeferred); 1027 Goto(&loop); 1028 BIND(&loop); 1029 { 1030 TNode<HeapObject> holder = var_holder.value(); 1031 GotoIf(IsNull(holder), &return_default); 1032 TNode<Map> holder_map = LoadMap(holder); 1033 TNode<Uint32T> holder_bit_field3 = LoadMapBitField3(holder_map); 1034 GotoIf(IsSetWord32<Map::Bits3::MayHaveInterestingSymbolsBit>( 1035 holder_bit_field3), 1036 &return_generic); 1037 var_holder = LoadMapPrototype(holder_map); 1038 Goto(&loop); 1039 } 1040 1041 BIND(&return_generic); 1042 { 1043 TNode<Object> tag = GetProperty(context, ToObject(context, receiver), 1044 ToStringTagSymbolConstant()); 1045 GotoIf(TaggedIsSmi(tag), &return_default); 1046 GotoIfNot(IsString(CAST(tag)), &return_default); 1047 ReturnToStringFormat(context, CAST(tag)); 1048 } 1049 1050 BIND(&return_default); 1051 Return(var_default.value()); 1052 } 1053} 1054 1055// ES #sec-object.create 1056TF_BUILTIN(ObjectCreate, ObjectBuiltinsAssembler) { 1057 int const kPrototypeArg = 0; 1058 int const kPropertiesArg = 1; 1059 1060 TNode<IntPtrT> argc = ChangeInt32ToIntPtr( 1061 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount)); 1062 CodeStubArguments args(this, argc); 1063 1064 TNode<Object> prototype = args.GetOptionalArgumentValue(kPrototypeArg); 1065 TNode<Object> properties = args.GetOptionalArgumentValue(kPropertiesArg); 1066 auto native_context = Parameter<NativeContext>(Descriptor::kContext); 1067 1068 Label call_runtime(this, Label::kDeferred), prototype_valid(this), 1069 no_properties(this); 1070 1071 { 1072 Comment("Argument 1 check: prototype"); 1073 GotoIf(IsNull(prototype), &prototype_valid); 1074 BranchIfJSReceiver(prototype, &prototype_valid, &call_runtime); 1075 } 1076 1077 BIND(&prototype_valid); 1078 { 1079 Comment("Argument 2 check: properties"); 1080 // Check that we have a simple object 1081 GotoIf(TaggedIsSmi(properties), &call_runtime); 1082 // Undefined implies no properties. 1083 GotoIf(IsUndefined(properties), &no_properties); 1084 TNode<Map> properties_map = LoadMap(CAST(properties)); 1085 GotoIf(IsSpecialReceiverMap(properties_map), &call_runtime); 1086 // Stay on the fast path only if there are no elements. 1087 GotoIfNot( 1088 TaggedEqual(LoadElements(CAST(properties)), EmptyFixedArrayConstant()), 1089 &call_runtime); 1090 // Handle dictionary objects or fast objects with properties in runtime. 1091 TNode<Uint32T> bit_field3 = LoadMapBitField3(properties_map); 1092 GotoIf(IsSetWord32<Map::Bits3::IsDictionaryMapBit>(bit_field3), 1093 &call_runtime); 1094 Branch(IsSetWord32<Map::Bits3::NumberOfOwnDescriptorsBits>(bit_field3), 1095 &call_runtime, &no_properties); 1096 } 1097 1098 // Create a new object with the given prototype. 1099 BIND(&no_properties); 1100 { 1101 TVARIABLE(Map, map); 1102 TVARIABLE(HeapObject, new_properties); 1103 Label null_proto(this), non_null_proto(this), instantiate_map(this); 1104 1105 Branch(IsNull(prototype), &null_proto, &non_null_proto); 1106 1107 BIND(&null_proto); 1108 { 1109 map = LoadSlowObjectWithNullPrototypeMap(native_context); 1110 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) { 1111 new_properties = 1112 AllocateSwissNameDictionary(SwissNameDictionary::kInitialCapacity); 1113 } else { 1114 new_properties = 1115 AllocateNameDictionary(NameDictionary::kInitialCapacity); 1116 } 1117 Goto(&instantiate_map); 1118 } 1119 1120 BIND(&non_null_proto); 1121 { 1122 new_properties = EmptyFixedArrayConstant(); 1123 map = LoadObjectFunctionInitialMap(native_context); 1124 GotoIf(TaggedEqual(prototype, LoadMapPrototype(map.value())), 1125 &instantiate_map); 1126 // Try loading the prototype info. 1127 TNode<PrototypeInfo> prototype_info = 1128 LoadMapPrototypeInfo(LoadMap(CAST(prototype)), &call_runtime); 1129 Comment("Load ObjectCreateMap from PrototypeInfo"); 1130 TNode<MaybeObject> maybe_map = LoadMaybeWeakObjectField( 1131 prototype_info, PrototypeInfo::kObjectCreateMapOffset); 1132 GotoIf(TaggedEqual(maybe_map, UndefinedConstant()), &call_runtime); 1133 map = CAST(GetHeapObjectAssumeWeak(maybe_map, &call_runtime)); 1134 Goto(&instantiate_map); 1135 } 1136 1137 BIND(&instantiate_map); 1138 { 1139 TNode<JSObject> instance = 1140 AllocateJSObjectFromMap(map.value(), new_properties.value()); 1141 args.PopAndReturn(instance); 1142 } 1143 } 1144 1145 BIND(&call_runtime); 1146 { 1147 TNode<Object> result = CallRuntime(Runtime::kObjectCreate, native_context, 1148 prototype, properties); 1149 args.PopAndReturn(result); 1150 } 1151} 1152 1153// ES #sec-object.is 1154TF_BUILTIN(ObjectIs, ObjectBuiltinsAssembler) { 1155 const auto left = Parameter<Object>(Descriptor::kLeft); 1156 const auto right = Parameter<Object>(Descriptor::kRight); 1157 1158 Label return_true(this), return_false(this); 1159 BranchIfSameValue(left, right, &return_true, &return_false); 1160 1161 BIND(&return_true); 1162 Return(TrueConstant()); 1163 1164 BIND(&return_false); 1165 Return(FalseConstant()); 1166} 1167 1168TF_BUILTIN(CreateIterResultObject, ObjectBuiltinsAssembler) { 1169 const auto value = Parameter<Object>(Descriptor::kValue); 1170 const auto done = Parameter<Oddball>(Descriptor::kDone); 1171 const auto context = Parameter<Context>(Descriptor::kContext); 1172 1173 const TNode<NativeContext> native_context = LoadNativeContext(context); 1174 const TNode<Map> map = CAST( 1175 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX)); 1176 1177 const TNode<JSObject> result = AllocateJSObjectFromMap(map); 1178 1179 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset, value); 1180 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset, done); 1181 1182 Return(result); 1183} 1184 1185TF_BUILTIN(HasProperty, ObjectBuiltinsAssembler) { 1186 auto key = Parameter<Object>(Descriptor::kKey); 1187 auto object = Parameter<Object>(Descriptor::kObject); 1188 auto context = Parameter<Context>(Descriptor::kContext); 1189 1190 Return(HasProperty(context, object, key, kHasProperty)); 1191} 1192 1193TF_BUILTIN(InstanceOf, ObjectBuiltinsAssembler) { 1194 auto object = Parameter<Object>(Descriptor::kLeft); 1195 auto callable = Parameter<Object>(Descriptor::kRight); 1196 auto context = Parameter<Context>(Descriptor::kContext); 1197 1198 Return(InstanceOf(object, callable, context)); 1199} 1200 1201TF_BUILTIN(InstanceOf_WithFeedback, ObjectBuiltinsAssembler) { 1202 auto object = Parameter<Object>(Descriptor::kLeft); 1203 auto callable = Parameter<Object>(Descriptor::kRight); 1204 auto context = Parameter<Context>(Descriptor::kContext); 1205 auto feedback_vector = Parameter<HeapObject>(Descriptor::kFeedbackVector); 1206 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); 1207 1208 CollectInstanceOfFeedback(callable, context, feedback_vector, slot); 1209 Return(InstanceOf(object, callable, context)); 1210} 1211 1212TF_BUILTIN(InstanceOf_Baseline, ObjectBuiltinsAssembler) { 1213 auto object = Parameter<Object>(Descriptor::kLeft); 1214 auto callable = Parameter<Object>(Descriptor::kRight); 1215 auto context = LoadContextFromBaseline(); 1216 auto feedback_vector = LoadFeedbackVectorFromBaseline(); 1217 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); 1218 1219 CollectInstanceOfFeedback(callable, context, feedback_vector, slot); 1220 Return(InstanceOf(object, callable, context)); 1221} 1222 1223// ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) 1224TF_BUILTIN(OrdinaryHasInstance, ObjectBuiltinsAssembler) { 1225 auto constructor = Parameter<Object>(Descriptor::kLeft); 1226 auto object = Parameter<Object>(Descriptor::kRight); 1227 auto context = Parameter<Context>(Descriptor::kContext); 1228 1229 Return(OrdinaryHasInstance(context, constructor, object)); 1230} 1231 1232TF_BUILTIN(CreateGeneratorObject, ObjectBuiltinsAssembler) { 1233 auto closure = Parameter<JSFunction>(Descriptor::kClosure); 1234 auto receiver = Parameter<Object>(Descriptor::kReceiver); 1235 auto context = Parameter<Context>(Descriptor::kContext); 1236 1237 // Get the initial map from the function, jumping to the runtime if we don't 1238 // have one. 1239 Label done(this), runtime(this); 1240 GotoIfNot(IsFunctionWithPrototypeSlotMap(LoadMap(closure)), &runtime); 1241 TNode<HeapObject> maybe_map = LoadObjectField<HeapObject>( 1242 closure, JSFunction::kPrototypeOrInitialMapOffset); 1243 GotoIf(DoesntHaveInstanceType(maybe_map, MAP_TYPE), &runtime); 1244 TNode<Map> map = CAST(maybe_map); 1245 1246 TNode<SharedFunctionInfo> shared = LoadObjectField<SharedFunctionInfo>( 1247 closure, JSFunction::kSharedFunctionInfoOffset); 1248 TNode<BytecodeArray> bytecode_array = 1249 LoadSharedFunctionInfoBytecodeArray(shared); 1250 1251 TNode<IntPtrT> formal_parameter_count = ChangeInt32ToIntPtr( 1252 LoadSharedFunctionInfoFormalParameterCountWithoutReceiver(shared)); 1253 TNode<IntPtrT> frame_size = ChangeInt32ToIntPtr( 1254 LoadObjectField<Int32T>(bytecode_array, BytecodeArray::kFrameSizeOffset)); 1255 TNode<IntPtrT> size = 1256 IntPtrAdd(WordSar(frame_size, IntPtrConstant(kTaggedSizeLog2)), 1257 formal_parameter_count); 1258 TNode<FixedArrayBase> parameters_and_registers = AllocateFixedArray( 1259 HOLEY_ELEMENTS, size, AllocationFlag::kAllowLargeObjectAllocation); 1260 FillFixedArrayWithValue(HOLEY_ELEMENTS, parameters_and_registers, 1261 IntPtrConstant(0), size, RootIndex::kUndefinedValue); 1262 // TODO(cbruni): support start_offset to avoid double initialization. 1263 TNode<JSObject> result = 1264 AllocateJSObjectFromMap(map, base::nullopt, base::nullopt, 1265 AllocationFlag::kNone, kWithSlackTracking); 1266 StoreObjectFieldNoWriteBarrier(result, JSGeneratorObject::kFunctionOffset, 1267 closure); 1268 StoreObjectFieldNoWriteBarrier(result, JSGeneratorObject::kContextOffset, 1269 context); 1270 StoreObjectFieldNoWriteBarrier(result, JSGeneratorObject::kReceiverOffset, 1271 receiver); 1272 StoreObjectFieldNoWriteBarrier( 1273 result, JSGeneratorObject::kParametersAndRegistersOffset, 1274 parameters_and_registers); 1275 TNode<Smi> resume_mode = SmiConstant(JSGeneratorObject::ResumeMode::kNext); 1276 StoreObjectFieldNoWriteBarrier(result, JSGeneratorObject::kResumeModeOffset, 1277 resume_mode); 1278 TNode<Smi> executing = SmiConstant(JSGeneratorObject::kGeneratorExecuting); 1279 StoreObjectFieldNoWriteBarrier(result, JSGeneratorObject::kContinuationOffset, 1280 executing); 1281 GotoIfNot(InstanceTypeEqual(LoadMapInstanceType(map), 1282 JS_ASYNC_GENERATOR_OBJECT_TYPE), 1283 &done); 1284 StoreObjectFieldNoWriteBarrier( 1285 result, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(0)); 1286 Goto(&done); 1287 1288 BIND(&done); 1289 { Return(result); } 1290 1291 BIND(&runtime); 1292 { 1293 Return(CallRuntime(Runtime::kCreateJSGeneratorObject, context, closure, 1294 receiver)); 1295 } 1296} 1297 1298// ES6 section 19.1.2.7 Object.getOwnPropertyDescriptor ( O, P ) 1299TF_BUILTIN(ObjectGetOwnPropertyDescriptor, ObjectBuiltinsAssembler) { 1300 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount); 1301 auto context = Parameter<Context>(Descriptor::kContext); 1302 CSA_DCHECK(this, IsUndefined(Parameter<Object>(Descriptor::kJSNewTarget))); 1303 1304 CodeStubArguments args(this, argc); 1305 TNode<Object> object_input = args.GetOptionalArgumentValue(0); 1306 TNode<Object> key = args.GetOptionalArgumentValue(1); 1307 1308 // 1. Let obj be ? ToObject(O). 1309 TNode<JSReceiver> object = ToObject_Inline(context, object_input); 1310 1311 // 2. Let key be ? ToPropertyKey(P). 1312 key = CallBuiltin(Builtin::kToName, context, key); 1313 1314 // 3. Let desc be ? obj.[[GetOwnProperty]](key). 1315 Label if_keyisindex(this), if_iskeyunique(this), 1316 call_runtime(this, Label::kDeferred), 1317 return_undefined(this, Label::kDeferred), if_notunique_name(this); 1318 1319 TNode<Map> map = LoadMap(object); 1320 TNode<Uint16T> instance_type = LoadMapInstanceType(map); 1321 GotoIf(IsSpecialReceiverInstanceType(instance_type), &call_runtime); 1322 { 1323 TVARIABLE(IntPtrT, var_index, IntPtrConstant(0)); 1324 TVARIABLE(Name, var_name); 1325 1326 TryToName(key, &if_keyisindex, &var_index, &if_iskeyunique, &var_name, 1327 &call_runtime, &if_notunique_name); 1328 1329 BIND(&if_notunique_name); 1330 { 1331 Label not_in_string_table(this); 1332 TryInternalizeString(CAST(key), &if_keyisindex, &var_index, 1333 &if_iskeyunique, &var_name, ¬_in_string_table, 1334 &call_runtime); 1335 1336 BIND(¬_in_string_table); 1337 { 1338 // If the string was not found in the string table, then no regular 1339 // object can have a property with that name, so return |undefined|. 1340 Goto(&return_undefined); 1341 } 1342 } 1343 1344 BIND(&if_iskeyunique); 1345 { 1346 Label if_found_value(this), return_empty(this), if_not_found(this); 1347 1348 TVARIABLE(Object, var_value); 1349 TVARIABLE(Uint32T, var_details); 1350 TVARIABLE(Object, var_raw_value); 1351 1352 TryGetOwnProperty(context, object, object, map, instance_type, 1353 var_name.value(), &if_found_value, &var_value, 1354 &var_details, &var_raw_value, &return_empty, 1355 &if_not_found, kReturnAccessorPair); 1356 1357 BIND(&if_found_value); 1358 // 4. Return FromPropertyDetails(desc). 1359 TNode<JSObject> js_desc = FromPropertyDetails( 1360 context, var_value.value(), var_details.value(), &call_runtime); 1361 args.PopAndReturn(js_desc); 1362 1363 BIND(&return_empty); 1364 var_value = UndefinedConstant(); 1365 args.PopAndReturn(UndefinedConstant()); 1366 1367 BIND(&if_not_found); 1368 Goto(&call_runtime); 1369 } 1370 } 1371 1372 BIND(&if_keyisindex); 1373 Goto(&call_runtime); 1374 1375 BIND(&call_runtime); 1376 { 1377 TNode<Object> desc = 1378 CallRuntime(Runtime::kGetOwnPropertyDescriptor, context, object, key); 1379 1380 GotoIf(IsUndefined(desc), &return_undefined); 1381 1382 TNode<PropertyDescriptorObject> desc_object = CAST(desc); 1383 1384 // 4. Return FromPropertyDescriptor(desc). 1385 TNode<JSObject> js_desc = FromPropertyDescriptor(context, desc_object); 1386 args.PopAndReturn(js_desc); 1387 } 1388 BIND(&return_undefined); 1389 args.PopAndReturn(UndefinedConstant()); 1390} 1391 1392// TODO(v8:11167) remove remove |context| and |object| parameters once 1393// OrderedNameDictionary supported. 1394void ObjectBuiltinsAssembler::AddToDictionaryIf( 1395 TNode<BoolT> condition, TNode<Context> context, TNode<Object> object, 1396 TNode<HeapObject> name_dictionary, Handle<Name> name, TNode<Object> value, 1397 Label* bailout) { 1398 Label done(this); 1399 GotoIfNot(condition, &done); 1400 1401 Add<PropertyDictionary>(CAST(name_dictionary), HeapConstant(name), value, 1402 bailout); 1403 Goto(&done); 1404 1405 BIND(&done); 1406} 1407 1408TNode<JSObject> ObjectBuiltinsAssembler::FromPropertyDescriptor( 1409 TNode<Context> context, TNode<PropertyDescriptorObject> desc) { 1410 TVARIABLE(JSObject, js_descriptor); 1411 1412 TNode<Int32T> flags = LoadAndUntagToWord32ObjectField( 1413 desc, PropertyDescriptorObject::kFlagsOffset); 1414 1415 TNode<Int32T> has_flags = 1416 Word32And(flags, Int32Constant(PropertyDescriptorObject::kHasMask)); 1417 1418 Label if_accessor_desc(this), if_data_desc(this), if_generic_desc(this), 1419 return_desc(this); 1420 GotoIf( 1421 Word32Equal(has_flags, 1422 Int32Constant( 1423 PropertyDescriptorObject::kRegularAccessorPropertyBits)), 1424 &if_accessor_desc); 1425 GotoIf(Word32Equal( 1426 has_flags, 1427 Int32Constant(PropertyDescriptorObject::kRegularDataPropertyBits)), 1428 &if_data_desc); 1429 Goto(&if_generic_desc); 1430 1431 BIND(&if_accessor_desc); 1432 { 1433 js_descriptor = ConstructAccessorDescriptor( 1434 context, LoadObjectField(desc, PropertyDescriptorObject::kGetOffset), 1435 LoadObjectField(desc, PropertyDescriptorObject::kSetOffset), 1436 IsSetWord32<PropertyDescriptorObject::IsEnumerableBit>(flags), 1437 IsSetWord32<PropertyDescriptorObject::IsConfigurableBit>(flags)); 1438 Goto(&return_desc); 1439 } 1440 1441 BIND(&if_data_desc); 1442 { 1443 js_descriptor = ConstructDataDescriptor( 1444 context, LoadObjectField(desc, PropertyDescriptorObject::kValueOffset), 1445 IsSetWord32<PropertyDescriptorObject::IsWritableBit>(flags), 1446 IsSetWord32<PropertyDescriptorObject::IsEnumerableBit>(flags), 1447 IsSetWord32<PropertyDescriptorObject::IsConfigurableBit>(flags)); 1448 Goto(&return_desc); 1449 } 1450 1451 BIND(&if_generic_desc); 1452 { 1453 TNode<NativeContext> native_context = LoadNativeContext(context); 1454 TNode<Map> map = CAST(LoadContextElement( 1455 native_context, Context::SLOW_OBJECT_WITH_OBJECT_PROTOTYPE_MAP)); 1456 // We want to preallocate the slots for value, writable, get, set, 1457 // enumerable and configurable - a total of 6 1458 TNode<HeapObject> properties = 1459 V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL 1460 ? TNode<HeapObject>(AllocateSwissNameDictionary(6)) 1461 : AllocateNameDictionary(6); 1462 TNode<JSObject> js_desc = AllocateJSObjectFromMap(map, properties); 1463 1464 Label bailout(this, Label::kDeferred); 1465 1466 Factory* factory = isolate()->factory(); 1467 TNode<Object> value = 1468 LoadObjectField(desc, PropertyDescriptorObject::kValueOffset); 1469 AddToDictionaryIf(IsNotTheHole(value), context, js_desc, properties, 1470 factory->value_string(), value, &bailout); 1471 AddToDictionaryIf( 1472 IsSetWord32<PropertyDescriptorObject::HasWritableBit>(flags), context, 1473 js_desc, properties, factory->writable_string(), 1474 SelectBooleanConstant( 1475 IsSetWord32<PropertyDescriptorObject::IsWritableBit>(flags)), 1476 &bailout); 1477 1478 TNode<Object> get = 1479 LoadObjectField(desc, PropertyDescriptorObject::kGetOffset); 1480 AddToDictionaryIf(IsNotTheHole(get), context, js_desc, properties, 1481 factory->get_string(), get, &bailout); 1482 TNode<Object> set = 1483 LoadObjectField(desc, PropertyDescriptorObject::kSetOffset); 1484 AddToDictionaryIf(IsNotTheHole(set), context, js_desc, properties, 1485 factory->set_string(), set, &bailout); 1486 1487 AddToDictionaryIf( 1488 IsSetWord32<PropertyDescriptorObject::HasEnumerableBit>(flags), context, 1489 js_desc, properties, factory->enumerable_string(), 1490 SelectBooleanConstant( 1491 IsSetWord32<PropertyDescriptorObject::IsEnumerableBit>(flags)), 1492 &bailout); 1493 AddToDictionaryIf( 1494 IsSetWord32<PropertyDescriptorObject::HasConfigurableBit>(flags), 1495 context, js_desc, properties, factory->configurable_string(), 1496 SelectBooleanConstant( 1497 IsSetWord32<PropertyDescriptorObject::IsConfigurableBit>(flags)), 1498 &bailout); 1499 1500 js_descriptor = js_desc; 1501 Goto(&return_desc); 1502 1503 BIND(&bailout); 1504 CSA_DCHECK(this, Int32Constant(0)); 1505 Unreachable(); 1506 } 1507 1508 BIND(&return_desc); 1509 return js_descriptor.value(); 1510} 1511 1512TNode<JSObject> ObjectBuiltinsAssembler::FromPropertyDetails( 1513 TNode<Context> context, TNode<Object> raw_value, TNode<Word32T> details, 1514 Label* if_bailout) { 1515 TVARIABLE(JSObject, js_descriptor); 1516 1517 Label if_accessor_desc(this), if_data_desc(this), return_desc(this); 1518 BranchIfAccessorPair(raw_value, &if_accessor_desc, &if_data_desc); 1519 1520 BIND(&if_accessor_desc); 1521 { 1522 TNode<AccessorPair> accessor_pair_value = CAST(raw_value); 1523 TNode<HeapObject> getter = LoadObjectField<HeapObject>( 1524 accessor_pair_value, AccessorPair::kGetterOffset); 1525 TNode<HeapObject> setter = LoadObjectField<HeapObject>( 1526 accessor_pair_value, AccessorPair::kSetterOffset); 1527 js_descriptor = ConstructAccessorDescriptor( 1528 context, GetAccessorOrUndefined(getter, if_bailout), 1529 GetAccessorOrUndefined(setter, if_bailout), 1530 IsNotSetWord32(details, PropertyDetails::kAttributesDontEnumMask), 1531 IsNotSetWord32(details, PropertyDetails::kAttributesDontDeleteMask)); 1532 Goto(&return_desc); 1533 } 1534 1535 BIND(&if_data_desc); 1536 { 1537 js_descriptor = ConstructDataDescriptor( 1538 context, raw_value, 1539 IsNotSetWord32(details, PropertyDetails::kAttributesReadOnlyMask), 1540 IsNotSetWord32(details, PropertyDetails::kAttributesDontEnumMask), 1541 IsNotSetWord32(details, PropertyDetails::kAttributesDontDeleteMask)); 1542 Goto(&return_desc); 1543 } 1544 1545 BIND(&return_desc); 1546 return js_descriptor.value(); 1547} 1548 1549TNode<HeapObject> ObjectBuiltinsAssembler::GetAccessorOrUndefined( 1550 TNode<HeapObject> accessor, Label* if_bailout) { 1551 Label bind_undefined(this, Label::kDeferred), return_result(this); 1552 TVARIABLE(HeapObject, result); 1553 1554 GotoIf(IsNull(accessor), &bind_undefined); 1555 result = accessor; 1556 TNode<Map> map = LoadMap(accessor); 1557 // TODO(ishell): probe template instantiations cache. 1558 GotoIf(IsFunctionTemplateInfoMap(map), if_bailout); 1559 Goto(&return_result); 1560 1561 BIND(&bind_undefined); 1562 result = UndefinedConstant(); 1563 Goto(&return_result); 1564 1565 BIND(&return_result); 1566 return result.value(); 1567} 1568} // namespace internal 1569} // namespace v8 1570