| // 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/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-inl.h" |
| #include "src/logging/counters.h" |
| #include "src/logging/log.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/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/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/frame-array-inl.h" |
| #include "src/objects/free-space-inl.h" |
| #include "src/objects/function-kind.h" |
| #include "src/objects/hash-table-inl.h" |
| #include "src/objects/instance-type.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/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/stack-frame-info-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/ostreams.h" |
| #include "src/utils/utils-inl.h" |
| #include "src/wasm/wasm-engine.h" |
| #include "src/wasm/wasm-objects.h" |
| #include "src/zone/zone.h" |
| |
| 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_optimized() || it.frame()->is_interpreted())) { |
| continue; |
| } |
| // Get the language mode from closure. |
| JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(it.frame()); |
| std::vector<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) { |
| switch (instance_type) { |
| #define WRITE_TYPE(TYPE) \ |
| case TYPE: \ |
| return os << #TYPE; |
| INSTANCE_TYPE_LIST(WRITE_TYPE) |
| #undef WRITE_TYPE |
| } |
| UNREACHABLE(); |
| } |
| |
| Handle<FieldType> Object::OptimalType(Isolate* isolate, |
| Representation representation) { |
| if (representation.IsNone()) return FieldType::None(isolate); |
| if (FLAG_track_field_types) { |
| if (representation.IsHeapObject() && IsHeapObject()) { |
| // We can track only JavaScript objects with stable maps. |
| Handle<Map> map(HeapObject::cast(*this).map(), isolate); |
| if (map->is_stable() && map->IsJSReceiverMap()) { |
| 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; |
| auto result = isolate->factory()->NewHeapNumberWithHoleNaN(); |
| if (object->IsUninitialized(isolate)) { |
| result->set_value_as_bits(kHoleNanInt64); |
| } else if (object->IsHeapNumber()) { |
| // 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()); |
| } |
| return result; |
| } |
| |
| Handle<Object> Object::WrapForRead(Isolate* isolate, Handle<Object> object, |
| Representation representation) { |
| DCHECK(!object->IsUninitialized(isolate)); |
| if (!representation.IsDouble()) { |
| DCHECK(object->FitsRepresentation(representation)); |
| return object; |
| } |
| return isolate->factory()->NewHeapNumberFromBits( |
| HeapNumber::cast(*object).value_as_bits()); |
| } |
| |
| MaybeHandle<JSReceiver> Object::ToObjectImpl(Isolate* isolate, |
| Handle<Object> object, |
| const char* method_name) { |
| DCHECK(!object->IsJSReceiver()); // Use ToObject() for fast path. |
| Handle<Context> native_context = isolate->native_context(); |
| Handle<JSFunction> constructor; |
| if (object->IsSmi()) { |
| 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 (object->IsJSReceiver()) return Handle<JSReceiver>::cast(object); |
| if (object->IsNullOrUndefined(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 (input->IsNumber()) { |
| return input; |
| } |
| if (input->IsString()) { |
| return String::ToNumber(isolate, Handle<String>::cast(input)); |
| } |
| if (input->IsOddball()) { |
| return Oddball::ToNumber(isolate, Handle<Oddball>::cast(input)); |
| } |
| if (input->IsSymbol()) { |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber), |
| Object); |
| } |
| if (input->IsBigInt()) { |
| 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(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 (input->IsSmi()) return input; |
| return isolate->factory()->NewNumber(DoubleToInteger(input->Number())); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::ConvertToInt32(Isolate* isolate, |
| Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object); |
| if (input->IsSmi()) return input; |
| return isolate->factory()->NewNumberFromInt(DoubleToInt32(input->Number())); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::ConvertToUint32(Isolate* isolate, |
| Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object); |
| if (input->IsSmi()) return handle(Smi::cast(*input).ToUint32Smi(), isolate); |
| return isolate->factory()->NewNumberFromUint(DoubleToUint32(input->Number())); |
| } |
| |
| // static |
| MaybeHandle<Name> Object::ConvertToName(Isolate* isolate, |
| Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, Object::ToPrimitive(input, ToPrimitiveHint::kString), |
| Name); |
| if (input->IsName()) 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(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 (key->IsSymbol()) return key; |
| // 4. Return ToString(key). |
| // Extending spec'ed behavior, we'd be happy to return an element index. |
| if (key->IsSmi()) return key; |
| if (key->IsHeapNumber()) { |
| uint32_t uint_value; |
| if (value->ToArrayLength(&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 (input->IsOddball()) { |
| return handle(Handle<Oddball>::cast(input)->to_string(), isolate); |
| } |
| if (input->IsNumber()) { |
| return isolate->factory()->NumberToString(input); |
| } |
| if (input->IsSymbol()) { |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString), |
| String); |
| } |
| if (input->IsBigInt()) { |
| return BigInt::ToString(isolate, Handle<BigInt>::cast(input)); |
| } |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, |
| JSReceiver::ToPrimitive(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 (input->IsString()) { |
| return Handle<String>::cast(input); |
| } |
| } |
| } |
| |
| namespace { |
| |
| bool IsErrorObject(Isolate* isolate, Handle<Object> object) { |
| if (!object->IsJSReceiver()) return false; |
| Handle<Symbol> symbol = isolate->factory()->stack_trace_symbol(); |
| return JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol) |
| .FromMaybe(false); |
| } |
| |
| Handle<String> AsStringOrEmpty(Isolate* isolate, Handle<Object> object) { |
| return object->IsString() ? 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(error, name_key); |
| Handle<String> name_str = AsStringOrEmpty(isolate, name); |
| |
| Handle<Name> msg_key = isolate->factory()->message_string(); |
| Handle<Object> msg = JSReceiver::GetDataProperty(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.AppendCString(": "); |
| |
| if (builder.Length() + msg_str->length() <= String::kMaxLength) { |
| builder.AppendString(msg_str); |
| } else { |
| builder.AppendCString("<a very large string>"); |
| } |
| |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| } // namespace |
| |
| // static |
| Handle<String> Object::NoSideEffectsToString(Isolate* isolate, |
| Handle<Object> input) { |
| DisallowJavascriptExecution no_js(isolate); |
| |
| if (input->IsString() || input->IsNumber() || input->IsOddball()) { |
| return Object::ToString(isolate, input).ToHandleChecked(); |
| } else if (input->IsBigInt()) { |
| MaybeHandle<String> maybe_string = |
| BigInt::ToString(isolate, Handle<BigInt>::cast(input), 10, kDontThrow); |
| Handle<String> result; |
| if (maybe_string.ToHandle(&result)) return result; |
| // BigInt-to-String conversion can fail on 32-bit platforms where |
| // String::kMaxLength is too small to fit this BigInt. |
| return isolate->factory()->NewStringFromStaticChars( |
| "<a very large BigInt>"); |
| } else if (input->IsFunction()) { |
| // -- F u n c t i o n |
| Handle<String> fun_str; |
| if (input->IsJSBoundFunction()) { |
| fun_str = JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(input)); |
| } else { |
| DCHECK(input->IsJSFunction()); |
| 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.AppendCString("...<omitted>..."); |
| builder.AppendString(isolate->factory()->NewSubString( |
| fun_str, fun_str->length() - 2, fun_str->length())); |
| |
| return builder.Finish().ToHandleChecked(); |
| } |
| return fun_str; |
| } else if (input->IsSymbol()) { |
| // -- 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.AppendCString("Symbol("); |
| if (symbol->description().IsString()) { |
| builder.AppendString( |
| handle(String::cast(symbol->description()), isolate)); |
| } |
| builder.AppendCharacter(')'); |
| |
| return builder.Finish().ToHandleChecked(); |
| } else if (input->IsJSReceiver()) { |
| // -- J S R e c e i v e r |
| Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input); |
| Handle<Object> to_string = JSReceiver::GetDataProperty( |
| 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( |
| receiver, isolate->factory()->constructor_string()); |
| if (ctor->IsFunction()) { |
| Handle<String> ctor_name; |
| if (ctor->IsJSBoundFunction()) { |
| ctor_name = JSBoundFunction::GetName( |
| isolate, Handle<JSBoundFunction>::cast(ctor)) |
| .ToHandleChecked(); |
| } else if (ctor->IsJSFunction()) { |
| Handle<Object> ctor_name_obj = |
| JSFunction::GetName(isolate, Handle<JSFunction>::cast(ctor)); |
| ctor_name = AsStringOrEmpty(isolate, ctor_name_obj); |
| } |
| |
| if (ctor_name->length() != 0) { |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendCString("#<"); |
| builder.AppendString(ctor_name); |
| builder.AppendCString(">"); |
| |
| return builder.Finish().ToHandleChecked(); |
| } |
| } |
| } |
| } |
| |
| // At this point, input is either none of the above or a JSReceiver. |
| |
| Handle<JSReceiver> receiver; |
| if (input->IsJSReceiver()) { |
| receiver = Handle<JSReceiver>::cast(input); |
| } else { |
| // This is the only case where Object::ToObject throws. |
| DCHECK(!input->IsSmi()); |
| 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( |
| receiver, isolate->factory()->to_string_tag_symbol()); |
| Handle<String> tag = |
| tag_obj->IsString() ? Handle<String>::cast(tag_obj) : builtin_tag; |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendCString("[object "); |
| builder.AppendString(tag); |
| builder.AppendCString("]"); |
| |
| 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 (input->IsSmi()) { |
| int value = std::max(Smi::ToInt(*input), 0); |
| return handle(Smi::FromInt(value), isolate); |
| } |
| double len = DoubleToInteger(input->Number()); |
| 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 (input->IsUndefined(isolate)) return handle(Smi::zero(), isolate); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input), Object); |
| if (input->IsSmi() && Smi::ToInt(*input) >= 0) return input; |
| double len = DoubleToInteger(input->Number()); |
| auto 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; |
| } |
| |
| bool Object::BooleanValue(Isolate* isolate) { |
| if (IsSmi()) return Smi::ToInt(*this) != 0; |
| DCHECK(IsHeapObject()); |
| if (IsBoolean()) return IsTrue(isolate); |
| if (IsNullOrUndefined(isolate)) return false; |
| if (IsUndetectable()) return false; // Undetectable object is false. |
| if (IsString()) return String::cast(*this).length() != 0; |
| if (IsHeapNumber()) return DoubleToBoolean(HeapNumber::cast(*this).value()); |
| if (IsBigInt()) return BigInt::cast(*this).ToBoolean(); |
| return true; |
| } |
| |
| Object Object::ToBoolean(Isolate* isolate) { |
| if (IsBoolean()) return *this; |
| return isolate->heap()->ToBoolean(BooleanValue(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 Object x, const Object y) { |
| return StrictNumberEquals(x.Number(), y.Number()); |
| } |
| |
| 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(x, ToPrimitiveHint::kNumber).ToHandle(&x) || |
| !Object::ToPrimitive(y, ToPrimitiveHint::kNumber).ToHandle(&y)) { |
| return Nothing<ComparisonResult>(); |
| } |
| if (x->IsString() && y->IsString()) { |
| // ES6 section 7.2.11 Abstract Relational Comparison step 5. |
| return Just(String::Compare(isolate, Handle<String>::cast(x), |
| Handle<String>::cast(y))); |
| } |
| if (x->IsBigInt() && y->IsString()) { |
| return BigInt::CompareToString(isolate, Handle<BigInt>::cast(x), |
| Handle<String>::cast(y)); |
| } |
| if (x->IsString() && y->IsBigInt()) { |
| 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 = x->IsNumber(); |
| bool y_is_number = y->IsNumber(); |
| if (x_is_number && y_is_number) { |
| return Just(StrictNumberCompare(x->Number(), y->Number())); |
| } 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 (x->IsNumber()) { |
| if (y->IsNumber()) { |
| return Just(StrictNumberEquals(x, y)); |
| } else if (y->IsBoolean()) { |
| return Just( |
| StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); |
| } else if (y->IsString()) { |
| return Just(StrictNumberEquals( |
| x, String::ToNumber(isolate, Handle<String>::cast(y)))); |
| } else if (y->IsBigInt()) { |
| return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x)); |
| } else if (y->IsJSReceiver()) { |
| if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(false); |
| } |
| } else if (x->IsString()) { |
| if (y->IsString()) { |
| return Just(String::Equals(isolate, Handle<String>::cast(x), |
| Handle<String>::cast(y))); |
| } else if (y->IsNumber()) { |
| x = String::ToNumber(isolate, Handle<String>::cast(x)); |
| return Just(StrictNumberEquals(x, y)); |
| } else if (y->IsBoolean()) { |
| x = String::ToNumber(isolate, Handle<String>::cast(x)); |
| return Just( |
| StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); |
| } else if (y->IsBigInt()) { |
| return BigInt::EqualToString(isolate, Handle<BigInt>::cast(y), |
| Handle<String>::cast(x)); |
| } else if (y->IsJSReceiver()) { |
| if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(false); |
| } |
| } else if (x->IsBoolean()) { |
| if (y->IsOddball()) { |
| return Just(x.is_identical_to(y)); |
| } else if (y->IsNumber()) { |
| return Just( |
| StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); |
| } else if (y->IsString()) { |
| y = String::ToNumber(isolate, Handle<String>::cast(y)); |
| return Just( |
| StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); |
| } else if (y->IsBigInt()) { |
| x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x)); |
| return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x)); |
| } else if (y->IsJSReceiver()) { |
| if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x)); |
| } else { |
| return Just(false); |
| } |
| } else if (x->IsSymbol()) { |
| if (y->IsSymbol()) { |
| return Just(x.is_identical_to(y)); |
| } else if (y->IsJSReceiver()) { |
| if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(false); |
| } |
| } else if (x->IsBigInt()) { |
| if (y->IsBigInt()) { |
| return Just(BigInt::EqualToBigInt(BigInt::cast(*x), BigInt::cast(*y))); |
| } |
| return Equals(isolate, y, x); |
| } else if (x->IsJSReceiver()) { |
| if (y->IsJSReceiver()) { |
| return Just(x.is_identical_to(y)); |
| } else if (y->IsUndetectable()) { |
| return Just(x->IsUndetectable()); |
| } else if (y->IsBoolean()) { |
| y = Oddball::ToNumber(isolate, Handle<Oddball>::cast(y)); |
| } else if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(x)) |
| .ToHandle(&x)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(x->IsUndetectable() && y->IsUndetectable()); |
| } |
| } |
| } |
| |
| bool Object::StrictEquals(Object that) { |
| if (this->IsNumber()) { |
| if (!that.IsNumber()) return false; |
| return StrictNumberEquals(*this, that); |
| } else if (this->IsString()) { |
| if (!that.IsString()) return false; |
| return String::cast(*this).Equals(String::cast(that)); |
| } else if (this->IsBigInt()) { |
| if (!that.IsBigInt()) return false; |
| return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(that)); |
| } |
| return *this == that; |
| } |
| |
| // static |
| Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) { |
| if (object->IsNumber()) return isolate->factory()->number_string(); |
| if (object->IsOddball()) |
| return handle(Oddball::cast(*object).type_of(), isolate); |
| if (object->IsUndetectable()) { |
| return isolate->factory()->undefined_string(); |
| } |
| if (object->IsString()) return isolate->factory()->string_string(); |
| if (object->IsSymbol()) return isolate->factory()->symbol_string(); |
| if (object->IsBigInt()) return isolate->factory()->bigint_string(); |
| if (object->IsCallable()) return isolate->factory()->function_string(); |
| return isolate->factory()->object_string(); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::Add(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (lhs->IsNumber() && rhs->IsNumber()) { |
| return isolate->factory()->NewNumber(lhs->Number() + rhs->Number()); |
| } else if (lhs->IsString() && rhs->IsString()) { |
| return isolate->factory()->NewConsString(Handle<String>::cast(lhs), |
| Handle<String>::cast(rhs)); |
| } |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToPrimitive(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToPrimitive(rhs), Object); |
| if (lhs->IsString() || rhs->IsString()) { |
| 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(lhs->Number() + rhs->Number()); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate, |
| Handle<Object> callable, |
| Handle<Object> object) { |
| // The {callable} must have a [[Call]] internal method. |
| if (!callable->IsCallable()) 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 (callable->IsJSBoundFunction()) { |
| 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 (!object->IsJSReceiver()) 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 (!prototype->IsJSReceiver()) { |
| 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 (!callable->IsJSReceiver()) { |
| 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(Handle<JSReceiver>::cast(callable), |
| isolate->factory()->has_instance_symbol()), |
| Object); |
| if (!inst_of_handler->IsUndefined(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(result->BooleanValue(isolate)); |
| } |
| |
| // The {callable} must have a [[Call]] internal method. |
| if (!callable->IsCallable()) { |
| 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(Handle<JSReceiver> receiver, |
| Handle<Name> name) { |
| Handle<Object> func; |
| Isolate* isolate = receiver->GetIsolate(); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, func, JSReceiver::GetProperty(isolate, receiver, name), Object); |
| if (func->IsNullOrUndefined(isolate)) { |
| return isolate->factory()->undefined_value(); |
| } |
| if (!func->IsCallable()) { |
| 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 (object->IsJSArray()) { |
| Handle<JSArray> array = Handle<JSArray>::cast(object); |
| uint32_t length; |
| if (!array->HasArrayPrototype(isolate) || |
| !array->length().ToUint32(&length) || !array->HasFastElements() || |
| !JSObject::PrototypeHasNoElements(isolate, *array)) { |
| return MaybeHandle<FixedArray>(); |
| } |
| return array->GetElementsAccessor()->CreateListFromArrayLike( |
| isolate, array, length); |
| } else if (object->IsJSTypedArray()) { |
| Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(object); |
| size_t length = array->length(); |
| if (array->WasDetached() || |
| 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 (!object->IsJSReceiver()) { |
| 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 (!raw_length_number->ToUint32(&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 (!next->IsName()) { |
| 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->IsFound(); it->Next()) { |
| switch (it->state()) { |
| case LookupIterator::NOT_FOUND: |
| 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 (receiver->IsJSGlobalObject()) { |
| 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::INTERCEPTOR: { |
| bool done; |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| it->isolate(), result, |
| JSObject::GetPropertyWithInterceptor(it, &done), Object); |
| if (done) return result; |
| break; |
| } |
| case LookupIterator::ACCESS_CHECK: |
| if (it->HasAccess()) break; |
| return JSObject::GetPropertyWithFailedAccessCheck(it); |
| case LookupIterator::ACCESSOR: |
| return GetPropertyWithAccessor(it); |
| case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| return it->isolate()->factory()->undefined_value(); |
| case LookupIterator::DATA: |
| return it->GetDataValue(); |
| } |
| } |
| |
| return it->isolate()->factory()->undefined_value(); |
| } |
| |
| // 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(Handle<JSReceiver>::cast(handler), trap_name), Object); |
| // 7. If trap is undefined, then |
| if (trap->IsUndefined(isolate)) { |
| // 7.a Return target.[[Get]](P, Receiver). |
| LookupIterator::Key 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() && |
| !trap_result->SameValue(*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() && |
| target_desc.get()->IsUndefined(isolate) && |
| !trap_result->IsUndefined(isolate); |
| } else { |
| inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && |
| !target_desc.configurable() && |
| target_desc.set()->IsUndefined(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(); |
| } |
| |
| bool Object::ToInt32(int32_t* value) { |
| if (IsSmi()) { |
| *value = Smi::ToInt(*this); |
| return true; |
| } |
| if (IsHeapNumber()) { |
| double num = HeapNumber::cast(*this).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; |
| } |
| |
| Handle<SharedFunctionInfo> FunctionTemplateInfo::GetOrCreateSharedFunctionInfo( |
| Isolate* isolate, Handle<FunctionTemplateInfo> info, |
| MaybeHandle<Name> maybe_name) { |
| Object current_info = info->shared_function_info(); |
| if (current_info.IsSharedFunctionInfo()) { |
| return handle(SharedFunctionInfo::cast(current_info), isolate); |
| } |
| Handle<Name> name; |
| Handle<String> name_string; |
| if (maybe_name.ToHandle(&name) && name->IsString()) { |
| name_string = Handle<String>::cast(name); |
| } else if (info->class_name().IsString()) { |
| name_string = handle(String::cast(info->class_name()), isolate); |
| } else { |
| name_string = isolate->factory()->empty_string(); |
| } |
| FunctionKind function_kind; |
| if (info->remove_prototype()) { |
| function_kind = kConciseMethod; |
| } else { |
| function_kind = kNormalFunction; |
| } |
| Handle<SharedFunctionInfo> result = |
| isolate->factory()->NewSharedFunctionInfoForApiFunction(name_string, info, |
| function_kind); |
| |
| result->set_length(info->length()); |
| result->DontAdaptArguments(); |
| DCHECK(result->IsApiFunction()); |
| |
| info->set_shared_function_info(*result); |
| return result; |
| } |
| |
| bool FunctionTemplateInfo::IsTemplateFor(Map map) { |
| // There is a constraint on the object; check. |
| if (!map.IsJSObjectMap()) return false; |
| // Fetch the constructor function of the object. |
| Object cons_obj = map.GetConstructor(); |
| Object type; |
| if (cons_obj.IsJSFunction()) { |
| JSFunction fun = JSFunction::cast(cons_obj); |
| type = fun.shared().function_data(kAcquireLoad); |
| } else if (cons_obj.IsFunctionTemplateInfo()) { |
| type = FunctionTemplateInfo::cast(cons_obj); |
| } else { |
| return false; |
| } |
| // Iterate through the chain of inheriting function templates to |
| // see if the required one occurs. |
| while (type.IsFunctionTemplateInfo()) { |
| if (type == *this) return true; |
| type = FunctionTemplateInfo::cast(type).GetParentTemplate(); |
| } |
| // Didn't find the required type in the inheritance chain. |
| return false; |
| } |
| |
| // static |
| FunctionTemplateRareData FunctionTemplateInfo::AllocateFunctionTemplateRareData( |
| Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info) { |
| DCHECK(function_template_info->rare_data().IsUndefined(isolate)); |
| Handle<Struct> struct_obj = isolate->factory()->NewStruct( |
| FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld); |
| Handle<FunctionTemplateRareData> rare_data = |
| i::Handle<FunctionTemplateRareData>::cast(struct_obj); |
| rare_data->set_c_function(Smi(0)); |
| rare_data->set_c_signature(Smi(0)); |
| function_template_info->set_rare_data(*rare_data); |
| return *rare_data; |
| } |
| |
| // static |
| Handle<TemplateList> TemplateList::New(Isolate* isolate, int size) { |
| Handle<FixedArray> list = |
| isolate->factory()->NewFixedArray(kLengthIndex + size); |
| list->set(kLengthIndex, Smi::zero()); |
| return Handle<TemplateList>::cast(list); |
| } |
| |
| // static |
| Handle<TemplateList> TemplateList::Add(Isolate* isolate, |
| Handle<TemplateList> list, |
| Handle<i::Object> value) { |
| STATIC_ASSERT(kFirstElementIndex == 1); |
| int index = list->length() + 1; |
| Handle<i::FixedArray> fixed_array = Handle<FixedArray>::cast(list); |
| fixed_array = FixedArray::SetAndGrow(isolate, fixed_array, index, value); |
| fixed_array->set(kLengthIndex, Smi::FromInt(index)); |
| return Handle<TemplateList>::cast(fixed_array); |
| } |
| |
| // 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(handler, trap_name), HeapObject); |
| // 6. If trap is undefined, then return target.[[GetPrototypeOf]](). |
| if (trap->IsUndefined(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 (!(handler_proto->IsJSReceiver() || handler_proto->IsNull(isolate))) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid), |
| HeapObject); |
| } |
| // 9. Let extensibleTarget be ? IsExtensible(target). |
| Maybe<bool> is_extensible = JSReceiver::IsExtensible(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 (!handler_proto->SameValue(*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 (receiver->IsJSGlobalObject()) { |
| 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(!structure->IsForeign()); |
| |
| // API style callbacks. |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| if (structure->IsAccessorInfo()) { |
| Handle<Name> name = it->GetName(); |
| Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure); |
| if (!info->IsCompatibleReceiver(*receiver)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, |
| name, receiver), |
| Object); |
| } |
| |
| if (!info->has_getter()) return isolate->factory()->undefined_value(); |
| |
| if (info->is_sloppy() && !receiver->IsJSReceiver()) { |
| 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_SCHEDULED_EXCEPTION(isolate, Object); |
| if (result.is_null()) return isolate->factory()->undefined_value(); |
| Handle<Object> reboxed_result = handle(*result, isolate); |
| if (info->replace_on_access() && receiver->IsJSReceiver()) { |
| RETURN_ON_EXCEPTION(isolate, |
| Accessors::ReplaceAccessorWithDataProperty( |
| isolate, receiver, holder, name, result), |
| Object); |
| } |
| return reboxed_result; |
| } |
| |
| // AccessorPair with 'cached' private property. |
| if (it->TryLookupCachedProperty()) { |
| return Object::GetProperty(it); |
| } |
| |
| // Regular accessor. |
| Handle<Object> getter(AccessorPair::cast(*structure).getter(), isolate); |
| if (getter->IsFunctionTemplateInfo()) { |
| SaveAndSwitchContext save(isolate, *holder->GetCreationContext()); |
| return Builtins::InvokeApiFunction( |
| isolate, false, Handle<FunctionTemplateInfo>::cast(getter), receiver, 0, |
| nullptr, isolate->factory()->undefined_value()); |
| } else if (getter->IsCallable()) { |
| // 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(); |
| } |
| |
| // static |
| Address AccessorInfo::redirect(Address address, AccessorComponent component) { |
| ApiFunction fun(address); |
| DCHECK_EQ(ACCESSOR_GETTER, component); |
| ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL; |
| return ExternalReference::Create(&fun, type).address(); |
| } |
| |
| Address AccessorInfo::redirected_getter() const { |
| Address accessor = v8::ToCData<Address>(getter()); |
| if (accessor == kNullAddress) return kNullAddress; |
| return redirect(accessor, ACCESSOR_GETTER); |
| } |
| |
| Address CallHandlerInfo::redirected_callback() const { |
| Address address = v8::ToCData<Address>(callback()); |
| ApiFunction fun(address); |
| ExternalReference::Type type = ExternalReference::DIRECT_API_CALL; |
| return ExternalReference::Create(&fun, type).address(); |
| } |
| |
| bool AccessorInfo::IsCompatibleReceiverMap(Handle<AccessorInfo> info, |
| Handle<Map> map) { |
| if (!info->HasExpectedReceiverType()) return true; |
| if (!map->IsJSObjectMap()) return false; |
| return FunctionTemplateInfo::cast(info->expected_receiver_type()) |
| .IsTemplateFor(*map); |
| } |
| |
| 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 (receiver->IsJSGlobalObject()) { |
| 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(!structure->IsForeign()); |
| |
| // API style callbacks. |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| if (structure->IsAccessorInfo()) { |
| Handle<Name> name = it->GetName(); |
| Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure); |
| if (!info->IsCompatibleReceiver(*receiver)) { |
| isolate->Throw(*isolate->factory()->NewTypeError( |
| MessageTemplate::kIncompatibleMethodReceiver, name, receiver)); |
| return Nothing<bool>(); |
| } |
| |
| if (!info->has_setter()) { |
| // 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() && !receiver->IsJSReceiver()) { |
| 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_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); |
| if (result.is_null()) return Just(true); |
| DCHECK(result->BooleanValue(isolate) || |
| GetShouldThrow(isolate, maybe_should_throw) == kDontThrow); |
| return Just(result->BooleanValue(isolate)); |
| } |
| |
| // Regular accessor. |
| Handle<Object> setter(AccessorPair::cast(*structure).setter(), isolate); |
| if (setter->IsFunctionTemplateInfo()) { |
| SaveAndSwitchContext save(isolate, *holder->GetCreationContext()); |
| 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 (setter->IsCallable()) { |
| // 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); |
| } |
| |
| Map Object::GetPrototypeChainRootMap(Isolate* isolate) const { |
| DisallowHeapAllocation no_alloc; |
| if (IsSmi()) { |
| Context native_context = isolate->context().native_context(); |
| return native_context.number_function().initial_map(); |
| } |
| |
| const HeapObject heap_object = HeapObject::cast(*this); |
| return heap_object.map().GetPrototypeChainRootMap(isolate); |
| } |
| |
| Smi Object::GetOrCreateHash(Isolate* isolate) { |
| DisallowHeapAllocation no_gc; |
| Object hash = Object::GetSimpleHash(*this); |
| if (hash.IsSmi()) return Smi::cast(hash); |
| |
| DCHECK(IsJSReceiver()); |
| return JSReceiver::cast(*this).GetOrCreateIdentityHash(isolate); |
| } |
| |
| bool Object::SameValue(Object other) { |
| if (other == *this) return true; |
| |
| if (IsNumber() && other.IsNumber()) { |
| return SameNumberValue(Number(), other.Number()); |
| } |
| if (IsString() && other.IsString()) { |
| return String::cast(*this).Equals(String::cast(other)); |
| } |
| if (IsBigInt() && other.IsBigInt()) { |
| return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(other)); |
| } |
| return false; |
| } |
| |
| bool Object::SameValueZero(Object other) { |
| if (other == *this) return true; |
| |
| if (IsNumber() && other.IsNumber()) { |
| double this_value = Number(); |
| double other_value = other.Number(); |
| // +0 == -0 is true |
| return this_value == other_value || |
| (std::isnan(this_value) && std::isnan(other_value)); |
| } |
| if (IsString() && other.IsString()) { |
| return String::cast(*this).Equals(String::cast(other)); |
| } |
| if (IsBigInt() && other.IsBigInt()) { |
| return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(other)); |
| } |
| return false; |
| } |
| |
| MaybeHandle<Object> Object::ArraySpeciesConstructor( |
| Isolate* isolate, Handle<Object> original_array) { |
| Handle<Object> default_species = isolate->array_function(); |
| if (original_array->IsJSArray() && |
| Handle<JSArray>::cast(original_array)->HasArrayPrototype(isolate) && |
| Protectors::IsArraySpeciesLookupChainIntact(isolate)) { |
| return default_species; |
| } |
| Handle<Object> constructor = isolate->factory()->undefined_value(); |
| Maybe<bool> is_array = Object::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 (constructor->IsConstructor()) { |
| Handle<Context> 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 (constructor->IsJSReceiver()) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, constructor, |
| JSReceiver::GetProperty(isolate, |
| Handle<JSReceiver>::cast(constructor), |
| isolate->factory()->species_symbol()), |
| Object); |
| if (constructor->IsNull(isolate)) { |
| constructor = isolate->factory()->undefined_value(); |
| } |
| } |
| } |
| if (constructor->IsUndefined(isolate)) { |
| return default_species; |
| } else { |
| if (!constructor->IsConstructor()) { |
| 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 (ctor_obj->IsUndefined(isolate)) return default_ctor; |
| |
| if (!ctor_obj->IsJSReceiver()) { |
| 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 (species->IsNullOrUndefined(isolate)) { |
| return default_ctor; |
| } |
| |
| if (species->IsConstructor()) return species; |
| |
| THROW_NEW_ERROR( |
| isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object); |
| } |
| |
| bool Object::IterationHasObservableEffects() { |
| // Check that this object is an array. |
| if (!IsJSArray()) return true; |
| JSArray array = JSArray::cast(*this); |
| Isolate* isolate = array.GetIsolate(); |
| |
| #ifdef V8_ENABLE_FORCE_SLOW_PATH |
| if (isolate->force_slow_path()) return true; |
| #endif |
| |
| // Check that we have the original ArrayPrototype. |
| if (!array.map().prototype().IsJSObject()) return true; |
| JSObject array_proto = JSObject::cast(array.map().prototype()); |
| if (!isolate->is_initial_array_prototype(array_proto)) return true; |
| |
| // 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; |
| } |
| |
| bool Object::IsCodeLike(Isolate* isolate) const { |
| DisallowGarbageCollection no_gc; |
| return IsJSReceiver() && JSReceiver::cast(*this).IsCodeLike(isolate); |
| } |
| |
| void Object::ShortPrint(FILE* out) const { |
| OFStream os(out); |
| os << Brief(*this); |
| } |
| |
| void Object::ShortPrint(StringStream* accumulator) const { |
| std::ostringstream os; |
| os << Brief(*this); |
| accumulator->Add(os.str().c_str()); |
| } |
| |
| void Object::ShortPrint(std::ostream& os) const { os << Brief(*this); } |
| |
| std::ostream& operator<<(std::ostream& os, const Object& obj) { |
| obj.ShortPrint(os); |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const Brief& v) { |
| MaybeObject maybe_object(v.value); |
| Smi smi; |
| HeapObject heap_object; |
| if (maybe_object->ToSmi(&smi)) { |
| smi.SmiPrint(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; |
| } |
| |
| void Smi::SmiPrint(std::ostream& os) const { // NOLINT |
| os << value(); |
| } |
| |
| void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT |
| os << AsHex::Address(this->ptr()) << " "; |
| |
| if (IsString()) { |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| String::cast(*this).StringShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| return; |
| } |
| if (IsJSObject()) { |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| JSObject::cast(*this).JSObjectShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| return; |
| } |
| switch (map().instance_type()) { |
| case MAP_TYPE: { |
| os << "<Map"; |
| Map mapInstance = Map::cast(*this); |
| if (mapInstance.IsJSObjectMap()) { |
| os << "(" << ElementsKindToString(mapInstance.elements_kind()) << ")"; |
| } else if (mapInstance.instance_size() != kVariableSizeSentinel) { |
| os << "[" << mapInstance.instance_size() << "]"; |
| } |
| os << ">"; |
| } break; |
| case AWAIT_CONTEXT_TYPE: { |
| os << "<AwaitContext generator= "; |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| Context::cast(*this).extension().ShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| os << '>'; |
| break; |
| } |
| case BLOCK_CONTEXT_TYPE: |
| os << "<BlockContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case CATCH_CONTEXT_TYPE: |
| os << "<CatchContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case DEBUG_EVALUATE_CONTEXT_TYPE: |
| os << "<DebugEvaluateContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case EVAL_CONTEXT_TYPE: |
| os << "<EvalContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case FUNCTION_CONTEXT_TYPE: |
| os << "<FunctionContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case MODULE_CONTEXT_TYPE: |
| os << "<ModuleContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case NATIVE_CONTEXT_TYPE: |
| os << "<NativeContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case SCRIPT_CONTEXT_TYPE: |
| os << "<ScriptContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case WITH_CONTEXT_TYPE: |
| os << "<WithContext[" << Context::cast(*this).length() << "]>"; |
| break; |
| case SCRIPT_CONTEXT_TABLE_TYPE: |
| os << "<ScriptContextTable[" << FixedArray::cast(*this).length() << "]>"; |
| break; |
| case HASH_TABLE_TYPE: |
| os << "<HashTable[" << FixedArray::cast(*this).length() << "]>"; |
| break; |
| case ORDERED_HASH_MAP_TYPE: |
| os << "<OrderedHashMap[" << FixedArray::cast(*this).length() << "]>"; |
| break; |
| case ORDERED_HASH_SET_TYPE: |
| os << "<OrderedHashSet[" << FixedArray::cast(*this).length() << "]>"; |
| break; |
| case ORDERED_NAME_DICTIONARY_TYPE: |
| os << "<OrderedNameDictionary[" << FixedArray::cast(*this).length() |
| << "]>"; |
| break; |
| case NAME_DICTIONARY_TYPE: |
| os << "<NameDictionary[" << FixedArray::cast(*this).length() << "]>"; |
| break; |
| case GLOBAL_DICTIONARY_TYPE: |
| os << "<GlobalDictionary[" << FixedArray::cast(*this).length() << "]>"; |
| break; |
| case NUMBER_DICTIONARY_TYPE: |
| os << "<NumberDictionary[" << FixedArray::cast(*this).length() << "]>"; |
| break; |
| case SIMPLE_NUMBER_DICTIONARY_TYPE: |
| os << "<SimpleNumberDictionary[" << FixedArray::cast(*this).length() |
| << "]>"; |
| break; |
| case FIXED_ARRAY_TYPE: |
| os << "<FixedArray[" << FixedArray::cast(*this).length() << "]>"; |
| break; |
| case OBJECT_BOILERPLATE_DESCRIPTION_TYPE: |
| os << "<ObjectBoilerplateDescription[" << FixedArray::cast(*this).length() |
| << "]>"; |
| break; |
| case FIXED_DOUBLE_ARRAY_TYPE: |
| os << "<FixedDoubleArray[" << FixedDoubleArray::cast(*this).length() |
| << "]>"; |
| break; |
| case BYTE_ARRAY_TYPE: |
| os << "<ByteArray[" << ByteArray::cast(*this).length() << "]>"; |
| break; |
| case BYTECODE_ARRAY_TYPE: |
| os << "<BytecodeArray[" << BytecodeArray::cast(*this).length() << "]>"; |
| break; |
| case DESCRIPTOR_ARRAY_TYPE: |
| os << "<DescriptorArray[" |
| << DescriptorArray::cast(*this).number_of_descriptors() << "]>"; |
| break; |
| case TRANSITION_ARRAY_TYPE: |
| os << "<TransitionArray[" << TransitionArray::cast(*this).length() |
| << "]>"; |
| break; |
| case PROPERTY_ARRAY_TYPE: |
| os << "<PropertyArray[" << PropertyArray::cast(*this).length() << "]>"; |
| break; |
| case FEEDBACK_CELL_TYPE: { |
| { |
| ReadOnlyRoots roots = GetReadOnlyRoots(); |
| os << "<FeedbackCell["; |
| if (map() == roots.no_closures_cell_map()) { |
| os << "no feedback"; |
| } else if (map() == roots.no_closures_cell_map()) { |
| os << "no closures"; |
| } else if (map() == roots.one_closure_cell_map()) { |
| os << "one closure"; |
| } else if (map() == roots.many_closures_cell_map()) { |
| os << "many closures"; |
| } else { |
| os << "!!!INVALID MAP!!!"; |
| } |
| os << "]>"; |
| } |
| break; |
| } |
| case CLOSURE_FEEDBACK_CELL_ARRAY_TYPE: |
| os << "<ClosureFeedbackCellArray[" |
| << ClosureFeedbackCellArray::cast(*this).length() << "]>"; |
| break; |
| case FEEDBACK_VECTOR_TYPE: |
| os << "<FeedbackVector[" << FeedbackVector::cast(*this).length() << "]>"; |
| break; |
| case FREE_SPACE_TYPE: |
| os << "<FreeSpace[" << FreeSpace::cast(*this).size() << "]>"; |
| break; |
| |
| case PREPARSE_DATA_TYPE: { |
| PreparseData data = PreparseData::cast(*this); |
| os << "<PreparseData[data=" << data.data_length() |
| << " children=" << data.children_length() << "]>"; |
| break; |
| } |
| |
| case UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE: { |
| UncompiledDataWithoutPreparseData data = |
| UncompiledDataWithoutPreparseData::cast(*this); |
| os << "<UncompiledDataWithoutPreparseData (" << data.start_position() |
| << ", " << data.end_position() << ")]>"; |
| break; |
| } |
| |
| case UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE: { |
| UncompiledDataWithPreparseData data = |
| UncompiledDataWithPreparseData::cast(*this); |
| os << "<UncompiledDataWithPreparseData (" << data.start_position() << ", " |
| << data.end_position() << ") preparsed=" << Brief(data.preparse_data()) |
| << ">"; |
| break; |
| } |
| |
| case SHARED_FUNCTION_INFO_TYPE: { |
| SharedFunctionInfo shared = SharedFunctionInfo::cast(*this); |
| std::unique_ptr<char[]> debug_name = shared.DebugName().ToCString(); |
| if (debug_name[0] != 0) { |
| os << "<SharedFunctionInfo " << debug_name.get() << ">"; |
| } else { |
| os << "<SharedFunctionInfo>"; |
| } |
| break; |
| } |
| case JS_MESSAGE_OBJECT_TYPE: |
| os << "<JSMessageObject>"; |
| break; |
| #define MAKE_STRUCT_CASE(TYPE, Name, name) \ |
| case TYPE: \ |
| os << "<" #Name; \ |
| Name::cast(*this).BriefPrintDetails(os); \ |
| os << ">"; \ |
| break; |
| STRUCT_LIST(MAKE_STRUCT_CASE) |
| #undef MAKE_STRUCT_CASE |
| case ALLOCATION_SITE_TYPE: { |
| os << "<AllocationSite"; |
| AllocationSite::cast(*this).BriefPrintDetails(os); |
| os << ">"; |
| break; |
| } |
| case SCOPE_INFO_TYPE: { |
| ScopeInfo scope = ScopeInfo::cast(*this); |
| os << "<ScopeInfo"; |
| if (scope.length()) os << " " << scope.scope_type() << " "; |
| os << "[" << scope.length() << "]>"; |
| break; |
| } |
| case CODE_TYPE: { |
| Code code = Code::cast(*this); |
| os << "<Code " << CodeKindToString(code.kind()); |
| if (code.is_builtin()) { |
| os << " " << Builtins::name(code.builtin_index()); |
| } |
| os << ">"; |
| break; |
| } |
| case ODDBALL_TYPE: { |
| if (IsUndefined()) { |
| os << "<undefined>"; |
| } else if (IsTheHole()) { |
| os << "<the_hole>"; |
| } else if (IsNull()) { |
| os << "<null>"; |
| } else if (IsTrue()) { |
| os << "<true>"; |
| } else if (IsFalse()) { |
| os << "<false>"; |
| } else { |
| os << "<Odd Oddball: "; |
| os << Oddball::cast(*this).to_string().ToCString().get(); |
| os << ">"; |
| } |
| break; |
| } |
| case SYMBOL_TYPE: { |
| Symbol symbol = Symbol::cast(*this); |
| symbol.SymbolShortPrint(os); |
| break; |
| } |
| case HEAP_NUMBER_TYPE: { |
| os << "<HeapNumber "; |
| HeapNumber::cast(*this).HeapNumberShortPrint(os); |
| os << ">"; |
| break; |
| } |
| case BIGINT_TYPE: { |
| os << "<BigInt "; |
| BigInt::cast(*this).BigIntShortPrint(os); |
| os << ">"; |
| break; |
| } |
| case JS_PROXY_TYPE: |
| os << "<JSProxy>"; |
| break; |
| case FOREIGN_TYPE: |
| os << "<Foreign>"; |
| break; |
| case CELL_TYPE: { |
| os << "<Cell value= "; |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| Cell::cast(*this).value().ShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| os << '>'; |
| break; |
| } |
| case PROPERTY_CELL_TYPE: { |
| PropertyCell cell = PropertyCell::cast(*this); |
| os << "<PropertyCell name="; |
| cell.name().ShortPrint(os); |
| os << " value="; |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| cell.value().ShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| os << '>'; |
| break; |
| } |
| case CALL_HANDLER_INFO_TYPE: { |
| CallHandlerInfo info = CallHandlerInfo::cast(*this); |
| os << "<CallHandlerInfo "; |
| os << "callback= " << Brief(info.callback()); |
| os << ", js_callback= " << Brief(info.js_callback()); |
| os << ", data= " << Brief(info.data()); |
| if (info.IsSideEffectFreeCallHandlerInfo()) { |
| os << ", side_effect_free= true>"; |
| } else { |
| os << ", side_effect_free= false>"; |
| } |
| break; |
| } |
| default: |
| os << "<Other heap object (" << map().instance_type() << ")>"; |
| break; |
| } |
| } |
| |
| void Struct::BriefPrintDetails(std::ostream& os) {} |
| |
| void Tuple2::BriefPrintDetails(std::ostream& os) { |
| os << " " << Brief(value1()) << ", " << Brief(value2()); |
| } |
| |
| void ClassPositions::BriefPrintDetails(std::ostream& os) { |
| os << " " << start() << ", " << end(); |
| } |
| |
| void ArrayBoilerplateDescription::BriefPrintDetails(std::ostream& os) { |
| os << " " << ElementsKindToString(elements_kind()) << ", " |
| << Brief(constant_elements()); |
| } |
| |
| void CallableTask::BriefPrintDetails(std::ostream& os) { |
| os << " callable=" << Brief(callable()); |
| } |
| |
| void HeapObject::Iterate(ObjectVisitor* v) { IterateFast<ObjectVisitor>(v); } |
| |
| void HeapObject::IterateBody(ObjectVisitor* v) { |
| Map m = map(); |
| IterateBodyFast<ObjectVisitor>(m, SizeFromMap(m), v); |
| } |
| |
| void HeapObject::IterateBody(Map map, int object_size, ObjectVisitor* v) { |
| IterateBodyFast<ObjectVisitor>(map, object_size, v); |
| } |
| |
| struct CallIsValidSlot { |
| template <typename BodyDescriptor> |
| static bool apply(Map map, HeapObject obj, int offset, int) { |
| return BodyDescriptor::IsValidSlot(map, obj, offset); |
| } |
| }; |
| |
| bool HeapObject::IsValidSlot(Map map, int offset) { |
| DCHECK_NE(0, offset); |
| return BodyDescriptorApply<CallIsValidSlot, bool>(map.instance_type(), map, |
| *this, offset, 0); |
| } |
| |
| int HeapObject::SizeFromMap(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::SizeFor( |
| FixedArray::unchecked_cast(*this).synchronized_length()); |
| } |
| 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 == ONE_BYTE_STRING_TYPE || |
| instance_type == ONE_BYTE_INTERNALIZED_STRING_TYPE) { |
| // Strings may get concurrently truncated, hence we have to access its |
| // length synchronized. |
| return SeqOneByteString::SizeFor( |
| SeqOneByteString::unchecked_cast(*this).synchronized_length()); |
| } |
| if (instance_type == BYTE_ARRAY_TYPE) { |
| return ByteArray::SizeFor( |
| ByteArray::unchecked_cast(*this).synchronized_length()); |
| } |
| if (instance_type == BYTECODE_ARRAY_TYPE) { |
| return BytecodeArray::SizeFor( |
| BytecodeArray::unchecked_cast(*this).synchronized_length()); |
| } |
| if (instance_type == FREE_SPACE_TYPE) { |
| return FreeSpace::unchecked_cast(*this).relaxed_read_size(); |
| } |
| if (instance_type == STRING_TYPE || |
| instance_type == INTERNALIZED_STRING_TYPE) { |
| // Strings may get concurrently truncated, hence we have to access its |
| // length synchronized. |
| return SeqTwoByteString::SizeFor( |
| SeqTwoByteString::unchecked_cast(*this).synchronized_length()); |
| } |
| if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) { |
| return FixedDoubleArray::SizeFor( |
| FixedDoubleArray::unchecked_cast(*this).synchronized_length()); |
| } |
| if (instance_type == FEEDBACK_METADATA_TYPE) { |
| return FeedbackMetadata::SizeFor( |
| FeedbackMetadata::unchecked_cast(*this).synchronized_slot_count()); |
| } |
| 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::SizeFor( |
| WeakFixedArray::unchecked_cast(*this).synchronized_length()); |
| } |
| if (instance_type == WEAK_ARRAY_LIST_TYPE) { |
| return WeakArrayList::SizeForCapacity( |
| WeakArrayList::unchecked_cast(*this).synchronized_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 == PROPERTY_ARRAY_TYPE) { |
| return PropertyArray::SizeFor( |
| PropertyArray::cast(*this).synchronized_length()); |
| } |
| 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) { |
| 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 == CODE_TYPE) { |
| return Code::unchecked_cast(*this).CodeSize(); |
| } |
| if (instance_type == COVERAGE_INFO_TYPE) { |
| return CoverageInfo::SizeFor( |
| CoverageInfo::unchecked_cast(*this).slot_count()); |
| } |
| if (instance_type == WASM_ARRAY_TYPE) { |
| return WasmArray::SizeFor(map, WasmArray::cast(*this).length()); |
| } |
| DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE); |
| return EmbedderDataArray::SizeFor( |
| EmbedderDataArray::unchecked_cast(*this).length()); |
| } |
| |
| bool HeapObject::NeedsRehashing() const { |
| return NeedsRehashing(map().instance_type()); |
| } |
| |
| bool HeapObject::NeedsRehashing(InstanceType instance_type) const { |
| 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 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 JS_MAP_TYPE: |
| case JS_SET_TYPE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool HeapObject::CanBeRehashed() const { |
| DCHECK(NeedsRehashing()); |
| switch (map().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 GLOBAL_DICTIONARY_TYPE: |
| case NUMBER_DICTIONARY_TYPE: |
| case SIMPLE_NUMBER_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; |
| } |
| return false; |
| } |
| |
| void HeapObject::RehashBasedOnMap(Isolate* isolate) { |
| switch (map().instance_type()) { |
| case HASH_TABLE_TYPE: |
| UNREACHABLE(); |
| case NAME_DICTIONARY_TYPE: |
| NameDictionary::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: |
| 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); |
| break; |
| } |
| case JS_SET_TYPE: { |
| JSSet::cast(*this).Rehash(isolate); |
| break; |
| } |
| case SMALL_ORDERED_NAME_DICTIONARY_TYPE: |
| DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements()); |
| break; |
| case ONE_BYTE_INTERNALIZED_STRING_TYPE: |
| case INTERNALIZED_STRING_TYPE: |
| // Rare case, rehash read-only space strings before they are sealed. |
| DCHECK(ReadOnlyHeap::Contains(*this)); |
| String::cast(*this).Hash(); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| bool HeapObject::IsExternal(Isolate* isolate) const { |
| return map().FindRootMap(isolate) == isolate->heap()->external_map(); |
| } |
| |
| 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() == kField) { |
| DCHECK_EQ(kData, details.kind()); |
| details = details.CopyWithConstness(PropertyConstness::kMutable); |
| 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()); |
| |
| do { |
| switch (it->state()) { |
| case LookupIterator::NOT_FOUND: |
| UNREACHABLE(); |
| |
| case LookupIterator::ACCESS_CHECK: |
| if (it->HasAccess()) break; |
| // 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 (receiver->IsJSGlobalObject()) { |
| receiver = handle(JSGlobalObject::cast(*receiver).global_proxy(), |
| it->isolate()); |
| } |
| return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(), |
| value, receiver, should_throw); |
| } |
| |
| case LookupIterator::INTERCEPTOR: { |
| if (it->HolderIsReceiverOrHiddenPrototype()) { |
| Maybe<bool> result = |
| JSObject::SetPropertyWithInterceptor(it, should_throw, value); |
| if (result.IsNothing() || result.FromJust()) return result; |
| } 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); |
| } |
| if (maybe_attributes.FromJust() == ABSENT) break; |
| *found = false; |
| return Nothing<bool>(); |
| } |
| break; |
| } |
| |
| case LookupIterator::ACCESSOR: { |
| if (it->IsReadOnly()) { |
| return WriteToReadOnlyProperty(it, value, should_throw); |
| } |
| Handle<Object> accessors = it->GetAccessors(); |
| if (accessors->IsAccessorInfo() && |
| !it->HolderIsReceiverOrHiddenPrototype() && |
| AccessorInfo::cast(*accessors).is_special_data_property()) { |
| *found = false; |
| return Nothing<bool>(); |
| } |
| return SetPropertyWithAccessor(it, value, should_throw); |
| } |
| case LookupIterator::INTEGER_INDEXED_EXOTIC: { |
| // 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. |
| auto 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::TRANSITION: |
| *found = false; |
| return Nothing<bool>(); |
| } |
| it->Next(); |
| } while (it->IsFound()); |
| |
| *found = false; |
| return Nothing<bool>(); |
| } |
| |
| 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 the receiver is the JSGlobalObject, the store was contextual. In case |
| // the property did not exist yet on the global object itself, we have to |
| // throw a reference error in strict mode. In sloppy mode, we continue. |
| if (it->GetReceiver()->IsJSGlobalObject() && |
| (GetShouldThrow(it->isolate(), should_throw) == |
| ShouldThrow::kThrowOnError)) { |
| it->isolate()->Throw(*it->isolate()->factory()->NewReferenceError( |
| MessageTemplate::kNotDefined, it->GetName())); |
| 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 (!it->GetReceiver()->IsJSReceiver()) { |
| return WriteToReadOnlyProperty(it, value, should_throw); |
| } |
| Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); |
| |
| LookupIterator::Configuration c = LookupIterator::OWN; |
| LookupIterator own_lookup = |
| it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c) |
| : LookupIterator(isolate, receiver, it->name(), c); |
| |
| for (; own_lookup.IsFound(); own_lookup.Next()) { |
| switch (own_lookup.state()) { |
| case LookupIterator::ACCESS_CHECK: |
| if (!own_lookup.HasAccess()) { |
| return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value, |
| should_throw); |
| } |
| break; |
| |
| case LookupIterator::ACCESSOR: |
| if (own_lookup.GetAccessors()->IsAccessorInfo()) { |
| if (own_lookup.IsReadOnly()) { |
| return WriteToReadOnlyProperty(&own_lookup, value, should_throw); |
|