| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/objects/objects.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <memory> |
| #include <sstream> |
| #include <vector> |
| |
| #include "src/api/api-arguments-inl.h" |
| #include "src/api/api-natives.h" |
| #include "src/api/api.h" |
| #include "src/ast/ast.h" |
| #include "src/ast/scopes.h" |
| #include "src/base/bits.h" |
| #include "src/base/debug/stack_trace.h" |
| #include "src/base/logging.h" |
| #include "src/base/overflowing-math.h" |
| #include "src/base/utils/random-number-generator.h" |
| #include "src/builtins/accessors.h" |
| #include "src/builtins/builtins.h" |
| #include "src/codegen/compiler.h" |
| #include "src/common/globals.h" |
| #include "src/common/message-template.h" |
| #include "src/date/date.h" |
| #include "src/debug/debug.h" |
| #include "src/diagnostics/code-tracer.h" |
| #include "src/execution/arguments.h" |
| #include "src/execution/execution.h" |
| #include "src/execution/frames-inl.h" |
| #include "src/execution/isolate-inl.h" |
| #include "src/execution/isolate-utils-inl.h" |
| #include "src/execution/isolate-utils.h" |
| #include "src/execution/microtask-queue.h" |
| #include "src/execution/protectors-inl.h" |
| #include "src/heap/factory-inl.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/heap/local-factory-inl.h" |
| #include "src/heap/read-only-heap.h" |
| #include "src/ic/ic.h" |
| #include "src/init/bootstrapper.h" |
| #include "src/logging/counters.h" |
| #include "src/logging/log.h" |
| #include "src/logging/runtime-call-stats-scope.h" |
| #include "src/objects/allocation-site-inl.h" |
| #include "src/objects/allocation-site-scopes.h" |
| #include "src/objects/api-callbacks.h" |
| #include "src/objects/arguments-inl.h" |
| #include "src/objects/bigint.h" |
| #include "src/objects/call-site-info-inl.h" |
| #include "src/objects/cell-inl.h" |
| #include "src/objects/code-inl.h" |
| #include "src/objects/compilation-cache-table-inl.h" |
| #include "src/objects/debug-objects-inl.h" |
| #include "src/objects/dictionary.h" |
| #include "src/objects/elements.h" |
| #include "src/objects/embedder-data-array-inl.h" |
| #include "src/objects/field-index-inl.h" |
| #include "src/objects/field-index.h" |
| #include "src/objects/field-type.h" |
| #include "src/objects/foreign.h" |
| #include "src/objects/free-space-inl.h" |
| #include "src/objects/function-kind.h" |
| #include "src/objects/hash-table-inl.h" |
| #include "src/objects/heap-object-inl.h" |
| #include "src/objects/instance-type.h" |
| #include "src/objects/js-array-buffer-inl.h" |
| #include "src/objects/js-array-inl.h" |
| #include "src/objects/keys.h" |
| #include "src/objects/lookup-inl.h" |
| #include "src/objects/map-updater.h" |
| #include "src/objects/objects-body-descriptors-inl.h" |
| #include "src/objects/objects-inl.h" |
| #include "src/objects/property-details.h" |
| #include "src/roots/roots.h" |
| #include "src/snapshot/deserializer.h" |
| #include "src/utils/identity-map.h" |
| #ifdef V8_INTL_SUPPORT |
| #include "src/objects/js-break-iterator.h" |
| #include "src/objects/js-collator.h" |
| #endif // V8_INTL_SUPPORT |
| #include "src/objects/js-collection-inl.h" |
| #ifdef V8_INTL_SUPPORT |
| #include "src/objects/js-date-time-format.h" |
| #endif // V8_INTL_SUPPORT |
| #include "src/objects/js-generator-inl.h" |
| #ifdef V8_INTL_SUPPORT |
| #include "src/objects/js-list-format.h" |
| #include "src/objects/js-locale.h" |
| #include "src/objects/js-number-format.h" |
| #include "src/objects/js-plural-rules.h" |
| #endif // V8_INTL_SUPPORT |
| #include "src/objects/js-regexp-inl.h" |
| #include "src/objects/js-regexp-string-iterator.h" |
| #ifdef V8_INTL_SUPPORT |
| #include "src/objects/js-relative-time-format.h" |
| #include "src/objects/js-segment-iterator.h" |
| #include "src/objects/js-segmenter.h" |
| #include "src/objects/js-segments.h" |
| #endif // V8_INTL_SUPPORT |
| #include "src/codegen/source-position-table.h" |
| #include "src/objects/js-weak-refs-inl.h" |
| #include "src/objects/literal-objects-inl.h" |
| #include "src/objects/map-inl.h" |
| #include "src/objects/map.h" |
| #include "src/objects/megadom-handler-inl.h" |
| #include "src/objects/microtask-inl.h" |
| #include "src/objects/module-inl.h" |
| #include "src/objects/promise-inl.h" |
| #include "src/objects/property-descriptor-object-inl.h" |
| #include "src/objects/property-descriptor.h" |
| #include "src/objects/prototype.h" |
| #include "src/objects/slots-atomic-inl.h" |
| #include "src/objects/string-comparator.h" |
| #include "src/objects/string-set-inl.h" |
| #include "src/objects/struct-inl.h" |
| #include "src/objects/template-objects-inl.h" |
| #include "src/objects/transitions-inl.h" |
| #include "src/parsing/preparse-data.h" |
| #include "src/regexp/regexp.h" |
| #include "src/strings/string-builder-inl.h" |
| #include "src/strings/string-search.h" |
| #include "src/strings/string-stream.h" |
| #include "src/strings/unicode-decoder.h" |
| #include "src/strings/unicode-inl.h" |
| #include "src/utils/hex-format.h" |
| #include "src/utils/ostreams.h" |
| #include "src/utils/sha-256.h" |
| #include "src/utils/utils-inl.h" |
| #include "src/zone/zone.h" |
| |
| #if V8_ENABLE_WEBASSEMBLY |
| #include "src/wasm/wasm-objects.h" |
| #endif // V8_ENABLE_WEBASSEMBLY |
| |
| namespace v8 { |
| namespace internal { |
| |
| ShouldThrow GetShouldThrow(Isolate* isolate, Maybe<ShouldThrow> should_throw) { |
| if (should_throw.IsJust()) return should_throw.FromJust(); |
| |
| LanguageMode mode = isolate->context()->scope_info()->language_mode(); |
| if (mode == LanguageMode::kStrict) return kThrowOnError; |
| |
| for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { |
| if (!it.frame()->is_java_script()) continue; |
| |
| // Get the language mode from closure. |
| JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(it.frame()); |
| std::vector<Tagged<SharedFunctionInfo>> functions; |
| js_frame->GetFunctions(&functions); |
| LanguageMode closure_language_mode = functions.back()->language_mode(); |
| if (closure_language_mode > mode) { |
| mode = closure_language_mode; |
| } |
| break; |
| } |
| |
| return is_sloppy(mode) ? kDontThrow : kThrowOnError; |
| } |
| |
| bool ComparisonResultToBool(Operation op, ComparisonResult result) { |
| switch (op) { |
| case Operation::kLessThan: |
| return result == ComparisonResult::kLessThan; |
| case Operation::kLessThanOrEqual: |
| return result == ComparisonResult::kLessThan || |
| result == ComparisonResult::kEqual; |
| case Operation::kGreaterThan: |
| return result == ComparisonResult::kGreaterThan; |
| case Operation::kGreaterThanOrEqual: |
| return result == ComparisonResult::kGreaterThan || |
| result == ComparisonResult::kEqual; |
| default: |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| std::ostream& operator<<(std::ostream& os, InstanceType instance_type) { |
| if (InstanceTypeChecker::IsJSApiObject(instance_type)) { |
| return os << "[api object] " |
| << static_cast<int16_t>(instance_type) - |
| i::Internals::kFirstJSApiObjectType; |
| } |
| switch (instance_type) { |
| #define WRITE_TYPE(TYPE) \ |
| case TYPE: \ |
| return os << #TYPE; |
| INSTANCE_TYPE_LIST(WRITE_TYPE) |
| #undef WRITE_TYPE |
| } |
| return os << "[unknown instance type " << static_cast<int16_t>(instance_type) |
| << "]"; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, PropertyCellType type) { |
| switch (type) { |
| case PropertyCellType::kUndefined: |
| return os << "Undefined"; |
| case PropertyCellType::kConstant: |
| return os << "Constant"; |
| case PropertyCellType::kConstantType: |
| return os << "ConstantType"; |
| case PropertyCellType::kMutable: |
| return os << "Mutable"; |
| case PropertyCellType::kInTransition: |
| return os << "InTransition"; |
| } |
| UNREACHABLE(); |
| } |
| |
| // static |
| Handle<FieldType> Object::OptimalType(Tagged<Object> obj, Isolate* isolate, |
| Representation representation) { |
| if (representation.IsNone()) return FieldType::None(isolate); |
| if (v8_flags.track_field_types) { |
| if (representation.IsHeapObject() && IsHeapObject(obj)) { |
| // We can track only JavaScript objects with stable maps. |
| Handle<Map> map(HeapObject::cast(obj)->map(), isolate); |
| if (map->is_stable() && IsJSReceiverMap(*map)) { |
| return FieldType::Class(map, isolate); |
| } |
| } |
| } |
| return FieldType::Any(isolate); |
| } |
| |
| Handle<Object> Object::NewStorageFor(Isolate* isolate, Handle<Object> object, |
| Representation representation) { |
| if (!representation.IsDouble()) return object; |
| Handle<HeapNumber> result = isolate->factory()->NewHeapNumberWithHoleNaN(); |
| if (IsUninitialized(*object, isolate)) { |
| result->set_value_as_bits(kHoleNanInt64); |
| } else if (IsHeapNumber(*object)) { |
| // Ensure that all bits of the double value are preserved. |
| result->set_value_as_bits(HeapNumber::cast(*object)->value_as_bits()); |
| } else { |
| result->set_value(Object::Number(*object)); |
| } |
| return result; |
| } |
| |
| template <AllocationType allocation_type, typename IsolateT> |
| Handle<Object> Object::WrapForRead(IsolateT* isolate, Handle<Object> object, |
| Representation representation) { |
| DCHECK(!IsUninitialized(*object, isolate)); |
| if (!representation.IsDouble()) { |
| DCHECK(Object::FitsRepresentation(*object, representation)); |
| return object; |
| } |
| return isolate->factory()->template NewHeapNumberFromBits<allocation_type>( |
| HeapNumber::cast(*object)->value_as_bits()); |
| } |
| |
| template Handle<Object> Object::WrapForRead<AllocationType::kYoung>( |
| Isolate* isolate, Handle<Object> object, Representation representation); |
| template Handle<Object> Object::WrapForRead<AllocationType::kOld>( |
| LocalIsolate* isolate, Handle<Object> object, |
| Representation representation); |
| |
| MaybeHandle<JSReceiver> Object::ToObjectImpl(Isolate* isolate, |
| Handle<Object> object, |
| const char* method_name) { |
| DCHECK(!IsJSReceiver(*object)); // Use ToObject() for fast path. |
| Handle<Context> native_context = isolate->native_context(); |
| Handle<JSFunction> constructor; |
| if (IsSmi(*object)) { |
| constructor = handle(native_context->number_function(), isolate); |
| } else { |
| int constructor_function_index = |
| Handle<HeapObject>::cast(object)->map()->GetConstructorFunctionIndex(); |
| if (constructor_function_index == Map::kNoConstructorFunctionIndex) { |
| if (method_name != nullptr) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError( |
| MessageTemplate::kCalledOnNullOrUndefined, |
| isolate->factory()->NewStringFromAsciiChecked(method_name)), |
| JSReceiver); |
| } |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kUndefinedOrNullToObject), |
| JSReceiver); |
| } |
| constructor = handle( |
| JSFunction::cast(native_context->get(constructor_function_index)), |
| isolate); |
| } |
| Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); |
| Handle<JSPrimitiveWrapper>::cast(result)->set_value(*object); |
| return result; |
| } |
| |
| // ES6 section 9.2.1.2, OrdinaryCallBindThis for sloppy callee. |
| // static |
| MaybeHandle<JSReceiver> Object::ConvertReceiver(Isolate* isolate, |
| Handle<Object> object) { |
| if (IsJSReceiver(*object)) return Handle<JSReceiver>::cast(object); |
| if (IsNullOrUndefined(*object, isolate)) { |
| return isolate->global_proxy(); |
| } |
| return Object::ToObject(isolate, object); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::ConvertToNumberOrNumeric(Isolate* isolate, |
| Handle<Object> input, |
| Conversion mode) { |
| while (true) { |
| if (IsNumber(*input)) { |
| return input; |
| } |
| if (IsString(*input)) { |
| return String::ToNumber(isolate, Handle<String>::cast(input)); |
| } |
| if (IsOddball(*input)) { |
| return Oddball::ToNumber(isolate, Handle<Oddball>::cast(input)); |
| } |
| if (IsSymbol(*input)) { |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber), |
| Object); |
| } |
| if (IsBigInt(*input)) { |
| if (mode == Conversion::kToNumeric) return input; |
| DCHECK_EQ(mode, Conversion::kToNumber); |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kBigIntToNumber), |
| Object); |
| } |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(input), |
| ToPrimitiveHint::kNumber), |
| Object); |
| } |
| } |
| |
| // static |
| MaybeHandle<Object> Object::ConvertToInteger(Isolate* isolate, |
| Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object); |
| if (IsSmi(*input)) return input; |
| return isolate->factory()->NewNumber(DoubleToInteger(Object::Number(*input))); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::ConvertToInt32(Isolate* isolate, |
| Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object); |
| if (IsSmi(*input)) return input; |
| return isolate->factory()->NewNumberFromInt( |
| DoubleToInt32(Object::Number(*input))); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::ConvertToUint32(Isolate* isolate, |
| Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object); |
| if (IsSmi(*input)) |
| return handle(Smi::ToUint32Smi(Smi::cast(*input)), isolate); |
| return isolate->factory()->NewNumberFromUint( |
| DoubleToUint32(Object::Number(*input))); |
| } |
| |
| // static |
| MaybeHandle<Name> Object::ConvertToName(Isolate* isolate, |
| Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| Object::ToPrimitive(isolate, input, ToPrimitiveHint::kString), Name); |
| if (IsName(*input)) return Handle<Name>::cast(input); |
| return ToString(isolate, input); |
| } |
| |
| // ES6 7.1.14 |
| // static |
| MaybeHandle<Object> Object::ConvertToPropertyKey(Isolate* isolate, |
| Handle<Object> value) { |
| // 1. Let key be ToPrimitive(argument, hint String). |
| MaybeHandle<Object> maybe_key = |
| Object::ToPrimitive(isolate, value, ToPrimitiveHint::kString); |
| // 2. ReturnIfAbrupt(key). |
| Handle<Object> key; |
| if (!maybe_key.ToHandle(&key)) return key; |
| // 3. If Type(key) is Symbol, then return key. |
| if (IsSymbol(*key)) return key; |
| // 4. Return ToString(key). |
| // Extending spec'ed behavior, we'd be happy to return an element index. |
| if (IsSmi(*key)) return key; |
| if (IsHeapNumber(*key)) { |
| uint32_t uint_value; |
| if (Object::ToArrayLength(*value, &uint_value) && |
| uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) { |
| return handle(Smi::FromInt(static_cast<int>(uint_value)), isolate); |
| } |
| } |
| return Object::ToString(isolate, key); |
| } |
| |
| // static |
| MaybeHandle<String> Object::ConvertToString(Isolate* isolate, |
| Handle<Object> input) { |
| while (true) { |
| if (IsOddball(*input)) { |
| return handle(Handle<Oddball>::cast(input)->to_string(), isolate); |
| } |
| if (IsNumber(*input)) { |
| return isolate->factory()->NumberToString(input); |
| } |
| if (IsSymbol(*input)) { |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString), |
| String); |
| } |
| if (IsBigInt(*input)) { |
| return BigInt::ToString(isolate, Handle<BigInt>::cast(input)); |
| } |
| #if V8_ENABLE_WEBASSEMBLY |
| // We generally don't let the WasmNull escape into the JavaScript world, |
| // but some builtins may encounter it when called directly from Wasm code. |
| if (IsWasmNull(*input)) { |
| return isolate->factory()->null_string(); |
| } |
| #endif |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(input), |
| ToPrimitiveHint::kString), |
| String); |
| // The previous isString() check happened in Object::ToString and thus we |
| // put it at the end of the loop in this helper. |
| if (IsString(*input)) { |
| return Handle<String>::cast(input); |
| } |
| } |
| } |
| |
| namespace { |
| |
| bool IsErrorObject(Isolate* isolate, Handle<Object> object) { |
| if (!IsJSObject(*object)) return false; |
| return ErrorUtils::HasErrorStackSymbolOwnProperty( |
| isolate, Handle<JSObject>::cast(object)); |
| } |
| |
| Handle<String> AsStringOrEmpty(Isolate* isolate, Handle<Object> object) { |
| return IsString(*object) ? Handle<String>::cast(object) |
| : isolate->factory()->empty_string(); |
| } |
| |
| Handle<String> NoSideEffectsErrorToString(Isolate* isolate, |
| Handle<JSReceiver> error) { |
| Handle<Name> name_key = isolate->factory()->name_string(); |
| Handle<Object> name = JSReceiver::GetDataProperty(isolate, error, name_key); |
| Handle<String> name_str = AsStringOrEmpty(isolate, name); |
| |
| Handle<Name> msg_key = isolate->factory()->message_string(); |
| Handle<Object> msg = JSReceiver::GetDataProperty(isolate, error, msg_key); |
| Handle<String> msg_str = AsStringOrEmpty(isolate, msg); |
| |
| if (name_str->length() == 0) return msg_str; |
| if (msg_str->length() == 0) return name_str; |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendString(name_str); |
| builder.AppendCStringLiteral(": "); |
| |
| if (builder.Length() + msg_str->length() <= String::kMaxLength) { |
| builder.AppendString(msg_str); |
| } else { |
| builder.AppendCStringLiteral("<a very large string>"); |
| } |
| |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| } // namespace |
| |
| // static |
| MaybeHandle<String> Object::NoSideEffectsToMaybeString(Isolate* isolate, |
| Handle<Object> input) { |
| DisallowJavascriptExecution no_js(isolate); |
| |
| if (IsString(*input) || IsNumber(*input) || IsOddball(*input)) { |
| return Object::ToString(isolate, input).ToHandleChecked(); |
| } else if (IsJSProxy(*input)) { |
| Handle<Object> currInput = input; |
| do { |
| Tagged<HeapObject> target = |
| Handle<JSProxy>::cast(currInput)->target(isolate); |
| currInput = Handle<Object>(target, isolate); |
| } while (IsJSProxy(*currInput)); |
| return NoSideEffectsToString(isolate, currInput); |
| } else if (IsBigInt(*input)) { |
| return BigInt::NoSideEffectsToString(isolate, Handle<BigInt>::cast(input)); |
| } else if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*input)) { |
| // -- F u n c t i o n |
| Handle<String> fun_str; |
| if (IsJSBoundFunction(*input)) { |
| fun_str = JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(input)); |
| } else if (IsJSWrappedFunction(*input)) { |
| fun_str = |
| JSWrappedFunction::ToString(Handle<JSWrappedFunction>::cast(input)); |
| } else { |
| DCHECK(IsJSFunction(*input)); |
| fun_str = JSFunction::ToString(Handle<JSFunction>::cast(input)); |
| } |
| |
| if (fun_str->length() > 128) { |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111)); |
| builder.AppendCStringLiteral("...<omitted>..."); |
| builder.AppendString(isolate->factory()->NewSubString( |
| fun_str, fun_str->length() - 2, fun_str->length())); |
| |
| return builder.Finish().ToHandleChecked(); |
| } |
| return fun_str; |
| } else if (IsSymbol(*input)) { |
| // -- S y m b o l |
| Handle<Symbol> symbol = Handle<Symbol>::cast(input); |
| |
| if (symbol->is_private_name()) { |
| return Handle<String>(String::cast(symbol->description()), isolate); |
| } |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendCStringLiteral("Symbol("); |
| if (IsString(symbol->description())) { |
| Handle<String> description = |
| handle(String::cast(symbol->description()), isolate); |
| if (description->length() > 128) { |
| builder.AppendString( |
| isolate->factory()->NewSubString(description, 0, 56)); |
| builder.AppendCStringLiteral("...<omitted>..."); |
| builder.AppendString(isolate->factory()->NewSubString( |
| description, description->length() - 56, description->length())); |
| } else { |
| builder.AppendString(description); |
| } |
| } |
| builder.AppendCharacter(')'); |
| |
| return builder.Finish().ToHandleChecked(); |
| } else if (IsJSReceiver(*input)) { |
| // -- J S R e c e i v e r |
| Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input); |
| Handle<Object> to_string = JSReceiver::GetDataProperty( |
| isolate, receiver, isolate->factory()->toString_string()); |
| |
| if (IsErrorObject(isolate, input) || |
| *to_string == *isolate->error_to_string()) { |
| // When internally formatting error objects, use a side-effects-free |
| // version of Error.prototype.toString independent of the actually |
| // installed toString method. |
| return NoSideEffectsErrorToString(isolate, |
| Handle<JSReceiver>::cast(input)); |
| } else if (*to_string == *isolate->object_to_string()) { |
| Handle<Object> ctor = JSReceiver::GetDataProperty( |
| isolate, receiver, isolate->factory()->constructor_string()); |
| if (IsJSFunctionOrBoundFunctionOrWrappedFunction(*ctor)) { |
| Handle<String> ctor_name; |
| if (IsJSBoundFunction(*ctor)) { |
| ctor_name = JSBoundFunction::GetName( |
| isolate, Handle<JSBoundFunction>::cast(ctor)) |
| .ToHandleChecked(); |
| } else if (IsJSFunction(*ctor)) { |
| ctor_name = |
| JSFunction::GetName(isolate, Handle<JSFunction>::cast(ctor)); |
| } |
| |
| if (ctor_name->length() != 0) { |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendCStringLiteral("#<"); |
| builder.AppendString(ctor_name); |
| builder.AppendCharacter('>'); |
| |
| return builder.Finish().ToHandleChecked(); |
| } |
| } |
| } |
| } |
| return MaybeHandle<String>(kNullMaybeHandle); |
| } |
| |
| // static |
| Handle<String> Object::NoSideEffectsToString(Isolate* isolate, |
| Handle<Object> input) { |
| DisallowJavascriptExecution no_js(isolate); |
| |
| // Try to convert input to a meaningful string. |
| MaybeHandle<String> maybe_string = NoSideEffectsToMaybeString(isolate, input); |
| Handle<String> string_handle; |
| if (maybe_string.ToHandle(&string_handle)) { |
| return string_handle; |
| } |
| |
| // At this point, input is either none of the above or a JSReceiver. |
| |
| Handle<JSReceiver> receiver; |
| if (IsJSReceiver(*input)) { |
| receiver = Handle<JSReceiver>::cast(input); |
| } else { |
| // This is the only case where Object::ToObject throws. |
| DCHECK(!IsSmi(*input)); |
| int constructor_function_index = |
| Handle<HeapObject>::cast(input)->map()->GetConstructorFunctionIndex(); |
| if (constructor_function_index == Map::kNoConstructorFunctionIndex) { |
| return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]"); |
| } |
| |
| receiver = Object::ToObjectImpl(isolate, input).ToHandleChecked(); |
| } |
| |
| Handle<String> builtin_tag = handle(receiver->class_name(), isolate); |
| Handle<Object> tag_obj = JSReceiver::GetDataProperty( |
| isolate, receiver, isolate->factory()->to_string_tag_symbol()); |
| Handle<String> tag = |
| IsString(*tag_obj) ? Handle<String>::cast(tag_obj) : builtin_tag; |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendCStringLiteral("[object "); |
| builder.AppendString(tag); |
| builder.AppendCharacter(']'); |
| |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::ConvertToLength(Isolate* isolate, |
| Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input), Object); |
| if (IsSmi(*input)) { |
| int value = std::max(Smi::ToInt(*input), 0); |
| return handle(Smi::FromInt(value), isolate); |
| } |
| double len = DoubleToInteger(Object::Number(*input)); |
| if (len <= 0.0) { |
| return handle(Smi::zero(), isolate); |
| } else if (len >= kMaxSafeInteger) { |
| len = kMaxSafeInteger; |
| } |
| return isolate->factory()->NewNumber(len); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::ConvertToIndex(Isolate* isolate, |
| Handle<Object> input, |
| MessageTemplate error_index) { |
| if (IsUndefined(*input, isolate)) return handle(Smi::zero(), isolate); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input), Object); |
| if (IsSmi(*input) && Smi::ToInt(*input) >= 0) return input; |
| double len = DoubleToInteger(Object::Number(*input)); |
| Handle<Object> js_len = isolate->factory()->NewNumber(len); |
| if (len < 0.0 || len > kMaxSafeInteger) { |
| THROW_NEW_ERROR(isolate, NewRangeError(error_index, js_len), Object); |
| } |
| return js_len; |
| } |
| |
| template <typename IsolateT> |
| // static |
| bool Object::BooleanValue(Tagged<Object> obj, IsolateT* isolate) { |
| if (IsSmi(obj)) return Smi::ToInt(obj) != 0; |
| DCHECK(IsHeapObject(obj)); |
| if (IsBoolean(obj)) return IsTrue(obj, isolate); |
| if (IsNullOrUndefined(obj, isolate)) return false; |
| #ifdef V8_ENABLE_WEBASSEMBLY |
| if (IsWasmNull(obj)) return false; |
| #endif |
| if (IsUndetectable(obj)) return false; // Undetectable object is false. |
| if (IsString(obj)) return String::cast(obj)->length() != 0; |
| if (IsHeapNumber(obj)) return DoubleToBoolean(HeapNumber::cast(obj)->value()); |
| if (IsBigInt(obj)) return BigInt::cast(obj)->ToBoolean(); |
| return true; |
| } |
| template bool Object::BooleanValue(Tagged<Object>, Isolate*); |
| template bool Object::BooleanValue(Tagged<Object>, LocalIsolate*); |
| |
| // static |
| Tagged<Object> Object::ToBoolean(Tagged<Object> obj, Isolate* isolate) { |
| if (IsBoolean(obj)) return obj; |
| return isolate->heap()->ToBoolean(Object::BooleanValue(obj, isolate)); |
| } |
| |
| namespace { |
| |
| // TODO(bmeurer): Maybe we should introduce a marker interface Number, |
| // where we put all these methods at some point? |
| ComparisonResult StrictNumberCompare(double x, double y) { |
| if (std::isnan(x) || std::isnan(y)) { |
| return ComparisonResult::kUndefined; |
| } else if (x < y) { |
| return ComparisonResult::kLessThan; |
| } else if (x > y) { |
| return ComparisonResult::kGreaterThan; |
| } else { |
| return ComparisonResult::kEqual; |
| } |
| } |
| |
| // See Number case of ES6#sec-strict-equality-comparison |
| // Returns false if x or y is NaN, treats -0.0 as equal to 0.0. |
| bool StrictNumberEquals(double x, double y) { |
| // Must check explicitly for NaN's on Windows, but -0 works fine. |
| if (std::isnan(x) || std::isnan(y)) return false; |
| return x == y; |
| } |
| |
| bool StrictNumberEquals(const Tagged<Object> x, const Tagged<Object> y) { |
| return StrictNumberEquals(Object::Number(x), Object::Number(y)); |
| } |
| |
| bool StrictNumberEquals(Handle<Object> x, Handle<Object> y) { |
| return StrictNumberEquals(*x, *y); |
| } |
| |
| ComparisonResult Reverse(ComparisonResult result) { |
| if (result == ComparisonResult::kLessThan) { |
| return ComparisonResult::kGreaterThan; |
| } |
| if (result == ComparisonResult::kGreaterThan) { |
| return ComparisonResult::kLessThan; |
| } |
| return result; |
| } |
| |
| } // anonymous namespace |
| |
| // static |
| Maybe<ComparisonResult> Object::Compare(Isolate* isolate, Handle<Object> x, |
| Handle<Object> y) { |
| // ES6 section 7.2.11 Abstract Relational Comparison step 3 and 4. |
| if (!Object::ToPrimitive(isolate, x, ToPrimitiveHint::kNumber).ToHandle(&x) || |
| !Object::ToPrimitive(isolate, y, ToPrimitiveHint::kNumber).ToHandle(&y)) { |
| return Nothing<ComparisonResult>(); |
| } |
| if (IsString(*x) && IsString(*y)) { |
| // ES6 section 7.2.11 Abstract Relational Comparison step 5. |
| return Just(String::Compare(isolate, Handle<String>::cast(x), |
| Handle<String>::cast(y))); |
| } |
| if (IsBigInt(*x) && IsString(*y)) { |
| return BigInt::CompareToString(isolate, Handle<BigInt>::cast(x), |
| Handle<String>::cast(y)); |
| } |
| if (IsString(*x) && IsBigInt(*y)) { |
| Maybe<ComparisonResult> maybe_result = BigInt::CompareToString( |
| isolate, Handle<BigInt>::cast(y), Handle<String>::cast(x)); |
| ComparisonResult result; |
| if (maybe_result.To(&result)) { |
| return Just(Reverse(result)); |
| } else { |
| return Nothing<ComparisonResult>(); |
| } |
| } |
| // ES6 section 7.2.11 Abstract Relational Comparison step 6. |
| if (!Object::ToNumeric(isolate, x).ToHandle(&x) || |
| !Object::ToNumeric(isolate, y).ToHandle(&y)) { |
| return Nothing<ComparisonResult>(); |
| } |
| |
| bool x_is_number = IsNumber(*x); |
| bool y_is_number = IsNumber(*y); |
| if (x_is_number && y_is_number) { |
| return Just(StrictNumberCompare(Object::Number(*x), Object::Number(*y))); |
| } else if (!x_is_number && !y_is_number) { |
| return Just(BigInt::CompareToBigInt(Handle<BigInt>::cast(x), |
| Handle<BigInt>::cast(y))); |
| } else if (x_is_number) { |
| return Just(Reverse(BigInt::CompareToNumber(Handle<BigInt>::cast(y), x))); |
| } else { |
| return Just(BigInt::CompareToNumber(Handle<BigInt>::cast(x), y)); |
| } |
| } |
| |
| // static |
| Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x, |
| Handle<Object> y) { |
| // This is the generic version of Abstract Equality Comparison. Must be in |
| // sync with CodeStubAssembler::Equal. |
| while (true) { |
| if (IsNumber(*x)) { |
| if (IsNumber(*y)) { |
| return Just(StrictNumberEquals(x, y)); |
| } else if (IsBoolean(*y)) { |
| return Just( |
| StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); |
| } else if (IsString(*y)) { |
| return Just(StrictNumberEquals( |
| x, String::ToNumber(isolate, Handle<String>::cast(y)))); |
| } else if (IsBigInt(*y)) { |
| return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x)); |
| } else if (IsJSReceiver(*y)) { |
| if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(false); |
| } |
| } else if (IsString(*x)) { |
| if (IsString(*y)) { |
| return Just(String::Equals(isolate, Handle<String>::cast(x), |
| Handle<String>::cast(y))); |
| } else if (IsNumber(*y)) { |
| x = String::ToNumber(isolate, Handle<String>::cast(x)); |
| return Just(StrictNumberEquals(x, y)); |
| } else if (IsBoolean(*y)) { |
| x = String::ToNumber(isolate, Handle<String>::cast(x)); |
| return Just( |
| StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); |
| } else if (IsBigInt(*y)) { |
| return BigInt::EqualToString(isolate, Handle<BigInt>::cast(y), |
| Handle<String>::cast(x)); |
| } else if (IsJSReceiver(*y)) { |
| if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(false); |
| } |
| } else if (IsBoolean(*x)) { |
| if (IsOddball(*y)) { |
| return Just(x.is_identical_to(y)); |
| } else if (IsNumber(*y)) { |
| return Just( |
| StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); |
| } else if (IsString(*y)) { |
| y = String::ToNumber(isolate, Handle<String>::cast(y)); |
| return Just( |
| StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); |
| } else if (IsBigInt(*y)) { |
| x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x)); |
| return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x)); |
| } else if (IsJSReceiver(*y)) { |
| if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x)); |
| } else { |
| return Just(false); |
| } |
| } else if (IsSymbol(*x)) { |
| if (IsSymbol(*y)) { |
| return Just(x.is_identical_to(y)); |
| } else if (IsJSReceiver(*y)) { |
| if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(false); |
| } |
| } else if (IsBigInt(*x)) { |
| if (IsBigInt(*y)) { |
| return Just(BigInt::EqualToBigInt(BigInt::cast(*x), BigInt::cast(*y))); |
| } |
| return Equals(isolate, y, x); |
| } else if (IsJSReceiver(*x)) { |
| if (IsJSReceiver(*y)) { |
| return Just(x.is_identical_to(y)); |
| } else if (IsUndetectable(*y)) { |
| return Just(IsUndetectable(*x)); |
| } else if (IsBoolean(*y)) { |
| y = Oddball::ToNumber(isolate, Handle<Oddball>::cast(y)); |
| } else if (!JSReceiver::ToPrimitive(isolate, Handle<JSReceiver>::cast(x)) |
| .ToHandle(&x)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(IsUndetectable(*x) && IsUndetectable(*y)); |
| } |
| } |
| } |
| |
| // static |
| bool Object::StrictEquals(Tagged<Object> obj, Tagged<Object> that) { |
| if (IsNumber(obj)) { |
| if (!IsNumber(that)) return false; |
| return StrictNumberEquals(obj, that); |
| } else if (IsString(obj)) { |
| if (!IsString(that)) return false; |
| return String::cast(obj)->Equals(String::cast(that)); |
| } else if (IsBigInt(obj)) { |
| if (!IsBigInt(that)) return false; |
| return BigInt::EqualToBigInt(BigInt::cast(obj), BigInt::cast(that)); |
| } |
| return obj == that; |
| } |
| |
| // static |
| Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) { |
| if (IsNumber(*object)) return isolate->factory()->number_string(); |
| if (IsOddball(*object)) |
| return handle(Oddball::cast(*object)->type_of(), isolate); |
| if (IsUndetectable(*object)) { |
| return isolate->factory()->undefined_string(); |
| } |
| if (IsString(*object)) return isolate->factory()->string_string(); |
| if (IsSymbol(*object)) return isolate->factory()->symbol_string(); |
| if (IsBigInt(*object)) return isolate->factory()->bigint_string(); |
| if (IsCallable(*object)) return isolate->factory()->function_string(); |
| return isolate->factory()->object_string(); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::Add(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (IsNumber(*lhs) && IsNumber(*rhs)) { |
| return isolate->factory()->NewNumber(Object::Number(*lhs) + |
| Object::Number(*rhs)); |
| } else if (IsString(*lhs) && IsString(*rhs)) { |
| return isolate->factory()->NewConsString(Handle<String>::cast(lhs), |
| Handle<String>::cast(rhs)); |
| } |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToPrimitive(isolate, lhs), |
| Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToPrimitive(isolate, rhs), |
| Object); |
| if (IsString(*lhs) || IsString(*rhs)) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToString(isolate, rhs), |
| Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToString(isolate, lhs), |
| Object); |
| return isolate->factory()->NewConsString(Handle<String>::cast(lhs), |
| Handle<String>::cast(rhs)); |
| } |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(isolate, rhs), |
| Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(isolate, lhs), |
| Object); |
| return isolate->factory()->NewNumber(Object::Number(*lhs) + |
| Object::Number(*rhs)); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate, |
| Handle<Object> callable, |
| Handle<Object> object) { |
| // The {callable} must have a [[Call]] internal method. |
| if (!IsCallable(*callable)) return isolate->factory()->false_value(); |
| |
| // Check if {callable} is a bound function, and if so retrieve its |
| // [[BoundTargetFunction]] and use that instead of {callable}. |
| if (IsJSBoundFunction(*callable)) { |
| // Since there is a mutual recursion here, we might run out of stack |
| // space for long chains of bound functions. |
| STACK_CHECK(isolate, MaybeHandle<Object>()); |
| Handle<Object> bound_callable( |
| Handle<JSBoundFunction>::cast(callable)->bound_target_function(), |
| isolate); |
| return Object::InstanceOf(isolate, object, bound_callable); |
| } |
| |
| // If {object} is not a receiver, return false. |
| if (!IsJSReceiver(*object)) return isolate->factory()->false_value(); |
| |
| // Get the "prototype" of {callable}; raise an error if it's not a receiver. |
| Handle<Object> prototype; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, prototype, |
| Object::GetProperty(isolate, callable, |
| isolate->factory()->prototype_string()), |
| Object); |
| if (!IsJSReceiver(*prototype)) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype), |
| Object); |
| } |
| |
| // Return whether or not {prototype} is in the prototype chain of {object}. |
| Maybe<bool> result = JSReceiver::HasInPrototypeChain( |
| isolate, Handle<JSReceiver>::cast(object), prototype); |
| if (result.IsNothing()) return MaybeHandle<Object>(); |
| return isolate->factory()->ToBoolean(result.FromJust()); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::InstanceOf(Isolate* isolate, Handle<Object> object, |
| Handle<Object> callable) { |
| // The {callable} must be a receiver. |
| if (!IsJSReceiver(*callable)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck), |
| Object); |
| } |
| |
| // Lookup the @@hasInstance method on {callable}. |
| Handle<Object> inst_of_handler; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, inst_of_handler, |
| Object::GetMethod(isolate, Handle<JSReceiver>::cast(callable), |
| isolate->factory()->has_instance_symbol()), |
| Object); |
| if (!IsUndefined(*inst_of_handler, isolate)) { |
| // Call the {inst_of_handler} on the {callable}. |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, |
| Execution::Call(isolate, inst_of_handler, callable, 1, &object), |
| Object); |
| return isolate->factory()->ToBoolean( |
| Object::BooleanValue(*result, isolate)); |
| } |
| |
| // The {callable} must have a [[Call]] internal method. |
| if (!IsCallable(*callable)) { |
| THROW_NEW_ERROR( |
| isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck), |
| Object); |
| } |
| |
| // Fall back to OrdinaryHasInstance with {callable} and {object}. |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, Object::OrdinaryHasInstance(isolate, callable, object), |
| Object); |
| return result; |
| } |
| |
| // static |
| MaybeHandle<Object> Object::GetMethod(Isolate* isolate, |
| Handle<JSReceiver> receiver, |
| Handle<Name> name) { |
| Handle<Object> func; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, func, JSReceiver::GetProperty(isolate, receiver, name), Object); |
| if (IsNullOrUndefined(*func, isolate)) { |
| return isolate->factory()->undefined_value(); |
| } |
| if (!IsCallable(*func)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kPropertyNotFunction, func, |
| name, receiver), |
| Object); |
| } |
| return func; |
| } |
| |
| namespace { |
| |
| MaybeHandle<FixedArray> CreateListFromArrayLikeFastPath( |
| Isolate* isolate, Handle<Object> object, ElementTypes element_types) { |
| if (element_types == ElementTypes::kAll) { |
| if (IsJSArray(*object)) { |
| Handle<JSArray> array = Handle<JSArray>::cast(object); |
| uint32_t length; |
| if (!array->HasArrayPrototype(isolate) || |
| !Object::ToUint32(array->length(), &length) || |
| !array->HasFastElements() || |
| !JSObject::PrototypeHasNoElements(isolate, *array)) { |
| return MaybeHandle<FixedArray>(); |
| } |
| return array->GetElementsAccessor()->CreateListFromArrayLike( |
| isolate, array, length); |
| } else if (IsJSTypedArray(*object)) { |
| Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(object); |
| size_t length = array->GetLength(); |
| if (array->IsDetachedOrOutOfBounds() || |
| length > static_cast<size_t>(FixedArray::kMaxLength)) { |
| return MaybeHandle<FixedArray>(); |
| } |
| static_assert(FixedArray::kMaxLength <= |
| std::numeric_limits<uint32_t>::max()); |
| return array->GetElementsAccessor()->CreateListFromArrayLike( |
| isolate, array, static_cast<uint32_t>(length)); |
| } |
| } |
| return MaybeHandle<FixedArray>(); |
| } |
| |
| } // namespace |
| |
| // static |
| MaybeHandle<FixedArray> Object::CreateListFromArrayLike( |
| Isolate* isolate, Handle<Object> object, ElementTypes element_types) { |
| // Fast-path for JSArray and JSTypedArray. |
| MaybeHandle<FixedArray> fast_result = |
| CreateListFromArrayLikeFastPath(isolate, object, element_types); |
| if (!fast_result.is_null()) return fast_result; |
| // 1. ReturnIfAbrupt(object). |
| // 2. (default elementTypes -- not applicable.) |
| // 3. If Type(obj) is not Object, throw a TypeError exception. |
| if (!IsJSReceiver(*object)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kCalledOnNonObject, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "CreateListFromArrayLike")), |
| FixedArray); |
| } |
| |
| // 4. Let len be ? ToLength(? Get(obj, "length")). |
| Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object); |
| Handle<Object> raw_length_number; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number, |
| Object::GetLengthFromArrayLike(isolate, receiver), |
| FixedArray); |
| uint32_t len; |
| if (!Object::ToUint32(*raw_length_number, &len) || |
| len > static_cast<uint32_t>(FixedArray::kMaxLength)) { |
| THROW_NEW_ERROR(isolate, |
| NewRangeError(MessageTemplate::kInvalidArrayLength), |
| FixedArray); |
| } |
| // 5. Let list be an empty List. |
| Handle<FixedArray> list = isolate->factory()->NewFixedArray(len); |
| // 6. Let index be 0. |
| // 7. Repeat while index < len: |
| for (uint32_t index = 0; index < len; ++index) { |
| // 7a. Let indexName be ToString(index). |
| // 7b. Let next be ? Get(obj, indexName). |
| Handle<Object> next; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, next, |
| JSReceiver::GetElement(isolate, receiver, index), |
| FixedArray); |
| switch (element_types) { |
| case ElementTypes::kAll: |
| // Nothing to do. |
| break; |
| case ElementTypes::kStringAndSymbol: { |
| // 7c. If Type(next) is not an element of elementTypes, throw a |
| // TypeError exception. |
| if (!IsName(*next)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kNotPropertyName, next), |
| FixedArray); |
| } |
| // 7d. Append next as the last element of list. |
| // Internalize on the fly so we can use pointer identity later. |
| next = isolate->factory()->InternalizeName(Handle<Name>::cast(next)); |
| break; |
| } |
| } |
| list->set(index, *next); |
| // 7e. Set index to index + 1. (See loop header.) |
| } |
| // 8. Return list. |
| return list; |
| } |
| |
| // static |
| MaybeHandle<Object> Object::GetLengthFromArrayLike(Isolate* isolate, |
| Handle<JSReceiver> object) { |
| Handle<Object> val; |
| Handle<Name> key = isolate->factory()->length_string(); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, val, JSReceiver::GetProperty(isolate, object, key), Object); |
| return Object::ToLength(isolate, val); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::GetProperty(LookupIterator* it, |
| bool is_global_reference) { |
| for (;; it->Next()) { |
| switch (it->state()) { |
| case LookupIterator::TRANSITION: |
| UNREACHABLE(); |
| case LookupIterator::JSPROXY: { |
| bool was_found; |
| Handle<Object> receiver = it->GetReceiver(); |
| // In case of global IC, the receiver is the global object. Replace by |
| // the global proxy. |
| if (IsJSGlobalObject(*receiver)) { |
| receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), |
| it->isolate()); |
| } |
| if (is_global_reference) { |
| Maybe<bool> maybe = JSProxy::HasProperty( |
| it->isolate(), it->GetHolder<JSProxy>(), it->GetName()); |
| if (maybe.IsNothing()) return MaybeHandle<Object>(); |
| if (!maybe.FromJust()) { |
| it->NotFound(); |
| return it->isolate()->factory()->undefined_value(); |
| } |
| } |
| MaybeHandle<Object> result = |
| JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(), |
| it->GetName(), receiver, &was_found); |
| if (!was_found && !is_global_reference) it->NotFound(); |
| return result; |
| } |
| case LookupIterator::WASM_OBJECT: |
| return it->isolate()->factory()->undefined_value(); |
| case LookupIterator::INTERCEPTOR: { |
| bool done; |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| it->isolate(), result, |
| JSObject::GetPropertyWithInterceptor(it, &done), Object); |
| if (done) return result; |
| continue; |
| } |
| case LookupIterator::ACCESS_CHECK: |
| if (it->HasAccess()) continue; |
| return JSObject::GetPropertyWithFailedAccessCheck(it); |
| case LookupIterator::ACCESSOR: |
| return GetPropertyWithAccessor(it); |
| case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND: |
| return it->isolate()->factory()->undefined_value(); |
| case LookupIterator::DATA: |
| return it->GetDataValue(); |
| case LookupIterator::NOT_FOUND: |
| if (it->IsPrivateName()) { |
| Handle<Symbol> private_symbol = Handle<Symbol>::cast(it->name()); |
| Handle<String> name_string( |
| String::cast(private_symbol->description()), it->isolate()); |
| if (private_symbol->is_private_brand()) { |
| Handle<String> class_name = |
| (name_string->length() == 0) |
| ? it->isolate()->factory()->anonymous_string() |
| : name_string; |
| THROW_NEW_ERROR( |
| it->isolate(), |
| NewTypeError(MessageTemplate::kInvalidPrivateBrandInstance, |
| class_name), |
| Object); |
| } |
| THROW_NEW_ERROR( |
| it->isolate(), |
| NewTypeError(MessageTemplate::kInvalidPrivateMemberRead, |
| name_string), |
| Object); |
| } |
| |
| return it->isolate()->factory()->undefined_value(); |
| } |
| UNREACHABLE(); |
| } |
| } |
| |
| // static |
| MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate, |
| Handle<JSProxy> proxy, |
| Handle<Name> name, |
| Handle<Object> receiver, |
| bool* was_found) { |
| *was_found = true; |
| |
| DCHECK(!name->IsPrivate()); |
| STACK_CHECK(isolate, MaybeHandle<Object>()); |
| Handle<Name> trap_name = isolate->factory()->get_string(); |
| // 1. Assert: IsPropertyKey(P) is true. |
| // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. |
| Handle<Object> handler(proxy->handler(), isolate); |
| // 3. If handler is null, throw a TypeError exception. |
| // 4. Assert: Type(handler) is Object. |
| if (proxy->IsRevoked()) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kProxyRevoked, trap_name), |
| Object); |
| } |
| // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. |
| Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); |
| // 6. Let trap be ? GetMethod(handler, "get"). |
| Handle<Object> trap; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, trap, |
| Object::GetMethod(isolate, Handle<JSReceiver>::cast(handler), trap_name), |
| Object); |
| // 7. If trap is undefined, then |
| if (IsUndefined(*trap, isolate)) { |
| // 7.a Return target.[[Get]](P, Receiver). |
| PropertyKey key(isolate, name); |
| LookupIterator it(isolate, receiver, key, target); |
| MaybeHandle<Object> result = Object::GetProperty(&it); |
| *was_found = it.IsFound(); |
| return result; |
| } |
| // 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»). |
| Handle<Object> trap_result; |
| Handle<Object> args[] = {target, name, receiver}; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, trap_result, |
| Execution::Call(isolate, trap, handler, arraysize(args), args), Object); |
| |
| MaybeHandle<Object> result = |
| JSProxy::CheckGetSetTrapResult(isolate, name, target, trap_result, kGet); |
| if (result.is_null()) { |
| return result; |
| } |
| |
| // 11. Return trap_result |
| return trap_result; |
| } |
| |
| // static |
| MaybeHandle<Object> JSProxy::CheckGetSetTrapResult(Isolate* isolate, |
| Handle<Name> name, |
| Handle<JSReceiver> target, |
| Handle<Object> trap_result, |
| AccessKind access_kind) { |
| // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). |
| PropertyDescriptor target_desc; |
| Maybe<bool> target_found = |
| JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); |
| MAYBE_RETURN_NULL(target_found); |
| // 10. If targetDesc is not undefined, then |
| if (target_found.FromJust()) { |
| // 10.a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is |
| // false and targetDesc.[[Writable]] is false, then |
| // 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false, |
| // throw a TypeError exception. |
| bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) && |
| !target_desc.configurable() && |
| !target_desc.writable() && |
| !Object::SameValue(*trap_result, *target_desc.value()); |
| if (inconsistent) { |
| if (access_kind == kGet) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kProxyGetNonConfigurableData, name, |
| target_desc.value(), trap_result), |
| Object); |
| } else { |
| isolate->Throw(*isolate->factory()->NewTypeError( |
| MessageTemplate::kProxySetFrozenData, name)); |
| return MaybeHandle<Object>(); |
| } |
| } |
| // 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] |
| // is false and targetDesc.[[Get]] is undefined, then |
| // 10.b.i. If trapResult is not undefined, throw a TypeError exception. |
| if (access_kind == kGet) { |
| inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && |
| !target_desc.configurable() && |
| IsUndefined(*target_desc.get(), isolate) && |
| !IsUndefined(*trap_result, isolate); |
| } else { |
| inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && |
| !target_desc.configurable() && |
| IsUndefined(*target_desc.set(), isolate); |
| } |
| if (inconsistent) { |
| if (access_kind == kGet) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor, |
| name, trap_result), |
| Object); |
| } else { |
| isolate->Throw(*isolate->factory()->NewTypeError( |
| MessageTemplate::kProxySetFrozenAccessor, name)); |
| return MaybeHandle<Object>(); |
| } |
| } |
| } |
| return isolate->factory()->undefined_value(); |
| } |
| |
| // static |
| bool Object::ToInt32(Tagged<Object> obj, int32_t* value) { |
| if (IsSmi(obj)) { |
| *value = Smi::ToInt(obj); |
| return true; |
| } |
| if (IsHeapNumber(obj)) { |
| double num = HeapNumber::cast(obj)->value(); |
| // Check range before conversion to avoid undefined behavior. |
| if (num >= kMinInt && num <= kMaxInt && FastI2D(FastD2I(num)) == num) { |
| *value = FastD2I(num); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // ES6 9.5.1 |
| // static |
| MaybeHandle<HeapObject> JSProxy::GetPrototype(Handle<JSProxy> proxy) { |
| Isolate* isolate = proxy->GetIsolate(); |
| Handle<String> trap_name = isolate->factory()->getPrototypeOf_string(); |
| |
| STACK_CHECK(isolate, MaybeHandle<HeapObject>()); |
| |
| // 1. Let handler be the value of the [[ProxyHandler]] internal slot. |
| // 2. If handler is null, throw a TypeError exception. |
| // 3. Assert: Type(handler) is Object. |
| // 4. Let target be the value of the [[ProxyTarget]] internal slot. |
| if (proxy->IsRevoked()) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kProxyRevoked, trap_name), |
| HeapObject); |
| } |
| Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); |
| Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); |
| |
| // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). |
| Handle<Object> trap; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, trap, |
| Object::GetMethod(isolate, handler, trap_name), |
| HeapObject); |
| // 6. If trap is undefined, then return target.[[GetPrototypeOf]](). |
| if (IsUndefined(*trap, isolate)) { |
| return JSReceiver::GetPrototype(isolate, target); |
| } |
| // 7. Let handlerProto be ? Call(trap, handler, «target»). |
| Handle<Object> argv[] = {target}; |
| Handle<Object> handler_proto; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, handler_proto, |
| Execution::Call(isolate, trap, handler, arraysize(argv), argv), |
| HeapObject); |
| // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError. |
| if (!(IsJSReceiver(*handler_proto) || IsNull(*handler_proto, isolate))) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid), |
| HeapObject); |
| } |
| // 9. Let extensibleTarget be ? IsExtensible(target). |
| Maybe<bool> is_extensible = JSReceiver::IsExtensible(isolate, target); |
| MAYBE_RETURN(is_extensible, MaybeHandle<HeapObject>()); |
| // 10. If extensibleTarget is true, return handlerProto. |
| if (is_extensible.FromJust()) return Handle<HeapObject>::cast(handler_proto); |
| // 11. Let targetProto be ? target.[[GetPrototypeOf]](). |
| Handle<HeapObject> target_proto; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto, |
| JSReceiver::GetPrototype(isolate, target), |
| HeapObject); |
| // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError. |
| if (!Object::SameValue(*handler_proto, *target_proto)) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible), |
| HeapObject); |
| } |
| // 13. Return handlerProto. |
| return Handle<HeapObject>::cast(handler_proto); |
| } |
| |
| MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) { |
| Isolate* isolate = it->isolate(); |
| Handle<Object> structure = it->GetAccessors(); |
| Handle<Object> receiver = it->GetReceiver(); |
| // In case of global IC, the receiver is the global object. Replace by the |
| // global proxy. |
| if (IsJSGlobalObject(*receiver)) { |
| receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), isolate); |
| } |
| |
| // We should never get here to initialize a const with the hole value since a |
| // const declaration would conflict with the getter. |
| DCHECK(!IsForeign(*structure)); |
| |
| // API style callbacks. |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| if (IsAccessorInfo(*structure)) { |
| Handle<Name> name = it->GetName(); |
| Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure); |
| |
| if (!info->has_getter(isolate)) { |
| return isolate->factory()->undefined_value(); |
| } |
| |
| if (info->is_sloppy() && !IsJSReceiver(*receiver)) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver, |
| Object::ConvertReceiver(isolate, receiver), |
| Object); |
| } |
| |
| PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder, |
| Just(kDontThrow)); |
| Handle<Object> result = args.CallAccessorGetter(info, name); |
| RETURN_EXCEPTION_IF_EXCEPTION(isolate, Object); |
| if (result.is_null()) return isolate->factory()->undefined_value(); |
| Handle<Object> reboxed_result = handle(*result, isolate); |
| if (info->replace_on_access() && IsJSReceiver(*receiver)) { |
| RETURN_ON_EXCEPTION(isolate, |
| Accessors::ReplaceAccessorWithDataProperty( |
| isolate, receiver, holder, name, result), |
| Object); |
| } |
| return reboxed_result; |
| } |
| |
| Handle<AccessorPair> accessor_pair = Handle<AccessorPair>::cast(structure); |
| // AccessorPair with 'cached' private property. |
| if (it->TryLookupCachedProperty(accessor_pair)) { |
| return Object::GetProperty(it); |
| } |
| |
| // Regular accessor. |
| Handle<Object> getter(accessor_pair->getter(), isolate); |
| if (IsFunctionTemplateInfo(*getter)) { |
| SaveAndSwitchContext save(isolate, holder->GetCreationContext().value()); |
| return Builtins::InvokeApiFunction( |
| isolate, false, Handle<FunctionTemplateInfo>::cast(getter), receiver, 0, |
| nullptr, isolate->factory()->undefined_value()); |
| } else if (IsCallable(*getter)) { |
| // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| return Object::GetPropertyWithDefinedGetter( |
| receiver, Handle<JSReceiver>::cast(getter)); |
| } |
| // Getter is not a function. |
| return isolate->factory()->undefined_value(); |
| } |
| |
| Maybe<bool> Object::SetPropertyWithAccessor( |
| LookupIterator* it, Handle<Object> value, |
| Maybe<ShouldThrow> maybe_should_throw) { |
| Isolate* isolate = it->isolate(); |
| Handle<Object> structure = it->GetAccessors(); |
| Handle<Object> receiver = it->GetReceiver(); |
| // In case of global IC, the receiver is the global object. Replace by the |
| // global proxy. |
| if (IsJSGlobalObject(*receiver)) { |
| receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), isolate); |
| } |
| |
| // We should never get here to initialize a const with the hole value since a |
| // const declaration would conflict with the setter. |
| DCHECK(!IsForeign(*structure)); |
| |
| // API style callbacks. |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| if (IsAccessorInfo(*structure)) { |
| Handle<Name> name = it->GetName(); |
| Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure); |
| |
| if (!info->has_setter(isolate)) { |
| // TODO(verwaest): We should not get here anymore once all AccessorInfos |
| // are marked as special_data_property. They cannot both be writable and |
| // not have a setter. |
| return Just(true); |
| } |
| |
| if (info->is_sloppy() && !IsJSReceiver(*receiver)) { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, receiver, Object::ConvertReceiver(isolate, receiver), |
| Nothing<bool>()); |
| } |
| |
| // The actual type of setter callback is either |
| // v8::AccessorNameSetterCallback or |
| // i::Accesors::AccessorNameBooleanSetterCallback, depending on whether the |
| // AccessorInfo was created by the API or internally (see accessors.cc). |
| // Here we handle both cases using GenericNamedPropertySetterCallback and |
| // its Call method. |
| PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder, |
| maybe_should_throw); |
| Handle<Object> result = args.CallAccessorSetter(info, name, value); |
| // In the case of AccessorNameSetterCallback, we know that the result value |
| // cannot have been set, so the result of Call will be null. In the case of |
| // AccessorNameBooleanSetterCallback, the result will either be null |
| // (signalling an exception) or a boolean Oddball. |
| RETURN_VALUE_IF_EXCEPTION(isolate, Nothing<bool>()); |
| if (result.is_null()) return Just(true); |
| DCHECK(Object::BooleanValue(*result, isolate) || |
| GetShouldThrow(isolate, maybe_should_throw) == kDontThrow); |
| return Just(Object::BooleanValue(*result, isolate)); |
| } |
| |
| // Regular accessor. |
| Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); |
| if (IsFunctionTemplateInfo(*setter)) { |
| SaveAndSwitchContext save(isolate, holder->GetCreationContext().value()); |
| Handle<Object> argv[] = {value}; |
| RETURN_ON_EXCEPTION_VALUE( |
| isolate, |
| Builtins::InvokeApiFunction(isolate, false, |
| Handle<FunctionTemplateInfo>::cast(setter), |
| receiver, arraysize(argv), argv, |
| isolate->factory()->undefined_value()), |
| Nothing<bool>()); |
| return Just(true); |
| } else if (IsCallable(*setter)) { |
| // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| return SetPropertyWithDefinedSetter( |
| receiver, Handle<JSReceiver>::cast(setter), value, maybe_should_throw); |
| } |
| |
| RETURN_FAILURE(isolate, GetShouldThrow(isolate, maybe_should_throw), |
| NewTypeError(MessageTemplate::kNoSetterInCallback, |
| it->GetName(), it->GetHolder<JSObject>())); |
| } |
| |
| MaybeHandle<Object> Object::GetPropertyWithDefinedGetter( |
| Handle<Object> receiver, Handle<JSReceiver> getter) { |
| Isolate* isolate = getter->GetIsolate(); |
| |
| // Platforms with simulators like arm/arm64 expose a funny issue. If the |
| // simulator has a separate JS stack pointer from the C++ stack pointer, it |
| // can miss C++ stack overflows in the stack guard at the start of JavaScript |
| // functions. It would be very expensive to check the C++ stack pointer at |
| // that location. The best solution seems to be to break the impasse by |
| // adding checks at possible recursion points. What's more, we don't put |
| // this stack check behind the USE_SIMULATOR define in order to keep |
| // behavior the same between hardware and simulators. |
| StackLimitCheck check(isolate); |
| if (check.JsHasOverflowed()) { |
| isolate->StackOverflow(); |
| return MaybeHandle<Object>(); |
| } |
| |
| return Execution::Call(isolate, getter, receiver, 0, nullptr); |
| } |
| |
| Maybe<bool> Object::SetPropertyWithDefinedSetter( |
| Handle<Object> receiver, Handle<JSReceiver> setter, Handle<Object> value, |
| Maybe<ShouldThrow> should_throw) { |
| Isolate* isolate = setter->GetIsolate(); |
| |
| Handle<Object> argv[] = {value}; |
| RETURN_ON_EXCEPTION_VALUE( |
| isolate, |
| Execution::Call(isolate, setter, receiver, arraysize(argv), argv), |
| Nothing<bool>()); |
| return Just(true); |
| } |
| |
| // static |
| Tagged<Map> Object::GetPrototypeChainRootMap(Tagged<Object> obj, |
| Isolate* isolate) { |
| DisallowGarbageCollection no_alloc; |
| if (IsSmi(obj)) { |
| Tagged<Context> native_context = isolate->context()->native_context(); |
| return native_context->number_function()->initial_map(); |
| } |
| |
| const Tagged<HeapObject> heap_object = HeapObject::cast(obj); |
| return heap_object->map()->GetPrototypeChainRootMap(isolate); |
| } |
| |
| // static |
| Tagged<Smi> Object::GetOrCreateHash(Tagged<Object> obj, Isolate* isolate) { |
| DisallowGarbageCollection no_gc; |
| Tagged<Object> hash = Object::GetSimpleHash(obj); |
| if (IsSmi(hash)) return Smi::cast(hash); |
| |
| DCHECK(IsJSReceiver(obj)); |
| return JSReceiver::cast(obj)->GetOrCreateIdentityHash(isolate); |
| } |
| |
| // static |
| bool Object::SameValue(Tagged<Object> obj, Tagged<Object> other) { |
| if (other == obj) return true; |
| |
| if (IsNumber(obj) && IsNumber(other)) { |
| return SameNumberValue(Object::Number(obj), Object::Number(other)); |
| } |
| if (IsString(obj) && IsString(other)) { |
| return String::cast(obj)->Equals(String::cast(other)); |
| } |
| if (IsBigInt(obj) && IsBigInt(other)) { |
| return BigInt::EqualToBigInt(BigInt::cast(obj), BigInt::cast(other)); |
| } |
| return false; |
| } |
| |
| // static |
| bool Object::SameValueZero(Tagged<Object> obj, Tagged<Object> other) { |
| if (other == obj) return true; |
| |
| if (IsNumber(obj) && IsNumber(other)) { |
| double this_value = Object::Number(obj); |
| double other_value = Object::Number(other); |
| // +0 == -0 is true |
| return this_value == other_value || |
| (std::isnan(this_value) && std::isnan(other_value)); |
| } |
| if (IsString(obj) && IsString(other)) { |
| return String::cast(obj)->Equals(String::cast(other)); |
| } |
| if (IsBigInt(obj) && IsBigInt(other)) { |
| return BigInt::EqualToBigInt(BigInt::cast(obj), BigInt::cast(other)); |
| } |
| return false; |
| } |
| |
| MaybeHandle<Object> Object::ArraySpeciesConstructor( |
| Isolate* isolate, Handle<Object> original_array) { |
| Handle<Object> default_species = isolate->array_function(); |
| if (!v8_flags.builtin_subclassing) return default_species; |
| if (IsJSArray(*original_array) && |
| Handle<JSArray>::cast(original_array)->HasArrayPrototype(isolate) && |
| Protectors::IsArraySpeciesLookupChainIntact(isolate)) { |
| return default_species; |
| } |
| Handle<Object> constructor = isolate->factory()->undefined_value(); |
| Maybe<bool> is_array = IsArray(original_array); |
| MAYBE_RETURN_NULL(is_array); |
| if (is_array.FromJust()) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, constructor, |
| Object::GetProperty(isolate, original_array, |
| isolate->factory()->constructor_string()), |
| Object); |
| if (IsConstructor(*constructor)) { |
| Handle<NativeContext> constructor_context; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, constructor_context, |
| JSReceiver::GetFunctionRealm(Handle<JSReceiver>::cast(constructor)), |
| Object); |
| if (*constructor_context != *isolate->native_context() && |
| *constructor == constructor_context->array_function()) { |
| constructor = isolate->factory()->undefined_value(); |
| } |
| } |
| if (IsJSReceiver(*constructor)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, constructor, |
| JSReceiver::GetProperty(isolate, |
| Handle<JSReceiver>::cast(constructor), |
| isolate->factory()->species_symbol()), |
| Object); |
| if (IsNull(*constructor, isolate)) { |
| constructor = isolate->factory()->undefined_value(); |
| } |
| } |
| } |
| if (IsUndefined(*constructor, isolate)) { |
| return default_species; |
| } else { |
| if (!IsConstructor(*constructor)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kSpeciesNotConstructor), |
| Object); |
| } |
| return constructor; |
| } |
| } |
| |
| // ES6 section 7.3.20 SpeciesConstructor ( O, defaultConstructor ) |
| V8_WARN_UNUSED_RESULT MaybeHandle<Object> Object::SpeciesConstructor( |
| Isolate* isolate, Handle<JSReceiver> recv, |
| Handle<JSFunction> default_ctor) { |
| Handle<Object> ctor_obj; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, ctor_obj, |
| JSObject::GetProperty(isolate, recv, |
| isolate->factory()->constructor_string()), |
| Object); |
| |
| if (IsUndefined(*ctor_obj, isolate)) return default_ctor; |
| |
| if (!IsJSReceiver(*ctor_obj)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kConstructorNotReceiver), |
| Object); |
| } |
| |
| Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj); |
| |
| Handle<Object> species; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, species, |
| JSObject::GetProperty(isolate, ctor, |
| isolate->factory()->species_symbol()), |
| Object); |
| |
| if (IsNullOrUndefined(*species, isolate)) { |
| return default_ctor; |
| } |
| |
| if (IsConstructor(*species)) return species; |
| |
| THROW_NEW_ERROR( |
| isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object); |
| } |
| |
| // static |
| bool Object::IterationHasObservableEffects(Tagged<Object> obj) { |
| DisallowGarbageCollection no_gc; |
| // Check that this object is an array. |
| if (!IsJSArray(obj)) return true; |
| Tagged<JSArray> array = JSArray::cast(obj); |
| |
| // Check that we have the original ArrayPrototype. |
| Tagged<Object> array_proto = array->map()->prototype(); |
| if (!IsJSObject(array_proto)) return true; |
| Tagged<NativeContext> native_context = array->GetCreationContext().value(); |
| auto initial_array_prototype = native_context->initial_array_prototype(); |
| if (initial_array_prototype != JSObject::cast(array_proto)) return true; |
| |
| Isolate* isolate = array->GetIsolate(); |
| // Check that the ArrayPrototype hasn't been modified in a way that would |
| // affect iteration. |
| if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return true; |
| |
| // For FastPacked kinds, iteration will have the same effect as simply |
| // accessing each property in order. |
| ElementsKind array_kind = array->GetElementsKind(); |
| if (IsFastPackedElementsKind(array_kind)) return false; |
| |
| // For FastHoley kinds, an element access on a hole would cause a lookup on |
| // the prototype. This could have different results if the prototype has been |
| // changed. |
| if (IsHoleyElementsKind(array_kind) && |
| Protectors::IsNoElementsIntact(isolate)) { |
| return false; |
| } |
| return true; |
| } |
| |
| // static |
| bool Object::IsCodeLike(Tagged<Object> obj, Isolate* isolate) { |
| DisallowGarbageCollection no_gc; |
| return IsJSReceiver(obj) && JSReceiver::cast(obj)->IsCodeLike(isolate); |
| } |
| |
| void ShortPrint(Tagged<Object> obj, FILE* out) { |
| OFStream os(out); |
| os << Brief(obj); |
| } |
| |
| void ShortPrint(Tagged<Object> obj, StringStream* accumulator) { |
| std::ostringstream os; |
| os << Brief(obj); |
| accumulator->Add(os.str().c_str()); |
| } |
| |
| void ShortPrint(Tagged<Object> obj, std::ostream& os) { os << Brief(obj); } |
| |
| std::ostream& operator<<(std::ostream& os, Tagged<Object> obj) { |
| ShortPrint(obj, os); |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const Brief& v) { |
| MaybeObject maybe_object(v.value); |
| Tagged<Smi> smi; |
| Tagged<HeapObject> heap_object; |
| if (maybe_object->ToSmi(&smi)) { |
| Smi::SmiPrint(smi, os); |
| } else if (maybe_object->IsCleared()) { |
| os << "[cleared]"; |
| } else if (maybe_object.GetHeapObjectIfWeak(&heap_object)) { |
| os << "[weak] "; |
| heap_object->HeapObjectShortPrint(os); |
| } else if (maybe_object.GetHeapObjectIfStrong(&heap_object)) { |
| heap_object->HeapObjectShortPrint(os); |
| } else { |
| UNREACHABLE(); |
| } |
| return os; |
| } |
| |
| // static |
| void Smi::SmiPrint(Tagged<Smi> smi, std::ostream& os) { os << smi.value(); } |
| |
| void Struct::BriefPrintDetails(std::ostream& os) {} |
| |
| void Tuple2::BriefPrintDetails(std::ostream& os) { |
| os << " " << Brief(value1()) << ", " << Brief(value2()); |
| } |
| |
| void MegaDomHandler::BriefPrintDetails(std::ostream& os) { |
| os << " " << Brief(accessor(kAcquireLoad)) << ", " << Brief(context()); |
| } |
| |
| void ClassPositions::BriefPrintDetails(std::ostream& os) { |
| os << " " << start() << ", " << end(); |
| } |
| |
| void CallableTask::BriefPrintDetails(std::ostream& os) { |
| os << " callable=" << Brief(callable()); |
| } |
| |
| void HeapObject::Iterate(PtrComprCageBase cage_base, ObjectVisitor* v) { |
| IterateFast<ObjectVisitor>(cage_base, v); |
| } |
| |
| void HeapObject::IterateBody(PtrComprCageBase cage_base, ObjectVisitor* v) { |
| Tagged<Map> m = map(cage_base); |
| IterateBodyFast<ObjectVisitor>(m, SizeFromMap(m), v); |
| } |
| |
| void HeapObject::IterateBody(Tagged<Map> map, int object_size, |
| ObjectVisitor* v) { |
| IterateBodyFast<ObjectVisitor>(map, object_size, v); |
| } |
| |
| int HeapObjectLayout::SizeFromMap(Tagged<Map> map) const { |
| return Tagged<HeapObject>(this)->SizeFromMap(map); |
| } |
| |
| int HeapObject::SizeFromMap(Tagged<Map> map) const { |
| int instance_size = map->instance_size(); |
| if (instance_size != kVariableSizeSentinel) return instance_size; |
| // Only inline the most frequent cases. |
| InstanceType instance_type = map->instance_type(); |
| if (base::IsInRange(instance_type, FIRST_FIXED_ARRAY_TYPE, |
| LAST_FIXED_ARRAY_TYPE)) { |
| return FixedArray::unchecked_cast(*this)->AllocatedSize(); |
| } |
| #define CASE(TypeCamelCase, TYPE_UPPER_CASE) \ |
| if (instance_type == TYPE_UPPER_CASE##_TYPE) { \ |
| return TypeCamelCase::unchecked_cast(*this)->AllocatedSize(); \ |
| } |
| SIMPLE_HEAP_OBJECT_LIST2(CASE) |
| #undef CASE |
| if (instance_type == SLOPPY_ARGUMENTS_ELEMENTS_TYPE) { |
| return SloppyArgumentsElements::unchecked_cast(*this)->AllocatedSize(); |
| } |
| if (base::IsInRange(instance_type, FIRST_CONTEXT_TYPE, LAST_CONTEXT_TYPE)) { |
| if (instance_type == NATIVE_CONTEXT_TYPE) return NativeContext::kSize; |
| return Context::SizeFor(Context::unchecked_cast(*this)->length()); |
| } |
| if (instance_type == SEQ_ONE_BYTE_STRING_TYPE || |
| instance_type == INTERNALIZED_ONE_BYTE_STRING_TYPE || |
| instance_type == SHARED_SEQ_ONE_BYTE_STRING_TYPE) { |
| // Strings may get concurrently truncated, hence we have to access its |
| // length synchronized. |
| return SeqOneByteString::SizeFor( |
| SeqOneByteString::unchecked_cast(*this)->length(kAcquireLoad)); |
| } |
| if (instance_type == BYTECODE_ARRAY_TYPE) { |
| return BytecodeArray::SizeFor( |
| BytecodeArray::unchecked_cast(*this)->length(kAcquireLoad)); |
| } |
| if (instance_type == EXTERNAL_POINTER_ARRAY_TYPE) { |
| return ExternalPointerArray::SizeFor( |
| ExternalPointerArray::unchecked_cast(*this)->length(kAcquireLoad)); |
| } |
| if (instance_type == FREE_SPACE_TYPE) { |
| return FreeSpace::unchecked_cast(*this)->size(kRelaxedLoad); |
| } |
| if (instance_type == SEQ_TWO_BYTE_STRING_TYPE || |
| instance_type == INTERNALIZED_TWO_BYTE_STRING_TYPE || |
| instance_type == SHARED_SEQ_TWO_BYTE_STRING_TYPE) { |
| // Strings may get concurrently truncated, hence we have to access its |
| // length synchronized. |
| return SeqTwoByteString::SizeFor( |
| SeqTwoByteString::unchecked_cast(*this)->length(kAcquireLoad)); |
| } |
| if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) { |
| return FixedDoubleArray::unchecked_cast(*this)->AllocatedSize(); |
| } |
| if (instance_type == TRUSTED_FIXED_ARRAY_TYPE) { |
| return TrustedFixedArray::unchecked_cast(*this)->AllocatedSize(); |
| } |
| if (instance_type == TRUSTED_BYTE_ARRAY_TYPE) { |
| return TrustedByteArray::unchecked_cast(*this)->AllocatedSize(); |
| } |
| if (instance_type == FEEDBACK_METADATA_TYPE) { |
| return FeedbackMetadata::SizeFor( |
| FeedbackMetadata::unchecked_cast(*this)->slot_count(kAcquireLoad)); |
| } |
| if (base::IsInRange(instance_type, FIRST_DESCRIPTOR_ARRAY_TYPE, |
| LAST_DESCRIPTOR_ARRAY_TYPE)) { |
| return DescriptorArray::SizeFor( |
| DescriptorArray::unchecked_cast(*this)->number_of_all_descriptors()); |
| } |
| if (base::IsInRange(instance_type, FIRST_WEAK_FIXED_ARRAY_TYPE, |
| LAST_WEAK_FIXED_ARRAY_TYPE)) { |
| return WeakFixedArray::unchecked_cast(*this)->AllocatedSize(); |
| } |
| if (instance_type == WEAK_ARRAY_LIST_TYPE) { |
| return WeakArrayList::SizeForCapacity( |
| WeakArrayList::unchecked_cast(*this)->capacity()); |
| } |
| if (instance_type == SMALL_ORDERED_HASH_SET_TYPE) { |
| return SmallOrderedHashSet::SizeFor( |
| SmallOrderedHashSet::unchecked_cast(*this)->Capacity()); |
| } |
| if (instance_type == SMALL_ORDERED_HASH_MAP_TYPE) { |
| return SmallOrderedHashMap::SizeFor( |
| SmallOrderedHashMap::unchecked_cast(*this)->Capacity()); |
| } |
| if (instance_type == SMALL_ORDERED_NAME_DICTIONARY_TYPE) { |
| return SmallOrderedNameDictionary::SizeFor( |
| SmallOrderedNameDictionary::unchecked_cast(*this)->Capacity()); |
| } |
| if (instance_type == SWISS_NAME_DICTIONARY_TYPE) { |
| return SwissNameDictionary::SizeFor( |
| SwissNameDictionary::unchecked_cast(*this)->Capacity()); |
| } |
| if (instance_type == PROPERTY_ARRAY_TYPE) { |
| return PropertyArray::SizeFor( |
| PropertyArray::unchecked_cast(*this)->length(kAcquireLoad)); |
| } |
| if (instance_type == FEEDBACK_VECTOR_TYPE) { |
| return FeedbackVector::SizeFor( |
| FeedbackVector::unchecked_cast(*this)->length()); |
| } |
| if (instance_type == BIGINT_TYPE) { |
| return BigInt::SizeFor(BigInt::unchecked_cast(*this)->length()); |
| } |
| if (instance_type == PREPARSE_DATA_TYPE) { |
| Tagged<PreparseData> data = PreparseData::unchecked_cast(*this); |
| return PreparseData::SizeFor(data->data_length(), data->children_length()); |
| } |
| #define MAKE_TORQUE_SIZE_FOR(TYPE, TypeName) \ |
| if (instance_type == TYPE) { \ |
| return TypeName::unchecked_cast(*this)->AllocatedSize(); \ |
| } |
| TORQUE_INSTANCE_TYPE_TO_BODY_DESCRIPTOR_LIST(MAKE_TORQUE_SIZE_FOR) |
| #undef MAKE_TORQUE_SIZE_FOR |
| |
| if (instance_type == INSTRUCTION_STREAM_TYPE) { |
| return InstructionStream::unchecked_cast(*this)->Size(); |
| } |
| if (instance_type == COVERAGE_INFO_TYPE) { |
| return CoverageInfo::SizeFor( |
| CoverageInfo::unchecked_cast(*this)->slot_count()); |
| } |
| #if V8_ENABLE_WEBASSEMBLY |
| if (instance_type == WASM_TYPE_INFO_TYPE) { |
| return WasmTypeInfo::SizeFor( |
| WasmTypeInfo::unchecked_cast(*this)->supertypes_length()); |
| } |
| if (instance_type == WASM_STRUCT_TYPE) { |
| return WasmStruct::GcSafeSize(map); |
| } |
| if (instance_type == WASM_ARRAY_TYPE) { |
| return WasmArray::SizeFor(map, WasmArray::unchecked_cast(*this)->length()); |
| } |
| if (instance_type == WASM_NULL_TYPE) { |
| return WasmNull::kSize; |
| } |
| #endif // V8_ENABLE_WEBASSEMBLY |
| DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE); |
| return EmbedderDataArray::SizeFor( |
| EmbedderDataArray::unchecked_cast(*this)->length()); |
| } |
| |
| bool HeapObject::NeedsRehashing(PtrComprCageBase cage_base) const { |
| return NeedsRehashing(map(cage_base)->instance_type()); |
| } |
| |
| bool HeapObject::NeedsRehashing(InstanceType instance_type) const { |
| if (V8_EXTERNAL_CODE_SPACE_BOOL) { |
| // Use map() only when it's guaranteed that it's not a InstructionStream |
| // object. |
| DCHECK_IMPLIES(instance_type != INSTRUCTION_STREAM_TYPE, |
| instance_type == map()->instance_type()); |
| } else { |
| DCHECK_EQ(instance_type, map()->instance_type()); |
| } |
| switch (instance_type) { |
| case DESCRIPTOR_ARRAY_TYPE: |
| case STRONG_DESCRIPTOR_ARRAY_TYPE: |
| return DescriptorArray::cast(*this)->number_of_descriptors() > 1; |
| case TRANSITION_ARRAY_TYPE: |
| return TransitionArray::cast(*this)->number_of_entries() > 1; |
| case ORDERED_HASH_MAP_TYPE: |
| case ORDERED_HASH_SET_TYPE: |
| return false; // We'll rehash from the JSMap or JSSet referencing them. |
| case NAME_DICTIONARY_TYPE: |
| case NAME_TO_INDEX_HASH_TABLE_TYPE: |
| case REGISTERED_SYMBOL_TABLE_TYPE: |
| case GLOBAL_DICTIONARY_TYPE: |
| case NUMBER_DICTIONARY_TYPE: |
| case SIMPLE_NUMBER_DICTIONARY_TYPE: |
| case HASH_TABLE_TYPE: |
| case SMALL_ORDERED_HASH_MAP_TYPE: |
| case SMALL_ORDERED_HASH_SET_TYPE: |
| case SMALL_ORDERED_NAME_DICTIONARY_TYPE: |
| case SWISS_NAME_DICTIONARY_TYPE: |
| case JS_MAP_TYPE: |
| case JS_SET_TYPE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool HeapObject::CanBeRehashed(PtrComprCageBase cage_base) const { |
| DCHECK(NeedsRehashing(cage_base)); |
| switch (map(cage_base)->instance_type()) { |
| case JS_MAP_TYPE: |
| case JS_SET_TYPE: |
| return true; |
| case ORDERED_HASH_MAP_TYPE: |
| case ORDERED_HASH_SET_TYPE: |
| UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them. |
| case ORDERED_NAME_DICTIONARY_TYPE: |
| return false; |
| case NAME_DICTIONARY_TYPE: |
| case NAME_TO_INDEX_HASH_TABLE_TYPE: |
| case REGISTERED_SYMBOL_TABLE_TYPE: |
| case GLOBAL_DICTIONARY_TYPE: |
| case NUMBER_DICTIONARY_TYPE: |
| case SIMPLE_NUMBER_DICTIONARY_TYPE: |
| case SWISS_NAME_DICTIONARY_TYPE: |
| return true; |
| case DESCRIPTOR_ARRAY_TYPE: |
| case STRONG_DESCRIPTOR_ARRAY_TYPE: |
| return true; |
| case TRANSITION_ARRAY_TYPE: |
| return true; |
| case SMALL_ORDERED_HASH_MAP_TYPE: |
| return SmallOrderedHashMap::cast(*this)->NumberOfElements() == 0; |
| case SMALL_ORDERED_HASH_SET_TYPE: |
| return SmallOrderedHashMap::cast(*this)->NumberOfElements() == 0; |
| case SMALL_ORDERED_NAME_DICTIONARY_TYPE: |
| return SmallOrderedNameDictionary::cast(*this)->NumberOfElements() == 0; |
| default: |
| return false; |
| } |
| } |
| |
| template <typename IsolateT> |
| void HeapObject::RehashBasedOnMap(IsolateT* isolate) { |
| switch (map(isolate)->instance_type()) { |
| case HASH_TABLE_TYPE: |
| UNREACHABLE(); |
| case NAME_DICTIONARY_TYPE: |
| NameDictionary::cast(*this)->Rehash(isolate); |
| break; |
| case NAME_TO_INDEX_HASH_TABLE_TYPE: |
| NameToIndexHashTable::cast(*this)->Rehash(isolate); |
| break; |
| case REGISTERED_SYMBOL_TABLE_TYPE: |
| RegisteredSymbolTable::cast(*this)->Rehash(isolate); |
| break; |
| case SWISS_NAME_DICTIONARY_TYPE: |
| SwissNameDictionary::cast(*this)->Rehash(isolate); |
| break; |
| case GLOBAL_DICTIONARY_TYPE: |
| GlobalDictionary::cast(*this)->Rehash(isolate); |
| break; |
| case NUMBER_DICTIONARY_TYPE: |
| NumberDictionary::cast(*this)->Rehash(isolate); |
| break; |
| case SIMPLE_NUMBER_DICTIONARY_TYPE: |
| SimpleNumberDictionary::cast(*this)->Rehash(isolate); |
| break; |
| case DESCRIPTOR_ARRAY_TYPE: |
| case STRONG_DESCRIPTOR_ARRAY_TYPE: |
| DCHECK_LE(1, DescriptorArray::cast(*this)->number_of_descriptors()); |
| DescriptorArray::cast(*this)->Sort(); |
| break; |
| case TRANSITION_ARRAY_TYPE: |
| TransitionArray::cast(*this)->Sort(); |
| break; |
| case SMALL_ORDERED_HASH_MAP_TYPE: |
| DCHECK_EQ(0, SmallOrderedHashMap::cast(*this)->NumberOfElements()); |
| break; |
| case SMALL_ORDERED_HASH_SET_TYPE: |
| DCHECK_EQ(0, SmallOrderedHashSet::cast(*this)->NumberOfElements()); |
| break; |
| case ORDERED_HASH_MAP_TYPE: |
| case ORDERED_HASH_SET_TYPE: |
| UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them. |
| case JS_MAP_TYPE: { |
| JSMap::cast(*this)->Rehash(isolate->AsIsolate()); |
| break; |
| } |
| case JS_SET_TYPE: { |
| JSSet::cast(*this)->Rehash(isolate->AsIsolate()); |
| break; |
| } |
| case SMALL_ORDERED_NAME_DICTIONARY_TYPE: |
| DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this)->NumberOfElements()); |
| break; |
| case INTERNALIZED_ONE_BYTE_STRING_TYPE: |
| case INTERNALIZED_TWO_BYTE_STRING_TYPE: |
| // Rare case, rehash read-only space strings before they are sealed. |
| DCHECK(ReadOnlyHeap::Contains(*this)); |
| String::cast(*this)->EnsureHash(); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| template void HeapObject::RehashBasedOnMap(Isolate* isolate); |
| template void HeapObject::RehashBasedOnMap(LocalIsolate* isolate); |
| |
| void DescriptorArray::GeneralizeAllFields() { |
| int length = number_of_descriptors(); |
| for (InternalIndex i : InternalIndex::Range(length)) { |
| PropertyDetails details = GetDetails(i); |
| details = details.CopyWithRepresentation(Representation::Tagged()); |
| if (details.location() == PropertyLocation::kField) { |
| DCHECK_EQ(PropertyKind::kData, details.kind()); |
| SetValue(i, MaybeObject::FromObject(FieldType::Any())); |
| } |
| SetDetails(i, details); |
| } |
| } |
| |
| MaybeHandle<Object> Object::SetProperty(Isolate* isolate, Handle<Object> object, |
| Handle<Name> name, Handle<Object> value, |
| StoreOrigin store_origin, |
| Maybe<ShouldThrow> should_throw) { |
| LookupIterator it(isolate, object, name); |
| MAYBE_RETURN_NULL(SetProperty(&it, value, store_origin, should_throw)); |
| return value; |
| } |
| |
| Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, |
| Handle<Object> value, |
| Maybe<ShouldThrow> should_throw, |
| StoreOrigin store_origin, bool* found) { |
| it->UpdateProtector(); |
| DCHECK(it->IsFound()); |
| |
| // Make sure that the top context does not change when doing callbacks or |
| // interceptor calls. |
| AssertNoContextChange ncc(it->isolate()); |
| |
| for (;; it->Next()) { |
| switch (it->state()) { |
| case LookupIterator::ACCESS_CHECK: |
| if (it->HasAccess()) continue; |
| // Check whether it makes sense to reuse the lookup iterator. Here it |
| // might still call into setters up the prototype chain. |
| return JSObject::SetPropertyWithFailedAccessCheck(it, value, |
| should_throw); |
| |
| case LookupIterator::JSPROXY: { |
| Handle<Object> receiver = it->GetReceiver(); |
| // In case of global IC, the receiver is the global object. Replace by |
| // the global proxy. |
| if (IsJSGlobalObject(*receiver)) { |
| receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), |
| it->isolate()); |
| } |
| return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(), |
| value, receiver, should_throw); |
| } |
| |
| case LookupIterator::WASM_OBJECT: |
| RETURN_FAILURE(it->isolate(), kThrowOnError, |
| NewTypeError(MessageTemplate::kWasmObjectsAreOpaque)); |
| |
| case LookupIterator::INTERCEPTOR: { |
| if (it->HolderIsReceiverOrHiddenPrototype()) { |
| Maybe<bool> result = |
| JSObject::SetPropertyWithInterceptor(it, should_throw, value); |
| if (result.IsNothing() || result.FromJust()) return result; |
| // Assuming that the callback have side effects, we use |
| // Object::SetSuperProperty() which works properly regardless on |
| // whether the property was present on the receiver or not when |
| // storing to the receiver. |
| // Proceed lookup from the next state. |
| it->Next(); |
| } else { |
| Maybe<PropertyAttributes> maybe_attributes = |
| JSObject::GetPropertyAttributesWithInterceptor(it); |
| if (maybe_attributes.IsNothing()) return Nothing<bool>(); |
| if ((maybe_attributes.FromJust() & READ_ONLY) != 0) { |
| return WriteToReadOnlyProperty(it, value, should_throw); |
| } |
| // At this point we might have called interceptor's query or getter |
| // callback. Assuming that the callbacks have side effects, we use |
| // Object::SetSuperProperty() which works properly regardless on |
| // whether the property was present on the receiver or not when |
| // storing to the receiver. |
| if (maybe_attributes.FromJust() == ABSENT) { |
| // Proceed lookup from the next state. |
| it->Next(); |
| } else { |
| // Finish lookup in order to make Object::SetSuperProperty() store |
| // property to the receiver. |
| it->NotFound(); |
| } |
| } |
| return Object::SetSuperProperty(it, value, store_origin, should_throw); |
| } |
| |
| case LookupIterator::ACCESSOR: { |
| if (it->IsReadOnly()) { |
| return WriteToReadOnlyProperty(it, value, should_throw); |
| } |
| Handle<Object> accessors = it->GetAccessors(); |
| if (IsAccessorInfo(*accessors) && |
| !it->HolderIsReceiverOrHiddenPrototype() && |
| AccessorInfo::cast(*accessors)->is_special_data_property()) { |
| *found = false; |
| return Nothing<bool>(); |
| } |
| return SetPropertyWithAccessor(it, value, should_throw); |
| } |
| case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND: { |
| // IntegerIndexedElementSet converts value to a Number/BigInt prior to |
| // the bounds check. The bounds check has already happened here, but |
| // perform the possibly effectful ToNumber (or ToBigInt) operation |
| // anyways. |
| Handle<JSTypedArray> holder = it->GetHolder<JSTypedArray>(); |
| Handle<Object> throwaway_value; |
| if (holder->type() == kExternalBigInt64Array || |
| holder->type() == kExternalBigUint64Array) { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| it->isolate(), throwaway_value, |
| BigInt::FromObject(it->isolate(), value), Nothing<bool>()); |
| } else { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| it->isolate(), throwaway_value, |
| Object::ToNumber(it->isolate(), value), Nothing<bool>()); |
| } |
| |
| // FIXME: Throw a TypeError if the holder is detached here |
| // (IntegerIndexedElementSpec step 5). |
| |
| // TODO(verwaest): Per spec, we should return false here (steps 6-9 |
| // in IntegerIndexedElementSpec), resulting in an exception being thrown |
| // on OOB accesses in strict code. Historically, v8 has not done made |
| // this change due to uncertainty about web compat. (v8:4901) |
| return Just(true); |
| } |
| |
| case LookupIterator::DATA: |
| if (it->IsReadOnly()) { |
| return WriteToReadOnlyProperty(it, value, should_throw); |
| } |
| if (it->HolderIsReceiverOrHiddenPrototype()) { |
| return SetDataProperty(it, value); |
| } |
| V8_FALLTHROUGH; |
| case LookupIterator::NOT_FOUND: |
| case LookupIterator::TRANSITION: |
| *found = false; |
| return Nothing<bool>(); |
| } |
| UNREACHABLE(); |
| } |
| } |
| |
| bool Object::CheckContextualStoreToJSGlobalObject( |
| LookupIterator* it, Maybe<ShouldThrow> should_throw) { |
| Isolate* isolate = it->isolate(); |
| |
| if (IsJSGlobalObject(*it->GetReceiver(), isolate) && |
| (GetShouldThrow(isolate, should_throw) == ShouldThrow::kThrowOnError)) { |
| if (it->state() == LookupIterator::TRANSITION) { |
| // The property cell that we have created is garbage because we are going |
| // to throw now instead of putting it into the global dictionary. However, |
| // the cell might already have been stored into the feedback vector, so |
| // we must invalidate it nevertheless. |
| it->transition_cell()->ClearAndInvalidate(ReadOnlyRoots(isolate)); |
| } |
| isolate->Throw(*isolate->factory()->NewReferenceError( |
| MessageTemplate::kNotDefined, it->GetName())); |
| return false; |
| } |
| return true; |
| } |
| |
| Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value, |
| StoreOrigin store_origin, |
| Maybe<ShouldThrow> should_throw) { |
| if (it->IsFound()) { |
| bool found = true; |
| Maybe<bool> result = |
| SetPropertyInternal(it, value, should_throw, store_origin, &found); |
| if (found) return result; |
| } |
| |
| if (!CheckContextualStoreToJSGlobalObject(it, should_throw)) { |
| return Nothing<bool>(); |
| } |
| return AddDataProperty(it, value, NONE, should_throw, store_origin); |
| } |
| |
| Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, |
| StoreOrigin store_origin, |
| Maybe<ShouldThrow> should_throw) { |
| Isolate* isolate = it->isolate(); |
| |
| if (it->IsFound()) { |
| bool found = true; |
| Maybe<bool> result = |
| SetPropertyInternal(it, value, should_throw, store_origin, &found); |
| if (found) return result; |
| } |
| |
| it->UpdateProtector(); |
| |
| // The property either doesn't exist on the holder or exists there as a data |
| // property. |
| |
| if (!IsJSReceiver(*it->GetReceiver())) { |
| return WriteToReadOnlyProperty(it, value, should_throw); |
| } |
| Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); |
| |
| // Note, the callers rely on the fact that this code is redoing the full own |
| // lookup from scratch. |
| LookupIterator own_lookup(isolate, receiver, it->GetKey(), |
| LookupIterator::OWN); |
| for (;; own_lookup.Next()) { |
| switch (own_lookup.state()) { |
| case LookupIterator::ACCESS_CHECK: |
| if (!own_lookup.HasAccess()) { |
| return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value, |
| should_throw); |
| } |
| continue; |
| |
| case LookupIterator::ACCESSOR: |
| if (IsAccessorInfo(*own_lookup.GetAccessors())) { |
| if (own_lookup.IsReadOnly()) { |
| return WriteToReadOnlyProperty(&own_lookup, value, should_throw); |
| } |
| return Object::SetPropertyWithAccessor(&own_lookup, value, |
| should_throw); |
| } |
| V8_FALLTHROUGH; |
| case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND: |
| return RedefineIncompatibleProperty(isolate, it->GetName(), value, |
| should_throw); |
| |
| case LookupIterator::DATA: { |
| if (own_lookup.IsReadOnly()) { |
| return WriteToReadOnlyProperty(&own_lookup, value, should_throw); |
| } |
| return SetDataProperty(&own_lookup, value); |
| } |
| |
| case LookupIterator::INTERCEPTOR: |
| case LookupIterator::JSPROXY: { |
| PropertyDescriptor desc; |
| Maybe<bool> owned = |
| JSReceiver::GetOwnPropertyDescriptor(&own_lookup, &desc); |
| MAYBE_RETURN(owned, Nothing<bool>()); |
| if (!owned.FromJust()) { |
| // |own_lookup| might become outdated at this point anyway. |
| own_lookup.Restart(); |
| if (!CheckContextualStoreToJSGlobalObject(&own_lookup, |
| should_throw)) { |
| return Nothing<bool>(); |
| } |
| return JSReceiver::CreateDataProperty(&own_lookup, value, |
| should_throw); |
| } |
| if (PropertyDescriptor::IsAccessorDescriptor(&desc) || |
| !desc.writable()) { |
| return RedefineIncompatibleProperty(isolate, it->GetName(), value, |
| should_throw); |
| } |
| |
| PropertyDescriptor value_desc; |
| value_desc.set_value(value); |
| return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(), |
| &value_desc, should_throw); |
| } |
| |
| case LookupIterator::NOT_FOUND: |
| if (!CheckContextualStoreToJSGlobalObject(&own_lookup, should_throw)) { |
| return Nothing<bool>(); |
| } |
| return AddDataProperty(&own_lookup, value, NONE, should_throw, |
| store_origin); |
| |
| case LookupIterator::TRANSITION: |
| case LookupIterator::WASM_OBJECT: |
| UNREACHABLE(); |
| } |
| UNREACHABLE(); |
| } |
| } |
| |
| Maybe<bool> Object::CannotCreateProperty(Isolate* isolate, |
| Handle<Object> receiver, |
| Handle<Object> name, |
| Handle<Object> value, |
| Maybe<ShouldThrow> should_throw) { |
| RETURN_FAILURE( |
| isolate, GetShouldThrow(isolate, should_throw), |
| NewTypeError(MessageTemplate::kStrictCannotCreateProperty, name, |
| Object::TypeOf(isolate, receiver), receiver)); |
| } |
| |
| Maybe<bool> Object::WriteToReadOnlyProperty( |
| LookupIterator* it, Handle<Object> value, |
| Maybe<ShouldThrow> maybe_should_throw) { |
| ShouldThrow should_throw = GetShouldThrow(it->isolate(), maybe_should_throw); |
| if (it->IsFound() && !it->HolderIsReceiver()) { |
| // "Override mistake" attempted, record a use count to track this per |
| // v8:8175 |
| v8::Isolate::UseCounterFeature feature = |
| should_throw == kThrowOnError |
| ? v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeStrict |
| : v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeSloppy; |
| it->isolate()->CountUsage(feature); |
| } |
| return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(), |
| it->GetName(), value, should_throw); |
| } |
| |
| Maybe<bool> Object::WriteToReadOnlyProperty(Isolate* isolate, |
| Handle<Object> receiver, |
| Handle<Object> name, |
| Handle<Object> value, |
| ShouldThrow should_throw) { |
| RETURN_FAILURE(isolate, should_throw, |
| NewTypeError(MessageTemplate::kStrictReadOnlyProperty, name, |
| Object::TypeOf(isolate, receiver), receiver)); |
| } |
| |
| Maybe<bool> Object::RedefineIncompatibleProperty( |
| Isolate* isolate, Handle<Object> name, Handle<Object> value, |
| Maybe<ShouldThrow> should_throw) { |
| RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), |
| NewTypeError(MessageTemplate::kRedefineDisallowed, name)); |
| } |
| |
| Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) { |
| Isolate* isolate = it->isolate(); |
| DCHECK_IMPLIES(IsJSProxy(*it->GetReceiver(), isolate), |
| it->GetName()->IsPrivateName()); |
| DCHECK_IMPLIES(!it->IsElement() && it->GetName()->IsPrivateName(), |
| it->state() == LookupIterator::DATA); |
| Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); |
| |
| // Store on the holder which may be hidden behind the receiver. |
| DCHECK(it->HolderIsReceiverOrHiddenPrototype()); |
| |
| Handle<Object> to_assign = value; |
| // Convert the incoming value to a number for storing into typed arrays. |
| if (it->IsElement() && IsJSObject(*receiver, isolate) && |
| JSObject::cast(*receiver)->HasTypedArrayOrRabGsabTypedArrayElements( |
| isolate)) { |
| Handle<JSTypedArray> receiver_ta = Handle<JSTypedArray>::cast(receiver); |
| ElementsKind elements_kind = JSObject::cast(*receiver)->GetElementsKind(); |
| if (IsBigIntTypedArrayElementsKind(elements_kind)) { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, to_assign, |
| BigInt::FromObject(isolate, value), |
| Nothing<bool>()); |
| if (V8_UNLIKELY(receiver_ta->IsDetachedOrOutOfBounds() || |
| it->index() >= receiver_ta->GetLength())) { |
| return Just(true); |
| } |
| } else if (!IsNumber(*value) && !IsUndefined(*value, isolate)) { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, to_assign, |
| Object::ToNumber(isolate, value), |
| Nothing<bool>()); |
| if (V8_UNLIKELY(receiver_ta->IsDetachedOrOutOfBounds() || |
| it->index() >= receiver_ta->GetLength())) { |
| return Just(true); |
| } |
| } |
| } |
| |
| DCHECK(!IsWasmObject(*receiver, isolate)); |
| if (V8_UNLIKELY(IsJSSharedStruct(*receiver, isolate) || |
| IsJSSharedArray(*receiver, isolate))) { |
| // Shared structs can only point to primitives or shared values. |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, to_assign, Object::Share(isolate, to_assign, kThrowOnError), |
| Nothing<bool>()); |
| it->WriteDataValue(to_assign, false); |
| } else { |
| // Possibly migrate to the most up-to-date map that will be able to store |
| // |value| under it->name(). |
| it->PrepareForDataProperty(to_assign); |
| |
| // Write the property value. |
| it->WriteDataValue(to_assign, false); |
| } |
| |
| #if VERIFY_HEAP |
| if (v8_flags.verify_heap) { |
| receiver->HeapObjectVerify(isolate); |
| } |
| #endif |
| return Just(true); |
| } |
| |
| Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value, |
| PropertyAttributes attributes, |
| Maybe<ShouldThrow> should_throw, |
| StoreOrigin store_origin, |
| EnforceDefineSemantics semantics) { |
| if (!IsJSReceiver(*it->GetReceiver())) { |
| return CannotCreateProperty(it->isolate(), it->GetReceiver(), it->GetName(), |
| value, should_throw); |
| } |
| |
| // Private symbols should be installed on JSProxy using |
| // JSProxy::SetPrivateSymbol. |
| if (IsJSProxy(*it->GetReceiver()) && it->GetName()->IsPrivate() && |
| !it->GetName()->IsPrivateName()) { |
| RETURN_FAILURE(it->isolate(), GetShouldThrow(it->isolate(), should_throw), |
| NewTypeError(MessageTemplate::kProxyPrivate)); |
| } |
| |
| DCHECK_NE(LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND, it->state()); |
| |
| Handle<JSReceiver> receiver = it->GetStoreTarget<JSReceiver>(); |
| DCHECK_IMPLIES(IsJSProxy(*receiver), it->GetName()->IsPrivateName()); |
| DCHECK_IMPLIES(IsJSProxy(*receiver), |
| it->state() == LookupIterator::NOT_FOUND); |
| |
| // If the receiver is a JSGlobalProxy, store on the prototype (JSGlobalObject) |
| // instead. If the prototype is Null, the proxy is detached. |
| if (IsJSGlobalProxy(*receiver)) return Just(true); |
| |
| Isolate* isolate = it->isolate(); |
| |
| if (it->ExtendingNonExtensible(receiver)) { |
| bool is_shared_object = IsAlwaysSharedSpaceJSObject(*receiver); |
| RETURN_FAILURE( |
| isolate, GetShouldThrow(it->isolate(), should_throw), |
| NewTypeError( |
| semantics == EnforceDefineSemantics::kDefine |
| ? (is_shared_object |
| ? MessageTemplate::kDefineDisallowedFixedLayout |
| : MessageTemplate::kDefineDisallowed) |
| : (is_shared_object ? MessageTemplate::kObjectFixedLayout |
| : MessageTemplate::kObjectNotExtensible), |
| it->GetName())); |
| } |
| |
| if (it->IsElement(*receiver)) { |
| if (IsJSArray(*receiver)) { |
| Handle<JSArray> array = Handle<JSArray>::cast(receiver); |
| if (JSArray::WouldChangeReadOnlyLength(array, it->array_index())) { |
| RETURN_FAILURE(isolate, GetShouldThrow(it->isolate(), should_throw), |
| NewTypeError(MessageTemplate::kStrictReadOnlyProperty, |
| isolate->factory()->length_string(), |
| Object::TypeOf(isolate, array), array)); |
| } |
| } |
| |
| Handle<JSObject> receiver_obj = Handle<JSObject>::cast(receiver); |
| MAYBE_RETURN(JSObject::AddDataElement(receiver_obj, it->array_index(), |
| value, attributes), |
| Nothing<bool>()); |
| JSObject::ValidateElements(*receiver_obj); |
| return Just(true); |
| } |
| |
| return Object::TransitionAndWriteDataProperty(it, value, attributes, |
| should_throw, store_origin); |
| } |
| |
| // static |
| Maybe<bool> Object::TransitionAndWriteDataProperty( |
| LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, |
| Maybe<ShouldThrow> should_throw, StoreOrigin store_origin) { |
| Handle<JSReceiver |