1 // Copyright 2019 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/objects/js-objects.h"
6
7 #include "src/api/api-arguments-inl.h"
8 #include "src/base/optional.h"
9 #include "src/common/globals.h"
10 #include "src/date/date.h"
11 #include "src/execution/arguments.h"
12 #include "src/execution/frames.h"
13 #include "src/execution/isolate.h"
14 #include "src/handles/handles-inl.h"
15 #include "src/handles/maybe-handles.h"
16 #include "src/heap/factory-inl.h"
17 #include "src/heap/heap-inl.h"
18 #include "src/heap/memory-chunk.h"
19 #include "src/init/bootstrapper.h"
20 #include "src/logging/counters.h"
21 #include "src/logging/log.h"
22 #include "src/objects/allocation-site-inl.h"
23 #include "src/objects/api-callbacks.h"
24 #include "src/objects/arguments-inl.h"
25 #include "src/objects/dictionary.h"
26 #include "src/objects/elements.h"
27 #include "src/objects/field-type.h"
28 #include "src/objects/fixed-array.h"
29 #include "src/objects/heap-number.h"
30 #include "src/objects/heap-object.h"
31 #include "src/objects/js-array-buffer-inl.h"
32 #include "src/objects/js-array-inl.h"
33 #include "src/objects/lookup.h"
34 #include "src/objects/map-updater.h"
35 #include "src/objects/objects-inl.h"
36 #ifdef V8_INTL_SUPPORT
37 #include "src/objects/js-break-iterator.h"
38 #include "src/objects/js-collator.h"
39 #endif // V8_INTL_SUPPORT
40 #include "src/objects/js-collection.h"
41 #ifdef V8_INTL_SUPPORT
42 #include "src/objects/js-date-time-format.h"
43 #include "src/objects/js-display-names.h"
44 #endif // V8_INTL_SUPPORT
45 #include "src/objects/js-generator-inl.h"
46 #ifdef V8_INTL_SUPPORT
47 #include "src/objects/js-list-format.h"
48 #include "src/objects/js-locale.h"
49 #include "src/objects/js-number-format.h"
50 #include "src/objects/js-plural-rules.h"
51 #endif // V8_INTL_SUPPORT
52 #include "src/objects/js-promise.h"
53 #include "src/objects/js-regexp-inl.h"
54 #include "src/objects/js-regexp-string-iterator.h"
55 #include "src/objects/js-shadow-realms.h"
56 #ifdef V8_INTL_SUPPORT
57 #include "src/objects/js-relative-time-format.h"
58 #include "src/objects/js-segment-iterator.h"
59 #include "src/objects/js-segmenter.h"
60 #include "src/objects/js-segments.h"
61 #endif // V8_INTL_SUPPORT
62 #include "src/objects/js-struct-inl.h"
63 #include "src/objects/js-temporal-objects-inl.h"
64 #include "src/objects/js-weak-refs.h"
65 #include "src/objects/map-inl.h"
66 #include "src/objects/module.h"
67 #include "src/objects/oddball.h"
68 #include "src/objects/property-cell.h"
69 #include "src/objects/property-descriptor.h"
70 #include "src/objects/property.h"
71 #include "src/objects/prototype-info.h"
72 #include "src/objects/prototype.h"
73 #include "src/objects/shared-function-info.h"
74 #include "src/objects/swiss-name-dictionary-inl.h"
75 #include "src/objects/transitions.h"
76 #include "src/strings/string-builder-inl.h"
77 #include "src/strings/string-stream.h"
78 #include "src/utils/ostreams.h"
79
80 #if V8_ENABLE_WEBASSEMBLY
81 #include "src/wasm/wasm-objects.h"
82 #include "src/debug/debug-wasm-objects.h"
83 #endif // V8_ENABLE_WEBASSEMBLY
84
85 namespace v8 {
86 namespace internal {
87
88 // static
HasProperty(LookupIterator * it)89 Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) {
90 for (; it->IsFound(); it->Next()) {
91 switch (it->state()) {
92 case LookupIterator::NOT_FOUND:
93 case LookupIterator::TRANSITION:
94 UNREACHABLE();
95 case LookupIterator::JSPROXY:
96 return JSProxy::HasProperty(it->isolate(), it->GetHolder<JSProxy>(),
97 it->GetName());
98 case LookupIterator::INTERCEPTOR: {
99 Maybe<PropertyAttributes> result =
100 JSObject::GetPropertyAttributesWithInterceptor(it);
101 if (result.IsNothing()) return Nothing<bool>();
102 if (result.FromJust() != ABSENT) return Just(true);
103 break;
104 }
105 case LookupIterator::ACCESS_CHECK: {
106 if (it->HasAccess()) break;
107 Maybe<PropertyAttributes> result =
108 JSObject::GetPropertyAttributesWithFailedAccessCheck(it);
109 if (result.IsNothing()) return Nothing<bool>();
110 return Just(result.FromJust() != ABSENT);
111 }
112 case LookupIterator::INTEGER_INDEXED_EXOTIC:
113 // TypedArray out-of-bounds access.
114 return Just(false);
115 case LookupIterator::ACCESSOR:
116 case LookupIterator::DATA:
117 return Just(true);
118 }
119 }
120 return Just(false);
121 }
122
123 // static
HasOwnProperty(Isolate * isolate,Handle<JSReceiver> object,Handle<Name> name)124 Maybe<bool> JSReceiver::HasOwnProperty(Isolate* isolate,
125 Handle<JSReceiver> object,
126 Handle<Name> name) {
127 if (object->IsJSModuleNamespace()) {
128 PropertyDescriptor desc;
129 return JSReceiver::GetOwnPropertyDescriptor(isolate, object, name, &desc);
130 }
131
132 if (object->IsJSObject()) { // Shortcut.
133 PropertyKey key(isolate, name);
134 LookupIterator it(isolate, object, key, LookupIterator::OWN);
135 return HasProperty(&it);
136 }
137
138 Maybe<PropertyAttributes> attributes =
139 JSReceiver::GetOwnPropertyAttributes(object, name);
140 MAYBE_RETURN(attributes, Nothing<bool>());
141 return Just(attributes.FromJust() != ABSENT);
142 }
143
GetDataProperty(LookupIterator * it,AllocationPolicy allocation_policy)144 Handle<Object> JSReceiver::GetDataProperty(LookupIterator* it,
145 AllocationPolicy allocation_policy) {
146 for (; it->IsFound(); it->Next()) {
147 switch (it->state()) {
148 case LookupIterator::INTERCEPTOR:
149 case LookupIterator::NOT_FOUND:
150 case LookupIterator::TRANSITION:
151 UNREACHABLE();
152 case LookupIterator::ACCESS_CHECK:
153 // Support calling this method without an active context, but refuse
154 // access to access-checked objects in that case.
155 if (!it->isolate()->context().is_null() && it->HasAccess()) continue;
156 V8_FALLTHROUGH;
157 case LookupIterator::JSPROXY:
158 it->NotFound();
159 return it->isolate()->factory()->undefined_value();
160 case LookupIterator::ACCESSOR:
161 // TODO(verwaest): For now this doesn't call into AccessorInfo, since
162 // clients don't need it. Update once relevant.
163 it->NotFound();
164 return it->isolate()->factory()->undefined_value();
165 case LookupIterator::INTEGER_INDEXED_EXOTIC:
166 return it->isolate()->factory()->undefined_value();
167 case LookupIterator::DATA:
168 return it->GetDataValue(allocation_policy);
169 }
170 }
171 return it->isolate()->factory()->undefined_value();
172 }
173
174 // static
HasInPrototypeChain(Isolate * isolate,Handle<JSReceiver> object,Handle<Object> proto)175 Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate,
176 Handle<JSReceiver> object,
177 Handle<Object> proto) {
178 PrototypeIterator iter(isolate, object, kStartAtReceiver);
179 while (true) {
180 if (!iter.AdvanceFollowingProxies()) return Nothing<bool>();
181 if (iter.IsAtEnd()) return Just(false);
182 if (PrototypeIterator::GetCurrent(iter).is_identical_to(proto)) {
183 return Just(true);
184 }
185 }
186 }
187
188 // static
CheckPrivateNameStore(LookupIterator * it,bool is_define)189 bool JSReceiver::CheckPrivateNameStore(LookupIterator* it, bool is_define) {
190 DCHECK(it->GetName()->IsPrivateName());
191 Isolate* isolate = it->isolate();
192 Handle<String> name_string(
193 String::cast(Handle<Symbol>::cast(it->GetName())->description()),
194 isolate);
195 bool should_throw = GetShouldThrow(isolate, Nothing<ShouldThrow>()) ==
196 ShouldThrow::kThrowOnError;
197 for (; it->IsFound(); it->Next()) {
198 switch (it->state()) {
199 case LookupIterator::TRANSITION:
200 case LookupIterator::INTERCEPTOR:
201 case LookupIterator::JSPROXY:
202 case LookupIterator::NOT_FOUND:
203 case LookupIterator::INTEGER_INDEXED_EXOTIC:
204 case LookupIterator::ACCESSOR:
205 UNREACHABLE();
206 case LookupIterator::ACCESS_CHECK:
207 if (!it->HasAccess()) {
208 isolate->ReportFailedAccessCheck(
209 Handle<JSObject>::cast(it->GetReceiver()));
210 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, false);
211 return false;
212 }
213 break;
214 case LookupIterator::DATA:
215 if (is_define && should_throw) {
216 MessageTemplate message =
217 it->GetName()->IsPrivateBrand()
218 ? MessageTemplate::kInvalidPrivateBrandReinitialization
219 : MessageTemplate::kInvalidPrivateFieldReinitialization;
220 isolate->Throw(*(isolate->factory()->NewTypeError(
221 message, name_string, it->GetReceiver())));
222 return false;
223 }
224 return true;
225 }
226 }
227 DCHECK(!it->IsFound());
228 if (!is_define && should_throw) {
229 isolate->Throw(*(isolate->factory()->NewTypeError(
230 MessageTemplate::kInvalidPrivateMemberWrite, name_string,
231 it->GetReceiver())));
232 return false;
233 }
234 return true;
235 }
236
237 // static
CheckIfCanDefine(Isolate * isolate,LookupIterator * it,Handle<Object> value,Maybe<ShouldThrow> should_throw)238 Maybe<bool> JSReceiver::CheckIfCanDefine(Isolate* isolate, LookupIterator* it,
239 Handle<Object> value,
240 Maybe<ShouldThrow> should_throw) {
241 if (it->IsFound()) {
242 Maybe<PropertyAttributes> attributes = GetPropertyAttributes(it);
243 MAYBE_RETURN(attributes, Nothing<bool>());
244 if ((attributes.FromJust() & DONT_DELETE) != 0) {
245 RETURN_FAILURE(
246 isolate, GetShouldThrow(isolate, should_throw),
247 NewTypeError(MessageTemplate::kRedefineDisallowed, it->GetName()));
248 }
249 } else if (!JSObject::IsExtensible(
250 Handle<JSObject>::cast(it->GetReceiver()))) {
251 RETURN_FAILURE(
252 isolate, GetShouldThrow(isolate, should_throw),
253 NewTypeError(MessageTemplate::kDefineDisallowed, it->GetName()));
254 }
255 return Just(true);
256 }
257
258 namespace {
259
HasExcludedProperty(const base::ScopedVector<Handle<Object>> * excluded_properties,Handle<Object> search_element)260 bool HasExcludedProperty(
261 const base::ScopedVector<Handle<Object>>* excluded_properties,
262 Handle<Object> search_element) {
263 // TODO(gsathya): Change this to be a hashtable.
264 for (int i = 0; i < excluded_properties->length(); i++) {
265 if (search_element->SameValue(*excluded_properties->at(i))) {
266 return true;
267 }
268 }
269
270 return false;
271 }
272
FastAssign(Handle<JSReceiver> target,Handle<Object> source,PropertiesEnumerationMode mode,const base::ScopedVector<Handle<Object>> * excluded_properties,bool use_set)273 V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(
274 Handle<JSReceiver> target, Handle<Object> source,
275 PropertiesEnumerationMode mode,
276 const base::ScopedVector<Handle<Object>>* excluded_properties,
277 bool use_set) {
278 // Non-empty strings are the only non-JSReceivers that need to be handled
279 // explicitly by Object.assign.
280 if (!source->IsJSReceiver()) {
281 return Just(!source->IsString() || String::cast(*source).length() == 0);
282 }
283
284 Isolate* isolate = target->GetIsolate();
285
286 // If the target is deprecated, the object will be updated on first store. If
287 // the source for that store equals the target, this will invalidate the
288 // cached representation of the source. Preventively upgrade the target.
289 // Do this on each iteration since any property load could cause deprecation.
290 if (target->map().is_deprecated()) {
291 JSObject::MigrateInstance(isolate, Handle<JSObject>::cast(target));
292 }
293
294 Handle<Map> map(JSReceiver::cast(*source).map(), isolate);
295
296 if (!map->IsJSObjectMap()) return Just(false);
297 if (!map->OnlyHasSimpleProperties()) return Just(false);
298
299 Handle<JSObject> from = Handle<JSObject>::cast(source);
300 if (from->elements() != ReadOnlyRoots(isolate).empty_fixed_array()) {
301 return Just(false);
302 }
303
304 // We should never try to copy properties from an object itself.
305 CHECK_IMPLIES(!use_set, !target.is_identical_to(from));
306
307 Handle<DescriptorArray> descriptors(map->instance_descriptors(isolate),
308 isolate);
309
310 bool stable = true;
311
312 // Process symbols last and only do that if we found symbols.
313 bool has_symbol = false;
314 bool process_symbol_only = false;
315 while (true) {
316 for (InternalIndex i : map->IterateOwnDescriptors()) {
317 HandleScope inner_scope(isolate);
318
319 Handle<Name> next_key(descriptors->GetKey(i), isolate);
320 if (mode == PropertiesEnumerationMode::kEnumerationOrder) {
321 if (next_key->IsSymbol()) {
322 has_symbol = true;
323 if (!process_symbol_only) continue;
324 } else {
325 if (process_symbol_only) continue;
326 }
327 }
328 Handle<Object> prop_value;
329 // Directly decode from the descriptor array if |from| did not change
330 // shape.
331 if (stable) {
332 DCHECK_EQ(from->map(), *map);
333 DCHECK_EQ(*descriptors, map->instance_descriptors(isolate));
334
335 PropertyDetails details = descriptors->GetDetails(i);
336 if (!details.IsEnumerable()) continue;
337 if (details.kind() == PropertyKind::kData) {
338 if (details.location() == PropertyLocation::kDescriptor) {
339 prop_value = handle(descriptors->GetStrongValue(i), isolate);
340 } else {
341 Representation representation = details.representation();
342 FieldIndex index = FieldIndex::ForPropertyIndex(
343 *map, details.field_index(), representation);
344 prop_value =
345 JSObject::FastPropertyAt(isolate, from, representation, index);
346 }
347 } else {
348 LookupIterator it(isolate, from, next_key,
349 LookupIterator::OWN_SKIP_INTERCEPTOR);
350 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
351 isolate, prop_value, Object::GetProperty(&it), Nothing<bool>());
352 stable = from->map() == *map;
353 descriptors.PatchValue(map->instance_descriptors(isolate));
354 }
355 } else {
356 // If the map did change, do a slower lookup. We are still guaranteed
357 // that the object has a simple shape, and that the key is a name.
358 LookupIterator it(isolate, from, next_key, from,
359 LookupIterator::OWN_SKIP_INTERCEPTOR);
360 if (!it.IsFound()) continue;
361 DCHECK(it.state() == LookupIterator::DATA ||
362 it.state() == LookupIterator::ACCESSOR);
363 if (!it.IsEnumerable()) continue;
364 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
365 isolate, prop_value, Object::GetProperty(&it), Nothing<bool>());
366 }
367
368 if (use_set) {
369 // The lookup will walk the prototype chain, so we have to be careful
370 // to treat any key correctly for any receiver/holder.
371 PropertyKey key(isolate, next_key);
372 LookupIterator it(isolate, target, key);
373 Maybe<bool> result =
374 Object::SetProperty(&it, prop_value, StoreOrigin::kNamed,
375 Just(ShouldThrow::kThrowOnError));
376 if (result.IsNothing()) return result;
377 if (stable) {
378 stable = from->map() == *map;
379 descriptors.PatchValue(map->instance_descriptors(isolate));
380 }
381 } else {
382 // No element indexes should get here or the exclusion check may
383 // yield false negatives for type mismatch.
384 if (excluded_properties != nullptr &&
385 HasExcludedProperty(excluded_properties, next_key)) {
386 continue;
387 }
388
389 // 4a ii 2. Perform ? CreateDataProperty(target, nextKey, propValue).
390 // This is an OWN lookup, so constructing a named-mode LookupIterator
391 // from {next_key} is safe.
392 LookupIterator it(isolate, target, next_key, LookupIterator::OWN);
393 CHECK(JSObject::CreateDataProperty(&it, prop_value, Just(kThrowOnError))
394 .FromJust());
395 }
396 }
397 if (mode == PropertiesEnumerationMode::kEnumerationOrder) {
398 if (process_symbol_only || !has_symbol) {
399 return Just(true);
400 }
401 if (has_symbol) {
402 process_symbol_only = true;
403 }
404 } else {
405 DCHECK_EQ(mode, PropertiesEnumerationMode::kPropertyAdditionOrder);
406 return Just(true);
407 }
408 }
409 UNREACHABLE();
410 }
411 } // namespace
412
413 // static
SetOrCopyDataProperties(Isolate * isolate,Handle<JSReceiver> target,Handle<Object> source,PropertiesEnumerationMode mode,const base::ScopedVector<Handle<Object>> * excluded_properties,bool use_set)414 Maybe<bool> JSReceiver::SetOrCopyDataProperties(
415 Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source,
416 PropertiesEnumerationMode mode,
417 const base::ScopedVector<Handle<Object>>* excluded_properties,
418 bool use_set) {
419 Maybe<bool> fast_assign =
420 FastAssign(target, source, mode, excluded_properties, use_set);
421 if (fast_assign.IsNothing()) return Nothing<bool>();
422 if (fast_assign.FromJust()) return Just(true);
423
424 Handle<JSReceiver> from = Object::ToObject(isolate, source).ToHandleChecked();
425
426 // 3b. Let keys be ? from.[[OwnPropertyKeys]]().
427 Handle<FixedArray> keys;
428 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
429 isolate, keys,
430 KeyAccumulator::GetKeys(from, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES,
431 GetKeysConversion::kKeepNumbers),
432 Nothing<bool>());
433
434 if (!from->HasFastProperties() && target->HasFastProperties() &&
435 !target->IsJSGlobalProxy()) {
436 // JSProxy is always in slow-mode.
437 DCHECK(!target->IsJSProxy());
438 // Convert to slow properties if we're guaranteed to overflow the number of
439 // descriptors.
440 int source_length;
441 if (from->IsJSGlobalObject()) {
442 source_length = JSGlobalObject::cast(*from)
443 .global_dictionary(kAcquireLoad)
444 .NumberOfEnumerableProperties();
445 } else if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
446 source_length =
447 from->property_dictionary_swiss().NumberOfEnumerableProperties();
448 } else {
449 source_length =
450 from->property_dictionary().NumberOfEnumerableProperties();
451 }
452 if (source_length > kMaxNumberOfDescriptors) {
453 JSObject::NormalizeProperties(isolate, Handle<JSObject>::cast(target),
454 CLEAR_INOBJECT_PROPERTIES, source_length,
455 "Copying data properties");
456 }
457 }
458
459 // 4. Repeat for each element nextKey of keys in List order,
460 for (int i = 0; i < keys->length(); ++i) {
461 Handle<Object> next_key(keys->get(i), isolate);
462 if (excluded_properties != nullptr &&
463 HasExcludedProperty(excluded_properties, next_key)) {
464 continue;
465 }
466
467 // 4a i. Let desc be ? from.[[GetOwnProperty]](nextKey).
468 PropertyDescriptor desc;
469 Maybe<bool> found =
470 JSReceiver::GetOwnPropertyDescriptor(isolate, from, next_key, &desc);
471 if (found.IsNothing()) return Nothing<bool>();
472 // 4a ii. If desc is not undefined and desc.[[Enumerable]] is true, then
473 if (found.FromJust() && desc.enumerable()) {
474 // 4a ii 1. Let propValue be ? Get(from, nextKey).
475 Handle<Object> prop_value;
476 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
477 isolate, prop_value,
478 Runtime::GetObjectProperty(isolate, from, next_key), Nothing<bool>());
479
480 if (use_set) {
481 // 4c ii 2. Let status be ? Set(to, nextKey, propValue, true).
482 Handle<Object> status;
483 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
484 isolate, status,
485 Runtime::SetObjectProperty(isolate, target, next_key, prop_value,
486 StoreOrigin::kMaybeKeyed,
487 Just(ShouldThrow::kThrowOnError)),
488 Nothing<bool>());
489 } else {
490 // 4a ii 2. Perform ! CreateDataProperty(target, nextKey, propValue).
491 PropertyKey key(isolate, next_key);
492 LookupIterator it(isolate, target, key, LookupIterator::OWN);
493 CHECK(JSObject::CreateDataProperty(&it, prop_value, Just(kThrowOnError))
494 .FromJust());
495 }
496 }
497 }
498
499 return Just(true);
500 }
501
class_name()502 String JSReceiver::class_name() {
503 ReadOnlyRoots roots = GetReadOnlyRoots();
504 if (IsFunction()) return roots.Function_string();
505 if (IsJSArgumentsObject()) return roots.Arguments_string();
506 if (IsJSArray()) return roots.Array_string();
507 if (IsJSArrayBuffer()) {
508 if (JSArrayBuffer::cast(*this).is_shared()) {
509 return roots.SharedArrayBuffer_string();
510 }
511 return roots.ArrayBuffer_string();
512 }
513 if (IsJSArrayIterator()) return roots.ArrayIterator_string();
514 if (IsJSDate()) return roots.Date_string();
515 if (IsJSError()) return roots.Error_string();
516 if (IsJSGeneratorObject()) return roots.Generator_string();
517 if (IsJSMap()) return roots.Map_string();
518 if (IsJSMapIterator()) return roots.MapIterator_string();
519 if (IsJSProxy()) {
520 return map().is_callable() ? roots.Function_string()
521 : roots.Object_string();
522 }
523 if (IsJSRegExp()) return roots.RegExp_string();
524 if (IsJSSet()) return roots.Set_string();
525 if (IsJSSetIterator()) return roots.SetIterator_string();
526 if (IsJSTypedArray()) {
527 #define SWITCH_KIND(Type, type, TYPE, ctype) \
528 if (map().elements_kind() == TYPE##_ELEMENTS) { \
529 return roots.Type##Array_string(); \
530 }
531 TYPED_ARRAYS(SWITCH_KIND)
532 #undef SWITCH_KIND
533 }
534 if (IsJSPrimitiveWrapper()) {
535 Object value = JSPrimitiveWrapper::cast(*this).value();
536 if (value.IsBoolean()) return roots.Boolean_string();
537 if (value.IsString()) return roots.String_string();
538 if (value.IsNumber()) return roots.Number_string();
539 if (value.IsBigInt()) return roots.BigInt_string();
540 if (value.IsSymbol()) return roots.Symbol_string();
541 if (value.IsScript()) return roots.Script_string();
542 UNREACHABLE();
543 }
544 if (IsJSWeakMap()) return roots.WeakMap_string();
545 if (IsJSWeakSet()) return roots.WeakSet_string();
546 if (IsJSGlobalProxy()) return roots.global_string();
547
548 return roots.Object_string();
549 }
550
551 namespace {
GetConstructorHelper(Isolate * isolate,Handle<JSReceiver> receiver)552 std::pair<MaybeHandle<JSFunction>, Handle<String>> GetConstructorHelper(
553 Isolate* isolate, Handle<JSReceiver> receiver) {
554 // If the object was instantiated simply with base == new.target, the
555 // constructor on the map provides the most accurate name.
556 // Don't provide the info for prototypes, since their constructors are
557 // reclaimed and replaced by Object in OptimizeAsPrototype.
558 if (!receiver->IsJSProxy() && receiver->map().new_target_is_base() &&
559 !receiver->map().is_prototype_map()) {
560 Handle<Object> maybe_constructor(receiver->map().GetConstructor(), isolate);
561 if (maybe_constructor->IsJSFunction()) {
562 Handle<JSFunction> constructor =
563 Handle<JSFunction>::cast(maybe_constructor);
564 Handle<String> name =
565 SharedFunctionInfo::DebugName(handle(constructor->shared(), isolate));
566 if (name->length() != 0 &&
567 !name->Equals(ReadOnlyRoots(isolate).Object_string())) {
568 return std::make_pair(constructor, name);
569 }
570 }
571 }
572
573 for (PrototypeIterator it(isolate, receiver, kStartAtReceiver); !it.IsAtEnd();
574 it.AdvanceIgnoringProxies()) {
575 auto current = PrototypeIterator::GetCurrent<JSReceiver>(it);
576
577 LookupIterator it_to_string_tag(
578 isolate, receiver, isolate->factory()->to_string_tag_symbol(), current,
579 LookupIterator::OWN_SKIP_INTERCEPTOR);
580 auto maybe_to_string_tag = JSReceiver::GetDataProperty(
581 &it_to_string_tag, AllocationPolicy::kAllocationDisallowed);
582 if (maybe_to_string_tag->IsString()) {
583 return std::make_pair(MaybeHandle<JSFunction>(),
584 Handle<String>::cast(maybe_to_string_tag));
585 }
586
587 // Consider the following example:
588 //
589 // function A() {}
590 // function B() {}
591 // B.prototype = new A();
592 // B.prototype.constructor = B;
593 //
594 // The constructor name for `B.prototype` must yield "A", so we don't take
595 // "constructor" into account for the receiver itself, but only starting
596 // on the prototype chain.
597 if (!receiver.is_identical_to(current)) {
598 LookupIterator it_constructor(
599 isolate, receiver, isolate->factory()->constructor_string(), current,
600 LookupIterator::OWN_SKIP_INTERCEPTOR);
601 auto maybe_constructor = JSReceiver::GetDataProperty(
602 &it_constructor, AllocationPolicy::kAllocationDisallowed);
603 if (maybe_constructor->IsJSFunction()) {
604 auto constructor = Handle<JSFunction>::cast(maybe_constructor);
605 auto name = SharedFunctionInfo::DebugName(
606 handle(constructor->shared(), isolate));
607
608 if (name->length() != 0 &&
609 !name->Equals(ReadOnlyRoots(isolate).Object_string())) {
610 return std::make_pair(constructor, name);
611 }
612 }
613 }
614 }
615
616 return std::make_pair(MaybeHandle<JSFunction>(),
617 handle(receiver->class_name(), isolate));
618 }
619 } // anonymous namespace
620
621 // static
GetConstructor(Isolate * isolate,Handle<JSReceiver> receiver)622 MaybeHandle<JSFunction> JSReceiver::GetConstructor(
623 Isolate* isolate, Handle<JSReceiver> receiver) {
624 return GetConstructorHelper(isolate, receiver).first;
625 }
626
627 // static
GetConstructorName(Isolate * isolate,Handle<JSReceiver> receiver)628 Handle<String> JSReceiver::GetConstructorName(Isolate* isolate,
629 Handle<JSReceiver> receiver) {
630 return GetConstructorHelper(isolate, receiver).second;
631 }
632
GetCreationContext()633 MaybeHandle<NativeContext> JSReceiver::GetCreationContext() {
634 JSReceiver receiver = *this;
635 // Externals are JSObjects with null as a constructor.
636 DCHECK(!receiver.IsJSExternalObject());
637 Object constructor = receiver.map().GetConstructor();
638 JSFunction function;
639 if (constructor.IsJSFunction()) {
640 function = JSFunction::cast(constructor);
641 } else if (constructor.IsFunctionTemplateInfo()) {
642 // Remote objects don't have a creation context.
643 return MaybeHandle<NativeContext>();
644 } else if (receiver.IsJSGeneratorObject()) {
645 function = JSGeneratorObject::cast(receiver).function();
646 } else if (receiver.IsJSFunction()) {
647 function = JSFunction::cast(receiver);
648 } else {
649 return MaybeHandle<NativeContext>();
650 }
651
652 return function.has_context()
653 ? Handle<NativeContext>(function.native_context(),
654 receiver.GetIsolate())
655 : MaybeHandle<NativeContext>();
656 }
657
658 // static
GetFunctionRealm(Handle<JSReceiver> receiver)659 MaybeHandle<NativeContext> JSReceiver::GetFunctionRealm(
660 Handle<JSReceiver> receiver) {
661 Isolate* isolate = receiver->GetIsolate();
662 // This is implemented as a loop because it's possible to construct very
663 // long chains of bound functions or proxies where a recursive implementation
664 // would run out of stack space.
665 DisallowGarbageCollection no_gc;
666 JSReceiver current = *receiver;
667 do {
668 DCHECK(current.map().is_constructor());
669 if (current.IsJSProxy()) {
670 JSProxy proxy = JSProxy::cast(current);
671 if (proxy.IsRevoked()) {
672 AllowGarbageCollection allow_allocating_errors;
673 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyRevoked),
674 NativeContext);
675 }
676 current = JSReceiver::cast(proxy.target());
677 continue;
678 }
679 if (current.IsJSFunction()) {
680 JSFunction function = JSFunction::cast(current);
681 return handle(function.native_context(), isolate);
682 }
683 if (current.IsJSBoundFunction()) {
684 JSBoundFunction function = JSBoundFunction::cast(current);
685 current = function.bound_target_function();
686 continue;
687 }
688 if (current.IsJSWrappedFunction()) {
689 JSWrappedFunction function = JSWrappedFunction::cast(current);
690 current = function.wrapped_target_function();
691 continue;
692 }
693 JSObject object = JSObject::cast(current);
694 DCHECK(!object.IsJSFunction());
695 return object.GetCreationContext();
696 } while (true);
697 }
698
699 // static
GetContextForMicrotask(Handle<JSReceiver> receiver)700 MaybeHandle<NativeContext> JSReceiver::GetContextForMicrotask(
701 Handle<JSReceiver> receiver) {
702 Isolate* isolate = receiver->GetIsolate();
703 while (receiver->IsJSBoundFunction() || receiver->IsJSProxy()) {
704 if (receiver->IsJSBoundFunction()) {
705 receiver = handle(
706 Handle<JSBoundFunction>::cast(receiver)->bound_target_function(),
707 isolate);
708 } else {
709 DCHECK(receiver->IsJSProxy());
710 Handle<Object> target(Handle<JSProxy>::cast(receiver)->target(), isolate);
711 if (!target->IsJSReceiver()) return MaybeHandle<NativeContext>();
712 receiver = Handle<JSReceiver>::cast(target);
713 }
714 }
715
716 if (!receiver->IsJSFunction()) return MaybeHandle<NativeContext>();
717 return handle(Handle<JSFunction>::cast(receiver)->native_context(), isolate);
718 }
719
GetPropertyAttributes(LookupIterator * it)720 Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes(
721 LookupIterator* it) {
722 for (; it->IsFound(); it->Next()) {
723 switch (it->state()) {
724 case LookupIterator::NOT_FOUND:
725 case LookupIterator::TRANSITION:
726 UNREACHABLE();
727 case LookupIterator::JSPROXY:
728 return JSProxy::GetPropertyAttributes(it);
729 case LookupIterator::INTERCEPTOR: {
730 Maybe<PropertyAttributes> result =
731 JSObject::GetPropertyAttributesWithInterceptor(it);
732 if (result.IsNothing()) return result;
733 if (result.FromJust() != ABSENT) return result;
734 break;
735 }
736 case LookupIterator::ACCESS_CHECK:
737 if (it->HasAccess()) break;
738 return JSObject::GetPropertyAttributesWithFailedAccessCheck(it);
739 case LookupIterator::INTEGER_INDEXED_EXOTIC:
740 return Just(ABSENT);
741 case LookupIterator::ACCESSOR:
742 if (it->GetHolder<Object>()->IsJSModuleNamespace()) {
743 return JSModuleNamespace::GetPropertyAttributes(it);
744 } else {
745 return Just(it->property_attributes());
746 }
747 case LookupIterator::DATA:
748 return Just(it->property_attributes());
749 }
750 }
751 return Just(ABSENT);
752 }
753
754 namespace {
755
SetHashAndUpdateProperties(HeapObject properties,int hash)756 Object SetHashAndUpdateProperties(HeapObject properties, int hash) {
757 DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
758 DCHECK(PropertyArray::HashField::is_valid(hash));
759
760 ReadOnlyRoots roots = properties.GetReadOnlyRoots();
761 if (properties == roots.empty_fixed_array() ||
762 properties == roots.empty_property_array() ||
763 properties == roots.empty_property_dictionary() ||
764 properties == roots.empty_swiss_property_dictionary()) {
765 return Smi::FromInt(hash);
766 }
767
768 if (properties.IsPropertyArray()) {
769 PropertyArray::cast(properties).SetHash(hash);
770 DCHECK_LT(0, PropertyArray::cast(properties).length());
771 return properties;
772 }
773
774 if (properties.IsGlobalDictionary()) {
775 GlobalDictionary::cast(properties).SetHash(hash);
776 return properties;
777 }
778
779 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
780 DCHECK(properties.IsSwissNameDictionary());
781 SwissNameDictionary::cast(properties).SetHash(hash);
782 } else {
783 DCHECK(properties.IsNameDictionary());
784 NameDictionary::cast(properties).SetHash(hash);
785 }
786 return properties;
787 }
788
GetIdentityHashHelper(JSReceiver object)789 int GetIdentityHashHelper(JSReceiver object) {
790 DisallowGarbageCollection no_gc;
791 Object properties = object.raw_properties_or_hash();
792 if (properties.IsSmi()) {
793 return Smi::ToInt(properties);
794 }
795
796 if (properties.IsPropertyArray()) {
797 return PropertyArray::cast(properties).Hash();
798 }
799 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL &&
800 properties.IsSwissNameDictionary()) {
801 return SwissNameDictionary::cast(properties).Hash();
802 }
803
804 if (properties.IsNameDictionary()) {
805 DCHECK(!V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL);
806 return NameDictionary::cast(properties).Hash();
807 }
808
809 if (properties.IsGlobalDictionary()) {
810 return GlobalDictionary::cast(properties).Hash();
811 }
812
813 #ifdef DEBUG
814 ReadOnlyRoots roots = object.GetReadOnlyRoots();
815 DCHECK(properties == roots.empty_fixed_array() ||
816 properties == roots.empty_property_dictionary() ||
817 properties == roots.empty_swiss_property_dictionary());
818 #endif
819
820 return PropertyArray::kNoHashSentinel;
821 }
822 } // namespace
823
SetIdentityHash(int hash)824 void JSReceiver::SetIdentityHash(int hash) {
825 DisallowGarbageCollection no_gc;
826 DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
827 DCHECK(PropertyArray::HashField::is_valid(hash));
828
829 HeapObject existing_properties = HeapObject::cast(raw_properties_or_hash());
830 Object new_properties = SetHashAndUpdateProperties(existing_properties, hash);
831 set_raw_properties_or_hash(new_properties, kRelaxedStore);
832 }
833
SetProperties(HeapObject properties)834 void JSReceiver::SetProperties(HeapObject properties) {
835 DCHECK_IMPLIES(properties.IsPropertyArray() &&
836 PropertyArray::cast(properties).length() == 0,
837 properties == GetReadOnlyRoots().empty_property_array());
838 DisallowGarbageCollection no_gc;
839 int hash = GetIdentityHashHelper(*this);
840 Object new_properties = properties;
841
842 // TODO(cbruni): Make GetIdentityHashHelper return a bool so that we
843 // don't have to manually compare against kNoHashSentinel.
844 if (hash != PropertyArray::kNoHashSentinel) {
845 new_properties = SetHashAndUpdateProperties(properties, hash);
846 }
847
848 set_raw_properties_or_hash(new_properties, kRelaxedStore);
849 }
850
GetIdentityHash()851 Object JSReceiver::GetIdentityHash() {
852 DisallowGarbageCollection no_gc;
853
854 int hash = GetIdentityHashHelper(*this);
855 if (hash == PropertyArray::kNoHashSentinel) {
856 return GetReadOnlyRoots().undefined_value();
857 }
858
859 return Smi::FromInt(hash);
860 }
861
862 // static
CreateIdentityHash(Isolate * isolate,JSReceiver key)863 Smi JSReceiver::CreateIdentityHash(Isolate* isolate, JSReceiver key) {
864 DisallowGarbageCollection no_gc;
865 int hash = isolate->GenerateIdentityHash(PropertyArray::HashField::kMax);
866 DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
867
868 key.SetIdentityHash(hash);
869 return Smi::FromInt(hash);
870 }
871
GetOrCreateIdentityHash(Isolate * isolate)872 Smi JSReceiver::GetOrCreateIdentityHash(Isolate* isolate) {
873 DisallowGarbageCollection no_gc;
874
875 int hash = GetIdentityHashHelper(*this);
876 if (hash != PropertyArray::kNoHashSentinel) {
877 return Smi::FromInt(hash);
878 }
879
880 return JSReceiver::CreateIdentityHash(isolate, *this);
881 }
882
DeleteNormalizedProperty(Handle<JSReceiver> object,InternalIndex entry)883 void JSReceiver::DeleteNormalizedProperty(Handle<JSReceiver> object,
884 InternalIndex entry) {
885 DCHECK(!object->HasFastProperties());
886 Isolate* isolate = object->GetIsolate();
887 DCHECK(entry.is_found());
888
889 if (object->IsJSGlobalObject()) {
890 // If we have a global object, invalidate the cell and remove it from the
891 // global object's dictionary.
892 Handle<GlobalDictionary> dictionary(
893 JSGlobalObject::cast(*object).global_dictionary(kAcquireLoad), isolate);
894
895 Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
896
897 Handle<GlobalDictionary> new_dictionary =
898 GlobalDictionary::DeleteEntry(isolate, dictionary, entry);
899 JSGlobalObject::cast(*object).set_global_dictionary(*new_dictionary,
900 kReleaseStore);
901
902 cell->ClearAndInvalidate(ReadOnlyRoots(isolate));
903 } else {
904 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
905 Handle<SwissNameDictionary> dictionary(
906 object->property_dictionary_swiss(), isolate);
907
908 dictionary = SwissNameDictionary::DeleteEntry(isolate, dictionary, entry);
909 object->SetProperties(*dictionary);
910 } else {
911 Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
912
913 dictionary = NameDictionary::DeleteEntry(isolate, dictionary, entry);
914 object->SetProperties(*dictionary);
915 }
916 }
917 if (object->map().is_prototype_map()) {
918 // Invalidate prototype validity cell as this may invalidate transitioning
919 // store IC handlers.
920 JSObject::InvalidatePrototypeChains(object->map());
921 }
922 }
923
DeleteProperty(LookupIterator * it,LanguageMode language_mode)924 Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it,
925 LanguageMode language_mode) {
926 it->UpdateProtector();
927
928 Isolate* isolate = it->isolate();
929
930 if (it->state() == LookupIterator::JSPROXY) {
931 return JSProxy::DeletePropertyOrElement(it->GetHolder<JSProxy>(),
932 it->GetName(), language_mode);
933 }
934
935 if (it->GetReceiver()->IsJSProxy()) {
936 if (it->state() != LookupIterator::NOT_FOUND) {
937 DCHECK_EQ(LookupIterator::DATA, it->state());
938 DCHECK(it->name()->IsPrivate());
939 it->Delete();
940 }
941 return Just(true);
942 }
943 Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver());
944
945 for (; it->IsFound(); it->Next()) {
946 switch (it->state()) {
947 case LookupIterator::JSPROXY:
948 case LookupIterator::NOT_FOUND:
949 case LookupIterator::TRANSITION:
950 UNREACHABLE();
951 case LookupIterator::ACCESS_CHECK:
952 if (it->HasAccess()) break;
953 isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>());
954 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
955 return Just(false);
956 case LookupIterator::INTERCEPTOR: {
957 ShouldThrow should_throw =
958 is_sloppy(language_mode) ? kDontThrow : kThrowOnError;
959 Maybe<bool> result =
960 JSObject::DeletePropertyWithInterceptor(it, should_throw);
961 // An exception was thrown in the interceptor. Propagate.
962 if (isolate->has_pending_exception()) return Nothing<bool>();
963 // Delete with interceptor succeeded. Return result.
964 // TODO(neis): In strict mode, we should probably throw if the
965 // interceptor returns false.
966 if (result.IsJust()) return result;
967 break;
968 }
969 case LookupIterator::INTEGER_INDEXED_EXOTIC:
970 return Just(true);
971 case LookupIterator::DATA:
972 case LookupIterator::ACCESSOR: {
973 Handle<JSObject> holder = it->GetHolder<JSObject>();
974 if (!it->IsConfigurable() ||
975 (holder->IsJSTypedArray() && it->IsElement(*holder))) {
976 // Fail if the property is not configurable if the property is a
977 // TypedArray element.
978 if (is_strict(language_mode)) {
979 isolate->Throw(*isolate->factory()->NewTypeError(
980 MessageTemplate::kStrictDeleteProperty, it->GetName(),
981 receiver));
982 return Nothing<bool>();
983 }
984 return Just(false);
985 }
986
987 it->Delete();
988
989 return Just(true);
990 }
991 }
992 }
993
994 return Just(true);
995 }
996
DeleteElement(Handle<JSReceiver> object,uint32_t index,LanguageMode language_mode)997 Maybe<bool> JSReceiver::DeleteElement(Handle<JSReceiver> object, uint32_t index,
998 LanguageMode language_mode) {
999 LookupIterator it(object->GetIsolate(), object, index, object,
1000 LookupIterator::OWN);
1001 return DeleteProperty(&it, language_mode);
1002 }
1003
DeleteProperty(Handle<JSReceiver> object,Handle<Name> name,LanguageMode language_mode)1004 Maybe<bool> JSReceiver::DeleteProperty(Handle<JSReceiver> object,
1005 Handle<Name> name,
1006 LanguageMode language_mode) {
1007 LookupIterator it(object->GetIsolate(), object, name, object,
1008 LookupIterator::OWN);
1009 return DeleteProperty(&it, language_mode);
1010 }
1011
DeletePropertyOrElement(Handle<JSReceiver> object,Handle<Name> name,LanguageMode language_mode)1012 Maybe<bool> JSReceiver::DeletePropertyOrElement(Handle<JSReceiver> object,
1013 Handle<Name> name,
1014 LanguageMode language_mode) {
1015 Isolate* isolate = object->GetIsolate();
1016 PropertyKey key(isolate, name);
1017 LookupIterator it(isolate, object, key, object, LookupIterator::OWN);
1018 return DeleteProperty(&it, language_mode);
1019 }
1020
1021 // ES6 19.1.2.4
1022 // static
DefineProperty(Isolate * isolate,Handle<Object> object,Handle<Object> key,Handle<Object> attributes)1023 Object JSReceiver::DefineProperty(Isolate* isolate, Handle<Object> object,
1024 Handle<Object> key,
1025 Handle<Object> attributes) {
1026 // 1. If Type(O) is not Object, throw a TypeError exception.
1027 if (!object->IsJSReceiver()) {
1028 Handle<String> fun_name =
1029 isolate->factory()->InternalizeUtf8String("Object.defineProperty");
1030 THROW_NEW_ERROR_RETURN_FAILURE(
1031 isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name));
1032 }
1033 // 2. Let key be ToPropertyKey(P).
1034 // 3. ReturnIfAbrupt(key).
1035 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, key,
1036 Object::ToPropertyKey(isolate, key));
1037 // 4. Let desc be ToPropertyDescriptor(Attributes).
1038 // 5. ReturnIfAbrupt(desc).
1039 PropertyDescriptor desc;
1040 if (!PropertyDescriptor::ToPropertyDescriptor(isolate, attributes, &desc)) {
1041 return ReadOnlyRoots(isolate).exception();
1042 }
1043 // 6. Let success be DefinePropertyOrThrow(O,key, desc).
1044 Maybe<bool> success =
1045 DefineOwnProperty(isolate, Handle<JSReceiver>::cast(object), key, &desc,
1046 Just(kThrowOnError));
1047 // 7. ReturnIfAbrupt(success).
1048 MAYBE_RETURN(success, ReadOnlyRoots(isolate).exception());
1049 CHECK(success.FromJust());
1050 // 8. Return O.
1051 return *object;
1052 }
1053
1054 // ES6 19.1.2.3.1
1055 // static
DefineProperties(Isolate * isolate,Handle<Object> object,Handle<Object> properties)1056 MaybeHandle<Object> JSReceiver::DefineProperties(Isolate* isolate,
1057 Handle<Object> object,
1058 Handle<Object> properties) {
1059 // 1. If Type(O) is not Object, throw a TypeError exception.
1060 if (!object->IsJSReceiver()) {
1061 Handle<String> fun_name =
1062 isolate->factory()->InternalizeUtf8String("Object.defineProperties");
1063 THROW_NEW_ERROR(isolate,
1064 NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name),
1065 Object);
1066 }
1067 // 2. Let props be ToObject(Properties).
1068 // 3. ReturnIfAbrupt(props).
1069 Handle<JSReceiver> props;
1070 ASSIGN_RETURN_ON_EXCEPTION(isolate, props,
1071 Object::ToObject(isolate, properties), Object);
1072
1073 // 4. Let keys be props.[[OwnPropertyKeys]]().
1074 // 5. ReturnIfAbrupt(keys).
1075 Handle<FixedArray> keys;
1076 ASSIGN_RETURN_ON_EXCEPTION(
1077 isolate, keys,
1078 KeyAccumulator::GetKeys(props, KeyCollectionMode::kOwnOnly,
1079 ALL_PROPERTIES),
1080 Object);
1081 // 6. Let descriptors be an empty List.
1082 int capacity = keys->length();
1083 std::vector<PropertyDescriptor> descriptors(capacity);
1084 size_t descriptors_index = 0;
1085 // 7. Repeat for each element nextKey of keys in List order,
1086 for (int i = 0; i < keys->length(); ++i) {
1087 Handle<Object> next_key(keys->get(i), isolate);
1088 // 7a. Let propDesc be props.[[GetOwnProperty]](nextKey).
1089 // 7b. ReturnIfAbrupt(propDesc).
1090 PropertyKey key(isolate, next_key);
1091 LookupIterator it(isolate, props, key, LookupIterator::OWN);
1092 Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
1093 if (maybe.IsNothing()) return MaybeHandle<Object>();
1094 PropertyAttributes attrs = maybe.FromJust();
1095 // 7c. If propDesc is not undefined and propDesc.[[Enumerable]] is true:
1096 if (attrs == ABSENT) continue;
1097 if (attrs & DONT_ENUM) continue;
1098 // 7c i. Let descObj be Get(props, nextKey).
1099 // 7c ii. ReturnIfAbrupt(descObj).
1100 Handle<Object> desc_obj;
1101 ASSIGN_RETURN_ON_EXCEPTION(isolate, desc_obj, Object::GetProperty(&it),
1102 Object);
1103 // 7c iii. Let desc be ToPropertyDescriptor(descObj).
1104 bool success = PropertyDescriptor::ToPropertyDescriptor(
1105 isolate, desc_obj, &descriptors[descriptors_index]);
1106 // 7c iv. ReturnIfAbrupt(desc).
1107 if (!success) return MaybeHandle<Object>();
1108 // 7c v. Append the pair (a two element List) consisting of nextKey and
1109 // desc to the end of descriptors.
1110 descriptors[descriptors_index].set_name(next_key);
1111 descriptors_index++;
1112 }
1113 // 8. For each pair from descriptors in list order,
1114 for (size_t i = 0; i < descriptors_index; ++i) {
1115 PropertyDescriptor* desc = &descriptors[i];
1116 // 8a. Let P be the first element of pair.
1117 // 8b. Let desc be the second element of pair.
1118 // 8c. Let status be DefinePropertyOrThrow(O, P, desc).
1119 Maybe<bool> status =
1120 DefineOwnProperty(isolate, Handle<JSReceiver>::cast(object),
1121 desc->name(), desc, Just(kThrowOnError));
1122 // 8d. ReturnIfAbrupt(status).
1123 if (status.IsNothing()) return MaybeHandle<Object>();
1124 CHECK(status.FromJust());
1125 }
1126 // 9. Return o.
1127 return object;
1128 }
1129
1130 // static
DefineOwnProperty(Isolate * isolate,Handle<JSReceiver> object,Handle<Object> key,PropertyDescriptor * desc,Maybe<ShouldThrow> should_throw)1131 Maybe<bool> JSReceiver::DefineOwnProperty(Isolate* isolate,
1132 Handle<JSReceiver> object,
1133 Handle<Object> key,
1134 PropertyDescriptor* desc,
1135 Maybe<ShouldThrow> should_throw) {
1136 if (object->IsJSArray()) {
1137 return JSArray::DefineOwnProperty(isolate, Handle<JSArray>::cast(object),
1138 key, desc, should_throw);
1139 }
1140 if (object->IsJSProxy()) {
1141 return JSProxy::DefineOwnProperty(isolate, Handle<JSProxy>::cast(object),
1142 key, desc, should_throw);
1143 }
1144 if (object->IsJSTypedArray()) {
1145 return JSTypedArray::DefineOwnProperty(
1146 isolate, Handle<JSTypedArray>::cast(object), key, desc, should_throw);
1147 }
1148 if (object->IsJSModuleNamespace()) {
1149 return JSModuleNamespace::DefineOwnProperty(
1150 isolate, Handle<JSModuleNamespace>::cast(object), key, desc,
1151 should_throw);
1152 }
1153
1154 // OrdinaryDefineOwnProperty, by virtue of calling
1155 // DefineOwnPropertyIgnoreAttributes, can handle arguments
1156 // (ES#sec-arguments-exotic-objects-defineownproperty-p-desc).
1157 return OrdinaryDefineOwnProperty(isolate, Handle<JSObject>::cast(object), key,
1158 desc, should_throw);
1159 }
1160
1161 // static
OrdinaryDefineOwnProperty(Isolate * isolate,Handle<JSObject> object,Handle<Object> key,PropertyDescriptor * desc,Maybe<ShouldThrow> should_throw)1162 Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(
1163 Isolate* isolate, Handle<JSObject> object, Handle<Object> key,
1164 PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw) {
1165 DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey.
1166 PropertyKey lookup_key(isolate, key);
1167 return OrdinaryDefineOwnProperty(isolate, object, lookup_key, desc,
1168 should_throw);
1169 }
1170
OrdinaryDefineOwnProperty(Isolate * isolate,Handle<JSObject> object,const PropertyKey & key,PropertyDescriptor * desc,Maybe<ShouldThrow> should_throw)1171 Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(
1172 Isolate* isolate, Handle<JSObject> object, const PropertyKey& key,
1173 PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw) {
1174 LookupIterator it(isolate, object, key, LookupIterator::OWN);
1175
1176 // Deal with access checks first.
1177 if (it.state() == LookupIterator::ACCESS_CHECK) {
1178 if (!it.HasAccess()) {
1179 isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>());
1180 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
1181 return Just(true);
1182 }
1183 it.Next();
1184 }
1185
1186 return OrdinaryDefineOwnProperty(&it, desc, should_throw);
1187 }
1188
1189 namespace {
1190
GetPropertyWithInterceptorInternal(LookupIterator * it,Handle<InterceptorInfo> interceptor,bool * done)1191 MaybeHandle<Object> GetPropertyWithInterceptorInternal(
1192 LookupIterator* it, Handle<InterceptorInfo> interceptor, bool* done) {
1193 *done = false;
1194 Isolate* isolate = it->isolate();
1195 // Make sure that the top context does not change when doing callbacks or
1196 // interceptor calls.
1197 AssertNoContextChange ncc(isolate);
1198
1199 if (interceptor->getter().IsUndefined(isolate)) {
1200 return isolate->factory()->undefined_value();
1201 }
1202
1203 Handle<JSObject> holder = it->GetHolder<JSObject>();
1204 Handle<Object> result;
1205 Handle<Object> receiver = it->GetReceiver();
1206 if (!receiver->IsJSReceiver()) {
1207 ASSIGN_RETURN_ON_EXCEPTION(
1208 isolate, receiver, Object::ConvertReceiver(isolate, receiver), Object);
1209 }
1210 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
1211 *holder, Just(kDontThrow));
1212
1213 if (it->IsElement(*holder)) {
1214 result = args.CallIndexedGetter(interceptor, it->array_index());
1215 } else {
1216 result = args.CallNamedGetter(interceptor, it->name());
1217 }
1218
1219 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
1220 if (result.is_null()) return isolate->factory()->undefined_value();
1221 *done = true;
1222 // Rebox handle before return
1223 return handle(*result, isolate);
1224 }
1225
GetPropertyAttributesWithInterceptorInternal(LookupIterator * it,Handle<InterceptorInfo> interceptor)1226 Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal(
1227 LookupIterator* it, Handle<InterceptorInfo> interceptor) {
1228 Isolate* isolate = it->isolate();
1229 // Make sure that the top context does not change when doing
1230 // callbacks or interceptor calls.
1231 AssertNoContextChange ncc(isolate);
1232 HandleScope scope(isolate);
1233
1234 Handle<JSObject> holder = it->GetHolder<JSObject>();
1235 DCHECK_IMPLIES(!it->IsElement(*holder) && it->name()->IsSymbol(),
1236 interceptor->can_intercept_symbols());
1237 Handle<Object> receiver = it->GetReceiver();
1238 if (!receiver->IsJSReceiver()) {
1239 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver,
1240 Object::ConvertReceiver(isolate, receiver),
1241 Nothing<PropertyAttributes>());
1242 }
1243 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
1244 *holder, Just(kDontThrow));
1245 if (!interceptor->query().IsUndefined(isolate)) {
1246 Handle<Object> result;
1247 if (it->IsElement(*holder)) {
1248 result = args.CallIndexedQuery(interceptor, it->array_index());
1249 } else {
1250 result = args.CallNamedQuery(interceptor, it->name());
1251 }
1252 if (!result.is_null()) {
1253 int32_t value;
1254 CHECK(result->ToInt32(&value));
1255 DCHECK_IMPLIES((value & ~PropertyAttributes::ALL_ATTRIBUTES_MASK) != 0,
1256 value == PropertyAttributes::ABSENT);
1257 return Just(static_cast<PropertyAttributes>(value));
1258 }
1259 } else if (!interceptor->getter().IsUndefined(isolate)) {
1260 // TODO(verwaest): Use GetPropertyWithInterceptor?
1261 Handle<Object> result;
1262 if (it->IsElement(*holder)) {
1263 result = args.CallIndexedGetter(interceptor, it->array_index());
1264 } else {
1265 result = args.CallNamedGetter(interceptor, it->name());
1266 }
1267 if (!result.is_null()) return Just(DONT_ENUM);
1268 }
1269
1270 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>());
1271 return Just(ABSENT);
1272 }
1273
SetPropertyWithInterceptorInternal(LookupIterator * it,Handle<InterceptorInfo> interceptor,Maybe<ShouldThrow> should_throw,Handle<Object> value)1274 Maybe<bool> SetPropertyWithInterceptorInternal(
1275 LookupIterator* it, Handle<InterceptorInfo> interceptor,
1276 Maybe<ShouldThrow> should_throw, Handle<Object> value) {
1277 Isolate* isolate = it->isolate();
1278 // Make sure that the top context does not change when doing callbacks or
1279 // interceptor calls.
1280 AssertNoContextChange ncc(isolate);
1281
1282 if (interceptor->setter().IsUndefined(isolate)) return Just(false);
1283
1284 Handle<JSObject> holder = it->GetHolder<JSObject>();
1285 bool result;
1286 Handle<Object> receiver = it->GetReceiver();
1287 if (!receiver->IsJSReceiver()) {
1288 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver,
1289 Object::ConvertReceiver(isolate, receiver),
1290 Nothing<bool>());
1291 }
1292 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
1293 *holder, should_throw);
1294
1295 if (it->IsElement(*holder)) {
1296 // TODO(neis): In the future, we may want to actually return the
1297 // interceptor's result, which then should be a boolean.
1298 result = !args.CallIndexedSetter(interceptor, it->array_index(), value)
1299 .is_null();
1300 } else {
1301 result = !args.CallNamedSetter(interceptor, it->name(), value).is_null();
1302 }
1303
1304 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>());
1305 return Just(result);
1306 }
1307
DefinePropertyWithInterceptorInternal(LookupIterator * it,Handle<InterceptorInfo> interceptor,Maybe<ShouldThrow> should_throw,PropertyDescriptor * desc)1308 Maybe<bool> DefinePropertyWithInterceptorInternal(
1309 LookupIterator* it, Handle<InterceptorInfo> interceptor,
1310 Maybe<ShouldThrow> should_throw, PropertyDescriptor* desc) {
1311 Isolate* isolate = it->isolate();
1312 // Make sure that the top context does not change when doing callbacks or
1313 // interceptor calls.
1314 AssertNoContextChange ncc(isolate);
1315
1316 if (interceptor->definer().IsUndefined(isolate)) return Just(false);
1317
1318 Handle<JSObject> holder = it->GetHolder<JSObject>();
1319 bool result;
1320 Handle<Object> receiver = it->GetReceiver();
1321 if (!receiver->IsJSReceiver()) {
1322 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver,
1323 Object::ConvertReceiver(isolate, receiver),
1324 Nothing<bool>());
1325 }
1326 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
1327 *holder, should_throw);
1328
1329 std::unique_ptr<v8::PropertyDescriptor> descriptor(
1330 new v8::PropertyDescriptor());
1331 if (PropertyDescriptor::IsAccessorDescriptor(desc)) {
1332 descriptor.reset(new v8::PropertyDescriptor(
1333 v8::Utils::ToLocal(desc->get()), v8::Utils::ToLocal(desc->set())));
1334 } else if (PropertyDescriptor::IsDataDescriptor(desc)) {
1335 if (desc->has_writable()) {
1336 descriptor.reset(new v8::PropertyDescriptor(
1337 v8::Utils::ToLocal(desc->value()), desc->writable()));
1338 } else {
1339 descriptor.reset(
1340 new v8::PropertyDescriptor(v8::Utils::ToLocal(desc->value())));
1341 }
1342 }
1343 if (desc->has_enumerable()) {
1344 descriptor->set_enumerable(desc->enumerable());
1345 }
1346 if (desc->has_configurable()) {
1347 descriptor->set_configurable(desc->configurable());
1348 }
1349
1350 if (it->IsElement(*holder)) {
1351 result =
1352 !args.CallIndexedDefiner(interceptor, it->array_index(), *descriptor)
1353 .is_null();
1354 } else {
1355 result =
1356 !args.CallNamedDefiner(interceptor, it->name(), *descriptor).is_null();
1357 }
1358
1359 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>());
1360 return Just(result);
1361 }
1362
1363 } // namespace
1364
1365 // ES6 9.1.6.1
1366 // static
OrdinaryDefineOwnProperty(LookupIterator * it,PropertyDescriptor * desc,Maybe<ShouldThrow> should_throw)1367 Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(
1368 LookupIterator* it, PropertyDescriptor* desc,
1369 Maybe<ShouldThrow> should_throw) {
1370 Isolate* isolate = it->isolate();
1371 // 1. Let current be O.[[GetOwnProperty]](P).
1372 // 2. ReturnIfAbrupt(current).
1373 PropertyDescriptor current;
1374 MAYBE_RETURN(GetOwnPropertyDescriptor(it, ¤t), Nothing<bool>());
1375
1376 it->Restart();
1377 // Handle interceptor
1378 for (; it->IsFound(); it->Next()) {
1379 if (it->state() == LookupIterator::INTERCEPTOR) {
1380 if (it->HolderIsReceiverOrHiddenPrototype()) {
1381 Maybe<bool> result = DefinePropertyWithInterceptorInternal(
1382 it, it->GetInterceptor(), should_throw, desc);
1383 if (result.IsNothing() || result.FromJust()) {
1384 return result;
1385 }
1386 }
1387 }
1388 }
1389
1390 // TODO(jkummerow/verwaest): It would be nice if we didn't have to reset
1391 // the iterator every time. Currently, the reasons why we need it are:
1392 // - handle interceptors correctly
1393 // - handle accessors correctly (which might change the holder's map)
1394 it->Restart();
1395 // 3. Let extensible be the value of the [[Extensible]] internal slot of O.
1396 Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver());
1397 bool extensible = JSObject::IsExtensible(object);
1398
1399 return ValidateAndApplyPropertyDescriptor(
1400 isolate, it, extensible, desc, ¤t, should_throw, Handle<Name>());
1401 }
1402
1403 // ES6 9.1.6.2
1404 // static
IsCompatiblePropertyDescriptor(Isolate * isolate,bool extensible,PropertyDescriptor * desc,PropertyDescriptor * current,Handle<Name> property_name,Maybe<ShouldThrow> should_throw)1405 Maybe<bool> JSReceiver::IsCompatiblePropertyDescriptor(
1406 Isolate* isolate, bool extensible, PropertyDescriptor* desc,
1407 PropertyDescriptor* current, Handle<Name> property_name,
1408 Maybe<ShouldThrow> should_throw) {
1409 // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined,
1410 // Extensible, Desc, Current).
1411 return ValidateAndApplyPropertyDescriptor(
1412 isolate, nullptr, extensible, desc, current, should_throw, property_name);
1413 }
1414
1415 // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
1416 // static
ValidateAndApplyPropertyDescriptor(Isolate * isolate,LookupIterator * it,bool extensible,PropertyDescriptor * desc,PropertyDescriptor * current,Maybe<ShouldThrow> should_throw,Handle<Name> property_name)1417 Maybe<bool> JSReceiver::ValidateAndApplyPropertyDescriptor(
1418 Isolate* isolate, LookupIterator* it, bool extensible,
1419 PropertyDescriptor* desc, PropertyDescriptor* current,
1420 Maybe<ShouldThrow> should_throw, Handle<Name> property_name) {
1421 // We either need a LookupIterator, or a property name.
1422 DCHECK((it == nullptr) != property_name.is_null());
1423 Handle<JSObject> object;
1424 if (it != nullptr) object = Handle<JSObject>::cast(it->GetReceiver());
1425 bool desc_is_data_descriptor = PropertyDescriptor::IsDataDescriptor(desc);
1426 bool desc_is_accessor_descriptor =
1427 PropertyDescriptor::IsAccessorDescriptor(desc);
1428 bool desc_is_generic_descriptor =
1429 PropertyDescriptor::IsGenericDescriptor(desc);
1430 // 1. (Assert)
1431 // 2. If current is undefined, then
1432 if (current->is_empty()) {
1433 // 2a. If extensible is false, return false.
1434 if (!extensible) {
1435 RETURN_FAILURE(
1436 isolate, GetShouldThrow(isolate, should_throw),
1437 NewTypeError(MessageTemplate::kDefineDisallowed,
1438 it != nullptr ? it->GetName() : property_name));
1439 }
1440 // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then:
1441 // (This is equivalent to !IsAccessorDescriptor(desc).)
1442 DCHECK_EQ(desc_is_generic_descriptor || desc_is_data_descriptor,
1443 !desc_is_accessor_descriptor);
1444 if (!desc_is_accessor_descriptor) {
1445 // 2c i. If O is not undefined, create an own data property named P of
1446 // object O whose [[Value]], [[Writable]], [[Enumerable]] and
1447 // [[Configurable]] attribute values are described by Desc. If the value
1448 // of an attribute field of Desc is absent, the attribute of the newly
1449 // created property is set to its default value.
1450 if (it != nullptr) {
1451 if (!desc->has_writable()) desc->set_writable(false);
1452 if (!desc->has_enumerable()) desc->set_enumerable(false);
1453 if (!desc->has_configurable()) desc->set_configurable(false);
1454 Handle<Object> value(
1455 desc->has_value()
1456 ? desc->value()
1457 : Handle<Object>::cast(isolate->factory()->undefined_value()));
1458 MaybeHandle<Object> result =
1459 JSObject::DefineOwnPropertyIgnoreAttributes(it, value,
1460 desc->ToAttributes());
1461 if (result.is_null()) return Nothing<bool>();
1462 }
1463 } else {
1464 // 2d. Else Desc must be an accessor Property Descriptor,
1465 DCHECK(desc_is_accessor_descriptor);
1466 // 2d i. If O is not undefined, create an own accessor property named P
1467 // of object O whose [[Get]], [[Set]], [[Enumerable]] and
1468 // [[Configurable]] attribute values are described by Desc. If the value
1469 // of an attribute field of Desc is absent, the attribute of the newly
1470 // created property is set to its default value.
1471 if (it != nullptr) {
1472 if (!desc->has_enumerable()) desc->set_enumerable(false);
1473 if (!desc->has_configurable()) desc->set_configurable(false);
1474 Handle<Object> getter(
1475 desc->has_get()
1476 ? desc->get()
1477 : Handle<Object>::cast(isolate->factory()->null_value()));
1478 Handle<Object> setter(
1479 desc->has_set()
1480 ? desc->set()
1481 : Handle<Object>::cast(isolate->factory()->null_value()));
1482 MaybeHandle<Object> result =
1483 JSObject::DefineAccessor(it, getter, setter, desc->ToAttributes());
1484 if (result.is_null()) return Nothing<bool>();
1485 }
1486 }
1487 // 2e. Return true.
1488 return Just(true);
1489 }
1490 // 3. If every field in Desc is absent, return true. (This also has a shortcut
1491 // not in the spec: if every field value matches the current value, return.)
1492 if ((!desc->has_enumerable() ||
1493 desc->enumerable() == current->enumerable()) &&
1494 (!desc->has_configurable() ||
1495 desc->configurable() == current->configurable()) &&
1496 (!desc->has_value() ||
1497 (current->has_value() && current->value()->SameValue(*desc->value()))) &&
1498 (!desc->has_writable() ||
1499 (current->has_writable() && current->writable() == desc->writable())) &&
1500 (!desc->has_get() ||
1501 (current->has_get() && current->get()->SameValue(*desc->get()))) &&
1502 (!desc->has_set() ||
1503 (current->has_set() && current->set()->SameValue(*desc->set())))) {
1504 return Just(true);
1505 }
1506 // 4. If current.[[Configurable]] is false, then
1507 if (!current->configurable()) {
1508 // 4a. If Desc.[[Configurable]] is present and its value is true, return
1509 // false.
1510 if (desc->has_configurable() && desc->configurable()) {
1511 RETURN_FAILURE(
1512 isolate, GetShouldThrow(isolate, should_throw),
1513 NewTypeError(MessageTemplate::kRedefineDisallowed,
1514 it != nullptr ? it->GetName() : property_name));
1515 }
1516 // 4b. If Desc.[[Enumerable]] is present and
1517 // ! SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) is false, return
1518 // false.
1519 if (desc->has_enumerable() && desc->enumerable() != current->enumerable()) {
1520 RETURN_FAILURE(
1521 isolate, GetShouldThrow(isolate, should_throw),
1522 NewTypeError(MessageTemplate::kRedefineDisallowed,
1523 it != nullptr ? it->GetName() : property_name));
1524 }
1525 }
1526
1527 bool current_is_data_descriptor =
1528 PropertyDescriptor::IsDataDescriptor(current);
1529 // 5. If ! IsGenericDescriptor(Desc) is true, no further validation is
1530 // required.
1531 if (desc_is_generic_descriptor) {
1532 // Nothing to see here.
1533
1534 // 6. Else if ! SameValue(!IsDataDescriptor(current),
1535 // !IsDataDescriptor(Desc)) is false, the
1536 } else if (current_is_data_descriptor != desc_is_data_descriptor) {
1537 // 6a. If current.[[Configurable]] is false, return false.
1538 if (!current->configurable()) {
1539 RETURN_FAILURE(
1540 isolate, GetShouldThrow(isolate, should_throw),
1541 NewTypeError(MessageTemplate::kRedefineDisallowed,
1542 it != nullptr ? it->GetName() : property_name));
1543 }
1544 // 6b. If IsDataDescriptor(current) is true, then:
1545 if (current_is_data_descriptor) {
1546 // 6b i. If O is not undefined, convert the property named P of object O
1547 // from a data property to an accessor property. Preserve the existing
1548 // values of the converted property's [[Configurable]] and [[Enumerable]]
1549 // attributes and set the rest of the property's attributes to their
1550 // default values.
1551 // --> Folded into step 9
1552 } else {
1553 // 6c i. If O is not undefined, convert the property named P of object O
1554 // from an accessor property to a data property. Preserve the existing
1555 // values of the converted property’s [[Configurable]] and [[Enumerable]]
1556 // attributes and set the rest of the property’s attributes to their
1557 // default values.
1558 // --> Folded into step 9
1559 }
1560
1561 // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both
1562 // true, then:
1563 } else if (current_is_data_descriptor && desc_is_data_descriptor) {
1564 // 7a. If current.[[Configurable]] is false and current.[[Writable]] is
1565 // false, then
1566 if (!current->configurable() && !current->writable()) {
1567 // 7a i. If Desc.[[Writable]] is present and Desc.[[Writable]] is true,
1568 // return false.
1569 if (desc->has_writable() && desc->writable()) {
1570 RETURN_FAILURE(
1571 isolate, GetShouldThrow(isolate, should_throw),
1572 NewTypeError(MessageTemplate::kRedefineDisallowed,
1573 it != nullptr ? it->GetName() : property_name));
1574 }
1575 // 7a ii. If Desc.[[Value]] is present and SameValue(Desc.[[Value]],
1576 // current.[[Value]]) is false, return false.
1577 if (desc->has_value() && !desc->value()->SameValue(*current->value())) {
1578 RETURN_FAILURE(
1579 isolate, GetShouldThrow(isolate, should_throw),
1580 NewTypeError(MessageTemplate::kRedefineDisallowed,
1581 it != nullptr ? it->GetName() : property_name));
1582 }
1583 }
1584 } else {
1585 // 8. Else,
1586 // 8a. Assert: ! IsAccessorDescriptor(current) and
1587 // ! IsAccessorDescriptor(Desc) are both true.
1588 DCHECK(PropertyDescriptor::IsAccessorDescriptor(current) &&
1589 desc_is_accessor_descriptor);
1590 // 8b. If current.[[Configurable]] is false, then:
1591 if (!current->configurable()) {
1592 // 8a i. If Desc.[[Set]] is present and SameValue(Desc.[[Set]],
1593 // current.[[Set]]) is false, return false.
1594 if (desc->has_set() && !desc->set()->SameValue(*current->set())) {
1595 RETURN_FAILURE(
1596 isolate, GetShouldThrow(isolate, should_throw),
1597 NewTypeError(MessageTemplate::kRedefineDisallowed,
1598 it != nullptr ? it->GetName() : property_name));
1599 }
1600 // 8a ii. If Desc.[[Get]] is present and SameValue(Desc.[[Get]],
1601 // current.[[Get]]) is false, return false.
1602 if (desc->has_get() && !desc->get()->SameValue(*current->get())) {
1603 RETURN_FAILURE(
1604 isolate, GetShouldThrow(isolate, should_throw),
1605 NewTypeError(MessageTemplate::kRedefineDisallowed,
1606 it != nullptr ? it->GetName() : property_name));
1607 }
1608 }
1609 }
1610
1611 // 9. If O is not undefined, then:
1612 if (it != nullptr) {
1613 // 9a. For each field of Desc that is present, set the corresponding
1614 // attribute of the property named P of object O to the value of the field.
1615 PropertyAttributes attrs = NONE;
1616
1617 if (desc->has_enumerable()) {
1618 attrs = static_cast<PropertyAttributes>(
1619 attrs | (desc->enumerable() ? NONE : DONT_ENUM));
1620 } else {
1621 attrs = static_cast<PropertyAttributes>(
1622 attrs | (current->enumerable() ? NONE : DONT_ENUM));
1623 }
1624 if (desc->has_configurable()) {
1625 attrs = static_cast<PropertyAttributes>(
1626 attrs | (desc->configurable() ? NONE : DONT_DELETE));
1627 } else {
1628 attrs = static_cast<PropertyAttributes>(
1629 attrs | (current->configurable() ? NONE : DONT_DELETE));
1630 }
1631 if (desc_is_data_descriptor ||
1632 (desc_is_generic_descriptor && current_is_data_descriptor)) {
1633 if (desc->has_writable()) {
1634 attrs = static_cast<PropertyAttributes>(
1635 attrs | (desc->writable() ? NONE : READ_ONLY));
1636 } else {
1637 attrs = static_cast<PropertyAttributes>(
1638 attrs | (current->writable() ? NONE : READ_ONLY));
1639 }
1640 Handle<Object> value(
1641 desc->has_value() ? desc->value()
1642 : current->has_value()
1643 ? current->value()
1644 : Handle<Object>::cast(
1645 isolate->factory()->undefined_value()));
1646 return JSObject::DefineOwnPropertyIgnoreAttributes(it, value, attrs,
1647 should_throw);
1648 } else {
1649 DCHECK(desc_is_accessor_descriptor ||
1650 (desc_is_generic_descriptor &&
1651 PropertyDescriptor::IsAccessorDescriptor(current)));
1652 Handle<Object> getter(
1653 desc->has_get()
1654 ? desc->get()
1655 : current->has_get()
1656 ? current->get()
1657 : Handle<Object>::cast(isolate->factory()->null_value()));
1658 Handle<Object> setter(
1659 desc->has_set()
1660 ? desc->set()
1661 : current->has_set()
1662 ? current->set()
1663 : Handle<Object>::cast(isolate->factory()->null_value()));
1664 MaybeHandle<Object> result =
1665 JSObject::DefineAccessor(it, getter, setter, attrs);
1666 if (result.is_null()) return Nothing<bool>();
1667 }
1668 }
1669
1670 // 10. Return true.
1671 return Just(true);
1672 }
1673
1674 // static
CreateDataProperty(Isolate * isolate,Handle<JSReceiver> object,Handle<Name> key,Handle<Object> value,Maybe<ShouldThrow> should_throw)1675 Maybe<bool> JSReceiver::CreateDataProperty(Isolate* isolate,
1676 Handle<JSReceiver> object,
1677 Handle<Name> key,
1678 Handle<Object> value,
1679 Maybe<ShouldThrow> should_throw) {
1680 PropertyKey lookup_key(isolate, key);
1681 LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN);
1682 return CreateDataProperty(&it, value, should_throw);
1683 }
1684
1685 // static
CreateDataProperty(LookupIterator * it,Handle<Object> value,Maybe<ShouldThrow> should_throw)1686 Maybe<bool> JSReceiver::CreateDataProperty(LookupIterator* it,
1687 Handle<Object> value,
1688 Maybe<ShouldThrow> should_throw) {
1689 DCHECK(!it->check_prototype_chain());
1690 Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
1691 Isolate* isolate = receiver->GetIsolate();
1692
1693 if (receiver->IsJSObject()) {
1694 return JSObject::CreateDataProperty(it, value, should_throw); // Shortcut.
1695 }
1696
1697 PropertyDescriptor new_desc;
1698 new_desc.set_value(value);
1699 new_desc.set_writable(true);
1700 new_desc.set_enumerable(true);
1701 new_desc.set_configurable(true);
1702
1703 return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(),
1704 &new_desc, should_throw);
1705 }
1706
1707 // static
AddPrivateField(LookupIterator * it,Handle<Object> value,Maybe<ShouldThrow> should_throw)1708 Maybe<bool> JSReceiver::AddPrivateField(LookupIterator* it,
1709 Handle<Object> value,
1710 Maybe<ShouldThrow> should_throw) {
1711 Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
1712 Isolate* isolate = receiver->GetIsolate();
1713 DCHECK(it->GetName()->IsPrivateName());
1714 Handle<Symbol> symbol = Handle<Symbol>::cast(it->GetName());
1715
1716 switch (it->state()) {
1717 case LookupIterator::JSPROXY: {
1718 PropertyDescriptor new_desc;
1719 new_desc.set_value(value);
1720 new_desc.set_writable(true);
1721 new_desc.set_enumerable(true);
1722 new_desc.set_configurable(true);
1723 return JSProxy::SetPrivateSymbol(isolate, Handle<JSProxy>::cast(receiver),
1724 symbol, &new_desc, should_throw);
1725 }
1726 case LookupIterator::DATA:
1727 case LookupIterator::INTERCEPTOR:
1728 case LookupIterator::ACCESSOR:
1729 case LookupIterator::INTEGER_INDEXED_EXOTIC:
1730 UNREACHABLE();
1731
1732 case LookupIterator::ACCESS_CHECK: {
1733 if (!it->HasAccess()) {
1734 it->isolate()->ReportFailedAccessCheck(it->GetHolder<JSObject>());
1735 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>());
1736 return Just(true);
1737 }
1738 break;
1739 }
1740
1741 case LookupIterator::TRANSITION:
1742 case LookupIterator::NOT_FOUND:
1743 break;
1744 }
1745
1746 return Object::TransitionAndWriteDataProperty(it, value, NONE, should_throw,
1747 StoreOrigin::kMaybeKeyed);
1748 }
1749
1750 // static
GetOwnPropertyDescriptor(Isolate * isolate,Handle<JSReceiver> object,Handle<Object> key,PropertyDescriptor * desc)1751 Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(Isolate* isolate,
1752 Handle<JSReceiver> object,
1753 Handle<Object> key,
1754 PropertyDescriptor* desc) {
1755 DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey.
1756 PropertyKey lookup_key(isolate, key);
1757 LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN);
1758 return GetOwnPropertyDescriptor(&it, desc);
1759 }
1760
1761 namespace {
1762
GetPropertyDescriptorWithInterceptor(LookupIterator * it,PropertyDescriptor * desc)1763 Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it,
1764 PropertyDescriptor* desc) {
1765 Handle<InterceptorInfo> interceptor;
1766
1767 if (it->state() == LookupIterator::ACCESS_CHECK) {
1768 if (it->HasAccess()) {
1769 it->Next();
1770 } else {
1771 interceptor = it->GetInterceptorForFailedAccessCheck();
1772 if (interceptor.is_null() &&
1773 (!JSObject::AllCanRead(it) ||
1774 it->state() != LookupIterator::INTERCEPTOR)) {
1775 it->Restart();
1776 return Just(false);
1777 }
1778 }
1779 }
1780
1781 if (it->state() == LookupIterator::INTERCEPTOR) {
1782 interceptor = it->GetInterceptor();
1783 }
1784 if (interceptor.is_null()) return Just(false);
1785 Isolate* isolate = it->isolate();
1786 if (interceptor->descriptor().IsUndefined(isolate)) return Just(false);
1787
1788 Handle<Object> result;
1789 Handle<JSObject> holder = it->GetHolder<JSObject>();
1790
1791 Handle<Object> receiver = it->GetReceiver();
1792 if (!receiver->IsJSReceiver()) {
1793 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver,
1794 Object::ConvertReceiver(isolate, receiver),
1795 Nothing<bool>());
1796 }
1797
1798 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
1799 *holder, Just(kDontThrow));
1800 if (it->IsElement(*holder)) {
1801 result = args.CallIndexedDescriptor(interceptor, it->array_index());
1802 } else {
1803 result = args.CallNamedDescriptor(interceptor, it->name());
1804 }
1805 // An exception was thrown in the interceptor. Propagate.
1806 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
1807 if (!result.is_null()) {
1808 // Request successfully intercepted, try to set the property
1809 // descriptor.
1810 Utils::ApiCheck(
1811 PropertyDescriptor::ToPropertyDescriptor(isolate, result, desc),
1812 it->IsElement(*holder) ? "v8::IndexedPropertyDescriptorCallback"
1813 : "v8::NamedPropertyDescriptorCallback",
1814 "Invalid property descriptor.");
1815
1816 return Just(true);
1817 }
1818
1819 it->Next();
1820 return Just(false);
1821 }
1822 } // namespace
1823
1824 // ES6 9.1.5.1
1825 // Returns true on success, false if the property didn't exist, nothing if
1826 // an exception was thrown.
1827 // static
GetOwnPropertyDescriptor(LookupIterator * it,PropertyDescriptor * desc)1828 Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it,
1829 PropertyDescriptor* desc) {
1830 Isolate* isolate = it->isolate();
1831 // "Virtual" dispatch.
1832 if (it->IsFound() && it->GetHolder<JSReceiver>()->IsJSProxy()) {
1833 return JSProxy::GetOwnPropertyDescriptor(isolate, it->GetHolder<JSProxy>(),
1834 it->GetName(), desc);
1835 }
1836
1837 Maybe<bool> intercepted = GetPropertyDescriptorWithInterceptor(it, desc);
1838 MAYBE_RETURN(intercepted, Nothing<bool>());
1839 if (intercepted.FromJust()) {
1840 return Just(true);
1841 }
1842
1843 // Request was not intercepted, continue as normal.
1844 // 1. (Assert)
1845 // 2. If O does not have an own property with key P, return undefined.
1846 Maybe<PropertyAttributes> maybe = JSObject::GetPropertyAttributes(it);
1847 MAYBE_RETURN(maybe, Nothing<bool>());
1848 PropertyAttributes attrs = maybe.FromJust();
1849 if (attrs == ABSENT) return Just(false);
1850 DCHECK(!isolate->has_pending_exception());
1851
1852 // 3. Let D be a newly created Property Descriptor with no fields.
1853 DCHECK(desc->is_empty());
1854 // 4. Let X be O's own property whose key is P.
1855 // 5. If X is a data property, then
1856 bool is_accessor_pair = it->state() == LookupIterator::ACCESSOR &&
1857 it->GetAccessors()->IsAccessorPair();
1858 if (!is_accessor_pair) {
1859 // 5a. Set D.[[Value]] to the value of X's [[Value]] attribute.
1860 Handle<Object> value;
1861 if (!Object::GetProperty(it).ToHandle(&value)) {
1862 DCHECK(isolate->has_pending_exception());
1863 return Nothing<bool>();
1864 }
1865 desc->set_value(value);
1866 // 5b. Set D.[[Writable]] to the value of X's [[Writable]] attribute
1867 desc->set_writable((attrs & READ_ONLY) == 0);
1868 } else {
1869 // 6. Else X is an accessor property, so
1870 Handle<AccessorPair> accessors =
1871 Handle<AccessorPair>::cast(it->GetAccessors());
1872 Handle<NativeContext> native_context =
1873 it->GetHolder<JSReceiver>()->GetCreationContext().ToHandleChecked();
1874 // 6a. Set D.[[Get]] to the value of X's [[Get]] attribute.
1875 desc->set_get(AccessorPair::GetComponent(isolate, native_context, accessors,
1876 ACCESSOR_GETTER));
1877 // 6b. Set D.[[Set]] to the value of X's [[Set]] attribute.
1878 desc->set_set(AccessorPair::GetComponent(isolate, native_context, accessors,
1879 ACCESSOR_SETTER));
1880 }
1881
1882 // 7. Set D.[[Enumerable]] to the value of X's [[Enumerable]] attribute.
1883 desc->set_enumerable((attrs & DONT_ENUM) == 0);
1884 // 8. Set D.[[Configurable]] to the value of X's [[Configurable]] attribute.
1885 desc->set_configurable((attrs & DONT_DELETE) == 0);
1886 // 9. Return D.
1887 DCHECK(PropertyDescriptor::IsAccessorDescriptor(desc) !=
1888 PropertyDescriptor::IsDataDescriptor(desc));
1889 return Just(true);
1890 }
SetIntegrityLevel(Handle<JSReceiver> receiver,IntegrityLevel level,ShouldThrow should_throw)1891 Maybe<bool> JSReceiver::SetIntegrityLevel(Handle<JSReceiver> receiver,
1892 IntegrityLevel level,
1893 ShouldThrow should_throw) {
1894 DCHECK(level == SEALED || level == FROZEN);
1895
1896 if (receiver->IsJSObject()) {
1897 Handle<JSObject> object = Handle<JSObject>::cast(receiver);
1898
1899 if (!object->HasSloppyArgumentsElements() &&
1900 !object->IsJSModuleNamespace()) { // Fast path.
1901 // Prevent memory leaks by not adding unnecessary transitions.
1902 Maybe<bool> test = JSObject::TestIntegrityLevel(object, level);
1903 MAYBE_RETURN(test, Nothing<bool>());
1904 if (test.FromJust()) return test;
1905
1906 if (level == SEALED) {
1907 return JSObject::PreventExtensionsWithTransition<SEALED>(object,
1908 should_throw);
1909 } else {
1910 return JSObject::PreventExtensionsWithTransition<FROZEN>(object,
1911 should_throw);
1912 }
1913 }
1914 }
1915
1916 Isolate* isolate = receiver->GetIsolate();
1917
1918 MAYBE_RETURN(JSReceiver::PreventExtensions(receiver, should_throw),
1919 Nothing<bool>());
1920
1921 Handle<FixedArray> keys;
1922 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
1923 isolate, keys, JSReceiver::OwnPropertyKeys(receiver), Nothing<bool>());
1924
1925 PropertyDescriptor no_conf;
1926 no_conf.set_configurable(false);
1927
1928 PropertyDescriptor no_conf_no_write;
1929 no_conf_no_write.set_configurable(false);
1930 no_conf_no_write.set_writable(false);
1931
1932 if (level == SEALED) {
1933 for (int i = 0; i < keys->length(); ++i) {
1934 Handle<Object> key(keys->get(i), isolate);
1935 MAYBE_RETURN(DefineOwnProperty(isolate, receiver, key, &no_conf,
1936 Just(kThrowOnError)),
1937 Nothing<bool>());
1938 }
1939 return Just(true);
1940 }
1941
1942 for (int i = 0; i < keys->length(); ++i) {
1943 Handle<Object> key(keys->get(i), isolate);
1944 PropertyDescriptor current_desc;
1945 Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor(
1946 isolate, receiver, key, ¤t_desc);
1947 MAYBE_RETURN(owned, Nothing<bool>());
1948 if (owned.FromJust()) {
1949 PropertyDescriptor desc =
1950 PropertyDescriptor::IsAccessorDescriptor(¤t_desc)
1951 ? no_conf
1952 : no_conf_no_write;
1953 MAYBE_RETURN(
1954 DefineOwnProperty(isolate, receiver, key, &desc, Just(kThrowOnError)),
1955 Nothing<bool>());
1956 }
1957 }
1958 return Just(true);
1959 }
1960
1961 namespace {
GenericTestIntegrityLevel(Handle<JSReceiver> receiver,PropertyAttributes level)1962 Maybe<bool> GenericTestIntegrityLevel(Handle<JSReceiver> receiver,
1963 PropertyAttributes level) {
1964 DCHECK(level == SEALED || level == FROZEN);
1965
1966 Maybe<bool> extensible = JSReceiver::IsExtensible(receiver);
1967 MAYBE_RETURN(extensible, Nothing<bool>());
1968 if (extensible.FromJust()) return Just(false);
1969
1970 Isolate* isolate = receiver->GetIsolate();
1971
1972 Handle<FixedArray> keys;
1973 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
1974 isolate, keys, JSReceiver::OwnPropertyKeys(receiver), Nothing<bool>());
1975
1976 for (int i = 0; i < keys->length(); ++i) {
1977 Handle<Object> key(keys->get(i), isolate);
1978 PropertyDescriptor current_desc;
1979 Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor(
1980 isolate, receiver, key, ¤t_desc);
1981 MAYBE_RETURN(owned, Nothing<bool>());
1982 if (owned.FromJust()) {
1983 if (current_desc.configurable()) return Just(false);
1984 if (level == FROZEN &&
1985 PropertyDescriptor::IsDataDescriptor(¤t_desc) &&
1986 current_desc.writable()) {
1987 return Just(false);
1988 }
1989 }
1990 }
1991 return Just(true);
1992 }
1993
1994 } // namespace
1995
TestIntegrityLevel(Handle<JSReceiver> receiver,IntegrityLevel level)1996 Maybe<bool> JSReceiver::TestIntegrityLevel(Handle<JSReceiver> receiver,
1997 IntegrityLevel level) {
1998 if (!receiver->map().IsCustomElementsReceiverMap()) {
1999 return JSObject::TestIntegrityLevel(Handle<JSObject>::cast(receiver),
2000 level);
2001 }
2002 return GenericTestIntegrityLevel(receiver, level);
2003 }
2004
PreventExtensions(Handle<JSReceiver> object,ShouldThrow should_throw)2005 Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object,
2006 ShouldThrow should_throw) {
2007 if (object->IsJSProxy()) {
2008 return JSProxy::PreventExtensions(Handle<JSProxy>::cast(object),
2009 should_throw);
2010 }
2011 DCHECK(object->IsJSObject());
2012 return JSObject::PreventExtensions(Handle<JSObject>::cast(object),
2013 should_throw);
2014 }
2015
IsExtensible(Handle<JSReceiver> object)2016 Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) {
2017 if (object->IsJSProxy()) {
2018 return JSProxy::IsExtensible(Handle<JSProxy>::cast(object));
2019 }
2020 return Just(JSObject::IsExtensible(Handle<JSObject>::cast(object)));
2021 }
2022
2023 // static
ToPrimitive(Isolate * isolate,Handle<JSReceiver> receiver,ToPrimitiveHint hint)2024 MaybeHandle<Object> JSReceiver::ToPrimitive(Isolate* isolate,
2025 Handle<JSReceiver> receiver,
2026 ToPrimitiveHint hint) {
2027 Handle<Object> exotic_to_prim;
2028 ASSIGN_RETURN_ON_EXCEPTION(
2029 isolate, exotic_to_prim,
2030 Object::GetMethod(receiver, isolate->factory()->to_primitive_symbol()),
2031 Object);
2032 if (!exotic_to_prim->IsUndefined(isolate)) {
2033 Handle<Object> hint_string =
2034 isolate->factory()->ToPrimitiveHintString(hint);
2035 Handle<Object> result;
2036 ASSIGN_RETURN_ON_EXCEPTION(
2037 isolate, result,
2038 Execution::Call(isolate, exotic_to_prim, receiver, 1, &hint_string),
2039 Object);
2040 if (result->IsPrimitive()) return result;
2041 THROW_NEW_ERROR(isolate,
2042 NewTypeError(MessageTemplate::kCannotConvertToPrimitive),
2043 Object);
2044 }
2045 return OrdinaryToPrimitive(isolate, receiver,
2046 (hint == ToPrimitiveHint::kString)
2047 ? OrdinaryToPrimitiveHint::kString
2048 : OrdinaryToPrimitiveHint::kNumber);
2049 }
2050
2051 // static
OrdinaryToPrimitive(Isolate * isolate,Handle<JSReceiver> receiver,OrdinaryToPrimitiveHint hint)2052 MaybeHandle<Object> JSReceiver::OrdinaryToPrimitive(
2053 Isolate* isolate, Handle<JSReceiver> receiver,
2054 OrdinaryToPrimitiveHint hint) {
2055 Handle<String> method_names[2];
2056 switch (hint) {
2057 case OrdinaryToPrimitiveHint::kNumber:
2058 method_names[0] = isolate->factory()->valueOf_string();
2059 method_names[1] = isolate->factory()->toString_string();
2060 break;
2061 case OrdinaryToPrimitiveHint::kString:
2062 method_names[0] = isolate->factory()->toString_string();
2063 method_names[1] = isolate->factory()->valueOf_string();
2064 break;
2065 }
2066 for (Handle<String> name : method_names) {
2067 Handle<Object> method;
2068 ASSIGN_RETURN_ON_EXCEPTION(isolate, method,
2069 JSReceiver::GetProperty(isolate, receiver, name),
2070 Object);
2071 if (method->IsCallable()) {
2072 Handle<Object> result;
2073 ASSIGN_RETURN_ON_EXCEPTION(
2074 isolate, result,
2075 Execution::Call(isolate, method, receiver, 0, nullptr), Object);
2076 if (result->IsPrimitive()) return result;
2077 }
2078 }
2079 THROW_NEW_ERROR(isolate,
2080 NewTypeError(MessageTemplate::kCannotConvertToPrimitive),
2081 Object);
2082 }
2083
FastGetOwnValuesOrEntries(Isolate * isolate,Handle<JSReceiver> receiver,bool get_entries,Handle<FixedArray> * result)2084 V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
2085 Isolate* isolate, Handle<JSReceiver> receiver, bool get_entries,
2086 Handle<FixedArray>* result) {
2087 Handle<Map> map(JSReceiver::cast(*receiver).map(), isolate);
2088
2089 if (!map->IsJSObjectMap()) return Just(false);
2090 if (!map->OnlyHasSimpleProperties()) return Just(false);
2091
2092 Handle<JSObject> object(JSObject::cast(*receiver), isolate);
2093 Handle<DescriptorArray> descriptors(map->instance_descriptors(isolate),
2094 isolate);
2095
2096 int number_of_own_descriptors = map->NumberOfOwnDescriptors();
2097 size_t number_of_own_elements =
2098 object->GetElementsAccessor()->GetCapacity(*object, object->elements());
2099
2100 if (number_of_own_elements >
2101 static_cast<size_t>(FixedArray::kMaxLength - number_of_own_descriptors)) {
2102 isolate->Throw(*isolate->factory()->NewRangeError(
2103 MessageTemplate::kInvalidArrayLength));
2104 return Nothing<bool>();
2105 }
2106 // The static cast is safe after the range check right above.
2107 Handle<FixedArray> values_or_entries = isolate->factory()->NewFixedArray(
2108 static_cast<int>(number_of_own_descriptors + number_of_own_elements));
2109 int count = 0;
2110
2111 if (object->elements() != ReadOnlyRoots(isolate).empty_fixed_array()) {
2112 MAYBE_RETURN(object->GetElementsAccessor()->CollectValuesOrEntries(
2113 isolate, object, values_or_entries, get_entries, &count,
2114 ENUMERABLE_STRINGS),
2115 Nothing<bool>());
2116 }
2117
2118 // We may have already lost stability, if CollectValuesOrEntries had
2119 // side-effects.
2120 bool stable = *map == object->map();
2121 if (stable) {
2122 descriptors.PatchValue(map->instance_descriptors(isolate));
2123 }
2124
2125 for (InternalIndex index : InternalIndex::Range(number_of_own_descriptors)) {
2126 HandleScope inner_scope(isolate);
2127
2128 Handle<Name> next_key(descriptors->GetKey(index), isolate);
2129 if (!next_key->IsString()) continue;
2130 Handle<Object> prop_value;
2131
2132 // Directly decode from the descriptor array if |from| did not change shape.
2133 if (stable) {
2134 DCHECK_EQ(object->map(), *map);
2135 DCHECK_EQ(*descriptors, map->instance_descriptors(isolate));
2136
2137 PropertyDetails details = descriptors->GetDetails(index);
2138 if (!details.IsEnumerable()) continue;
2139 if (details.kind() == PropertyKind::kData) {
2140 if (details.location() == PropertyLocation::kDescriptor) {
2141 prop_value = handle(descriptors->GetStrongValue(index), isolate);
2142 } else {
2143 Representation representation = details.representation();
2144 FieldIndex field_index = FieldIndex::ForPropertyIndex(
2145 *map, details.field_index(), representation);
2146 prop_value = JSObject::FastPropertyAt(isolate, object, representation,
2147 field_index);
2148 }
2149 } else {
2150 LookupIterator it(isolate, object, next_key,
2151 LookupIterator::OWN_SKIP_INTERCEPTOR);
2152 DCHECK_EQ(LookupIterator::ACCESSOR, it.state());
2153 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
2154 isolate, prop_value, Object::GetProperty(&it), Nothing<bool>());
2155 stable = object->map() == *map;
2156 descriptors.PatchValue(map->instance_descriptors(isolate));
2157 }
2158 } else {
2159 // If the map did change, do a slower lookup. We are still guaranteed that
2160 // the object has a simple shape, and that the key is a name.
2161 LookupIterator it(isolate, object, next_key,
2162 LookupIterator::OWN_SKIP_INTERCEPTOR);
2163 if (!it.IsFound()) continue;
2164 DCHECK(it.state() == LookupIterator::DATA ||
2165 it.state() == LookupIterator::ACCESSOR);
2166 if (!it.IsEnumerable()) continue;
2167 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
2168 isolate, prop_value, Object::GetProperty(&it), Nothing<bool>());
2169 }
2170
2171 if (get_entries) {
2172 prop_value = MakeEntryPair(isolate, next_key, prop_value);
2173 }
2174
2175 values_or_entries->set(count, *prop_value);
2176 count++;
2177 }
2178
2179 DCHECK_LE(count, values_or_entries->length());
2180 *result = FixedArray::ShrinkOrEmpty(isolate, values_or_entries, count);
2181 return Just(true);
2182 }
2183
GetOwnValuesOrEntries(Isolate * isolate,Handle<JSReceiver> object,PropertyFilter filter,bool try_fast_path,bool get_entries)2184 MaybeHandle<FixedArray> GetOwnValuesOrEntries(Isolate* isolate,
2185 Handle<JSReceiver> object,
2186 PropertyFilter filter,
2187 bool try_fast_path,
2188 bool get_entries) {
2189 Handle<FixedArray> values_or_entries;
2190 if (try_fast_path && filter == ENUMERABLE_STRINGS) {
2191 Maybe<bool> fast_values_or_entries = FastGetOwnValuesOrEntries(
2192 isolate, object, get_entries, &values_or_entries);
2193 if (fast_values_or_entries.IsNothing()) return MaybeHandle<FixedArray>();
2194 if (fast_values_or_entries.FromJust()) return values_or_entries;
2195 }
2196
2197 PropertyFilter key_filter =
2198 static_cast<PropertyFilter>(filter & ~ONLY_ENUMERABLE);
2199
2200 Handle<FixedArray> keys;
2201 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
2202 isolate, keys,
2203 KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, key_filter,
2204 GetKeysConversion::kConvertToString),
2205 MaybeHandle<FixedArray>());
2206
2207 values_or_entries = isolate->factory()->NewFixedArray(keys->length());
2208 int length = 0;
2209
2210 for (int i = 0; i < keys->length(); ++i) {
2211 Handle<Name> key =
2212 Handle<Name>::cast(handle(keys->get(isolate, i), isolate));
2213
2214 if (filter & ONLY_ENUMERABLE) {
2215 PropertyDescriptor descriptor;
2216 Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
2217 isolate, object, key, &descriptor);
2218 MAYBE_RETURN(did_get_descriptor, MaybeHandle<FixedArray>());
2219 if (!did_get_descriptor.FromJust() || !descriptor.enumerable()) continue;
2220 }
2221
2222 Handle<Object> value;
2223 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
2224 isolate, value, Object::GetPropertyOrElement(isolate, object, key),
2225 MaybeHandle<FixedArray>());
2226
2227 if (get_entries) {
2228 Handle<FixedArray> entry_storage = isolate->factory()->NewFixedArray(2);
2229 entry_storage->set(0, *key);
2230 entry_storage->set(1, *value);
2231 value = isolate->factory()->NewJSArrayWithElements(entry_storage,
2232 PACKED_ELEMENTS, 2);
2233 }
2234
2235 values_or_entries->set(length, *value);
2236 length++;
2237 }
2238 DCHECK_LE(length, values_or_entries->length());
2239 return FixedArray::ShrinkOrEmpty(isolate, values_or_entries, length);
2240 }
2241
GetOwnValues(Handle<JSReceiver> object,PropertyFilter filter,bool try_fast_path)2242 MaybeHandle<FixedArray> JSReceiver::GetOwnValues(Handle<JSReceiver> object,
2243 PropertyFilter filter,
2244 bool try_fast_path) {
2245 return GetOwnValuesOrEntries(object->GetIsolate(), object, filter,
2246 try_fast_path, false);
2247 }
2248
GetOwnEntries(Handle<JSReceiver> object,PropertyFilter filter,bool try_fast_path)2249 MaybeHandle<FixedArray> JSReceiver::GetOwnEntries(Handle<JSReceiver> object,
2250 PropertyFilter filter,
2251 bool try_fast_path) {
2252 return GetOwnValuesOrEntries(object->GetIsolate(), object, filter,
2253 try_fast_path, true);
2254 }
2255
SetPrototype(Isolate * isolate,Handle<JSReceiver> object,Handle<Object> value,bool from_javascript,ShouldThrow should_throw)2256 Maybe<bool> JSReceiver::SetPrototype(Isolate* isolate,
2257 Handle<JSReceiver> object,
2258 Handle<Object> value, bool from_javascript,
2259 ShouldThrow should_throw) {
2260 if (object->IsJSProxy()) {
2261 return JSProxy::SetPrototype(isolate, Handle<JSProxy>::cast(object), value,
2262 from_javascript, should_throw);
2263 }
2264 return JSObject::SetPrototype(isolate, Handle<JSObject>::cast(object), value,
2265 from_javascript, should_throw);
2266 }
2267
HasProxyInPrototype(Isolate * isolate)2268 bool JSReceiver::HasProxyInPrototype(Isolate* isolate) {
2269 for (PrototypeIterator iter(isolate, *this, kStartAtReceiver,
2270 PrototypeIterator::END_AT_NULL);
2271 !iter.IsAtEnd(); iter.AdvanceIgnoringProxies()) {
2272 if (iter.GetCurrent().IsJSProxy()) return true;
2273 }
2274 return false;
2275 }
2276
IsCodeLike(Isolate * isolate) const2277 bool JSReceiver::IsCodeLike(Isolate* isolate) const {
2278 DisallowGarbageCollection no_gc;
2279 Object maybe_constructor = map().GetConstructor();
2280 if (!maybe_constructor.IsJSFunction()) return false;
2281 if (!JSFunction::cast(maybe_constructor).shared().IsApiFunction()) {
2282 return false;
2283 }
2284 Object instance_template = JSFunction::cast(maybe_constructor)
2285 .shared()
2286 .get_api_func_data()
2287 .GetInstanceTemplate();
2288 if (instance_template.IsUndefined(isolate)) return false;
2289 return ObjectTemplateInfo::cast(instance_template).code_like();
2290 }
2291
2292 // static
New(Handle<JSFunction> constructor,Handle<JSReceiver> new_target,Handle<AllocationSite> site)2293 MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
2294 Handle<JSReceiver> new_target,
2295 Handle<AllocationSite> site) {
2296 // If called through new, new.target can be:
2297 // - a subclass of constructor,
2298 // - a proxy wrapper around constructor, or
2299 // - the constructor itself.
2300 // If called through Reflect.construct, it's guaranteed to be a constructor.
2301 Isolate* const isolate = constructor->GetIsolate();
2302 DCHECK(constructor->IsConstructor());
2303 DCHECK(new_target->IsConstructor());
2304 DCHECK(!constructor->has_initial_map() ||
2305 !InstanceTypeChecker::IsJSFunction(
2306 constructor->initial_map().instance_type()));
2307
2308 Handle<Map> initial_map;
2309 ASSIGN_RETURN_ON_EXCEPTION(
2310 isolate, initial_map,
2311 JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject);
2312 int initial_capacity = V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
2313 ? SwissNameDictionary::kInitialCapacity
2314 : NameDictionary::kInitialCapacity;
2315 Handle<JSObject> result = isolate->factory()->NewFastOrSlowJSObjectFromMap(
2316 initial_map, initial_capacity, AllocationType::kYoung, site);
2317 isolate->counters()->constructed_objects()->Increment();
2318 isolate->counters()->constructed_objects_runtime()->Increment();
2319 return result;
2320 }
2321
2322 // 9.1.12 ObjectCreate ( proto [ , internalSlotsList ] )
2323 // Notice: This is NOT 19.1.2.2 Object.create ( O, Properties )
ObjectCreate(Isolate * isolate,Handle<Object> prototype)2324 MaybeHandle<JSObject> JSObject::ObjectCreate(Isolate* isolate,
2325 Handle<Object> prototype) {
2326 // Generate the map with the specified {prototype} based on the Object
2327 // function's initial map from the current native context.
2328 // TODO(bmeurer): Use a dedicated cache for Object.create; think about
2329 // slack tracking for Object.create.
2330 Handle<Map> map =
2331 Map::GetObjectCreateMap(isolate, Handle<HeapObject>::cast(prototype));
2332
2333 // Actually allocate the object.
2334 return isolate->factory()->NewFastOrSlowJSObjectFromMap(map);
2335 }
2336
EnsureWritableFastElements(Handle<JSObject> object)2337 void JSObject::EnsureWritableFastElements(Handle<JSObject> object) {
2338 DCHECK(object->HasSmiOrObjectElements() ||
2339 object->HasFastStringWrapperElements() ||
2340 object->HasAnyNonextensibleElements());
2341 FixedArray raw_elems = FixedArray::cast(object->elements());
2342 Isolate* isolate = object->GetIsolate();
2343 if (raw_elems.map() != ReadOnlyRoots(isolate).fixed_cow_array_map()) return;
2344 Handle<FixedArray> elems(raw_elems, isolate);
2345 Handle<FixedArray> writable_elems = isolate->factory()->CopyFixedArrayWithMap(
2346 elems, isolate->factory()->fixed_array_map());
2347 object->set_elements(*writable_elems);
2348 isolate->counters()->cow_arrays_converted()->Increment();
2349 }
2350
GetHeaderSize(InstanceType type,bool function_has_prototype_slot)2351 int JSObject::GetHeaderSize(InstanceType type,
2352 bool function_has_prototype_slot) {
2353 switch (type) {
2354 case JS_API_OBJECT_TYPE:
2355 case JS_ITERATOR_PROTOTYPE_TYPE:
2356 case JS_MAP_ITERATOR_PROTOTYPE_TYPE:
2357 case JS_OBJECT_PROTOTYPE_TYPE:
2358 case JS_OBJECT_TYPE:
2359 case JS_PROMISE_PROTOTYPE_TYPE:
2360 case JS_REG_EXP_PROTOTYPE_TYPE:
2361 case JS_SET_ITERATOR_PROTOTYPE_TYPE:
2362 case JS_SET_PROTOTYPE_TYPE:
2363 case JS_SPECIAL_API_OBJECT_TYPE:
2364 case JS_STRING_ITERATOR_PROTOTYPE_TYPE:
2365 case JS_ARRAY_ITERATOR_PROTOTYPE_TYPE:
2366 case JS_TYPED_ARRAY_PROTOTYPE_TYPE:
2367 return JSObject::kHeaderSize;
2368 case JS_GENERATOR_OBJECT_TYPE:
2369 return JSGeneratorObject::kHeaderSize;
2370 case JS_ASYNC_FUNCTION_OBJECT_TYPE:
2371 return JSAsyncFunctionObject::kHeaderSize;
2372 case JS_ASYNC_GENERATOR_OBJECT_TYPE:
2373 return JSAsyncGeneratorObject::kHeaderSize;
2374 case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
2375 return JSAsyncFromSyncIterator::kHeaderSize;
2376 case JS_GLOBAL_PROXY_TYPE:
2377 return JSGlobalProxy::kHeaderSize;
2378 case JS_GLOBAL_OBJECT_TYPE:
2379 return JSGlobalObject::kHeaderSize;
2380 case JS_BOUND_FUNCTION_TYPE:
2381 return JSBoundFunction::kHeaderSize;
2382 case JS_FUNCTION_TYPE:
2383 case JS_CLASS_CONSTRUCTOR_TYPE:
2384 case JS_PROMISE_CONSTRUCTOR_TYPE:
2385 case JS_REG_EXP_CONSTRUCTOR_TYPE:
2386 case JS_ARRAY_CONSTRUCTOR_TYPE:
2387 #define TYPED_ARRAY_CONSTRUCTORS_SWITCH(Type, type, TYPE, Ctype) \
2388 case TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE:
2389 TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTORS_SWITCH)
2390 #undef TYPED_ARRAY_CONSTRUCTORS_SWITCH
2391 return JSFunction::GetHeaderSize(function_has_prototype_slot);
2392 case JS_PRIMITIVE_WRAPPER_TYPE:
2393 return JSPrimitiveWrapper::kHeaderSize;
2394 case JS_DATE_TYPE:
2395 return JSDate::kHeaderSize;
2396 case JS_ARRAY_TYPE:
2397 return JSArray::kHeaderSize;
2398 case JS_ARRAY_BUFFER_TYPE:
2399 return JSArrayBuffer::kHeaderSize;
2400 case JS_ARRAY_ITERATOR_TYPE:
2401 return JSArrayIterator::kHeaderSize;
2402 case JS_TYPED_ARRAY_TYPE:
2403 return JSTypedArray::kHeaderSize;
2404 case JS_DATA_VIEW_TYPE:
2405 return JSDataView::kHeaderSize;
2406 case JS_SET_TYPE:
2407 return JSSet::kHeaderSize;
2408 case JS_MAP_TYPE:
2409 return JSMap::kHeaderSize;
2410 case JS_SET_KEY_VALUE_ITERATOR_TYPE:
2411 case JS_SET_VALUE_ITERATOR_TYPE:
2412 return JSSetIterator::kHeaderSize;
2413 case JS_MAP_KEY_ITERATOR_TYPE:
2414 case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
2415 case JS_MAP_VALUE_ITERATOR_TYPE:
2416 return JSMapIterator::kHeaderSize;
2417 case JS_WEAK_REF_TYPE:
2418 return JSWeakRef::kHeaderSize;
2419 case JS_FINALIZATION_REGISTRY_TYPE:
2420 return JSFinalizationRegistry::kHeaderSize;
2421 case JS_WEAK_MAP_TYPE:
2422 return JSWeakMap::kHeaderSize;
2423 case JS_WEAK_SET_TYPE:
2424 return JSWeakSet::kHeaderSize;
2425 case JS_PROMISE_TYPE:
2426 return JSPromise::kHeaderSize;
2427 case JS_REG_EXP_TYPE:
2428 return JSRegExp::kHeaderSize;
2429 case JS_REG_EXP_STRING_ITERATOR_TYPE:
2430 return JSRegExpStringIterator::kHeaderSize;
2431 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
2432 return JSObject::kHeaderSize;
2433 case JS_MESSAGE_OBJECT_TYPE:
2434 return JSMessageObject::kHeaderSize;
2435 case JS_ARGUMENTS_OBJECT_TYPE:
2436 return JSObject::kHeaderSize;
2437 case JS_ERROR_TYPE:
2438 return JSObject::kHeaderSize;
2439 case JS_EXTERNAL_OBJECT_TYPE:
2440 return JSExternalObject::kHeaderSize;
2441 case JS_SHADOW_REALM_TYPE:
2442 return JSShadowRealm::kHeaderSize;
2443 case JS_STRING_ITERATOR_TYPE:
2444 return JSStringIterator::kHeaderSize;
2445 case JS_MODULE_NAMESPACE_TYPE:
2446 return JSModuleNamespace::kHeaderSize;
2447 case JS_SHARED_STRUCT_TYPE:
2448 return JSSharedStruct::kHeaderSize;
2449 case JS_TEMPORAL_CALENDAR_TYPE:
2450 return JSTemporalCalendar::kHeaderSize;
2451 case JS_TEMPORAL_DURATION_TYPE:
2452 return JSTemporalDuration::kHeaderSize;
2453 case JS_TEMPORAL_INSTANT_TYPE:
2454 return JSTemporalInstant::kHeaderSize;
2455 case JS_TEMPORAL_PLAIN_DATE_TYPE:
2456 return JSTemporalPlainDate::kHeaderSize;
2457 case JS_TEMPORAL_PLAIN_DATE_TIME_TYPE:
2458 return JSTemporalPlainDateTime::kHeaderSize;
2459 case JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE:
2460 return JSTemporalPlainMonthDay::kHeaderSize;
2461 case JS_TEMPORAL_PLAIN_TIME_TYPE:
2462 return JSTemporalPlainTime::kHeaderSize;
2463 case JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE:
2464 return JSTemporalPlainYearMonth::kHeaderSize;
2465 case JS_TEMPORAL_TIME_ZONE_TYPE:
2466 return JSTemporalTimeZone::kHeaderSize;
2467 case JS_TEMPORAL_ZONED_DATE_TIME_TYPE:
2468 return JSTemporalZonedDateTime::kHeaderSize;
2469 case JS_WRAPPED_FUNCTION_TYPE:
2470 return JSWrappedFunction::kHeaderSize;
2471 #ifdef V8_INTL_SUPPORT
2472 case JS_V8_BREAK_ITERATOR_TYPE:
2473 return JSV8BreakIterator::kHeaderSize;
2474 case JS_COLLATOR_TYPE:
2475 return JSCollator::kHeaderSize;
2476 case JS_DATE_TIME_FORMAT_TYPE:
2477 return JSDateTimeFormat::kHeaderSize;
2478 case JS_DISPLAY_NAMES_TYPE:
2479 return JSDisplayNames::kHeaderSize;
2480 case JS_LIST_FORMAT_TYPE:
2481 return JSListFormat::kHeaderSize;
2482 case JS_LOCALE_TYPE:
2483 return JSLocale::kHeaderSize;
2484 case JS_NUMBER_FORMAT_TYPE:
2485 return JSNumberFormat::kHeaderSize;
2486 case JS_PLURAL_RULES_TYPE:
2487 return JSPluralRules::kHeaderSize;
2488 case JS_RELATIVE_TIME_FORMAT_TYPE:
2489 return JSRelativeTimeFormat::kHeaderSize;
2490 case JS_SEGMENT_ITERATOR_TYPE:
2491 return JSSegmentIterator::kHeaderSize;
2492 case JS_SEGMENTER_TYPE:
2493 return JSSegmenter::kHeaderSize;
2494 case JS_SEGMENTS_TYPE:
2495 return JSSegments::kHeaderSize;
2496 #endif // V8_INTL_SUPPORT
2497 #if V8_ENABLE_WEBASSEMBLY
2498 case WASM_GLOBAL_OBJECT_TYPE:
2499 return WasmGlobalObject::kHeaderSize;
2500 case WASM_INSTANCE_OBJECT_TYPE:
2501 return WasmInstanceObject::kHeaderSize;
2502 case WASM_MEMORY_OBJECT_TYPE:
2503 return WasmMemoryObject::kHeaderSize;
2504 case WASM_MODULE_OBJECT_TYPE:
2505 return WasmModuleObject::kHeaderSize;
2506 case WASM_SUSPENDER_OBJECT_TYPE:
2507 return WasmSuspenderObject::kHeaderSize;
2508 case WASM_TABLE_OBJECT_TYPE:
2509 return WasmTableObject::kHeaderSize;
2510 case WASM_VALUE_OBJECT_TYPE:
2511 return WasmValueObject::kHeaderSize;
2512 case WASM_TAG_OBJECT_TYPE:
2513 return WasmTagObject::kHeaderSize;
2514 #endif // V8_ENABLE_WEBASSEMBLY
2515 default: {
2516 // Special type check for API Objects because they are in a large variable
2517 // instance type range.
2518 if (InstanceTypeChecker::IsJSApiObject(type)) {
2519 return JSObject::kHeaderSize;
2520 }
2521 std::stringstream ss;
2522 ss << type;
2523 FATAL("unexpected instance type: %s\n", ss.str().c_str());
2524 }
2525 }
2526 }
2527
2528 // static
AllCanRead(LookupIterator * it)2529 bool JSObject::AllCanRead(LookupIterator* it) {
2530 // Skip current iteration, it's in state ACCESS_CHECK or INTERCEPTOR, both of
2531 // which have already been checked.
2532 DCHECK(it->state() == LookupIterator::ACCESS_CHECK ||
2533 it->state() == LookupIterator::INTERCEPTOR);
2534 for (it->Next(); it->IsFound(); it->Next()) {
2535 if (it->state() == LookupIterator::ACCESSOR) {
2536 auto accessors = it->GetAccessors();
2537 if (accessors->IsAccessorInfo()) {
2538 if (AccessorInfo::cast(*accessors).all_can_read()) return true;
2539 }
2540 } else if (it->state() == LookupIterator::INTERCEPTOR) {
2541 if (it->GetInterceptor()->all_can_read()) return true;
2542 } else if (it->state() == LookupIterator::JSPROXY) {
2543 // Stop lookupiterating. And no, AllCanNotRead.
2544 return false;
2545 }
2546 }
2547 return false;
2548 }
2549
GetPropertyWithFailedAccessCheck(LookupIterator * it)2550 MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck(
2551 LookupIterator* it) {
2552 Isolate* isolate = it->isolate();
2553 Handle<JSObject> checked = it->GetHolder<JSObject>();
2554 Handle<InterceptorInfo> interceptor =
2555 it->GetInterceptorForFailedAccessCheck();
2556 if (interceptor.is_null()) {
2557 while (AllCanRead(it)) {
2558 if (it->state() == LookupIterator::ACCESSOR) {
2559 return Object::GetPropertyWithAccessor(it);
2560 }
2561 DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
2562 bool done;
2563 Handle<Object> result;
2564 ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
2565 GetPropertyWithInterceptor(it, &done), Object);
2566 if (done) return result;
2567 }
2568
2569 } else {
2570 Handle<Object> result;
2571 bool done;
2572 ASSIGN_RETURN_ON_EXCEPTION(
2573 isolate, result,
2574 GetPropertyWithInterceptorInternal(it, interceptor, &done), Object);
2575 if (done) return result;
2576 }
2577
2578 // Cross-Origin [[Get]] of Well-Known Symbols does not throw, and returns
2579 // undefined.
2580 Handle<Name> name = it->GetName();
2581 if (name->IsSymbol() && Symbol::cast(*name).is_well_known_symbol()) {
2582 return it->factory()->undefined_value();
2583 }
2584
2585 isolate->ReportFailedAccessCheck(checked);
2586 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
2587 return it->factory()->undefined_value();
2588 }
2589
GetPropertyAttributesWithFailedAccessCheck(LookupIterator * it)2590 Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck(
2591 LookupIterator* it) {
2592 Isolate* isolate = it->isolate();
2593 Handle<JSObject> checked = it->GetHolder<JSObject>();
2594 Handle<InterceptorInfo> interceptor =
2595 it->GetInterceptorForFailedAccessCheck();
2596 if (interceptor.is_null()) {
2597 while (AllCanRead(it)) {
2598 if (it->state() == LookupIterator::ACCESSOR) {
2599 return Just(it->property_attributes());
2600 }
2601 DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
2602 auto result = GetPropertyAttributesWithInterceptor(it);
2603 if (isolate->has_scheduled_exception()) break;
2604 if (result.IsJust() && result.FromJust() != ABSENT) return result;
2605 }
2606 } else {
2607 Maybe<PropertyAttributes> result =
2608 GetPropertyAttributesWithInterceptorInternal(it, interceptor);
2609 if (isolate->has_pending_exception()) return Nothing<PropertyAttributes>();
2610 if (result.FromMaybe(ABSENT) != ABSENT) return result;
2611 }
2612 isolate->ReportFailedAccessCheck(checked);
2613 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>());
2614 return Just(ABSENT);
2615 }
2616
2617 // static
AllCanWrite(LookupIterator * it)2618 bool JSObject::AllCanWrite(LookupIterator* it) {
2619 for (; it->IsFound() && it->state() != LookupIterator::JSPROXY; it->Next()) {
2620 if (it->state() == LookupIterator::ACCESSOR) {
2621 Handle<Object> accessors = it->GetAccessors();
2622 if (accessors->IsAccessorInfo()) {
2623 if (AccessorInfo::cast(*accessors).all_can_write()) return true;
2624 }
2625 }
2626 }
2627 return false;
2628 }
2629
SetPropertyWithFailedAccessCheck(LookupIterator * it,Handle<Object> value,Maybe<ShouldThrow> should_throw)2630 Maybe<bool> JSObject::SetPropertyWithFailedAccessCheck(
2631 LookupIterator* it, Handle<Object> value, Maybe<ShouldThrow> should_throw) {
2632 Isolate* isolate = it->isolate();
2633 Handle<JSObject> checked = it->GetHolder<JSObject>();
2634 Handle<InterceptorInfo> interceptor =
2635 it->GetInterceptorForFailedAccessCheck();
2636 if (interceptor.is_null()) {
2637 if (AllCanWrite(it)) {
2638 return Object::SetPropertyWithAccessor(it, value, should_throw);
2639 }
2640 } else {
2641 Maybe<bool> result = SetPropertyWithInterceptorInternal(
2642 it, interceptor, should_throw, value);
2643 if (isolate->has_pending_exception()) return Nothing<bool>();
2644 if (result.IsJust()) return result;
2645 }
2646 isolate->ReportFailedAccessCheck(checked);
2647 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
2648 return Just(true);
2649 }
2650
SetNormalizedProperty(Handle<JSObject> object,Handle<Name> name,Handle<Object> value,PropertyDetails details)2651 void JSObject::SetNormalizedProperty(Handle<JSObject> object, Handle<Name> name,
2652 Handle<Object> value,
2653 PropertyDetails details) {
2654 DCHECK(!object->HasFastProperties());
2655 DCHECK(name->IsUniqueName());
2656 Isolate* isolate = object->GetIsolate();
2657
2658 uint32_t hash = name->hash();
2659
2660 if (object->IsJSGlobalObject()) {
2661 Handle<JSGlobalObject> global_obj = Handle<JSGlobalObject>::cast(object);
2662 Handle<GlobalDictionary> dictionary(
2663 global_obj->global_dictionary(kAcquireLoad), isolate);
2664 ReadOnlyRoots roots(isolate);
2665 InternalIndex entry = dictionary->FindEntry(isolate, roots, name, hash);
2666
2667 if (entry.is_not_found()) {
2668 DCHECK_IMPLIES(global_obj->map().is_prototype_map(),
2669 Map::IsPrototypeChainInvalidated(global_obj->map()));
2670 auto cell_type = value->IsUndefined(roots) ? PropertyCellType::kUndefined
2671 : PropertyCellType::kConstant;
2672 details = details.set_cell_type(cell_type);
2673 auto cell = isolate->factory()->NewPropertyCell(name, details, value);
2674 dictionary =
2675 GlobalDictionary::Add(isolate, dictionary, name, cell, details);
2676 global_obj->set_global_dictionary(*dictionary, kReleaseStore);
2677 } else {
2678 PropertyCell::PrepareForAndSetValue(isolate, dictionary, entry, value,
2679 details);
2680 DCHECK_EQ(dictionary->CellAt(entry).value(), *value);
2681 }
2682 } else {
2683 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
2684 Handle<SwissNameDictionary> dictionary(
2685 object->property_dictionary_swiss(), isolate);
2686 InternalIndex entry = dictionary->FindEntry(isolate, *name);
2687 if (entry.is_not_found()) {
2688 DCHECK_IMPLIES(object->map().is_prototype_map(),
2689 Map::IsPrototypeChainInvalidated(object->map()));
2690 dictionary =
2691 SwissNameDictionary::Add(isolate, dictionary, name, value, details);
2692 object->SetProperties(*dictionary);
2693 } else {
2694 dictionary->ValueAtPut(entry, *value);
2695 dictionary->DetailsAtPut(entry, details);
2696 }
2697 } else {
2698 Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
2699 InternalIndex entry = dictionary->FindEntry(isolate, name);
2700 if (entry.is_not_found()) {
2701 DCHECK_IMPLIES(object->map().is_prototype_map(),
2702 Map::IsPrototypeChainInvalidated(object->map()));
2703 dictionary =
2704 NameDictionary::Add(isolate, dictionary, name, value, details);
2705 object->SetProperties(*dictionary);
2706 } else {
2707 PropertyDetails original_details = dictionary->DetailsAt(entry);
2708 int enumeration_index = original_details.dictionary_index();
2709 DCHECK_GT(enumeration_index, 0);
2710 details = details.set_index(enumeration_index);
2711 dictionary->SetEntry(entry, *name, *value, details);
2712 }
2713 }
2714 }
2715 }
2716
SetNormalizedElement(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyDetails details)2717 void JSObject::SetNormalizedElement(Handle<JSObject> object, uint32_t index,
2718 Handle<Object> value,
2719 PropertyDetails details) {
2720 DCHECK_EQ(object->GetElementsKind(), DICTIONARY_ELEMENTS);
2721
2722 Isolate* isolate = object->GetIsolate();
2723
2724 Handle<NumberDictionary> dictionary =
2725 handle(NumberDictionary::cast(object->elements()), isolate);
2726 dictionary =
2727 NumberDictionary::Set(isolate, dictionary, index, value, object, details);
2728 object->set_elements(*dictionary);
2729 }
2730
JSObjectShortPrint(StringStream * accumulator)2731 void JSObject::JSObjectShortPrint(StringStream* accumulator) {
2732 switch (map().instance_type()) {
2733 case JS_ARRAY_TYPE: {
2734 double length = JSArray::cast(*this).length().IsUndefined()
2735 ? 0
2736 : JSArray::cast(*this).length().Number();
2737 accumulator->Add("<JSArray[%u]>", static_cast<uint32_t>(length));
2738 break;
2739 }
2740 case JS_BOUND_FUNCTION_TYPE: {
2741 JSBoundFunction bound_function = JSBoundFunction::cast(*this);
2742 accumulator->Add("<JSBoundFunction");
2743 accumulator->Add(" (BoundTargetFunction %p)>",
2744 reinterpret_cast<void*>(
2745 bound_function.bound_target_function().ptr()));
2746 break;
2747 }
2748 case JS_WEAK_MAP_TYPE: {
2749 accumulator->Add("<JSWeakMap>");
2750 break;
2751 }
2752 case JS_WEAK_SET_TYPE: {
2753 accumulator->Add("<JSWeakSet>");
2754 break;
2755 }
2756 case JS_REG_EXP_TYPE: {
2757 accumulator->Add("<JSRegExp");
2758 JSRegExp regexp = JSRegExp::cast(*this);
2759 if (regexp.source().IsString()) {
2760 accumulator->Add(" ");
2761 String::cast(regexp.source()).StringShortPrint(accumulator);
2762 }
2763 accumulator->Add(">");
2764
2765 break;
2766 }
2767 case JS_PROMISE_CONSTRUCTOR_TYPE:
2768 case JS_REG_EXP_CONSTRUCTOR_TYPE:
2769 case JS_ARRAY_CONSTRUCTOR_TYPE:
2770 #define TYPED_ARRAY_CONSTRUCTORS_SWITCH(Type, type, TYPE, Ctype) \
2771 case TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE:
2772 TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTORS_SWITCH)
2773 #undef TYPED_ARRAY_CONSTRUCTORS_SWITCH
2774 case JS_CLASS_CONSTRUCTOR_TYPE:
2775 case JS_FUNCTION_TYPE: {
2776 JSFunction function = JSFunction::cast(*this);
2777 std::unique_ptr<char[]> fun_name = function.shared().DebugNameCStr();
2778 if (fun_name[0] != '\0') {
2779 accumulator->Add("<JSFunction ");
2780 accumulator->Add(fun_name.get());
2781 } else {
2782 accumulator->Add("<JSFunction");
2783 }
2784 if (FLAG_trace_file_names) {
2785 Object source_name = Script::cast(function.shared().script()).name();
2786 if (source_name.IsString()) {
2787 String str = String::cast(source_name);
2788 if (str.length() > 0) {
2789 accumulator->Add(" <");
2790 accumulator->Put(str);
2791 accumulator->Add(">");
2792 }
2793 }
2794 }
2795 accumulator->Add(" (sfi = %p)",
2796 reinterpret_cast<void*>(function.shared().ptr()));
2797 accumulator->Put('>');
2798 break;
2799 }
2800 case JS_GENERATOR_OBJECT_TYPE: {
2801 accumulator->Add("<JSGenerator>");
2802 break;
2803 }
2804 case JS_ASYNC_FUNCTION_OBJECT_TYPE: {
2805 accumulator->Add("<JSAsyncFunctionObject>");
2806 break;
2807 }
2808 case JS_ASYNC_GENERATOR_OBJECT_TYPE: {
2809 accumulator->Add("<JS AsyncGenerator>");
2810 break;
2811 }
2812
2813 // All other JSObjects are rather similar to each other (JSObject,
2814 // JSGlobalProxy, JSGlobalObject, JSUndetectable, JSPrimitiveWrapper).
2815 default: {
2816 Map map_of_this = map();
2817 Heap* heap = GetHeap();
2818 Object constructor = map_of_this.GetConstructor();
2819 bool printed = false;
2820 if (constructor.IsHeapObject() &&
2821 !heap->Contains(HeapObject::cast(constructor))) {
2822 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
2823 } else {
2824 bool is_global_proxy = IsJSGlobalProxy();
2825 if (constructor.IsJSFunction()) {
2826 if (!heap->Contains(JSFunction::cast(constructor).shared())) {
2827 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
2828 } else {
2829 String constructor_name =
2830 JSFunction::cast(constructor).shared().Name();
2831 if (constructor_name.length() > 0) {
2832 accumulator->Add(is_global_proxy ? "<GlobalObject " : "<");
2833 accumulator->Put(constructor_name);
2834 accumulator->Add(" %smap = %p",
2835 map_of_this.is_deprecated() ? "deprecated-" : "",
2836 map_of_this);
2837 printed = true;
2838 }
2839 }
2840 } else if (constructor.IsFunctionTemplateInfo()) {
2841 accumulator->Add("<RemoteObject>");
2842 printed = true;
2843 }
2844 if (!printed) {
2845 accumulator->Add("<JS");
2846 if (is_global_proxy) {
2847 accumulator->Add("GlobalProxy");
2848 } else if (IsJSGlobalObject()) {
2849 accumulator->Add("GlobalObject");
2850 } else {
2851 accumulator->Add("Object");
2852 }
2853 }
2854 }
2855 if (IsJSPrimitiveWrapper()) {
2856 accumulator->Add(" value = ");
2857 JSPrimitiveWrapper::cast(*this).value().ShortPrint(accumulator);
2858 }
2859 accumulator->Put('>');
2860 break;
2861 }
2862 }
2863 }
2864
PrintElementsTransition(FILE * file,Handle<JSObject> object,ElementsKind from_kind,Handle<FixedArrayBase> from_elements,ElementsKind to_kind,Handle<FixedArrayBase> to_elements)2865 void JSObject::PrintElementsTransition(FILE* file, Handle<JSObject> object,
2866 ElementsKind from_kind,
2867 Handle<FixedArrayBase> from_elements,
2868 ElementsKind to_kind,
2869 Handle<FixedArrayBase> to_elements) {
2870 if (from_kind != to_kind) {
2871 OFStream os(file);
2872 os << "elements transition [" << ElementsKindToString(from_kind) << " -> "
2873 << ElementsKindToString(to_kind) << "] in ";
2874 JavaScriptFrame::PrintTop(object->GetIsolate(), file, false, true);
2875 PrintF(file, " for ");
2876 object->ShortPrint(file);
2877 PrintF(file, " from ");
2878 from_elements->ShortPrint(file);
2879 PrintF(file, " to ");
2880 to_elements->ShortPrint(file);
2881 PrintF(file, "\n");
2882 }
2883 }
2884
PrintInstanceMigration(FILE * file,Map original_map,Map new_map)2885 void JSObject::PrintInstanceMigration(FILE* file, Map original_map,
2886 Map new_map) {
2887 if (new_map.is_dictionary_map()) {
2888 PrintF(file, "[migrating to slow]\n");
2889 return;
2890 }
2891 PrintF(file, "[migrating]");
2892 Isolate* isolate = GetIsolate();
2893 DescriptorArray o = original_map.instance_descriptors(isolate);
2894 DescriptorArray n = new_map.instance_descriptors(isolate);
2895 for (InternalIndex i : original_map.IterateOwnDescriptors()) {
2896 Representation o_r = o.GetDetails(i).representation();
2897 Representation n_r = n.GetDetails(i).representation();
2898 if (!o_r.Equals(n_r)) {
2899 String::cast(o.GetKey(i)).PrintOn(file);
2900 PrintF(file, ":%s->%s ", o_r.Mnemonic(), n_r.Mnemonic());
2901 } else if (o.GetDetails(i).location() == PropertyLocation::kDescriptor &&
2902 n.GetDetails(i).location() == PropertyLocation::kField) {
2903 Name name = o.GetKey(i);
2904 if (name.IsString()) {
2905 String::cast(name).PrintOn(file);
2906 } else {
2907 PrintF(file, "{symbol %p}", reinterpret_cast<void*>(name.ptr()));
2908 }
2909 PrintF(file, " ");
2910 }
2911 }
2912 if (original_map.elements_kind() != new_map.elements_kind()) {
2913 PrintF(file, "elements_kind[%i->%i]", original_map.elements_kind(),
2914 new_map.elements_kind());
2915 }
2916 PrintF(file, "\n");
2917 }
2918
IsUnmodifiedApiObject(FullObjectSlot o)2919 bool JSObject::IsUnmodifiedApiObject(FullObjectSlot o) {
2920 Object object = *o;
2921 if (object.IsSmi()) return false;
2922 HeapObject heap_object = HeapObject::cast(object);
2923 if (!object.IsJSObject()) return false;
2924 JSObject js_object = JSObject::cast(object);
2925 if (!js_object.IsDroppableApiObject()) return false;
2926 Object maybe_constructor = js_object.map().GetConstructor();
2927 if (!maybe_constructor.IsJSFunction()) return false;
2928 JSFunction constructor = JSFunction::cast(maybe_constructor);
2929 if (js_object.elements().length() != 0) return false;
2930 // Check that the object is not a key in a WeakMap (over-approximation).
2931 if (!js_object.GetIdentityHash().IsUndefined()) return false;
2932
2933 return constructor.initial_map() == heap_object.map();
2934 }
2935
2936 // static
UpdatePrototypeUserRegistration(Handle<Map> old_map,Handle<Map> new_map,Isolate * isolate)2937 void JSObject::UpdatePrototypeUserRegistration(Handle<Map> old_map,
2938 Handle<Map> new_map,
2939 Isolate* isolate) {
2940 DCHECK(old_map->is_prototype_map());
2941 DCHECK(new_map->is_prototype_map());
2942 bool was_registered = JSObject::UnregisterPrototypeUser(old_map, isolate);
2943 new_map->set_prototype_info(old_map->prototype_info(), kReleaseStore);
2944 old_map->set_prototype_info(Smi::zero(), kReleaseStore);
2945 if (FLAG_trace_prototype_users) {
2946 PrintF("Moving prototype_info %p from map %p to map %p.\n",
2947 reinterpret_cast<void*>(new_map->prototype_info().ptr()),
2948 reinterpret_cast<void*>(old_map->ptr()),
2949 reinterpret_cast<void*>(new_map->ptr()));
2950 }
2951 if (was_registered) {
2952 if (new_map->prototype_info().IsPrototypeInfo()) {
2953 // The new map isn't registered with its prototype yet; reflect this fact
2954 // in the PrototypeInfo it just inherited from the old map.
2955 PrototypeInfo::cast(new_map->prototype_info())
2956 .set_registry_slot(PrototypeInfo::UNREGISTERED);
2957 }
2958 JSObject::LazyRegisterPrototypeUser(new_map, isolate);
2959 }
2960 }
2961
2962 // static
NotifyMapChange(Handle<Map> old_map,Handle<Map> new_map,Isolate * isolate)2963 void JSObject::NotifyMapChange(Handle<Map> old_map, Handle<Map> new_map,
2964 Isolate* isolate) {
2965 if (!old_map->is_prototype_map()) return;
2966
2967 InvalidatePrototypeChains(*old_map);
2968
2969 // If the map was registered with its prototype before, ensure that it
2970 // registers with its new prototype now. This preserves the invariant that
2971 // when a map on a prototype chain is registered with its prototype, then
2972 // all prototypes further up the chain are also registered with their
2973 // respective prototypes.
2974 UpdatePrototypeUserRegistration(old_map, new_map, isolate);
2975 }
2976
2977 namespace {
2978
2979 // To migrate a fast instance to a fast map:
2980 // - First check whether the instance needs to be rewritten. If not, simply
2981 // change the map.
2982 // - Otherwise, allocate a fixed array large enough to hold all fields, in
2983 // addition to unused space.
2984 // - Copy all existing properties in, in the following order: backing store
2985 // properties, unused fields, inobject properties.
2986 // - If all allocation succeeded, commit the state atomically:
2987 // * Copy inobject properties from the backing store back into the object.
2988 // * Trim the difference in instance size of the object. This also cleanly
2989 // frees inobject properties that moved to the backing store.
2990 // * If there are properties left in the backing store, trim of the space used
2991 // to temporarily store the inobject properties.
2992 // * If there are properties left in the backing store, install the backing
2993 // store.
MigrateFastToFast(Isolate * isolate,Handle<JSObject> object,Handle<Map> new_map)2994 void MigrateFastToFast(Isolate* isolate, Handle<JSObject> object,
2995 Handle<Map> new_map) {
2996 Handle<Map> old_map(object->map(), isolate);
2997 // In case of a regular transition.
2998 if (new_map->GetBackPointer(isolate) == *old_map) {
2999 // If the map does not add named properties, simply set the map.
3000 if (old_map->NumberOfOwnDescriptors() ==
3001 new_map->NumberOfOwnDescriptors()) {
3002 object->set_map(*new_map, kReleaseStore);
3003 return;
3004 }
3005
3006 // If the map adds a new kDescriptor property, simply set the map.
3007 PropertyDetails details = new_map->GetLastDescriptorDetails(isolate);
3008 if (details.location() == PropertyLocation::kDescriptor) {
3009 object->set_map(*new_map, kReleaseStore);
3010 return;
3011 }
3012
3013 // Check if we still have space in the {object}, in which case we
3014 // can also simply set the map (modulo a special case for mutable
3015 // double boxes).
3016 FieldIndex index =
3017 FieldIndex::ForDescriptor(isolate, *new_map, new_map->LastAdded());
3018 if (index.is_inobject() || index.outobject_array_index() <
3019 object->property_array(isolate).length()) {
3020 // Allocate HeapNumbers for double fields.
3021 if (index.is_double()) {
3022 auto value = isolate->factory()->NewHeapNumberWithHoleNaN();
3023 object->FastPropertyAtPut(index, *value);
3024 }
3025 object->set_map(*new_map, kReleaseStore);
3026 return;
3027 }
3028
3029 // This migration is a transition from a map that has run out of property
3030 // space. Extend the backing store.
3031 int grow_by = new_map->UnusedPropertyFields() + 1;
3032 Handle<PropertyArray> old_storage(object->property_array(isolate), isolate);
3033 Handle<PropertyArray> new_storage =
3034 isolate->factory()->CopyPropertyArrayAndGrow(old_storage, grow_by);
3035
3036 // Properly initialize newly added property.
3037 Handle<Object> value;
3038 if (details.representation().IsDouble()) {
3039 value = isolate->factory()->NewHeapNumberWithHoleNaN();
3040 } else {
3041 value = isolate->factory()->uninitialized_value();
3042 }
3043 DCHECK_EQ(PropertyLocation::kField, details.location());
3044 DCHECK_EQ(PropertyKind::kData, details.kind());
3045 DCHECK(!index.is_inobject()); // Must be a backing store index.
3046 new_storage->set(index.outobject_array_index(), *value);
3047
3048 // From here on we cannot fail and we shouldn't GC anymore.
3049 DisallowGarbageCollection no_gc;
3050
3051 // Set the new property value and do the map transition.
3052 object->SetProperties(*new_storage);
3053 object->set_map(*new_map, kReleaseStore);
3054 return;
3055 }
3056
3057 int old_number_of_fields;
3058 int number_of_fields = new_map->NumberOfFields(ConcurrencyMode::kSynchronous);
3059 int inobject = new_map->GetInObjectProperties();
3060 int unused = new_map->UnusedPropertyFields();
3061
3062 // Nothing to do if no functions were converted to fields and no smis were
3063 // converted to doubles.
3064 if (!old_map->InstancesNeedRewriting(*new_map, number_of_fields, inobject,
3065 unused, &old_number_of_fields,
3066 ConcurrencyMode::kSynchronous)) {
3067 object->set_map(*new_map, kReleaseStore);
3068 return;
3069 }
3070
3071 int total_size = number_of_fields + unused;
3072 int external = total_size - inobject;
3073 Handle<PropertyArray> array = isolate->factory()->NewPropertyArray(external);
3074
3075 // We use this array to temporarily store the inobject properties.
3076 Handle<FixedArray> inobject_props =
3077 isolate->factory()->NewFixedArray(inobject);
3078
3079 Handle<DescriptorArray> old_descriptors(
3080 old_map->instance_descriptors(isolate), isolate);
3081 Handle<DescriptorArray> new_descriptors(
3082 new_map->instance_descriptors(isolate), isolate);
3083 int old_nof = old_map->NumberOfOwnDescriptors();
3084 int new_nof = new_map->NumberOfOwnDescriptors();
3085
3086 // This method only supports generalizing instances to at least the same
3087 // number of properties.
3088 DCHECK(old_nof <= new_nof);
3089
3090 for (InternalIndex i : InternalIndex::Range(old_nof)) {
3091 PropertyDetails details = new_descriptors->GetDetails(i);
3092 if (details.location() != PropertyLocation::kField) continue;
3093 DCHECK_EQ(PropertyKind::kData, details.kind());
3094 PropertyDetails old_details = old_descriptors->GetDetails(i);
3095 Representation old_representation = old_details.representation();
3096 Representation representation = details.representation();
3097 Handle<Object> value;
3098 if (old_details.location() == PropertyLocation::kDescriptor) {
3099 if (old_details.kind() == PropertyKind::kAccessor) {
3100 // In case of kAccessor -> kData property reconfiguration, the property
3101 // must already be prepared for data of certain type.
3102 DCHECK(!details.representation().IsNone());
3103 if (details.representation().IsDouble()) {
3104 value = isolate->factory()->NewHeapNumberWithHoleNaN();
3105 } else {
3106 value = isolate->factory()->uninitialized_value();
3107 }
3108 } else {
3109 DCHECK_EQ(PropertyKind::kData, old_details.kind());
3110 value = handle(old_descriptors->GetStrongValue(isolate, i), isolate);
3111 DCHECK(!old_representation.IsDouble() && !representation.IsDouble());
3112 }
3113 } else {
3114 DCHECK_EQ(PropertyLocation::kField, old_details.location());
3115 FieldIndex index = FieldIndex::ForDescriptor(isolate, *old_map, i);
3116 value = handle(object->RawFastPropertyAt(isolate, index), isolate);
3117 if (!old_representation.IsDouble() && representation.IsDouble()) {
3118 DCHECK_IMPLIES(old_representation.IsNone(),
3119 value->IsUninitialized(isolate));
3120 value = Object::NewStorageFor(isolate, value, representation);
3121 } else if (old_representation.IsDouble() && !representation.IsDouble()) {
3122 value = Object::WrapForRead(isolate, value, old_representation);
3123 }
3124 }
3125 DCHECK(!(representation.IsDouble() && value->IsSmi()));
3126 int target_index = new_descriptors->GetFieldIndex(i);
3127 if (target_index < inobject) {
3128 inobject_props->set(target_index, *value);
3129 } else {
3130 array->set(target_index - inobject, *value);
3131 }
3132 }
3133
3134 for (InternalIndex i : InternalIndex::Range(old_nof, new_nof)) {
3135 PropertyDetails details = new_descriptors->GetDetails(i);
3136 if (details.location() != PropertyLocation::kField) continue;
3137 DCHECK_EQ(PropertyKind::kData, details.kind());
3138 Handle<Object> value;
3139 if (details.representation().IsDouble()) {
3140 value = isolate->factory()->NewHeapNumberWithHoleNaN();
3141 } else {
3142 value = isolate->factory()->uninitialized_value();
3143 }
3144 int target_index = new_descriptors->GetFieldIndex(i);
3145 if (target_index < inobject) {
3146 inobject_props->set(target_index, *value);
3147 } else {
3148 array->set(target_index - inobject, *value);
3149 }
3150 }
3151
3152 // From here on we cannot fail and we shouldn't GC anymore.
3153 DisallowGarbageCollection no_gc;
3154
3155 Heap* heap = isolate->heap();
3156
3157 // Copy (real) inobject properties. If necessary, stop at number_of_fields to
3158 // avoid overwriting |one_pointer_filler_map|.
3159 int limit = std::min(inobject, number_of_fields);
3160 for (int i = 0; i < limit; i++) {
3161 FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
3162 Object value = inobject_props->get(isolate, i);
3163 object->FastPropertyAtPut(index, value);
3164 }
3165
3166 object->SetProperties(*array);
3167
3168 // Create filler object past the new instance size.
3169 int old_instance_size = old_map->instance_size();
3170 int new_instance_size = new_map->instance_size();
3171 int instance_size_delta = old_instance_size - new_instance_size;
3172 DCHECK_GE(instance_size_delta, 0);
3173
3174 if (instance_size_delta > 0) {
3175 Address address = object->address();
3176 heap->CreateFillerObjectAt(address + new_instance_size, instance_size_delta,
3177 ClearRecordedSlots::kYes);
3178 }
3179
3180 // We are storing the new map using release store after creating a filler for
3181 // the left-over space to avoid races with the sweeper thread.
3182 object->set_map(*new_map, kReleaseStore);
3183 }
3184
MigrateFastToSlow(Isolate * isolate,Handle<JSObject> object,Handle<Map> new_map,int expected_additional_properties)3185 void MigrateFastToSlow(Isolate* isolate, Handle<JSObject> object,
3186 Handle<Map> new_map,
3187 int expected_additional_properties) {
3188 // The global object is always normalized.
3189 DCHECK(!object->IsJSGlobalObject(isolate));
3190 // JSGlobalProxy must never be normalized
3191 DCHECK(!object->IsJSGlobalProxy(isolate));
3192
3193 DCHECK_IMPLIES(new_map->is_prototype_map(),
3194 Map::IsPrototypeChainInvalidated(*new_map));
3195
3196 HandleScope scope(isolate);
3197 Handle<Map> map(object->map(isolate), isolate);
3198
3199 // Allocate new content.
3200 int real_size = map->NumberOfOwnDescriptors();
3201 int property_count = real_size;
3202 if (expected_additional_properties > 0) {
3203 property_count += expected_additional_properties;
3204 } else {
3205 // Make space for two more properties.
3206 int initial_capacity = V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
3207 ? SwissNameDictionary::kInitialCapacity
3208 : NameDictionary::kInitialCapacity;
3209 property_count += initial_capacity;
3210 }
3211
3212 Handle<NameDictionary> dictionary;
3213 Handle<SwissNameDictionary> ord_dictionary;
3214 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
3215 ord_dictionary = isolate->factory()->NewSwissNameDictionary(property_count);
3216 } else {
3217 dictionary = isolate->factory()->NewNameDictionary(property_count);
3218 }
3219
3220 Handle<DescriptorArray> descs(map->instance_descriptors(isolate), isolate);
3221 for (InternalIndex i : InternalIndex::Range(real_size)) {
3222 PropertyDetails details = descs->GetDetails(i);
3223 Handle<Name> key(descs->GetKey(isolate, i), isolate);
3224 Handle<Object> value;
3225 if (details.location() == PropertyLocation::kField) {
3226 FieldIndex index = FieldIndex::ForDescriptor(isolate, *map, i);
3227 if (details.kind() == PropertyKind::kData) {
3228 value = handle(object->RawFastPropertyAt(isolate, index), isolate);
3229 if (details.representation().IsDouble()) {
3230 DCHECK(value->IsHeapNumber(isolate));
3231 double old_value = Handle<HeapNumber>::cast(value)->value();
3232 value = isolate->factory()->NewHeapNumber(old_value);
3233 }
3234 } else {
3235 DCHECK_EQ(PropertyKind::kAccessor, details.kind());
3236 value = handle(object->RawFastPropertyAt(isolate, index), isolate);
3237 }
3238
3239 } else {
3240 DCHECK_EQ(PropertyLocation::kDescriptor, details.location());
3241 value = handle(descs->GetStrongValue(isolate, i), isolate);
3242 }
3243 DCHECK(!value.is_null());
3244 PropertyConstness constness = V8_DICT_PROPERTY_CONST_TRACKING_BOOL
3245 ? details.constness()
3246 : PropertyConstness::kMutable;
3247 PropertyDetails d(details.kind(), details.attributes(), constness);
3248
3249 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
3250 ord_dictionary =
3251 SwissNameDictionary::Add(isolate, ord_dictionary, key, value, d);
3252 } else {
3253 dictionary = NameDictionary::Add(isolate, dictionary, key, value, d);
3254 }
3255 }
3256
3257 if (!V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
3258 // Copy the next enumeration index from instance descriptor.
3259 dictionary->set_next_enumeration_index(real_size + 1);
3260 }
3261
3262 // From here on we cannot fail and we shouldn't GC anymore.
3263 DisallowGarbageCollection no_gc;
3264
3265 Heap* heap = isolate->heap();
3266
3267 // Resize the object in the heap if necessary.
3268 int old_instance_size = map->instance_size();
3269 int new_instance_size = new_map->instance_size();
3270 int instance_size_delta = old_instance_size - new_instance_size;
3271 DCHECK_GE(instance_size_delta, 0);
3272
3273 if (instance_size_delta > 0) {
3274 heap->CreateFillerObjectAt(object->address() + new_instance_size,
3275 instance_size_delta, ClearRecordedSlots::kYes);
3276 }
3277
3278 // We are storing the new map using release store after creating a filler for
3279 // the left-over space to avoid races with the sweeper thread.
3280 object->set_map(*new_map, kReleaseStore);
3281
3282 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
3283 object->SetProperties(*ord_dictionary);
3284 } else {
3285 object->SetProperties(*dictionary);
3286 }
3287
3288 // Ensure that in-object space of slow-mode object does not contain random
3289 // garbage.
3290 int inobject_properties = new_map->GetInObjectProperties();
3291 if (inobject_properties) {
3292 for (int i = 0; i < inobject_properties; i++) {
3293 FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
3294 object->FastPropertyAtPut(index, Smi::zero());
3295 }
3296 }
3297
3298 isolate->counters()->props_to_dictionary()->Increment();
3299
3300 #ifdef DEBUG
3301 if (FLAG_trace_normalization) {
3302 StdoutStream os;
3303 os << "Object properties have been normalized:\n";
3304 object->Print(os);
3305 }
3306 #endif
3307 }
3308
3309 } // namespace
3310
MigrateToMap(Isolate * isolate,Handle<JSObject> object,Handle<Map> new_map,int expected_additional_properties)3311 void JSObject::MigrateToMap(Isolate* isolate, Handle<JSObject> object,
3312 Handle<Map> new_map,
3313 int expected_additional_properties) {
3314 if (object->map(isolate) == *new_map) return;
3315 Handle<Map> old_map(object->map(isolate), isolate);
3316 NotifyMapChange(old_map, new_map, isolate);
3317
3318 if (old_map->is_dictionary_map()) {
3319 // For slow-to-fast migrations JSObject::MigrateSlowToFast()
3320 // must be used instead.
3321 CHECK(new_map->is_dictionary_map());
3322
3323 // Slow-to-slow migration is trivial.
3324 object->set_map(*new_map, kReleaseStore);
3325 } else if (!new_map->is_dictionary_map()) {
3326 MigrateFastToFast(isolate, object, new_map);
3327 if (old_map->is_prototype_map()) {
3328 DCHECK(!old_map->is_stable());
3329 DCHECK(new_map->is_stable());
3330 DCHECK(new_map->owns_descriptors());
3331 DCHECK(old_map->owns_descriptors());
3332 // Transfer ownership to the new map. Keep the descriptor pointer of the
3333 // old map intact because the concurrent marker might be iterating the
3334 // object with the old map.
3335 old_map->set_owns_descriptors(false);
3336 DCHECK(old_map->is_abandoned_prototype_map());
3337 // Ensure that no transition was inserted for prototype migrations.
3338 DCHECK_EQ(0,
3339 TransitionsAccessor(isolate, *old_map).NumberOfTransitions());
3340 DCHECK(new_map->GetBackPointer(isolate).IsUndefined(isolate));
3341 DCHECK(object->map(isolate) != *old_map);
3342 }
3343 } else {
3344 MigrateFastToSlow(isolate, object, new_map, expected_additional_properties);
3345 }
3346
3347 // Careful: Don't allocate here!
3348 // For some callers of this method, |object| might be in an inconsistent
3349 // state now: the new map might have a new elements_kind, but the object's
3350 // elements pointer hasn't been updated yet. Callers will fix this, but in
3351 // the meantime, (indirectly) calling JSObjectVerify() must be avoided.
3352 // When adding code here, add a DisallowGarbageCollection too.
3353 }
3354
ForceSetPrototype(Isolate * isolate,Handle<JSObject> object,Handle<HeapObject> proto)3355 void JSObject::ForceSetPrototype(Isolate* isolate, Handle<JSObject> object,
3356 Handle<HeapObject> proto) {
3357 // object.__proto__ = proto;
3358 Handle<Map> old_map = Handle<Map>(object->map(), isolate);
3359 Handle<Map> new_map = Map::Copy(isolate, old_map, "ForceSetPrototype");
3360 Map::SetPrototype(isolate, new_map, proto);
3361 JSObject::MigrateToMap(isolate, object, new_map);
3362 }
3363
SetPropertyWithInterceptor(LookupIterator * it,Maybe<ShouldThrow> should_throw,Handle<Object> value)3364 Maybe<bool> JSObject::SetPropertyWithInterceptor(
3365 LookupIterator* it, Maybe<ShouldThrow> should_throw, Handle<Object> value) {
3366 DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
3367 return SetPropertyWithInterceptorInternal(it, it->GetInterceptor(),
3368 should_throw, value);
3369 }
3370
GetElementsTransitionMap(Handle<JSObject> object,ElementsKind to_kind)3371 Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object,
3372 ElementsKind to_kind) {
3373 Handle<Map> map(object->map(), object->GetIsolate());
3374 return Map::TransitionElementsTo(object->GetIsolate(), map, to_kind);
3375 }
3376
AllocateStorageForMap(Handle<JSObject> object,Handle<Map> map)3377 void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) {
3378 DCHECK(object->map().GetInObjectProperties() == map->GetInObjectProperties());
3379 ElementsKind obj_kind = object->map().elements_kind();
3380 ElementsKind map_kind = map->elements_kind();
3381 Isolate* isolate = object->GetIsolate();
3382 if (map_kind != obj_kind) {
3383 ElementsKind to_kind = GetMoreGeneralElementsKind(map_kind, obj_kind);
3384 if (IsDictionaryElementsKind(obj_kind)) {
3385 to_kind = obj_kind;
3386 }
3387 if (IsDictionaryElementsKind(to_kind)) {
3388 NormalizeElements(object);
3389 } else {
3390 TransitionElementsKind(object, to_kind);
3391 }
3392 map = MapUpdater{isolate, map}.ReconfigureElementsKind(to_kind);
3393 }
3394 int number_of_fields = map->NumberOfFields(ConcurrencyMode::kSynchronous);
3395 int inobject = map->GetInObjectProperties();
3396 int unused = map->UnusedPropertyFields();
3397 int total_size = number_of_fields + unused;
3398 int external = total_size - inobject;
3399 // Allocate mutable double boxes if necessary. It is always necessary if we
3400 // have external properties, but is also necessary if we only have inobject
3401 // properties but don't unbox double fields.
3402
3403 Handle<DescriptorArray> descriptors(map->instance_descriptors(isolate),
3404 isolate);
3405 Handle<FixedArray> storage = isolate->factory()->NewFixedArray(inobject);
3406
3407 Handle<PropertyArray> array = isolate->factory()->NewPropertyArray(external);
3408
3409 for (InternalIndex i : map->IterateOwnDescriptors()) {
3410 PropertyDetails details = descriptors->GetDetails(i);
3411 Representation representation = details.representation();
3412 if (!representation.IsDouble()) continue;
3413 FieldIndex index = FieldIndex::ForDescriptor(*map, i);
3414 auto box = isolate->factory()->NewHeapNumberWithHoleNaN();
3415 if (index.is_inobject()) {
3416 storage->set(index.property_index(), *box);
3417 } else {
3418 array->set(index.outobject_array_index(), *box);
3419 }
3420 }
3421
3422 object->SetProperties(*array);
3423 for (int i = 0; i < inobject; i++) {
3424 FieldIndex index = FieldIndex::ForPropertyIndex(*map, i);
3425 Object value = storage->get(i);
3426 object->FastPropertyAtPut(index, value);
3427 }
3428 object->set_map(*map, kReleaseStore);
3429 }
3430
MigrateInstance(Isolate * isolate,Handle<JSObject> object)3431 void JSObject::MigrateInstance(Isolate* isolate, Handle<JSObject> object) {
3432 Handle<Map> original_map(object->map(), isolate);
3433 Handle<Map> map = Map::Update(isolate, original_map);
3434 map->set_is_migration_target(true);
3435 JSObject::MigrateToMap(isolate, object, map);
3436 if (FLAG_trace_migration) {
3437 object->PrintInstanceMigration(stdout, *original_map, *map);
3438 }
3439 #if VERIFY_HEAP
3440 if (FLAG_verify_heap) {
3441 object->JSObjectVerify(isolate);
3442 }
3443 #endif
3444 }
3445
3446 // static
TryMigrateInstance(Isolate * isolate,Handle<JSObject> object)3447 bool JSObject::TryMigrateInstance(Isolate* isolate, Handle<JSObject> object) {
3448 DisallowDeoptimization no_deoptimization(isolate);
3449 Handle<Map> original_map(object->map(), isolate);
3450 Handle<Map> new_map;
3451 if (!Map::TryUpdate(isolate, original_map).ToHandle(&new_map)) {
3452 return false;
3453 }
3454 JSObject::MigrateToMap(isolate, object, new_map);
3455 if (FLAG_trace_migration && *original_map != object->map()) {
3456 object->PrintInstanceMigration(stdout, *original_map, object->map());
3457 }
3458 #if VERIFY_HEAP
3459 if (FLAG_verify_heap) {
3460 object->JSObjectVerify(isolate);
3461 }
3462 #endif
3463 return true;
3464 }
3465
AddProperty(Isolate * isolate,Handle<JSObject> object,Handle<Name> name,Handle<Object> value,PropertyAttributes attributes)3466 void JSObject::AddProperty(Isolate* isolate, Handle<JSObject> object,
3467 Handle<Name> name, Handle<Object> value,
3468 PropertyAttributes attributes) {
3469 LookupIterator it(isolate, object, name, object,
3470 LookupIterator::OWN_SKIP_INTERCEPTOR);
3471 CHECK_NE(LookupIterator::ACCESS_CHECK, it.state());
3472 #ifdef DEBUG
3473 uint32_t index;
3474 DCHECK(!object->IsJSProxy());
3475 DCHECK(!name->AsArrayIndex(&index));
3476 Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it);
3477 DCHECK(maybe.IsJust());
3478 DCHECK(!it.IsFound());
3479 DCHECK(object->map().is_extensible() || name->IsPrivate());
3480 #endif
3481 CHECK(Object::AddDataProperty(&it, value, attributes,
3482 Just(ShouldThrow::kThrowOnError),
3483 StoreOrigin::kNamed)
3484 .IsJust());
3485 }
3486
AddProperty(Isolate * isolate,Handle<JSObject> object,const char * name,Handle<Object> value,PropertyAttributes attributes)3487 void JSObject::AddProperty(Isolate* isolate, Handle<JSObject> object,
3488 const char* name, Handle<Object> value,
3489 PropertyAttributes attributes) {
3490 JSObject::AddProperty(isolate, object,
3491 isolate->factory()->InternalizeUtf8String(name), value,
3492 attributes);
3493 }
3494
3495 // Reconfigures a property to a data property with attributes, even if it is not
3496 // reconfigurable.
3497 // Requires a LookupIterator that does not look at the prototype chain beyond
3498 // hidden prototypes.
DefineOwnPropertyIgnoreAttributes(LookupIterator * it,Handle<Object> value,PropertyAttributes attributes,AccessorInfoHandling handling,EnforceDefineSemantics semantics)3499 MaybeHandle<Object> JSObject::DefineOwnPropertyIgnoreAttributes(
3500 LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
3501 AccessorInfoHandling handling, EnforceDefineSemantics semantics) {
3502 MAYBE_RETURN_NULL(DefineOwnPropertyIgnoreAttributes(
3503 it, value, attributes, Just(ShouldThrow::kThrowOnError), handling,
3504 semantics));
3505 return value;
3506 }
3507
DefineOwnPropertyIgnoreAttributes(LookupIterator * it,Handle<Object> value,PropertyAttributes attributes,Maybe<ShouldThrow> should_throw,AccessorInfoHandling handling,EnforceDefineSemantics semantics,StoreOrigin store_origin)3508 Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes(
3509 LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
3510 Maybe<ShouldThrow> should_throw, AccessorInfoHandling handling,
3511 EnforceDefineSemantics semantics, StoreOrigin store_origin) {
3512 it->UpdateProtector();
3513
3514 for (; it->IsFound(); it->Next()) {
3515 switch (it->state()) {
3516 case LookupIterator::JSPROXY:
3517 case LookupIterator::TRANSITION:
3518 case LookupIterator::NOT_FOUND:
3519 UNREACHABLE();
3520
3521 case LookupIterator::ACCESS_CHECK:
3522 if (!it->HasAccess()) {
3523 it->isolate()->ReportFailedAccessCheck(it->GetHolder<JSObject>());
3524 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>());
3525 return Just(true);
3526 }
3527 break;
3528
3529 // If there's an interceptor, try to store the property with the
3530 // interceptor.
3531 // In case of success, the attributes will have been reset to the default
3532 // attributes of the interceptor, rather than the incoming attributes.
3533 //
3534 // TODO(verwaest): JSProxy afterwards verify the attributes that the
3535 // JSProxy claims it has, and verifies that they are compatible. If not,
3536 // they throw. Here we should do the same.
3537 case LookupIterator::INTERCEPTOR: {
3538 Maybe<bool> result = Just(false);
3539 if (semantics == EnforceDefineSemantics::kDefine) {
3540 PropertyDescriptor descriptor;
3541 descriptor.set_configurable((attributes & DONT_DELETE) != 0);
3542 descriptor.set_enumerable((attributes & DONT_ENUM) != 0);
3543 descriptor.set_writable((attributes & READ_ONLY) != 0);
3544 descriptor.set_value(value);
3545 result = DefinePropertyWithInterceptorInternal(
3546 it, it->GetInterceptor(), should_throw, &descriptor);
3547 } else {
3548 DCHECK_EQ(semantics, EnforceDefineSemantics::kSet);
3549 if (handling == DONT_FORCE_FIELD) {
3550 result =
3551 JSObject::SetPropertyWithInterceptor(it, should_throw, value);
3552 }
3553 }
3554 if (result.IsNothing() || result.FromJust()) return result;
3555
3556 if (semantics == EnforceDefineSemantics::kDefine) {
3557 it->Restart();
3558 Maybe<bool> can_define = JSReceiver::CheckIfCanDefine(
3559 it->isolate(), it, value, should_throw);
3560 if (can_define.IsNothing() || !can_define.FromJust()) {
3561 return can_define;
3562 }
3563 }
3564
3565 // The interceptor declined to handle the operation, so proceed defining
3566 // own property without the interceptor.
3567 Isolate* isolate = it->isolate();
3568 Handle<Object> receiver = it->GetReceiver();
3569 LookupIterator::Configuration c = LookupIterator::OWN_SKIP_INTERCEPTOR;
3570 LookupIterator own_lookup =
3571 it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c)
3572 : LookupIterator(isolate, receiver, it->name(), c);
3573 return JSObject::DefineOwnPropertyIgnoreAttributes(
3574 &own_lookup, value, attributes, should_throw, handling, semantics,
3575 store_origin);
3576 }
3577
3578 case LookupIterator::ACCESSOR: {
3579 Handle<Object> accessors = it->GetAccessors();
3580
3581 // Special handling for AccessorInfo, which behaves like a data
3582 // property.
3583 if (accessors->IsAccessorInfo() && handling == DONT_FORCE_FIELD) {
3584 PropertyAttributes current_attributes = it->property_attributes();
3585 // Ensure the context isn't changed after calling into accessors.
3586 AssertNoContextChange ncc(it->isolate());
3587
3588 // Update the attributes before calling the setter. The setter may
3589 // later change the shape of the property.
3590 if (current_attributes != attributes) {
3591 it->TransitionToAccessorPair(accessors, attributes);
3592 }
3593
3594 return Object::SetPropertyWithAccessor(it, value, should_throw);
3595 }
3596
3597 it->ReconfigureDataProperty(value, attributes);
3598 return Just(true);
3599 }
3600 case LookupIterator::INTEGER_INDEXED_EXOTIC:
3601 return Object::RedefineIncompatibleProperty(
3602 it->isolate(), it->GetName(), value, should_throw);
3603
3604 case LookupIterator::DATA: {
3605 // Regular property update if the attributes match.
3606 if (it->property_attributes() == attributes) {
3607 return Object::SetDataProperty(it, value);
3608 }
3609
3610 // The non-matching attribute case for JSTypedArrays has already been
3611 // handled by JSTypedArray::DefineOwnProperty.
3612 DCHECK(!it->IsElement() ||
3613 !Handle<JSObject>::cast(it->GetReceiver())
3614 ->HasTypedArrayOrRabGsabTypedArrayElements());
3615 // Reconfigure the data property if the attributes mismatch.
3616 it->ReconfigureDataProperty(value, attributes);
3617
3618 return Just(true);
3619 }
3620 }
3621 }
3622
3623 return Object::AddDataProperty(it, value, attributes, should_throw,
3624 store_origin, semantics);
3625 }
3626
SetOwnPropertyIgnoreAttributes(Handle<JSObject> object,Handle<Name> name,Handle<Object> value,PropertyAttributes attributes)3627 MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
3628 Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
3629 PropertyAttributes attributes) {
3630 DCHECK(!value->IsTheHole());
3631 LookupIterator it(object->GetIsolate(), object, name, object,
3632 LookupIterator::OWN);
3633 return DefineOwnPropertyIgnoreAttributes(&it, value, attributes);
3634 }
3635
SetOwnElementIgnoreAttributes(Handle<JSObject> object,size_t index,Handle<Object> value,PropertyAttributes attributes)3636 MaybeHandle<Object> JSObject::SetOwnElementIgnoreAttributes(
3637 Handle<JSObject> object, size_t index, Handle<Object> value,
3638 PropertyAttributes attributes) {
3639 DCHECK(!object->IsJSTypedArray());
3640 Isolate* isolate = object->GetIsolate();
3641 LookupIterator it(isolate, object, index, object, LookupIterator::OWN);
3642 return DefineOwnPropertyIgnoreAttributes(&it, value, attributes);
3643 }
3644
DefinePropertyOrElementIgnoreAttributes(Handle<JSObject> object,Handle<Name> name,Handle<Object> value,PropertyAttributes attributes)3645 MaybeHandle<Object> JSObject::DefinePropertyOrElementIgnoreAttributes(
3646 Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
3647 PropertyAttributes attributes) {
3648 Isolate* isolate = object->GetIsolate();
3649 PropertyKey key(isolate, name);
3650 LookupIterator it(isolate, object, key, object, LookupIterator::OWN);
3651 return DefineOwnPropertyIgnoreAttributes(&it, value, attributes);
3652 }
3653
GetPropertyAttributesWithInterceptor(LookupIterator * it)3654 Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor(
3655 LookupIterator* it) {
3656 return GetPropertyAttributesWithInterceptorInternal(it, it->GetInterceptor());
3657 }
3658
NormalizeProperties(Isolate * isolate,Handle<JSObject> object,PropertyNormalizationMode mode,int expected_additional_properties,const char * reason)3659 void JSObject::NormalizeProperties(Isolate* isolate, Handle<JSObject> object,
3660 PropertyNormalizationMode mode,
3661 int expected_additional_properties,
3662 const char* reason) {
3663 if (!object->HasFastProperties()) return;
3664
3665 Handle<Map> map(object->map(), isolate);
3666 Handle<Map> new_map =
3667 Map::Normalize(isolate, map, map->elements_kind(), mode, reason);
3668
3669 JSObject::MigrateToMap(isolate, object, new_map,
3670 expected_additional_properties);
3671 }
3672
MigrateSlowToFast(Handle<JSObject> object,int unused_property_fields,const char * reason)3673 void JSObject::MigrateSlowToFast(Handle<JSObject> object,
3674 int unused_property_fields,
3675 const char* reason) {
3676 if (object->HasFastProperties()) return;
3677 DCHECK(!object->IsJSGlobalObject());
3678 Isolate* isolate = object->GetIsolate();
3679 Factory* factory = isolate->factory();
3680
3681 Handle<NameDictionary> dictionary;
3682 Handle<SwissNameDictionary> swiss_dictionary;
3683 int number_of_elements;
3684 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
3685 swiss_dictionary = handle(object->property_dictionary_swiss(), isolate);
3686 number_of_elements = swiss_dictionary->NumberOfElements();
3687 } else {
3688 dictionary = handle(object->property_dictionary(), isolate);
3689 number_of_elements = dictionary->NumberOfElements();
3690 }
3691
3692 // Make sure we preserve dictionary representation if there are too many
3693 // descriptors.
3694 if (number_of_elements > kMaxNumberOfDescriptors) return;
3695
3696 Handle<FixedArray> iteration_order;
3697 int iteration_length;
3698 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
3699 // |iteration_order| remains empty handle, we don't need it.
3700 iteration_length = swiss_dictionary->UsedCapacity();
3701 } else {
3702 iteration_order = NameDictionary::IterationIndices(isolate, dictionary);
3703 iteration_length = dictionary->NumberOfElements();
3704 }
3705
3706 int number_of_fields = 0;
3707
3708 // Compute the length of the instance descriptor.
3709 ReadOnlyRoots roots(isolate);
3710 for (int i = 0; i < iteration_length; i++) {
3711 PropertyKind kind;
3712 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
3713 InternalIndex index(swiss_dictionary->EntryForEnumerationIndex(i));
3714 Object key = swiss_dictionary->KeyAt(index);
3715 if (!SwissNameDictionary::IsKey(roots, key)) {
3716 // Ignore deleted entries.
3717 continue;
3718 }
3719 kind = swiss_dictionary->DetailsAt(index).kind();
3720 } else {
3721 InternalIndex index(Smi::ToInt(iteration_order->get(i)));
3722 DCHECK(dictionary->IsKey(roots, dictionary->KeyAt(isolate, index)));
3723 kind = dictionary->DetailsAt(index).kind();
3724 }
3725
3726 if (kind == PropertyKind::kData) {
3727 number_of_fields += 1;
3728 }
3729 }
3730
3731 Handle<Map> old_map(object->map(), isolate);
3732
3733 int inobject_props = old_map->GetInObjectProperties();
3734
3735 // Allocate new map.
3736 Handle<Map> new_map = Map::CopyDropDescriptors(isolate, old_map);
3737 // We should not only set this bit if we need to. We should not retain the
3738 // old bit because turning a map into dictionary always sets this bit.
3739 new_map->set_may_have_interesting_symbols(new_map->has_named_interceptor() ||
3740 new_map->is_access_check_needed());
3741 new_map->set_is_dictionary_map(false);
3742
3743 NotifyMapChange(old_map, new_map, isolate);
3744
3745 if (number_of_elements == 0) {
3746 DisallowGarbageCollection no_gc;
3747 DCHECK_LE(unused_property_fields, inobject_props);
3748 // Transform the object.
3749 new_map->SetInObjectUnusedPropertyFields(inobject_props);
3750 object->set_map(*new_map, kReleaseStore);
3751 object->SetProperties(ReadOnlyRoots(isolate).empty_fixed_array());
3752 // Check that it really works.
3753 DCHECK(object->HasFastProperties());
3754 if (FLAG_log_maps) {
3755 LOG(isolate, MapEvent("SlowToFast", old_map, new_map, reason));
3756 }
3757 return;
3758 }
3759
3760 // Allocate the instance descriptor.
3761 Handle<DescriptorArray> descriptors =
3762 DescriptorArray::Allocate(isolate, number_of_elements, 0);
3763
3764 int number_of_allocated_fields =
3765 number_of_fields + unused_property_fields - inobject_props;
3766 if (number_of_allocated_fields < 0) {
3767 // There is enough inobject space for all fields (including unused).
3768 number_of_allocated_fields = 0;
3769 unused_property_fields = inobject_props - number_of_fields;
3770 }
3771
3772 // Allocate the property array for the fields.
3773 Handle<PropertyArray> fields =
3774 factory->NewPropertyArray(number_of_allocated_fields);
3775
3776 bool is_transitionable_elements_kind =
3777 IsTransitionableFastElementsKind(old_map->elements_kind());
3778
3779 // Fill in the instance descriptor and the fields.
3780 int current_offset = 0;
3781 int descriptor_index = 0;
3782 for (int i = 0; i < iteration_length; i++) {
3783 Name k;
3784 Object value;
3785 PropertyDetails details = PropertyDetails::Empty();
3786
3787 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
3788 InternalIndex index(swiss_dictionary->EntryForEnumerationIndex(i));
3789 Object key_obj = swiss_dictionary->KeyAt(index);
3790 if (!SwissNameDictionary::IsKey(roots, key_obj)) {
3791 continue;
3792 }
3793 k = Name::cast(key_obj);
3794
3795 value = swiss_dictionary->ValueAt(index);
3796 details = swiss_dictionary->DetailsAt(index);
3797 } else {
3798 InternalIndex index(Smi::ToInt(iteration_order->get(i)));
3799 k = dictionary->NameAt(index);
3800
3801 value = dictionary->ValueAt(index);
3802 details = dictionary->DetailsAt(index);
3803 }
3804
3805 // Dictionary keys are internalized upon insertion.
3806 // TODO(jkummerow): Turn this into a DCHECK if it's not hit in the wild.
3807 CHECK(k.IsUniqueName());
3808 Handle<Name> key(k, isolate);
3809
3810 // Properly mark the {new_map} if the {key} is an "interesting symbol".
3811 if (key->IsInterestingSymbol()) {
3812 new_map->set_may_have_interesting_symbols(true);
3813 }
3814
3815 DCHECK_EQ(PropertyLocation::kField, details.location());
3816 DCHECK_IMPLIES(!V8_DICT_PROPERTY_CONST_TRACKING_BOOL,
3817 details.constness() == PropertyConstness::kMutable);
3818
3819 Descriptor d;
3820 if (details.kind() == PropertyKind::kData) {
3821 // Ensure that we make constant field only when elements kind is not
3822 // transitionable.
3823 PropertyConstness constness = is_transitionable_elements_kind
3824 ? PropertyConstness::kMutable
3825 : PropertyConstness::kConst;
3826 // TODO(v8:11248): Consider always setting constness to kMutable
3827 // once all prototypes stay in dictionary mode and we are not interested
3828 // in tracking constness for fast mode properties anymore.
3829
3830 d = Descriptor::DataField(
3831 key, current_offset, details.attributes(), constness,
3832 // TODO(verwaest): value->OptimalRepresentation();
3833 Representation::Tagged(), MaybeObjectHandle(FieldType::Any(isolate)));
3834 } else {
3835 DCHECK_EQ(PropertyKind::kAccessor, details.kind());
3836 d = Descriptor::AccessorConstant(key, handle(value, isolate),
3837 details.attributes());
3838 }
3839 details = d.GetDetails();
3840 if (details.location() == PropertyLocation::kField) {
3841 if (current_offset < inobject_props) {
3842 object->InObjectPropertyAtPut(current_offset, value,
3843 UPDATE_WRITE_BARRIER);
3844 } else {
3845 int offset = current_offset - inobject_props;
3846 fields->set(offset, value);
3847 }
3848 current_offset += details.field_width_in_words();
3849 }
3850 descriptors->Set(InternalIndex(descriptor_index++), &d);
3851 }
3852 DCHECK_EQ(current_offset, number_of_fields);
3853 DCHECK_EQ(descriptor_index, number_of_elements);
3854
3855 descriptors->Sort();
3856
3857 DisallowGarbageCollection no_gc;
3858 new_map->InitializeDescriptors(isolate, *descriptors);
3859 if (number_of_allocated_fields == 0) {
3860 new_map->SetInObjectUnusedPropertyFields(unused_property_fields);
3861 } else {
3862 new_map->SetOutOfObjectUnusedPropertyFields(unused_property_fields);
3863 }
3864
3865 if (FLAG_log_maps) {
3866 LOG(isolate, MapEvent("SlowToFast", old_map, new_map, reason));
3867 }
3868 // Transform the object.
3869 object->set_map(*new_map, kReleaseStore);
3870
3871 object->SetProperties(*fields);
3872 DCHECK(object->IsJSObject());
3873
3874 // Check that it really works.
3875 DCHECK(object->HasFastProperties());
3876 }
3877
RequireSlowElements(NumberDictionary dictionary)3878 void JSObject::RequireSlowElements(NumberDictionary dictionary) {
3879 DCHECK_NE(dictionary,
3880 ReadOnlyRoots(GetIsolate()).empty_slow_element_dictionary());
3881 if (dictionary.requires_slow_elements()) return;
3882 dictionary.set_requires_slow_elements();
3883 if (map().is_prototype_map()) {
3884 // If this object is a prototype (the callee will check), invalidate any
3885 // prototype chains involving it.
3886 InvalidatePrototypeChains(map());
3887 }
3888 }
3889
NormalizeElements(Handle<JSObject> object)3890 Handle<NumberDictionary> JSObject::NormalizeElements(Handle<JSObject> object) {
3891 DCHECK(!object->HasTypedArrayOrRabGsabTypedArrayElements());
3892 Isolate* isolate = object->GetIsolate();
3893 bool is_sloppy_arguments = object->HasSloppyArgumentsElements();
3894 {
3895 DisallowGarbageCollection no_gc;
3896 FixedArrayBase elements = object->elements();
3897
3898 if (is_sloppy_arguments) {
3899 elements = SloppyArgumentsElements::cast(elements).arguments();
3900 }
3901
3902 if (elements.IsNumberDictionary()) {
3903 return handle(NumberDictionary::cast(elements), isolate);
3904 }
3905 }
3906
3907 DCHECK(object->HasSmiOrObjectElements() || object->HasDoubleElements() ||
3908 object->HasFastArgumentsElements() ||
3909 object->HasFastStringWrapperElements() ||
3910 object->HasSealedElements() || object->HasNonextensibleElements());
3911
3912 Handle<NumberDictionary> dictionary =
3913 object->GetElementsAccessor()->Normalize(object);
3914
3915 // Switch to using the dictionary as the backing storage for elements.
3916 ElementsKind target_kind = is_sloppy_arguments
3917 ? SLOW_SLOPPY_ARGUMENTS_ELEMENTS
3918 : object->HasFastStringWrapperElements()
3919 ? SLOW_STRING_WRAPPER_ELEMENTS
3920 : DICTIONARY_ELEMENTS;
3921 Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, target_kind);
3922 // Set the new map first to satify the elements type assert in set_elements().
3923 JSObject::MigrateToMap(isolate, object, new_map);
3924
3925 if (is_sloppy_arguments) {
3926 SloppyArgumentsElements::cast(object->elements())
3927 .set_arguments(*dictionary);
3928 } else {
3929 object->set_elements(*dictionary);
3930 }
3931
3932 isolate->counters()->elements_to_dictionary()->Increment();
3933
3934 #ifdef DEBUG
3935 if (FLAG_trace_normalization) {
3936 StdoutStream os;
3937 os << "Object elements have been normalized:\n";
3938 object->Print(os);
3939 }
3940 #endif
3941
3942 DCHECK(object->HasDictionaryElements() ||
3943 object->HasSlowArgumentsElements() ||
3944 object->HasSlowStringWrapperElements());
3945 return dictionary;
3946 }
3947
DeletePropertyWithInterceptor(LookupIterator * it,ShouldThrow should_throw)3948 Maybe<bool> JSObject::DeletePropertyWithInterceptor(LookupIterator* it,
3949 ShouldThrow should_throw) {
3950 Isolate* isolate = it->isolate();
3951 // Make sure that the top context does not change when doing callbacks or
3952 // interceptor calls.
3953 AssertNoContextChange ncc(isolate);
3954
3955 DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
3956 Handle<InterceptorInfo> interceptor(it->GetInterceptor());
3957 if (interceptor->deleter().IsUndefined(isolate)) return Nothing<bool>();
3958
3959 Handle<JSObject> holder = it->GetHolder<JSObject>();
3960 Handle<Object> receiver = it->GetReceiver();
3961 if (!receiver->IsJSReceiver()) {
3962 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver,
3963 Object::ConvertReceiver(isolate, receiver),
3964 Nothing<bool>());
3965 }
3966
3967 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
3968 *holder, Just(should_throw));
3969 Handle<Object> result;
3970 if (it->IsElement(*holder)) {
3971 result = args.CallIndexedDeleter(interceptor, it->array_index());
3972 } else {
3973 result = args.CallNamedDeleter(interceptor, it->name());
3974 }
3975
3976 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
3977 if (result.is_null()) return Nothing<bool>();
3978
3979 DCHECK(result->IsBoolean());
3980 // Rebox CustomArguments::kReturnValueOffset before returning.
3981 return Just(result->IsTrue(isolate));
3982 }
3983
CreateDataProperty(LookupIterator * it,Handle<Object> value,Maybe<ShouldThrow> should_throw)3984 Maybe<bool> JSObject::CreateDataProperty(LookupIterator* it,
3985 Handle<Object> value,
3986 Maybe<ShouldThrow> should_throw) {
3987 DCHECK(it->GetReceiver()->IsJSObject());
3988 MAYBE_RETURN(JSReceiver::GetPropertyAttributes(it), Nothing<bool>());
3989 Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
3990 Isolate* isolate = receiver->GetIsolate();
3991
3992 Maybe<bool> can_define =
3993 JSReceiver::CheckIfCanDefine(isolate, it, value, should_throw);
3994 if (can_define.IsNothing() || !can_define.FromJust()) {
3995 return can_define;
3996 }
3997
3998 RETURN_ON_EXCEPTION_VALUE(it->isolate(),
3999 DefineOwnPropertyIgnoreAttributes(it, value, NONE),
4000 Nothing<bool>());
4001
4002 return Just(true);
4003 }
4004
4005 namespace {
4006
4007 template <typename Dictionary>
TestDictionaryPropertiesIntegrityLevel(Dictionary dict,ReadOnlyRoots roots,PropertyAttributes level)4008 bool TestDictionaryPropertiesIntegrityLevel(Dictionary dict,
4009 ReadOnlyRoots roots,
4010 PropertyAttributes level) {
4011 DCHECK(level == SEALED || level == FROZEN);
4012
4013 for (InternalIndex i : dict.IterateEntries()) {
4014 Object key;
4015 if (!dict.ToKey(roots, i, &key)) continue;
4016 if (key.FilterKey(ALL_PROPERTIES)) continue;
4017 PropertyDetails details = dict.DetailsAt(i);
4018 if (details.IsConfigurable()) return false;
4019 if (level == FROZEN && details.kind() == PropertyKind::kData &&
4020 !details.IsReadOnly()) {
4021 return false;
4022 }
4023 }
4024 return true;
4025 }
4026
TestFastPropertiesIntegrityLevel(Map map,PropertyAttributes level)4027 bool TestFastPropertiesIntegrityLevel(Map map, PropertyAttributes level) {
4028 DCHECK(level == SEALED || level == FROZEN);
4029 DCHECK(!map.IsCustomElementsReceiverMap());
4030 DCHECK(!map.is_dictionary_map());
4031
4032 DescriptorArray descriptors = map.instance_descriptors();
4033 for (InternalIndex i : map.IterateOwnDescriptors()) {
4034 if (descriptors.GetKey(i).IsPrivate()) continue;
4035 PropertyDetails details = descriptors.GetDetails(i);
4036 if (details.IsConfigurable()) return false;
4037 if (level == FROZEN && details.kind() == PropertyKind::kData &&
4038 !details.IsReadOnly()) {
4039 return false;
4040 }
4041 }
4042 return true;
4043 }
4044
TestPropertiesIntegrityLevel(JSObject object,PropertyAttributes level)4045 bool TestPropertiesIntegrityLevel(JSObject object, PropertyAttributes level) {
4046 DCHECK(!object.map().IsCustomElementsReceiverMap());
4047
4048 if (object.HasFastProperties()) {
4049 return TestFastPropertiesIntegrityLevel(object.map(), level);
4050 }
4051
4052 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
4053 return TestDictionaryPropertiesIntegrityLevel(
4054 object.property_dictionary_swiss(), object.GetReadOnlyRoots(), level);
4055 } else {
4056 return TestDictionaryPropertiesIntegrityLevel(
4057 object.property_dictionary(), object.GetReadOnlyRoots(), level);
4058 }
4059 }
4060
TestElementsIntegrityLevel(JSObject object,PropertyAttributes level)4061 bool TestElementsIntegrityLevel(JSObject object, PropertyAttributes level) {
4062 DCHECK(!object.HasSloppyArgumentsElements());
4063
4064 ElementsKind kind = object.GetElementsKind();
4065
4066 if (IsDictionaryElementsKind(kind)) {
4067 return TestDictionaryPropertiesIntegrityLevel(
4068 NumberDictionary::cast(object.elements()), object.GetReadOnlyRoots(),
4069 level);
4070 }
4071 if (IsTypedArrayElementsKind(kind)) {
4072 if (level == FROZEN && JSArrayBufferView::cast(object).byte_length() > 0) {
4073 return false; // TypedArrays with elements can't be frozen.
4074 }
4075 return TestPropertiesIntegrityLevel(object, level);
4076 }
4077 if (IsFrozenElementsKind(kind)) return true;
4078 if (IsSealedElementsKind(kind) && level != FROZEN) return true;
4079 if (IsNonextensibleElementsKind(kind) && level == NONE) return true;
4080
4081 ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
4082 // Only DICTIONARY_ELEMENTS and SLOW_SLOPPY_ARGUMENTS_ELEMENTS have
4083 // PropertyAttributes so just test if empty
4084 return accessor->NumberOfElements(object) == 0;
4085 }
4086
FastTestIntegrityLevel(JSObject object,PropertyAttributes level)4087 bool FastTestIntegrityLevel(JSObject object, PropertyAttributes level) {
4088 DCHECK(!object.map().IsCustomElementsReceiverMap());
4089
4090 return !object.map().is_extensible() &&
4091 TestElementsIntegrityLevel(object, level) &&
4092 TestPropertiesIntegrityLevel(object, level);
4093 }
4094
4095 } // namespace
4096
TestIntegrityLevel(Handle<JSObject> object,IntegrityLevel level)4097 Maybe<bool> JSObject::TestIntegrityLevel(Handle<JSObject> object,
4098 IntegrityLevel level) {
4099 if (!object->map().IsCustomElementsReceiverMap() &&
4100 !object->HasSloppyArgumentsElements()) {
4101 return Just(FastTestIntegrityLevel(*object, level));
4102 }
4103 return GenericTestIntegrityLevel(Handle<JSReceiver>::cast(object), level);
4104 }
4105
PreventExtensions(Handle<JSObject> object,ShouldThrow should_throw)4106 Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object,
4107 ShouldThrow should_throw) {
4108 Isolate* isolate = object->GetIsolate();
4109
4110 if (!object->HasSloppyArgumentsElements()) {
4111 return PreventExtensionsWithTransition<NONE>(object, should_throw);
4112 }
4113
4114 if (object->IsAccessCheckNeeded() &&
4115 !isolate->MayAccess(handle(isolate->context(), isolate), object)) {
4116 isolate->ReportFailedAccessCheck(object);
4117 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
4118 RETURN_FAILURE(isolate, should_throw,
4119 NewTypeError(MessageTemplate::kNoAccess));
4120 }
4121
4122 if (!object->map().is_extensible()) return Just(true);
4123
4124 if (object->IsJSGlobalProxy()) {
4125 PrototypeIterator iter(isolate, object);
4126 if (iter.IsAtEnd()) return Just(true);
4127 DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
4128 return PreventExtensions(PrototypeIterator::GetCurrent<JSObject>(iter),
4129 should_throw);
4130 }
4131
4132 if (object->map().has_named_interceptor() ||
4133 object->map().has_indexed_interceptor()) {
4134 RETURN_FAILURE(isolate, should_throw,
4135 NewTypeError(MessageTemplate::kCannotPreventExt));
4136 }
4137
4138 DCHECK(!object->HasTypedArrayOrRabGsabTypedArrayElements());
4139
4140 // Normalize fast elements.
4141 Handle<NumberDictionary> dictionary = NormalizeElements(object);
4142 DCHECK(object->HasDictionaryElements() || object->HasSlowArgumentsElements());
4143
4144 // Make sure that we never go back to fast case.
4145 if (*dictionary != ReadOnlyRoots(isolate).empty_slow_element_dictionary()) {
4146 object->RequireSlowElements(*dictionary);
4147 }
4148
4149 // Do a map transition, other objects with this map may still
4150 // be extensible.
4151 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
4152 Handle<Map> new_map =
4153 Map::Copy(isolate, handle(object->map(), isolate), "PreventExtensions");
4154
4155 new_map->set_is_extensible(false);
4156 JSObject::MigrateToMap(isolate, object, new_map);
4157 DCHECK(!object->map().is_extensible());
4158
4159 return Just(true);
4160 }
4161
IsExtensible(Handle<JSObject> object)4162 bool JSObject::IsExtensible(Handle<JSObject> object) {
4163 Isolate* isolate = object->GetIsolate();
4164 if (object->IsAccessCheckNeeded() &&
4165 !isolate->MayAccess(handle(isolate->context(), isolate), object)) {
4166 return true;
4167 }
4168 if (object->IsJSGlobalProxy()) {
4169 PrototypeIterator iter(isolate, *object);
4170 if (iter.IsAtEnd()) return false;
4171 DCHECK(iter.GetCurrent().IsJSGlobalObject());
4172 return iter.GetCurrent<JSObject>().map().is_extensible();
4173 }
4174 return object->map().is_extensible();
4175 }
4176
4177 // static
ReadFromOptionsBag(Handle<Object> options,Handle<String> option_name,Isolate * isolate)4178 MaybeHandle<Object> JSObject::ReadFromOptionsBag(Handle<Object> options,
4179 Handle<String> option_name,
4180 Isolate* isolate) {
4181 if (options->IsJSReceiver()) {
4182 Handle<JSReceiver> js_options = Handle<JSReceiver>::cast(options);
4183 return JSObject::GetProperty(isolate, js_options, option_name);
4184 }
4185 return MaybeHandle<Object>(isolate->factory()->undefined_value());
4186 }
4187
4188 template <typename Dictionary>
ApplyAttributesToDictionary(Isolate * isolate,ReadOnlyRoots roots,Handle<Dictionary> dictionary,const PropertyAttributes attributes)4189 void JSObject::ApplyAttributesToDictionary(
4190 Isolate* isolate, ReadOnlyRoots roots, Handle<Dictionary> dictionary,
4191 const PropertyAttributes attributes) {
4192 for (InternalIndex i : dictionary->IterateEntries()) {
4193 Object k;
4194 if (!dictionary->ToKey(roots, i, &k)) continue;
4195 if (k.FilterKey(ALL_PROPERTIES)) continue;
4196 PropertyDetails details = dictionary->DetailsAt(i);
4197 int attrs = attributes;
4198 // READ_ONLY is an invalid attribute for JS setters/getters.
4199 if ((attributes & READ_ONLY) && details.kind() == PropertyKind::kAccessor) {
4200 Object v = dictionary->ValueAt(i);
4201 if (v.IsAccessorPair()) attrs &= ~READ_ONLY;
4202 }
4203 details = details.CopyAddAttributes(PropertyAttributesFromInt(attrs));
4204 dictionary->DetailsAtPut(i, details);
4205 }
4206 }
4207
4208 template void JSObject::ApplyAttributesToDictionary(
4209 Isolate* isolate, ReadOnlyRoots roots, Handle<NumberDictionary> dictionary,
4210 const PropertyAttributes attributes);
4211
CreateElementDictionary(Isolate * isolate,Handle<JSObject> object)4212 Handle<NumberDictionary> CreateElementDictionary(Isolate* isolate,
4213 Handle<JSObject> object) {
4214 Handle<NumberDictionary> new_element_dictionary;
4215 if (!object->HasTypedArrayOrRabGsabTypedArrayElements() &&
4216 !object->HasDictionaryElements() &&
4217 !object->HasSlowStringWrapperElements()) {
4218 int length = object->IsJSArray()
4219 ? Smi::ToInt(Handle<JSArray>::cast(object)->length())
4220 : object->elements().length();
4221 new_element_dictionary =
4222 length == 0 ? isolate->factory()->empty_slow_element_dictionary()
4223 : object->GetElementsAccessor()->Normalize(object);
4224 }
4225 return new_element_dictionary;
4226 }
4227
4228 template <PropertyAttributes attrs>
PreventExtensionsWithTransition(Handle<JSObject> object,ShouldThrow should_throw)4229 Maybe<bool> JSObject::PreventExtensionsWithTransition(
4230 Handle<JSObject> object, ShouldThrow should_throw) {
4231 STATIC_ASSERT(attrs == NONE || attrs == SEALED || attrs == FROZEN);
4232
4233 // Sealing/freezing sloppy arguments or namespace objects should be handled
4234 // elsewhere.
4235 DCHECK(!object->HasSloppyArgumentsElements());
4236 DCHECK_IMPLIES(object->IsJSModuleNamespace(), attrs == NONE);
4237
4238 Isolate* isolate = object->GetIsolate();
4239 if (object->IsAccessCheckNeeded() &&
4240 !isolate->MayAccess(handle(isolate->context(), isolate), object)) {
4241 isolate->ReportFailedAccessCheck(object);
4242 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
4243 RETURN_FAILURE(isolate, should_throw,
4244 NewTypeError(MessageTemplate::kNoAccess));
4245 }
4246
4247 if (attrs == NONE && !object->map().is_extensible()) {
4248 return Just(true);
4249 }
4250
4251 {
4252 ElementsKind old_elements_kind = object->map().elements_kind();
4253 if (IsFrozenElementsKind(old_elements_kind)) return Just(true);
4254 if (attrs != FROZEN && IsSealedElementsKind(old_elements_kind)) {
4255 return Just(true);
4256 }
4257 }
4258
4259 if (object->IsJSGlobalProxy()) {
4260 PrototypeIterator iter(isolate, object);
4261 if (iter.IsAtEnd()) return Just(true);
4262 DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
4263 return PreventExtensionsWithTransition<attrs>(
4264 PrototypeIterator::GetCurrent<JSObject>(iter), should_throw);
4265 }
4266
4267 if (object->map().has_named_interceptor() ||
4268 object->map().has_indexed_interceptor()) {
4269 MessageTemplate message = MessageTemplate::kNone;
4270 switch (attrs) {
4271 case NONE:
4272 message = MessageTemplate::kCannotPreventExt;
4273 break;
4274
4275 case SEALED:
4276 message = MessageTemplate::kCannotSeal;
4277 break;
4278
4279 case FROZEN:
4280 message = MessageTemplate::kCannotFreeze;
4281 break;
4282 }
4283 RETURN_FAILURE(isolate, should_throw, NewTypeError(message));
4284 }
4285
4286 Handle<Symbol> transition_marker;
4287 if (attrs == NONE) {
4288 transition_marker = isolate->factory()->nonextensible_symbol();
4289 } else if (attrs == SEALED) {
4290 transition_marker = isolate->factory()->sealed_symbol();
4291 } else {
4292 DCHECK(attrs == FROZEN);
4293 transition_marker = isolate->factory()->frozen_symbol();
4294 }
4295
4296 // Currently, there are only have sealed/frozen Object element kinds and
4297 // Map::MigrateToMap doesn't handle properties' attributes reconfiguring and
4298 // elements kind change in one go. If seal or freeze with Smi or Double
4299 // elements kind, we will transition to Object elements kind first to make
4300 // sure of valid element access.
4301 if (FLAG_enable_sealed_frozen_elements_kind) {
4302 switch (object->map().elements_kind()) {
4303 case PACKED_SMI_ELEMENTS:
4304 case PACKED_DOUBLE_ELEMENTS:
4305 JSObject::TransitionElementsKind(object, PACKED_ELEMENTS);
4306 break;
4307 case HOLEY_SMI_ELEMENTS:
4308 case HOLEY_DOUBLE_ELEMENTS:
4309 JSObject::TransitionElementsKind(object, HOLEY_ELEMENTS);
4310 break;
4311 default:
4312 break;
4313 }
4314 }
4315
4316 // Make sure we only use this element dictionary in case we can't transition
4317 // to sealed, frozen elements kind.
4318 Handle<NumberDictionary> new_element_dictionary;
4319
4320 Handle<Map> old_map(object->map(), isolate);
4321 old_map = Map::Update(isolate, old_map);
4322 Handle<Map> transition_map;
4323 MaybeHandle<Map> maybe_transition_map =
4324 TransitionsAccessor::SearchSpecial(isolate, old_map, *transition_marker);
4325 if (maybe_transition_map.ToHandle(&transition_map)) {
4326 DCHECK(transition_map->has_dictionary_elements() ||
4327 transition_map->has_typed_array_or_rab_gsab_typed_array_elements() ||
4328 transition_map->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS ||
4329 transition_map->has_any_nonextensible_elements());
4330 DCHECK(!transition_map->is_extensible());
4331 if (!transition_map->has_any_nonextensible_elements()) {
4332 new_element_dictionary = CreateElementDictionary(isolate, object);
4333 }
4334 JSObject::MigrateToMap(isolate, object, transition_map);
4335 } else if (TransitionsAccessor::CanHaveMoreTransitions(isolate, old_map)) {
4336 // Create a new descriptor array with the appropriate property attributes
4337 Handle<Map> new_map = Map::CopyForPreventExtensions(
4338 isolate, old_map, attrs, transition_marker, "CopyForPreventExtensions");
4339 if (!new_map->has_any_nonextensible_elements()) {
4340 new_element_dictionary = CreateElementDictionary(isolate, object);
4341 }
4342 JSObject::MigrateToMap(isolate, object, new_map);
4343 } else {
4344 DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map());
4345 // Slow path: need to normalize properties for safety
4346 NormalizeProperties(isolate, object, CLEAR_INOBJECT_PROPERTIES, 0,
4347 "SlowPreventExtensions");
4348
4349 // Create a new map, since other objects with this map may be extensible.
4350 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
4351 Handle<Map> new_map = Map::Copy(isolate, handle(object->map(), isolate),
4352 "SlowCopyForPreventExtensions");
4353 new_map->set_is_extensible(false);
4354 new_element_dictionary = CreateElementDictionary(isolate, object);
4355 if (!new_element_dictionary.is_null()) {
4356 ElementsKind new_kind =
4357 IsStringWrapperElementsKind(old_map->elements_kind())
4358 ? SLOW_STRING_WRAPPER_ELEMENTS
4359 : DICTIONARY_ELEMENTS;
4360 new_map->set_elements_kind(new_kind);
4361 }
4362 JSObject::MigrateToMap(isolate, object, new_map);
4363
4364 if (attrs != NONE) {
4365 ReadOnlyRoots roots(isolate);
4366 if (object->IsJSGlobalObject()) {
4367 Handle<GlobalDictionary> dictionary(
4368 JSGlobalObject::cast(*object).global_dictionary(kAcquireLoad),
4369 isolate);
4370 JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary,
4371 attrs);
4372 } else if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
4373 Handle<SwissNameDictionary> dictionary(
4374 object->property_dictionary_swiss(), isolate);
4375 JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary,
4376 attrs);
4377 } else {
4378 Handle<NameDictionary> dictionary(object->property_dictionary(),
4379 isolate);
4380 JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary,
4381 attrs);
4382 }
4383 }
4384 }
4385
4386 if (object->map().has_any_nonextensible_elements()) {
4387 DCHECK(new_element_dictionary.is_null());
4388 return Just(true);
4389 }
4390
4391 // Both seal and preventExtensions always go through without modifications to
4392 // typed array elements. Freeze works only if there are no actual elements.
4393 if (object->HasTypedArrayOrRabGsabTypedArrayElements()) {
4394 DCHECK(new_element_dictionary.is_null());
4395 if (attrs == FROZEN && JSTypedArray::cast(*object).GetLength() > 0) {
4396 isolate->Throw(*isolate->factory()->NewTypeError(
4397 MessageTemplate::kCannotFreezeArrayBufferView));
4398 return Nothing<bool>();
4399 }
4400 return Just(true);
4401 }
4402
4403 DCHECK(object->map().has_dictionary_elements() ||
4404 object->map().elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS);
4405 if (!new_element_dictionary.is_null()) {
4406 object->set_elements(*new_element_dictionary);
4407 }
4408
4409 if (object->elements() !=
4410 ReadOnlyRoots(isolate).empty_slow_element_dictionary()) {
4411 Handle<NumberDictionary> dictionary(object->element_dictionary(), isolate);
4412 // Make sure we never go back to the fast case
4413 object->RequireSlowElements(*dictionary);
4414 if (attrs != NONE) {
4415 JSObject::ApplyAttributesToDictionary(isolate, ReadOnlyRoots(isolate),
4416 dictionary, attrs);
4417 }
4418 }
4419
4420 return Just(true);
4421 }
4422
FastPropertyAt(Isolate * isolate,Handle<JSObject> object,Representation representation,FieldIndex index)4423 Handle<Object> JSObject::FastPropertyAt(Isolate* isolate,
4424 Handle<JSObject> object,
4425 Representation representation,
4426 FieldIndex index) {
4427 Handle<Object> raw_value(object->RawFastPropertyAt(index), isolate);
4428 return Object::WrapForRead(isolate, raw_value, representation);
4429 }
4430
FastPropertyAt(Isolate * isolate,Handle<JSObject> object,Representation representation,FieldIndex index,SeqCstAccessTag tag)4431 Handle<Object> JSObject::FastPropertyAt(Isolate* isolate,
4432 Handle<JSObject> object,
4433 Representation representation,
4434 FieldIndex index, SeqCstAccessTag tag) {
4435 Handle<Object> raw_value(object->RawFastPropertyAt(index, tag), isolate);
4436 return Object::WrapForRead(isolate, raw_value, representation);
4437 }
4438
4439 // static
DictionaryPropertyAt(Isolate * isolate,Handle<JSObject> object,InternalIndex dict_index)4440 Handle<Object> JSObject::DictionaryPropertyAt(Isolate* isolate,
4441 Handle<JSObject> object,
4442 InternalIndex dict_index) {
4443 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
4444 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
4445 SwissNameDictionary dict = object->property_dictionary_swiss();
4446 return handle(dict.ValueAt(dict_index), isolate);
4447 } else {
4448 NameDictionary dict = object->property_dictionary();
4449 return handle(dict.ValueAt(dict_index), isolate);
4450 }
4451 }
4452
4453 // static
DictionaryPropertyAt(Handle<JSObject> object,InternalIndex dict_index,Heap * heap)4454 base::Optional<Object> JSObject::DictionaryPropertyAt(Handle<JSObject> object,
4455 InternalIndex dict_index,
4456 Heap* heap) {
4457 Object backing_store = object->raw_properties_or_hash(kRelaxedLoad);
4458 if (!backing_store.IsHeapObject()) return {};
4459 if (heap->IsPendingAllocation(HeapObject::cast(backing_store))) return {};
4460
4461 base::Optional<Object> maybe_obj;
4462 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
4463 if (!backing_store.IsSwissNameDictionary()) return {};
4464 maybe_obj = SwissNameDictionary::cast(backing_store).TryValueAt(dict_index);
4465 } else {
4466 if (!backing_store.IsNameDictionary()) return {};
4467 maybe_obj = NameDictionary::cast(backing_store).TryValueAt(dict_index);
4468 }
4469
4470 if (!maybe_obj) return {};
4471 return maybe_obj.value();
4472 }
4473
4474 // TODO(cbruni/jkummerow): Consider moving this into elements.cc.
HasEnumerableElements()4475 bool JSObject::HasEnumerableElements() {
4476 // TODO(cbruni): cleanup
4477 JSObject object = *this;
4478 switch (object.GetElementsKind()) {
4479 case PACKED_SMI_ELEMENTS:
4480 case PACKED_ELEMENTS:
4481 case PACKED_FROZEN_ELEMENTS:
4482 case PACKED_SEALED_ELEMENTS:
4483 case PACKED_NONEXTENSIBLE_ELEMENTS:
4484 case PACKED_DOUBLE_ELEMENTS: {
4485 int length = object.IsJSArray()
4486 ? Smi::ToInt(JSArray::cast(object).length())
4487 : object.elements().length();
4488 return length > 0;
4489 }
4490 case HOLEY_SMI_ELEMENTS:
4491 case HOLEY_FROZEN_ELEMENTS:
4492 case HOLEY_SEALED_ELEMENTS:
4493 case HOLEY_NONEXTENSIBLE_ELEMENTS:
4494 case HOLEY_ELEMENTS: {
4495 FixedArray elements = FixedArray::cast(object.elements());
4496 int length = object.IsJSArray()
4497 ? Smi::ToInt(JSArray::cast(object).length())
4498 : elements.length();
4499 Isolate* isolate = GetIsolate();
4500 for (int i = 0; i < length; i++) {
4501 if (!elements.is_the_hole(isolate, i)) return true;
4502 }
4503 return false;
4504 }
4505 case HOLEY_DOUBLE_ELEMENTS: {
4506 int length = object.IsJSArray()
4507 ? Smi::ToInt(JSArray::cast(object).length())
4508 : object.elements().length();
4509 // Zero-length arrays would use the empty FixedArray...
4510 if (length == 0) return false;
4511 // ...so only cast to FixedDoubleArray otherwise.
4512 FixedDoubleArray elements = FixedDoubleArray::cast(object.elements());
4513 for (int i = 0; i < length; i++) {
4514 if (!elements.is_the_hole(i)) return true;
4515 }
4516 return false;
4517 }
4518 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
4519
4520 TYPED_ARRAYS(TYPED_ARRAY_CASE) {
4521 size_t length = JSTypedArray::cast(object).length();
4522 return length > 0;
4523 }
4524
4525 RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
4526 #undef TYPED_ARRAY_CASE
4527 {
4528 size_t length = JSTypedArray::cast(object).GetLength();
4529 return length > 0;
4530 }
4531 case DICTIONARY_ELEMENTS: {
4532 NumberDictionary elements = NumberDictionary::cast(object.elements());
4533 return elements.NumberOfEnumerableProperties() > 0;
4534 }
4535 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
4536 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
4537 // We're approximating non-empty arguments objects here.
4538 return true;
4539 case FAST_STRING_WRAPPER_ELEMENTS:
4540 case SLOW_STRING_WRAPPER_ELEMENTS:
4541 if (String::cast(JSPrimitiveWrapper::cast(object).value()).length() > 0) {
4542 return true;
4543 }
4544 return object.elements().length() > 0;
4545 case WASM_ARRAY_ELEMENTS:
4546 UNIMPLEMENTED();
4547
4548 case NO_ELEMENTS:
4549 return false;
4550 }
4551 UNREACHABLE();
4552 }
4553
DefineAccessor(Handle<JSObject> object,Handle<Name> name,Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)4554 MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object,
4555 Handle<Name> name,
4556 Handle<Object> getter,
4557 Handle<Object> setter,
4558 PropertyAttributes attributes) {
4559 Isolate* isolate = object->GetIsolate();
4560
4561 PropertyKey key(isolate, name);
4562 LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
4563 return DefineAccessor(&it, getter, setter, attributes);
4564 }
4565
DefineAccessor(LookupIterator * it,Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)4566 MaybeHandle<Object> JSObject::DefineAccessor(LookupIterator* it,
4567 Handle<Object> getter,
4568 Handle<Object> setter,
4569 PropertyAttributes attributes) {
4570 Isolate* isolate = it->isolate();
4571
4572 it->UpdateProtector();
4573
4574 if (it->state() == LookupIterator::ACCESS_CHECK) {
4575 if (!it->HasAccess()) {
4576 isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>());
4577 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
4578 return isolate->factory()->undefined_value();
4579 }
4580 it->Next();
4581 }
4582
4583 Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver());
4584 // Ignore accessors on typed arrays.
4585 if (it->IsElement() && object->HasTypedArrayOrRabGsabTypedArrayElements()) {
4586 return it->factory()->undefined_value();
4587 }
4588
4589 DCHECK(getter->IsCallable() || getter->IsUndefined(isolate) ||
4590 getter->IsNull(isolate) || getter->IsFunctionTemplateInfo());
4591 DCHECK(setter->IsCallable() || setter->IsUndefined(isolate) ||
4592 setter->IsNull(isolate) || setter->IsFunctionTemplateInfo());
4593 it->TransitionToAccessorProperty(getter, setter, attributes);
4594
4595 return isolate->factory()->undefined_value();
4596 }
4597
SetAccessor(Handle<JSObject> object,Handle<Name> name,Handle<AccessorInfo> info,PropertyAttributes attributes)4598 MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object,
4599 Handle<Name> name,
4600 Handle<AccessorInfo> info,
4601 PropertyAttributes attributes) {
4602 Isolate* isolate = object->GetIsolate();
4603
4604 PropertyKey key(isolate, name);
4605 LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
4606
4607 // Duplicate ACCESS_CHECK outside of GetPropertyAttributes for the case that
4608 // the FailedAccessCheckCallbackFunction doesn't throw an exception.
4609 //
4610 // TODO(verwaest): Force throw an exception if the callback doesn't, so we can
4611 // remove reliance on default return values.
4612 if (it.state() == LookupIterator::ACCESS_CHECK) {
4613 if (!it.HasAccess()) {
4614 isolate->ReportFailedAccessCheck(object);
4615 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
4616 return it.factory()->undefined_value();
4617 }
4618 it.Next();
4619 }
4620
4621 // Ignore accessors on typed arrays.
4622 if (it.IsElement() && object->HasTypedArrayOrRabGsabTypedArrayElements()) {
4623 return it.factory()->undefined_value();
4624 }
4625
4626 CHECK(GetPropertyAttributes(&it).IsJust());
4627
4628 // ES5 forbids turning a property into an accessor if it's not
4629 // configurable. See 8.6.1 (Table 5).
4630 if (it.IsFound() && !it.IsConfigurable()) {
4631 return it.factory()->undefined_value();
4632 }
4633
4634 it.TransitionToAccessorPair(info, attributes);
4635
4636 return object;
4637 }
4638
SlowReverseLookup(Object value)4639 Object JSObject::SlowReverseLookup(Object value) {
4640 if (HasFastProperties()) {
4641 DescriptorArray descs = map().instance_descriptors();
4642 bool value_is_number = value.IsNumber();
4643 for (InternalIndex i : map().IterateOwnDescriptors()) {
4644 PropertyDetails details = descs.GetDetails(i);
4645 if (details.location() == PropertyLocation::kField) {
4646 DCHECK_EQ(PropertyKind::kData, details.kind());
4647 FieldIndex field_index = FieldIndex::ForDescriptor(map(), i);
4648 Object property = RawFastPropertyAt(field_index);
4649 if (field_index.is_double()) {
4650 DCHECK(property.IsHeapNumber());
4651 if (value_is_number && property.Number() == value.Number()) {
4652 return descs.GetKey(i);
4653 }
4654 } else if (property == value) {
4655 return descs.GetKey(i);
4656 }
4657 } else {
4658 DCHECK_EQ(PropertyLocation::kDescriptor, details.location());
4659 if (details.kind() == PropertyKind::kData) {
4660 if (descs.GetStrongValue(i) == value) {
4661 return descs.GetKey(i);
4662 }
4663 }
4664 }
4665 }
4666 return GetReadOnlyRoots().undefined_value();
4667 } else if (IsJSGlobalObject()) {
4668 return JSGlobalObject::cast(*this)
4669 .global_dictionary(kAcquireLoad)
4670 .SlowReverseLookup(value);
4671 } else if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
4672 return property_dictionary_swiss().SlowReverseLookup(GetIsolate(), value);
4673 } else {
4674 return property_dictionary().SlowReverseLookup(value);
4675 }
4676 }
4677
PrototypeRegistryCompactionCallback(HeapObject value,int old_index,int new_index)4678 void JSObject::PrototypeRegistryCompactionCallback(HeapObject value,
4679 int old_index,
4680 int new_index) {
4681 DCHECK(value.IsMap() && Map::cast(value).is_prototype_map());
4682 Map map = Map::cast(value);
4683 DCHECK(map.prototype_info().IsPrototypeInfo());
4684 PrototypeInfo proto_info = PrototypeInfo::cast(map.prototype_info());
4685 DCHECK_EQ(old_index, proto_info.registry_slot());
4686 proto_info.set_registry_slot(new_index);
4687 }
4688
4689 // static
MakePrototypesFast(Handle<Object> receiver,WhereToStart where_to_start,Isolate * isolate)4690 void JSObject::MakePrototypesFast(Handle<Object> receiver,
4691 WhereToStart where_to_start,
4692 Isolate* isolate) {
4693 if (!receiver->IsJSReceiver()) return;
4694 for (PrototypeIterator iter(isolate, Handle<JSReceiver>::cast(receiver),
4695 where_to_start);
4696 !iter.IsAtEnd(); iter.Advance()) {
4697 Handle<Object> current = PrototypeIterator::GetCurrent(iter);
4698 if (!current->IsJSObject()) return;
4699 Handle<JSObject> current_obj = Handle<JSObject>::cast(current);
4700 Map current_map = current_obj->map();
4701 if (current_map.is_prototype_map()) {
4702 // If the map is already marked as should be fast, we're done. Its
4703 // prototypes will have been marked already as well.
4704 if (current_map.should_be_fast_prototype_map()) return;
4705 Handle<Map> map(current_map, isolate);
4706 Map::SetShouldBeFastPrototypeMap(map, true, isolate);
4707 JSObject::OptimizeAsPrototype(current_obj);
4708 }
4709 }
4710 }
4711
PrototypeBenefitsFromNormalization(Handle<JSObject> object)4712 static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) {
4713 DisallowGarbageCollection no_gc;
4714 if (!object->HasFastProperties()) return false;
4715 if (object->IsJSGlobalProxy()) return false;
4716 // TODO(v8:11248) make bootstrapper create dict mode prototypes, too?
4717 if (object->GetIsolate()->bootstrapper()->IsActive()) return false;
4718 if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) return true;
4719 return !object->map().is_prototype_map() ||
4720 !object->map().should_be_fast_prototype_map();
4721 }
4722
4723 // static
OptimizeAsPrototype(Handle<JSObject> object,bool enable_setup_mode)4724 void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
4725 bool enable_setup_mode) {
4726 Isolate* isolate = object->GetIsolate();
4727 if (object->IsJSGlobalObject()) return;
4728 if (enable_setup_mode && PrototypeBenefitsFromNormalization(object)) {
4729 // First normalize to ensure all JSFunctions are DATA_CONSTANT.
4730 JSObject::NormalizeProperties(isolate, object, KEEP_INOBJECT_PROPERTIES, 0,
4731 "NormalizeAsPrototype");
4732 }
4733 if (object->map().is_prototype_map()) {
4734 if (!V8_DICT_PROPERTY_CONST_TRACKING_BOOL &&
4735 object->map().should_be_fast_prototype_map() &&
4736 !object->HasFastProperties()) {
4737 JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
4738 }
4739 } else {
4740 Handle<Map> new_map =
4741 Map::Copy(isolate, handle(object->map(), isolate), "CopyAsPrototype");
4742 new_map->set_is_prototype_map(true);
4743
4744 // Replace the pointer to the exact constructor with the Object function
4745 // from the same context if undetectable from JS. This is to avoid keeping
4746 // memory alive unnecessarily.
4747 Object maybe_constructor = new_map->GetConstructor();
4748 if (maybe_constructor.IsJSFunction()) {
4749 JSFunction constructor = JSFunction::cast(maybe_constructor);
4750 if (!constructor.shared().IsApiFunction()) {
4751 Context context = constructor.native_context();
4752 JSFunction object_function = context.object_function();
4753 new_map->SetConstructor(object_function);
4754 }
4755 }
4756 JSObject::MigrateToMap(isolate, object, new_map);
4757
4758 if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && !object->HasFastProperties()) {
4759 ReadOnlyRoots roots(isolate);
4760 DisallowHeapAllocation no_gc;
4761
4762 auto make_constant = [&](auto dict) {
4763 for (InternalIndex index : dict.IterateEntries()) {
4764 Object k;
4765 if (!dict.ToKey(roots, index, &k)) continue;
4766
4767 PropertyDetails details = dict.DetailsAt(index);
4768 details = details.CopyWithConstness(PropertyConstness::kConst);
4769 dict.DetailsAtPut(index, details);
4770 }
4771 };
4772 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
4773 make_constant(object->property_dictionary_swiss());
4774 } else {
4775 make_constant(object->property_dictionary());
4776 }
4777 }
4778 }
4779 #ifdef DEBUG
4780 bool should_be_dictionary = V8_DICT_PROPERTY_CONST_TRACKING_BOOL &&
4781 enable_setup_mode && !object->IsJSGlobalProxy() &&
4782 !object->GetIsolate()->bootstrapper()->IsActive();
4783 DCHECK_IMPLIES(should_be_dictionary, object->map().is_dictionary_map());
4784 #endif
4785 }
4786
4787 // static
ReoptimizeIfPrototype(Handle<JSObject> object)4788 void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
4789 if (!object->map().is_prototype_map()) return;
4790 if (!object->map().should_be_fast_prototype_map()) return;
4791 OptimizeAsPrototype(object);
4792 }
4793
4794 // static
LazyRegisterPrototypeUser(Handle<Map> user,Isolate * isolate)4795 void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
4796 // Contract: In line with InvalidatePrototypeChains()'s requirements,
4797 // leaf maps don't need to register as users, only prototypes do.
4798 DCHECK(user->is_prototype_map());
4799
4800 Handle<Map> current_user = user;
4801 Handle<PrototypeInfo> current_user_info =
4802 Map::GetOrCreatePrototypeInfo(user, isolate);
4803 for (PrototypeIterator iter(isolate, user); !iter.IsAtEnd(); iter.Advance()) {
4804 // Walk up the prototype chain as far as links haven't been registered yet.
4805 if (current_user_info->registry_slot() != PrototypeInfo::UNREGISTERED) {
4806 break;
4807 }
4808 Handle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter);
4809 // Proxies on the prototype chain are not supported. They make it
4810 // impossible to make any assumptions about the prototype chain anyway.
4811 if (maybe_proto->IsJSProxy()) return;
4812 Handle<JSObject> proto = Handle<JSObject>::cast(maybe_proto);
4813 Handle<PrototypeInfo> proto_info =
4814 Map::GetOrCreatePrototypeInfo(proto, isolate);
4815 Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
4816 Handle<WeakArrayList> registry =
4817 maybe_registry->IsSmi()
4818 ? handle(ReadOnlyRoots(isolate->heap()).empty_weak_array_list(),
4819 isolate)
4820 : Handle<WeakArrayList>::cast(maybe_registry);
4821 int slot = 0;
4822 Handle<WeakArrayList> new_array =
4823 PrototypeUsers::Add(isolate, registry, current_user, &slot);
4824 current_user_info->set_registry_slot(slot);
4825 if (!maybe_registry.is_identical_to(new_array)) {
4826 proto_info->set_prototype_users(*new_array);
4827 }
4828 if (FLAG_trace_prototype_users) {
4829 PrintF("Registering %p as a user of prototype %p (map=%p).\n",
4830 reinterpret_cast<void*>(current_user->ptr()),
4831 reinterpret_cast<void*>(proto->ptr()),
4832 reinterpret_cast<void*>(proto->map().ptr()));
4833 }
4834
4835 current_user = handle(proto->map(), isolate);
4836 current_user_info = proto_info;
4837 }
4838 }
4839
4840 // Can be called regardless of whether |user| was actually registered with
4841 // |prototype|. Returns true when there was a registration.
4842 // static
UnregisterPrototypeUser(Handle<Map> user,Isolate * isolate)4843 bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
4844 DCHECK(user->is_prototype_map());
4845 // If it doesn't have a PrototypeInfo, it was never registered.
4846 if (!user->prototype_info().IsPrototypeInfo()) return false;
4847 // If it had no prototype before, see if it had users that might expect
4848 // registration.
4849 if (!user->prototype().IsJSObject()) {
4850 Object users =
4851 PrototypeInfo::cast(user->prototype_info()).prototype_users();
4852 return users.IsWeakArrayList();
4853 }
4854 Handle<JSObject> prototype(JSObject::cast(user->prototype()), isolate);
4855 Handle<PrototypeInfo> user_info =
4856 Map::GetOrCreatePrototypeInfo(user, isolate);
4857 int slot = user_info->registry_slot();
4858 if (slot == PrototypeInfo::UNREGISTERED) return false;
4859 DCHECK(prototype->map().is_prototype_map());
4860 Object maybe_proto_info = prototype->map().prototype_info();
4861 // User knows its registry slot, prototype info and user registry must exist.
4862 DCHECK(maybe_proto_info.IsPrototypeInfo());
4863 Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(maybe_proto_info),
4864 isolate);
4865 Handle<WeakArrayList> prototype_users(
4866 WeakArrayList::cast(proto_info->prototype_users()), isolate);
4867 DCHECK_EQ(prototype_users->Get(slot), HeapObjectReference::Weak(*user));
4868 PrototypeUsers::MarkSlotEmpty(*prototype_users, slot);
4869 if (FLAG_trace_prototype_users) {
4870 PrintF("Unregistering %p as a user of prototype %p.\n",
4871 reinterpret_cast<void*>(user->ptr()),
4872 reinterpret_cast<void*>(prototype->ptr()));
4873 }
4874 return true;
4875 }
4876
4877 namespace {
4878
4879 // This function must be kept in sync with
4880 // AccessorAssembler::InvalidateValidityCellIfPrototype() which does pre-checks
4881 // before jumping here.
InvalidateOnePrototypeValidityCellInternal(Map map)4882 void InvalidateOnePrototypeValidityCellInternal(Map map) {
4883 DCHECK(map.is_prototype_map());
4884 if (FLAG_trace_prototype_users) {
4885 PrintF("Invalidating prototype map %p 's cell\n",
4886 reinterpret_cast<void*>(map.ptr()));
4887 }
4888 Object maybe_cell = map.prototype_validity_cell();
4889 if (maybe_cell.IsCell()) {
4890 // Just set the value; the cell will be replaced lazily.
4891 Cell cell = Cell::cast(maybe_cell);
4892 cell.set_value(Smi::FromInt(Map::kPrototypeChainInvalid));
4893 }
4894 Object maybe_prototype_info = map.prototype_info();
4895 if (maybe_prototype_info.IsPrototypeInfo()) {
4896 PrototypeInfo prototype_info = PrototypeInfo::cast(maybe_prototype_info);
4897 prototype_info.set_prototype_chain_enum_cache(Object());
4898 }
4899
4900 // We may inline accesses to constants stored in dictionary mode protoypes in
4901 // optimized code. When doing so, we install depenendies of group
4902 // |kPrototypeCheckGroup| on each prototype between the receiver's immediate
4903 // prototype and the holder of the constant property. This dependency is used
4904 // both to detect changes to the constant value itself, and other changes to
4905 // the prototype chain that invalidate the access to the given property from
4906 // the given receiver (like adding the property to another prototype between
4907 // the receiver and the (previous) holder). This works by de-opting this group
4908 // whenever the validity cell would be invalidated. However, the actual value
4909 // of the validity cell is not used. Therefore, we always trigger the de-opt
4910 // here, even if the cell was already invalid.
4911 if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && map.is_dictionary_map()) {
4912 // TODO(11527): pass Isolate as an argument.
4913 Isolate* isolate = GetIsolateFromWritableObject(map);
4914 map.dependent_code().DeoptimizeDependentCodeGroup(
4915 isolate, DependentCode::kPrototypeCheckGroup);
4916 }
4917 }
4918
InvalidatePrototypeChainsInternal(Map map)4919 void InvalidatePrototypeChainsInternal(Map map) {
4920 // We handle linear prototype chains by looping, and multiple children
4921 // by recursion, in order to reduce the likelihood of running into stack
4922 // overflows. So, conceptually, the outer loop iterates the depth of the
4923 // prototype tree, and the inner loop iterates the breadth of a node.
4924 Map next_map;
4925 for (; !map.is_null(); map = next_map, next_map = Map()) {
4926 InvalidateOnePrototypeValidityCellInternal(map);
4927
4928 Object maybe_proto_info = map.prototype_info();
4929 if (!maybe_proto_info.IsPrototypeInfo()) return;
4930 PrototypeInfo proto_info = PrototypeInfo::cast(maybe_proto_info);
4931 if (!proto_info.prototype_users().IsWeakArrayList()) {
4932 return;
4933 }
4934 WeakArrayList prototype_users =
4935 WeakArrayList::cast(proto_info.prototype_users());
4936 // For now, only maps register themselves as users.
4937 for (int i = PrototypeUsers::kFirstIndex; i < prototype_users.length();
4938 ++i) {
4939 HeapObject heap_object;
4940 if (prototype_users.Get(i)->GetHeapObjectIfWeak(&heap_object) &&
4941 heap_object.IsMap()) {
4942 // Walk the prototype chain (backwards, towards leaf objects) if
4943 // necessary.
4944 if (next_map.is_null()) {
4945 next_map = Map::cast(heap_object);
4946 } else {
4947 InvalidatePrototypeChainsInternal(Map::cast(heap_object));
4948 }
4949 }
4950 }
4951 }
4952 }
4953
4954 } // namespace
4955
4956 // static
InvalidatePrototypeChains(Map map)4957 Map JSObject::InvalidatePrototypeChains(Map map) {
4958 DisallowGarbageCollection no_gc;
4959 InvalidatePrototypeChainsInternal(map);
4960 return map;
4961 }
4962
4963 // We also invalidate global objects validity cell when a new lexical
4964 // environment variable is added. This is necessary to ensure that
4965 // Load/StoreGlobalIC handlers that load/store from global object's prototype
4966 // get properly invalidated.
4967 // Note, that the normal Load/StoreICs that load/store through the global object
4968 // in the prototype chain are not affected by appearance of a new lexical
4969 // variable and therefore we don't propagate invalidation down.
4970 // static
InvalidatePrototypeValidityCell(JSGlobalObject global)4971 void JSObject::InvalidatePrototypeValidityCell(JSGlobalObject global) {
4972 DisallowGarbageCollection no_gc;
4973 InvalidateOnePrototypeValidityCellInternal(global.map());
4974 }
4975
SetPrototype(Isolate * isolate,Handle<JSObject> object,Handle<Object> value,bool from_javascript,ShouldThrow should_throw)4976 Maybe<bool> JSObject::SetPrototype(Isolate* isolate, Handle<JSObject> object,
4977 Handle<Object> value, bool from_javascript,
4978 ShouldThrow should_throw) {
4979 #ifdef DEBUG
4980 int size = object->Size();
4981 #endif
4982
4983 if (from_javascript) {
4984 if (object->IsAccessCheckNeeded() &&
4985 !isolate->MayAccess(handle(isolate->context(), isolate), object)) {
4986 isolate->ReportFailedAccessCheck(object);
4987 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
4988 RETURN_FAILURE(isolate, should_throw,
4989 NewTypeError(MessageTemplate::kNoAccess));
4990 }
4991 } else {
4992 DCHECK(!object->IsAccessCheckNeeded());
4993 }
4994
4995 // Silently ignore the change if value is not a JSObject or null.
4996 // SpiderMonkey behaves this way.
4997 if (!value->IsJSReceiver() && !value->IsNull(isolate)) return Just(true);
4998
4999 bool all_extensible = object->map().is_extensible();
5000 Handle<JSObject> real_receiver = object;
5001 if (from_javascript) {
5002 // Find the first object in the chain whose prototype object is not
5003 // hidden.
5004 PrototypeIterator iter(isolate, real_receiver, kStartAtPrototype,
5005 PrototypeIterator::END_AT_NON_HIDDEN);
5006 while (!iter.IsAtEnd()) {
5007 // Casting to JSObject is fine because hidden prototypes are never
5008 // JSProxies.
5009 real_receiver = PrototypeIterator::GetCurrent<JSObject>(iter);
5010 iter.Advance();
5011 all_extensible = all_extensible && real_receiver->map().is_extensible();
5012 }
5013 }
5014 Handle<Map> map(real_receiver->map(), isolate);
5015
5016 // Nothing to do if prototype is already set.
5017 if (map->prototype() == *value) return Just(true);
5018
5019 bool immutable_proto = map->is_immutable_proto();
5020 if (immutable_proto) {
5021 RETURN_FAILURE(
5022 isolate, should_throw,
5023 NewTypeError(MessageTemplate::kImmutablePrototypeSet, object));
5024 }
5025
5026 // From 6.1.7.3 Invariants of the Essential Internal Methods
5027 //
5028 // [[SetPrototypeOf]] ( V )
5029 // * ...
5030 // * If target is non-extensible, [[SetPrototypeOf]] must return false,
5031 // unless V is the SameValue as the target's observed [[GetPrototypeOf]]
5032 // value.
5033 if (!all_extensible) {
5034 RETURN_FAILURE(isolate, should_throw,
5035 NewTypeError(MessageTemplate::kNonExtensibleProto, object));
5036 }
5037
5038 // Before we can set the prototype we need to be sure prototype cycles are
5039 // prevented. It is sufficient to validate that the receiver is not in the
5040 // new prototype chain.
5041 if (value->IsJSReceiver()) {
5042 for (PrototypeIterator iter(isolate, JSReceiver::cast(*value),
5043 kStartAtReceiver);
5044 !iter.IsAtEnd(); iter.Advance()) {
5045 if (iter.GetCurrent<JSReceiver>() == *object) {
5046 // Cycle detected.
5047 RETURN_FAILURE(isolate, should_throw,
5048 NewTypeError(MessageTemplate::kCyclicProto));
5049 }
5050 }
5051 }
5052
5053 // Set the new prototype of the object.
5054
5055 isolate->UpdateNoElementsProtectorOnSetPrototype(real_receiver);
5056
5057 Handle<Map> new_map =
5058 Map::TransitionToPrototype(isolate, map, Handle<HeapObject>::cast(value));
5059 DCHECK(new_map->prototype() == *value);
5060 JSObject::MigrateToMap(isolate, real_receiver, new_map);
5061
5062 DCHECK(size == object->Size());
5063 return Just(true);
5064 }
5065
5066 // static
SetImmutableProto(Handle<JSObject> object)5067 void JSObject::SetImmutableProto(Handle<JSObject> object) {
5068 Handle<Map> map(object->map(), object->GetIsolate());
5069
5070 // Nothing to do if prototype is already set.
5071 if (map->is_immutable_proto()) return;
5072
5073 Handle<Map> new_map =
5074 Map::TransitionToImmutableProto(object->GetIsolate(), map);
5075 object->set_map(*new_map, kReleaseStore);
5076 }
5077
EnsureCanContainElements(Handle<JSObject> object,JavaScriptArguments * args,uint32_t arg_count,EnsureElementsMode mode)5078 void JSObject::EnsureCanContainElements(Handle<JSObject> object,
5079 JavaScriptArguments* args,
5080 uint32_t arg_count,
5081 EnsureElementsMode mode) {
5082 return EnsureCanContainElements(object, args->first_slot(), arg_count, mode);
5083 }
5084
ValidateElements(JSObject object)5085 void JSObject::ValidateElements(JSObject object) {
5086 #ifdef ENABLE_SLOW_DCHECKS
5087 if (FLAG_enable_slow_asserts) {
5088 object.GetElementsAccessor()->Validate(object);
5089 }
5090 #endif
5091 }
5092
WouldConvertToSlowElements(uint32_t index)5093 bool JSObject::WouldConvertToSlowElements(uint32_t index) {
5094 if (!HasFastElements()) return false;
5095 uint32_t capacity = static_cast<uint32_t>(elements().length());
5096 uint32_t new_capacity;
5097 return ShouldConvertToSlowElements(*this, capacity, index, &new_capacity);
5098 }
5099
ShouldConvertToFastElements(JSObject object,NumberDictionary dictionary,uint32_t index,uint32_t * new_capacity)5100 static bool ShouldConvertToFastElements(JSObject object,
5101 NumberDictionary dictionary,
5102 uint32_t index,
5103 uint32_t* new_capacity) {
5104 // If properties with non-standard attributes or accessors were added, we
5105 // cannot go back to fast elements.
5106 if (dictionary.requires_slow_elements()) return false;
5107
5108 // Adding a property with this index will require slow elements.
5109 if (index >= static_cast<uint32_t>(Smi::kMaxValue)) return false;
5110
5111 if (object.IsJSArray()) {
5112 Object length = JSArray::cast(object).length();
5113 if (!length.IsSmi()) return false;
5114 *new_capacity = static_cast<uint32_t>(Smi::ToInt(length));
5115 } else if (object.IsJSArgumentsObject()) {
5116 return false;
5117 } else {
5118 *new_capacity = dictionary.max_number_key() + 1;
5119 }
5120 *new_capacity = std::max(index + 1, *new_capacity);
5121
5122 uint32_t dictionary_size = static_cast<uint32_t>(dictionary.Capacity()) *
5123 NumberDictionary::kEntrySize;
5124
5125 // Turn fast if the dictionary only saves 50% space.
5126 return 2 * dictionary_size >= *new_capacity;
5127 }
5128
BestFittingFastElementsKind(JSObject object)5129 static ElementsKind BestFittingFastElementsKind(JSObject object) {
5130 if (!object.map().CanHaveFastTransitionableElementsKind()) {
5131 return HOLEY_ELEMENTS;
5132 }
5133 if (object.HasSloppyArgumentsElements()) {
5134 return FAST_SLOPPY_ARGUMENTS_ELEMENTS;
5135 }
5136 if (object.HasStringWrapperElements()) {
5137 return FAST_STRING_WRAPPER_ELEMENTS;
5138 }
5139 DCHECK(object.HasDictionaryElements());
5140 NumberDictionary dictionary = object.element_dictionary();
5141 ElementsKind kind = HOLEY_SMI_ELEMENTS;
5142 for (InternalIndex i : dictionary.IterateEntries()) {
5143 Object key = dictionary.KeyAt(i);
5144 if (key.IsNumber()) {
5145 Object value = dictionary.ValueAt(i);
5146 if (!value.IsNumber()) return HOLEY_ELEMENTS;
5147 if (!value.IsSmi()) {
5148 if (!FLAG_unbox_double_arrays) return HOLEY_ELEMENTS;
5149 kind = HOLEY_DOUBLE_ELEMENTS;
5150 }
5151 }
5152 }
5153 return kind;
5154 }
5155
5156 // static
AddDataElement(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes)5157 Maybe<bool> JSObject::AddDataElement(Handle<JSObject> object, uint32_t index,
5158 Handle<Object> value,
5159 PropertyAttributes attributes) {
5160 Isolate* isolate = object->GetIsolate();
5161
5162 DCHECK(object->map(isolate).is_extensible());
5163
5164 uint32_t old_length = 0;
5165 uint32_t new_capacity = 0;
5166
5167 if (object->IsJSArray(isolate)) {
5168 CHECK(JSArray::cast(*object).length().ToArrayLength(&old_length));
5169 }
5170
5171 ElementsKind kind = object->GetElementsKind(isolate);
5172 FixedArrayBase elements = object->elements(isolate);
5173 ElementsKind dictionary_kind = DICTIONARY_ELEMENTS;
5174 if (IsSloppyArgumentsElementsKind(kind)) {
5175 elements = SloppyArgumentsElements::cast(elements).arguments(isolate);
5176 dictionary_kind = SLOW_SLOPPY_ARGUMENTS_ELEMENTS;
5177 } else if (IsStringWrapperElementsKind(kind)) {
5178 dictionary_kind = SLOW_STRING_WRAPPER_ELEMENTS;
5179 }
5180
5181 if (attributes != NONE) {
5182 kind = dictionary_kind;
5183 } else if (elements.IsNumberDictionary(isolate)) {
5184 kind = ShouldConvertToFastElements(
5185 *object, NumberDictionary::cast(elements), index, &new_capacity)
5186 ? BestFittingFastElementsKind(*object)
5187 : dictionary_kind;
5188 } else if (ShouldConvertToSlowElements(
5189 *object, static_cast<uint32_t>(elements.length()), index,
5190 &new_capacity)) {
5191 kind = dictionary_kind;
5192 }
5193
5194 ElementsKind to = value->OptimalElementsKind(isolate);
5195 if (IsHoleyElementsKind(kind) || !object->IsJSArray(isolate) ||
5196 index > old_length) {
5197 to = GetHoleyElementsKind(to);
5198 kind = GetHoleyElementsKind(kind);
5199 }
5200 to = GetMoreGeneralElementsKind(kind, to);
5201 ElementsAccessor* accessor = ElementsAccessor::ForKind(to);
5202 MAYBE_RETURN(accessor->Add(object, index, value, attributes, new_capacity),
5203 Nothing<bool>());
5204
5205 if (object->IsJSArray(isolate) && index >= old_length) {
5206 Handle<Object> new_length =
5207 isolate->factory()->NewNumberFromUint(index + 1);
5208 JSArray::cast(*object).set_length(*new_length);
5209 }
5210 return Just(true);
5211 }
5212
5213 template <AllocationSiteUpdateMode update_or_check>
UpdateAllocationSite(Handle<JSObject> object,ElementsKind to_kind)5214 bool JSObject::UpdateAllocationSite(Handle<JSObject> object,
5215 ElementsKind to_kind) {
5216 if (!object->IsJSArray()) return false;
5217
5218 if (!Heap::InYoungGeneration(*object)) return false;
5219
5220 if (Heap::IsLargeObject(*object)) return false;
5221
5222 Handle<AllocationSite> site;
5223 {
5224 DisallowGarbageCollection no_gc;
5225
5226 Heap* heap = object->GetHeap();
5227 AllocationMemento memento =
5228 heap->FindAllocationMemento<Heap::kForRuntime>(object->map(), *object);
5229 if (memento.is_null()) return false;
5230
5231 // Walk through to the Allocation Site
5232 site = handle(memento.GetAllocationSite(), heap->isolate());
5233 }
5234 return AllocationSite::DigestTransitionFeedback<update_or_check>(site,
5235 to_kind);
5236 }
5237
5238 template bool
5239 JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kCheckOnly>(
5240 Handle<JSObject> object, ElementsKind to_kind);
5241
5242 template bool JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kUpdate>(
5243 Handle<JSObject> object, ElementsKind to_kind);
5244
TransitionElementsKind(Handle<JSObject> object,ElementsKind to_kind)5245 void JSObject::TransitionElementsKind(Handle<JSObject> object,
5246 ElementsKind to_kind) {
5247 ElementsKind from_kind = object->GetElementsKind();
5248
5249 if (IsHoleyElementsKind(from_kind)) {
5250 to_kind = GetHoleyElementsKind(to_kind);
5251 }
5252
5253 if (from_kind == to_kind) return;
5254
5255 // This method should never be called for any other case.
5256 DCHECK(IsFastElementsKind(from_kind) ||
5257 IsNonextensibleElementsKind(from_kind));
5258 DCHECK(IsFastElementsKind(to_kind) || IsNonextensibleElementsKind(to_kind));
5259 DCHECK_NE(TERMINAL_FAST_ELEMENTS_KIND, from_kind);
5260
5261 UpdateAllocationSite(object, to_kind);
5262 Isolate* isolate = object->GetIsolate();
5263 if (object->elements() == ReadOnlyRoots(isolate).empty_fixed_array() ||
5264 IsDoubleElementsKind(from_kind) == IsDoubleElementsKind(to_kind)) {
5265 // No change is needed to the elements() buffer, the transition
5266 // only requires a map change.
5267 Handle<Map> new_map = GetElementsTransitionMap(object, to_kind);
5268 JSObject::MigrateToMap(isolate, object, new_map);
5269 if (FLAG_trace_elements_transitions) {
5270 Handle<FixedArrayBase> elms(object->elements(), isolate);
5271 PrintElementsTransition(stdout, object, from_kind, elms, to_kind, elms);
5272 }
5273 } else {
5274 DCHECK((IsSmiElementsKind(from_kind) && IsDoubleElementsKind(to_kind)) ||
5275 (IsDoubleElementsKind(from_kind) && IsObjectElementsKind(to_kind)));
5276 uint32_t c = static_cast<uint32_t>(object->elements().length());
5277 if (ElementsAccessor::ForKind(to_kind)
5278 ->GrowCapacityAndConvert(object, c)
5279 .IsNothing()) {
5280 // TODO(victorgomes): Temporarily forcing a fatal error here in case of
5281 // overflow, until all users of TransitionElementsKind can handle
5282 // exceptions.
5283 FATAL(
5284 "Fatal JavaScript invalid size error when transitioning elements "
5285 "kind");
5286 UNREACHABLE();
5287 }
5288 }
5289 }
5290
5291 template <typename BackingStore>
HoleyElementsUsage(JSObject object,BackingStore store)5292 static int HoleyElementsUsage(JSObject object, BackingStore store) {
5293 Isolate* isolate = object.GetIsolate();
5294 int limit = object.IsJSArray() ? Smi::ToInt(JSArray::cast(object).length())
5295 : store.length();
5296 int used = 0;
5297 for (int i = 0; i < limit; ++i) {
5298 if (!store.is_the_hole(isolate, i)) ++used;
5299 }
5300 return used;
5301 }
5302
GetFastElementsUsage()5303 int JSObject::GetFastElementsUsage() {
5304 FixedArrayBase store = elements();
5305 switch (GetElementsKind()) {
5306 case PACKED_SMI_ELEMENTS:
5307 case PACKED_DOUBLE_ELEMENTS:
5308 case PACKED_ELEMENTS:
5309 case PACKED_FROZEN_ELEMENTS:
5310 case PACKED_SEALED_ELEMENTS:
5311 case PACKED_NONEXTENSIBLE_ELEMENTS:
5312 return IsJSArray() ? Smi::ToInt(JSArray::cast(*this).length())
5313 : store.length();
5314 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
5315 store = SloppyArgumentsElements::cast(store).arguments();
5316 V8_FALLTHROUGH;
5317 case HOLEY_SMI_ELEMENTS:
5318 case HOLEY_ELEMENTS:
5319 case HOLEY_FROZEN_ELEMENTS:
5320 case HOLEY_SEALED_ELEMENTS:
5321 case HOLEY_NONEXTENSIBLE_ELEMENTS:
5322 case FAST_STRING_WRAPPER_ELEMENTS:
5323 return HoleyElementsUsage(*this, FixedArray::cast(store));
5324 case HOLEY_DOUBLE_ELEMENTS:
5325 if (elements().length() == 0) return 0;
5326 return HoleyElementsUsage(*this, FixedDoubleArray::cast(store));
5327
5328 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
5329 case SLOW_STRING_WRAPPER_ELEMENTS:
5330 case DICTIONARY_ELEMENTS:
5331 case WASM_ARRAY_ELEMENTS:
5332 case NO_ELEMENTS:
5333 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
5334
5335 TYPED_ARRAYS(TYPED_ARRAY_CASE)
5336 RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
5337 #undef TYPED_ARRAY_CASE
5338 UNREACHABLE();
5339 }
5340 return 0;
5341 }
5342
GetPropertyWithInterceptor(LookupIterator * it,bool * done)5343 MaybeHandle<Object> JSObject::GetPropertyWithInterceptor(LookupIterator* it,
5344 bool* done) {
5345 DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
5346 return GetPropertyWithInterceptorInternal(it, it->GetInterceptor(), done);
5347 }
5348
HasRealNamedProperty(Isolate * isolate,Handle<JSObject> object,Handle<Name> name)5349 Maybe<bool> JSObject::HasRealNamedProperty(Isolate* isolate,
5350 Handle<JSObject> object,
5351 Handle<Name> name) {
5352 PropertyKey key(isolate, name);
5353 LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
5354 return HasProperty(&it);
5355 }
5356
HasRealElementProperty(Isolate * isolate,Handle<JSObject> object,uint32_t index)5357 Maybe<bool> JSObject::HasRealElementProperty(Isolate* isolate,
5358 Handle<JSObject> object,
5359 uint32_t index) {
5360 LookupIterator it(isolate, object, index, object,
5361 LookupIterator::OWN_SKIP_INTERCEPTOR);
5362 return HasProperty(&it);
5363 }
5364
HasRealNamedCallbackProperty(Isolate * isolate,Handle<JSObject> object,Handle<Name> name)5365 Maybe<bool> JSObject::HasRealNamedCallbackProperty(Isolate* isolate,
5366 Handle<JSObject> object,
5367 Handle<Name> name) {
5368 PropertyKey key(isolate, name);
5369 LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
5370 Maybe<PropertyAttributes> maybe_result = GetPropertyAttributes(&it);
5371 return maybe_result.IsJust() ? Just(it.state() == LookupIterator::ACCESSOR)
5372 : Nothing<bool>();
5373 }
5374
IsDetached() const5375 bool JSGlobalProxy::IsDetached() const {
5376 return native_context().IsNull(GetIsolate());
5377 }
5378
InvalidatePropertyCell(Handle<JSGlobalObject> global,Handle<Name> name)5379 void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global,
5380 Handle<Name> name) {
5381 Isolate* isolate = global->GetIsolate();
5382 // Regardless of whether the property is there or not invalidate
5383 // Load/StoreGlobalICs that load/store through global object's prototype.
5384 JSObject::InvalidatePrototypeValidityCell(*global);
5385 DCHECK(!global->HasFastProperties());
5386 auto dictionary = handle(global->global_dictionary(kAcquireLoad), isolate);
5387 InternalIndex entry = dictionary->FindEntry(isolate, name);
5388 if (entry.is_not_found()) return;
5389
5390 Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
5391 Handle<Object> value(cell->value(), isolate);
5392 PropertyDetails details = cell->property_details();
5393 details = details.set_cell_type(PropertyCellType::kMutable);
5394 PropertyCell::InvalidateAndReplaceEntry(isolate, dictionary, entry, details,
5395 value);
5396 }
5397
5398 // static
New(Handle<JSFunction> constructor,Handle<JSReceiver> new_target,double tv)5399 MaybeHandle<JSDate> JSDate::New(Handle<JSFunction> constructor,
5400 Handle<JSReceiver> new_target, double tv) {
5401 Isolate* const isolate = constructor->GetIsolate();
5402 Handle<JSObject> result;
5403 ASSIGN_RETURN_ON_EXCEPTION(
5404 isolate, result,
5405 JSObject::New(constructor, new_target, Handle<AllocationSite>::null()),
5406 JSDate);
5407 if (-DateCache::kMaxTimeInMs <= tv && tv <= DateCache::kMaxTimeInMs) {
5408 tv = DoubleToInteger(tv) + 0.0;
5409 } else {
5410 tv = std::numeric_limits<double>::quiet_NaN();
5411 }
5412 Handle<Object> value = isolate->factory()->NewNumber(tv);
5413 Handle<JSDate>::cast(result)->SetValue(*value, std::isnan(tv));
5414 return Handle<JSDate>::cast(result);
5415 }
5416
5417 // static
CurrentTimeValue(Isolate * isolate)5418 double JSDate::CurrentTimeValue(Isolate* isolate) {
5419 if (FLAG_log_internal_timer_events) LOG(isolate, CurrentTimeEvent());
5420 if (FLAG_correctness_fuzzer_suppressions) return 4.2;
5421
5422 // According to ECMA-262, section 15.9.1, page 117, the precision of
5423 // the number in a Date object representing a particular instant in
5424 // time is milliseconds. Therefore, we floor the result of getting
5425 // the OS time.
5426 return std::floor(V8::GetCurrentPlatform()->CurrentClockTimeMillis());
5427 }
5428
5429 // static
GetField(Isolate * isolate,Address raw_object,Address smi_index)5430 Address JSDate::GetField(Isolate* isolate, Address raw_object,
5431 Address smi_index) {
5432 // Called through CallCFunction.
5433 DisallowGarbageCollection no_gc;
5434 DisallowHandleAllocation no_handles;
5435 DisallowJavascriptExecution no_js(isolate);
5436
5437 Object object(raw_object);
5438 Smi index(smi_index);
5439 return JSDate::cast(object)
5440 .DoGetField(isolate, static_cast<FieldIndex>(index.value()))
5441 .ptr();
5442 }
5443
DoGetField(Isolate * isolate,FieldIndex index)5444 Object JSDate::DoGetField(Isolate* isolate, FieldIndex index) {
5445 DCHECK_NE(index, kDateValue);
5446
5447 DateCache* date_cache = isolate->date_cache();
5448
5449 if (index < kFirstUncachedField) {
5450 Object stamp = cache_stamp();
5451 if (stamp != date_cache->stamp() && stamp.IsSmi()) {
5452 // Since the stamp is not NaN, the value is also not NaN.
5453 int64_t local_time_ms =
5454 date_cache->ToLocal(static_cast<int64_t>(value().Number()));
5455 SetCachedFields(local_time_ms, date_cache);
5456 }
5457 switch (index) {
5458 case kYear:
5459 return year();
5460 case kMonth:
5461 return month();
5462 case kDay:
5463 return day();
5464 case kWeekday:
5465 return weekday();
5466 case kHour:
5467 return hour();
5468 case kMinute:
5469 return min();
5470 case kSecond:
5471 return sec();
5472 default:
5473 UNREACHABLE();
5474 }
5475 }
5476
5477 if (index >= kFirstUTCField) {
5478 return GetUTCField(index, value().Number(), date_cache);
5479 }
5480
5481 double time = value().Number();
5482 if (std::isnan(time)) return GetReadOnlyRoots().nan_value();
5483
5484 int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(time));
5485 int days = DateCache::DaysFromTime(local_time_ms);
5486
5487 if (index == kDays) return Smi::FromInt(days);
5488
5489 int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
5490 if (index == kMillisecond) return Smi::FromInt(time_in_day_ms % 1000);
5491 DCHECK_EQ(index, kTimeInDay);
5492 return Smi::FromInt(time_in_day_ms);
5493 }
5494
GetUTCField(FieldIndex index,double value,DateCache * date_cache)5495 Object JSDate::GetUTCField(FieldIndex index, double value,
5496 DateCache* date_cache) {
5497 DCHECK_GE(index, kFirstUTCField);
5498
5499 if (std::isnan(value)) return GetReadOnlyRoots().nan_value();
5500
5501 int64_t time_ms = static_cast<int64_t>(value);
5502
5503 if (index == kTimezoneOffset) {
5504 return Smi::FromInt(date_cache->TimezoneOffset(time_ms));
5505 }
5506
5507 int days = DateCache::DaysFromTime(time_ms);
5508
5509 if (index == kWeekdayUTC) return Smi::FromInt(date_cache->Weekday(days));
5510
5511 if (index <= kDayUTC) {
5512 int year, month, day;
5513 date_cache->YearMonthDayFromDays(days, &year, &month, &day);
5514 if (index == kYearUTC) return Smi::FromInt(year);
5515 if (index == kMonthUTC) return Smi::FromInt(month);
5516 DCHECK_EQ(index, kDayUTC);
5517 return Smi::FromInt(day);
5518 }
5519
5520 int time_in_day_ms = DateCache::TimeInDay(time_ms, days);
5521 switch (index) {
5522 case kHourUTC:
5523 return Smi::FromInt(time_in_day_ms / (60 * 60 * 1000));
5524 case kMinuteUTC:
5525 return Smi::FromInt((time_in_day_ms / (60 * 1000)) % 60);
5526 case kSecondUTC:
5527 return Smi::FromInt((time_in_day_ms / 1000) % 60);
5528 case kMillisecondUTC:
5529 return Smi::FromInt(time_in_day_ms % 1000);
5530 case kDaysUTC:
5531 return Smi::FromInt(days);
5532 case kTimeInDayUTC:
5533 return Smi::FromInt(time_in_day_ms);
5534 default:
5535 UNREACHABLE();
5536 }
5537
5538 UNREACHABLE();
5539 }
5540
5541 // static
SetValue(Handle<JSDate> date,double v)5542 Handle<Object> JSDate::SetValue(Handle<JSDate> date, double v) {
5543 Isolate* const isolate = date->GetIsolate();
5544 Handle<Object> value = isolate->factory()->NewNumber(v);
5545 bool value_is_nan = std::isnan(v);
5546 date->SetValue(*value, value_is_nan);
5547 return value;
5548 }
5549
SetValue(Object value,bool is_value_nan)5550 void JSDate::SetValue(Object value, bool is_value_nan) {
5551 set_value(value);
5552 if (is_value_nan) {
5553 HeapNumber nan = GetReadOnlyRoots().nan_value();
5554 set_cache_stamp(nan, SKIP_WRITE_BARRIER);
5555 set_year(nan, SKIP_WRITE_BARRIER);
5556 set_month(nan, SKIP_WRITE_BARRIER);
5557 set_day(nan, SKIP_WRITE_BARRIER);
5558 set_hour(nan, SKIP_WRITE_BARRIER);
5559 set_min(nan, SKIP_WRITE_BARRIER);
5560 set_sec(nan, SKIP_WRITE_BARRIER);
5561 set_weekday(nan, SKIP_WRITE_BARRIER);
5562 } else {
5563 set_cache_stamp(Smi::FromInt(DateCache::kInvalidStamp), SKIP_WRITE_BARRIER);
5564 }
5565 }
5566
SetCachedFields(int64_t local_time_ms,DateCache * date_cache)5567 void JSDate::SetCachedFields(int64_t local_time_ms, DateCache* date_cache) {
5568 int days = DateCache::DaysFromTime(local_time_ms);
5569 int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
5570 int year, month, day;
5571 date_cache->YearMonthDayFromDays(days, &year, &month, &day);
5572 int weekday = date_cache->Weekday(days);
5573 int hour = time_in_day_ms / (60 * 60 * 1000);
5574 int min = (time_in_day_ms / (60 * 1000)) % 60;
5575 int sec = (time_in_day_ms / 1000) % 60;
5576 set_cache_stamp(date_cache->stamp());
5577 set_year(Smi::FromInt(year), SKIP_WRITE_BARRIER);
5578 set_month(Smi::FromInt(month), SKIP_WRITE_BARRIER);
5579 set_day(Smi::FromInt(day), SKIP_WRITE_BARRIER);
5580 set_weekday(Smi::FromInt(weekday), SKIP_WRITE_BARRIER);
5581 set_hour(Smi::FromInt(hour), SKIP_WRITE_BARRIER);
5582 set_min(Smi::FromInt(min), SKIP_WRITE_BARRIER);
5583 set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER);
5584 }
5585
5586 // static
EnsureSourcePositionsAvailable(Isolate * isolate,Handle<JSMessageObject> message)5587 void JSMessageObject::EnsureSourcePositionsAvailable(
5588 Isolate* isolate, Handle<JSMessageObject> message) {
5589 if (!message->DidEnsureSourcePositionsAvailable()) {
5590 DCHECK_EQ(message->start_position(), -1);
5591 DCHECK_GE(message->bytecode_offset().value(), kFunctionEntryBytecodeOffset);
5592 Handle<SharedFunctionInfo> shared_info(
5593 SharedFunctionInfo::cast(message->shared_info()), isolate);
5594 IsCompiledScope is_compiled_scope;
5595 SharedFunctionInfo::EnsureBytecodeArrayAvailable(
5596 isolate, shared_info, &is_compiled_scope, CreateSourcePositions::kYes);
5597 SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared_info);
5598 DCHECK(shared_info->HasBytecodeArray());
5599 int position = shared_info->abstract_code(isolate).SourcePosition(
5600 message->bytecode_offset().value());
5601 DCHECK_GE(position, 0);
5602 message->set_start_position(position);
5603 message->set_end_position(position + 1);
5604 message->set_shared_info(ReadOnlyRoots(isolate).undefined_value());
5605 }
5606 }
5607
GetLineNumber() const5608 int JSMessageObject::GetLineNumber() const {
5609 DCHECK(DidEnsureSourcePositionsAvailable());
5610 if (start_position() == -1) return Message::kNoLineNumberInfo;
5611
5612 Handle<Script> the_script(script(), GetIsolate());
5613
5614 Script::PositionInfo info;
5615 const Script::OffsetFlag offset_flag = Script::WITH_OFFSET;
5616 if (!Script::GetPositionInfo(the_script, start_position(), &info,
5617 offset_flag)) {
5618 return Message::kNoLineNumberInfo;
5619 }
5620
5621 return info.line + 1;
5622 }
5623
GetColumnNumber() const5624 int JSMessageObject::GetColumnNumber() const {
5625 DCHECK(DidEnsureSourcePositionsAvailable());
5626 if (start_position() == -1) return -1;
5627
5628 Handle<Script> the_script(script(), GetIsolate());
5629
5630 Script::PositionInfo info;
5631 const Script::OffsetFlag offset_flag = Script::WITH_OFFSET;
5632 if (!Script::GetPositionInfo(the_script, start_position(), &info,
5633 offset_flag)) {
5634 return -1;
5635 }
5636
5637 return info.column; // Note: No '+1' in contrast to GetLineNumber.
5638 }
5639
GetSource() const5640 String JSMessageObject::GetSource() const {
5641 Script script_object = script();
5642 if (script_object.HasValidSource()) {
5643 Object source = script_object.source();
5644 if (source.IsString()) return String::cast(source);
5645 }
5646 return ReadOnlyRoots(GetIsolate()).empty_string();
5647 }
5648
GetSourceLine() const5649 Handle<String> JSMessageObject::GetSourceLine() const {
5650 Isolate* isolate = GetIsolate();
5651 Handle<Script> the_script(script(), isolate);
5652
5653 #if V8_ENABLE_WEBASSEMBLY
5654 if (the_script->type() == Script::TYPE_WASM) {
5655 return isolate->factory()->empty_string();
5656 }
5657 #endif // V8_ENABLE_WEBASSEMBLY
5658
5659 Script::PositionInfo info;
5660 const Script::OffsetFlag offset_flag = Script::WITH_OFFSET;
5661 DCHECK(DidEnsureSourcePositionsAvailable());
5662 if (!Script::GetPositionInfo(the_script, start_position(), &info,
5663 offset_flag)) {
5664 return isolate->factory()->empty_string();
5665 }
5666
5667 Handle<String> src = handle(String::cast(the_script->source()), isolate);
5668 return isolate->factory()->NewSubString(src, info.line_start, info.line_end);
5669 }
5670
5671 } // namespace internal
5672 } // namespace v8
5673