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