// 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 #include #include #include #include #include "src/objects/objects-inl.h" #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/execution/arguments.h" #include "src/execution/execution.h" #include "src/execution/frames-inl.h" #include "src/execution/isolate-inl.h" #include "src/execution/microtask-queue.h" #include "src/execution/protectors-inl.h" #include "src/heap/heap-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-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/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/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" #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.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/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" #include "torque-generated/class-definitions-tq-inl.h" #include "torque-generated/internal-class-definitions-tq-inl.h" namespace v8 { namespace internal { ShouldThrow GetShouldThrow(Isolate* isolate, Maybe 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(it.frame()); std::vector 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 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(HeapObject::cast(*this).map(), isolate); if (map->is_stable() && map->IsJSReceiverMap()) { return FieldType::Class(map, isolate); } } } return FieldType::Any(isolate); } Handle Object::NewStorageFor(Isolate* isolate, Handle 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::WrapForRead(Isolate* isolate, Handle 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 Object::ToObjectImpl(Isolate* isolate, Handle object, const char* method_name) { DCHECK(!object->IsJSReceiver()); // Use ToObject() for fast path. Handle native_context = isolate->native_context(); Handle constructor; if (object->IsSmi()) { constructor = handle(native_context->number_function(), isolate); } else { int constructor_function_index = Handle::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 result = isolate->factory()->NewJSObject(constructor); Handle::cast(result)->set_value(*object); return result; } // ES6 section 9.2.1.2, OrdinaryCallBindThis for sloppy callee. // static MaybeHandle Object::ConvertReceiver(Isolate* isolate, Handle object) { if (object->IsJSReceiver()) return Handle::cast(object); if (object->IsNullOrUndefined(isolate)) { return isolate->global_proxy(); } return Object::ToObject(isolate, object); } // static MaybeHandle Object::ConvertToNumberOrNumeric(Isolate* isolate, Handle input, Conversion mode) { while (true) { if (input->IsNumber()) { return input; } if (input->IsString()) { return String::ToNumber(isolate, Handle::cast(input)); } if (input->IsOddball()) { return Oddball::ToNumber(isolate, Handle::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::cast(input), ToPrimitiveHint::kNumber), Object); } } // static MaybeHandle Object::ConvertToInteger(Isolate* isolate, Handle 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::ConvertToInt32(Isolate* isolate, Handle 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::ConvertToUint32(Isolate* isolate, Handle 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 Object::ConvertToName(Isolate* isolate, Handle input) { ASSIGN_RETURN_ON_EXCEPTION( isolate, input, Object::ToPrimitive(input, ToPrimitiveHint::kString), Name); if (input->IsName()) return Handle::cast(input); return ToString(isolate, input); } // ES6 7.1.14 // static MaybeHandle Object::ConvertToPropertyKey(Isolate* isolate, Handle value) { // 1. Let key be ToPrimitive(argument, hint String). MaybeHandle maybe_key = Object::ToPrimitive(value, ToPrimitiveHint::kString); // 2. ReturnIfAbrupt(key). Handle 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(Smi::kMaxValue)) { return handle(Smi::FromInt(static_cast(uint_value)), isolate); } } return Object::ToString(isolate, key); } // static MaybeHandle Object::ConvertToString(Isolate* isolate, Handle input) { while (true) { if (input->IsOddball()) { return handle(Handle::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::cast(input)); } ASSIGN_RETURN_ON_EXCEPTION( isolate, input, JSReceiver::ToPrimitive(Handle::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::cast(input); } } } namespace { bool IsErrorObject(Isolate* isolate, Handle object) { if (!object->IsJSReceiver()) return false; Handle symbol = isolate->factory()->stack_trace_symbol(); return JSReceiver::HasOwnProperty(Handle::cast(object), symbol) .FromMaybe(false); } Handle AsStringOrEmpty(Isolate* isolate, Handle object) { return object->IsString() ? Handle::cast(object) : isolate->factory()->empty_string(); } Handle NoSideEffectsErrorToString(Isolate* isolate, Handle input) { Handle receiver = Handle::cast(input); Handle name_key = isolate->factory()->name_string(); Handle name = JSReceiver::GetDataProperty(receiver, name_key); Handle name_str = AsStringOrEmpty(isolate, name); Handle msg_key = isolate->factory()->message_string(); Handle msg = JSReceiver::GetDataProperty(receiver, msg_key); Handle 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(": "); builder.AppendString(msg_str); return builder.Finish().ToHandleChecked(); } } // namespace // static Handle Object::NoSideEffectsToString(Isolate* isolate, Handle input) { DisallowJavascriptExecution no_js(isolate); if (input->IsString() || input->IsNumber() || input->IsOddball()) { return Object::ToString(isolate, input).ToHandleChecked(); } else if (input->IsBigInt()) { MaybeHandle maybe_string = BigInt::ToString(isolate, Handle::cast(input), 10, kDontThrow); Handle 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( ""); } else if (input->IsFunction()) { // -- F u n c t i o n Handle fun_str; if (input->IsJSBoundFunction()) { fun_str = JSBoundFunction::ToString(Handle::cast(input)); } else { DCHECK(input->IsJSFunction()); fun_str = JSFunction::ToString(Handle::cast(input)); } if (fun_str->length() > 128) { IncrementalStringBuilder builder(isolate); builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111)); builder.AppendCString("......"); 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 = Handle::cast(input); if (symbol->is_private_name()) { return Handle(String::cast(symbol->name()), isolate); } IncrementalStringBuilder builder(isolate); builder.AppendCString("Symbol("); if (symbol->name().IsString()) { builder.AppendString(handle(String::cast(symbol->name()), isolate)); } builder.AppendCharacter(')'); return builder.Finish().ToHandleChecked(); } else if (input->IsJSReceiver()) { // -- J S R e c e i v e r Handle receiver = Handle::cast(input); Handle 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, input); } else if (*to_string == *isolate->object_to_string()) { Handle ctor = JSReceiver::GetDataProperty( receiver, isolate->factory()->constructor_string()); if (ctor->IsFunction()) { Handle ctor_name; if (ctor->IsJSBoundFunction()) { ctor_name = JSBoundFunction::GetName( isolate, Handle::cast(ctor)) .ToHandleChecked(); } else if (ctor->IsJSFunction()) { Handle ctor_name_obj = JSFunction::GetName(isolate, Handle::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 receiver; if (input->IsJSReceiver()) { receiver = Handle::cast(input); } else { // This is the only case where Object::ToObject throws. DCHECK(!input->IsSmi()); int constructor_function_index = Handle::cast(input)->map().GetConstructorFunctionIndex(); if (constructor_function_index == Map::kNoConstructorFunctionIndex) { return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]"); } receiver = Object::ToObjectImpl(isolate, input).ToHandleChecked(); } Handle builtin_tag = handle(receiver->class_name(), isolate); Handle tag_obj = JSReceiver::GetDataProperty( receiver, isolate->factory()->to_string_tag_symbol()); Handle tag = tag_obj->IsString() ? Handle::cast(tag_obj) : builtin_tag; IncrementalStringBuilder builder(isolate); builder.AppendCString("[object "); builder.AppendString(tag); builder.AppendCString("]"); return builder.Finish().ToHandleChecked(); } // static MaybeHandle Object::ConvertToLength(Isolate* isolate, Handle 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::kZero, isolate); } else if (len >= kMaxSafeInteger) { len = kMaxSafeInteger; } return isolate->factory()->NewNumber(len); } // static MaybeHandle Object::ConvertToIndex(Isolate* isolate, Handle input, MessageTemplate error_index) { if (input->IsUndefined(isolate)) return handle(Smi::kZero, 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()) + 0.0; 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 x, Handle 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 Object::Compare(Isolate* isolate, Handle x, Handle 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(); } if (x->IsString() && y->IsString()) { // ES6 section 7.2.11 Abstract Relational Comparison step 5. return Just(String::Compare(isolate, Handle::cast(x), Handle::cast(y))); } if (x->IsBigInt() && y->IsString()) { return Just(BigInt::CompareToString(isolate, Handle::cast(x), Handle::cast(y))); } if (x->IsString() && y->IsBigInt()) { return Just(Reverse(BigInt::CompareToString( isolate, Handle::cast(y), Handle::cast(x)))); } // 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(); } 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::cast(x), Handle::cast(y))); } else if (x_is_number) { return Just(Reverse(BigInt::CompareToNumber(Handle::cast(y), x))); } else { return Just(BigInt::CompareToNumber(Handle::cast(x), y)); } } // static Maybe Object::Equals(Isolate* isolate, Handle x, Handle 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::cast(y)->to_number())); } else if (y->IsString()) { return Just(StrictNumberEquals( x, String::ToNumber(isolate, Handle::cast(y)))); } else if (y->IsBigInt()) { return Just(BigInt::EqualToNumber(Handle::cast(y), x)); } else if (y->IsJSReceiver()) { if (!JSReceiver::ToPrimitive(Handle::cast(y)) .ToHandle(&y)) { return Nothing(); } } else { return Just(false); } } else if (x->IsString()) { if (y->IsString()) { return Just(String::Equals(isolate, Handle::cast(x), Handle::cast(y))); } else if (y->IsNumber()) { x = String::ToNumber(isolate, Handle::cast(x)); return Just(StrictNumberEquals(x, y)); } else if (y->IsBoolean()) { x = String::ToNumber(isolate, Handle::cast(x)); return Just( StrictNumberEquals(*x, Handle::cast(y)->to_number())); } else if (y->IsBigInt()) { return Just(BigInt::EqualToString(isolate, Handle::cast(y), Handle::cast(x))); } else if (y->IsJSReceiver()) { if (!JSReceiver::ToPrimitive(Handle::cast(y)) .ToHandle(&y)) { return Nothing(); } } 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::cast(x)->to_number(), *y)); } else if (y->IsString()) { y = String::ToNumber(isolate, Handle::cast(y)); return Just( StrictNumberEquals(Handle::cast(x)->to_number(), *y)); } else if (y->IsBigInt()) { x = Oddball::ToNumber(isolate, Handle::cast(x)); return Just(BigInt::EqualToNumber(Handle::cast(y), x)); } else if (y->IsJSReceiver()) { if (!JSReceiver::ToPrimitive(Handle::cast(y)) .ToHandle(&y)) { return Nothing(); } x = Oddball::ToNumber(isolate, Handle::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::cast(y)) .ToHandle(&y)) { return Nothing(); } } 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::cast(y)); } else if (!JSReceiver::ToPrimitive(Handle::cast(x)) .ToHandle(&x)) { return Nothing(); } } 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 Object::TypeOf(Isolate* isolate, Handle 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::Add(Isolate* isolate, Handle lhs, Handle 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::cast(lhs), Handle::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::cast(lhs), Handle::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::OrdinaryHasInstance(Isolate* isolate, Handle callable, Handle 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 bound_callable( Handle::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 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 result = JSReceiver::HasInPrototypeChain( isolate, Handle::cast(object), prototype); if (result.IsNothing()) return MaybeHandle(); return isolate->factory()->ToBoolean(result.FromJust()); } // static MaybeHandle Object::InstanceOf(Isolate* isolate, Handle object, Handle 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 inst_of_handler; ASSIGN_RETURN_ON_EXCEPTION( isolate, inst_of_handler, Object::GetMethod(Handle::cast(callable), isolate->factory()->has_instance_symbol()), Object); if (!inst_of_handler->IsUndefined(isolate)) { // Call the {inst_of_handler} on the {callable}. Handle 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 result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, Object::OrdinaryHasInstance(isolate, callable, object), Object); return result; } // static MaybeHandle Object::GetMethod(Handle receiver, Handle name) { Handle 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 CreateListFromArrayLikeFastPath( Isolate* isolate, Handle object, ElementTypes element_types) { if (element_types == ElementTypes::kAll) { if (object->IsJSArray()) { Handle array = Handle::cast(object); uint32_t length; if (!array->HasArrayPrototype(isolate) || !array->length().ToUint32(&length) || !array->HasFastElements() || !JSObject::PrototypeHasNoElements(isolate, *array)) { return MaybeHandle(); } return array->GetElementsAccessor()->CreateListFromArrayLike( isolate, array, length); } else if (object->IsJSTypedArray()) { Handle array = Handle::cast(object); size_t length = array->length(); if (array->WasDetached() || length > static_cast(FixedArray::kMaxLength)) { return MaybeHandle(); } return array->GetElementsAccessor()->CreateListFromArrayLike( isolate, array, static_cast(length)); } } return MaybeHandle(); } } // namespace // static MaybeHandle Object::CreateListFromArrayLike( Isolate* isolate, Handle object, ElementTypes element_types) { // Fast-path for JSArray and JSTypedArray. MaybeHandle 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 receiver = Handle::cast(object); Handle 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(FixedArray::kMaxLength)) { THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidArrayLength), FixedArray); } // 5. Let list be an empty List. Handle 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 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::cast(next)); break; } } list->set(index, *next); // 7e. Set index to index + 1. (See loop header.) } // 8. Return list. return list; } // static MaybeHandle Object::GetLengthFromArrayLike(Isolate* isolate, Handle object) { Handle val; Handle 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::GetProperty(LookupIterator* it, OnNonExistent on_non_existent) { for (; it->IsFound(); it->Next()) { switch (it->state()) { case LookupIterator::NOT_FOUND: case LookupIterator::TRANSITION: UNREACHABLE(); case LookupIterator::JSPROXY: { bool was_found; Handle 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()); } MaybeHandle result = JSProxy::GetProperty(it->isolate(), it->GetHolder(), it->GetName(), receiver, &was_found); if (!was_found) it->NotFound(); return result; } case LookupIterator::INTERCEPTOR: { bool done; Handle 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(); } } if (on_non_existent == OnNonExistent::kThrowReferenceError) { THROW_NEW_ERROR(it->isolate(), NewReferenceError(MessageTemplate::kNotDefined, it->name()), Object); } return it->isolate()->factory()->undefined_value(); } // static MaybeHandle JSProxy::GetProperty(Isolate* isolate, Handle proxy, Handle name, Handle receiver, bool* was_found) { *was_found = true; DCHECK(!name->IsPrivate()); STACK_CHECK(isolate, MaybeHandle()); Handle 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 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 target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "get"). Handle trap; ASSIGN_RETURN_ON_EXCEPTION( isolate, trap, Object::GetMethod(Handle::cast(handler), trap_name), Object); // 7. If trap is undefined, then if (trap->IsUndefined(isolate)) { // 7.a Return target.[[Get]](P, Receiver). LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name, target); MaybeHandle result = Object::GetProperty(&it); *was_found = it.IsFound(); return result; } // 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»). Handle trap_result; Handle args[] = {target, name, receiver}; ASSIGN_RETURN_ON_EXCEPTION( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Object); MaybeHandle result = JSProxy::CheckGetSetTrapResult(isolate, name, target, trap_result, kGet); if (result.is_null()) { return result; } // 11. Return trap_result return trap_result; } // static MaybeHandle JSProxy::CheckGetSetTrapResult(Isolate* isolate, Handle name, Handle target, Handle trap_result, AccessKind access_kind) { // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe 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(); } } // 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(); } } } 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; } // static constexpr object declarations need a definition to make the // compiler happy. constexpr Object Smi::kZero; V8_EXPORT_PRIVATE constexpr Object SharedFunctionInfo::kNoSharedNameSentinel; Handle FunctionTemplateInfo::GetOrCreateSharedFunctionInfo( Isolate* isolate, Handle info, MaybeHandle maybe_name) { Object current_info = info->shared_function_info(); if (current_info.IsSharedFunctionInfo()) { return handle(SharedFunctionInfo::cast(current_info), isolate); } Handle name; Handle name_string; if (maybe_name.ToHandle(&name) && name->IsString()) { name_string = Handle::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 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(); } 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 function_template_info) { DCHECK(function_template_info->rare_data().IsUndefined(isolate)); Handle struct_obj = isolate->factory()->NewStruct( FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld); Handle rare_data = i::Handle::cast(struct_obj); function_template_info->set_rare_data(*rare_data); return *rare_data; } // static Handle TemplateList::New(Isolate* isolate, int size) { Handle list = isolate->factory()->NewFixedArray(kLengthIndex + size); list->set(kLengthIndex, Smi::kZero); return Handle::cast(list); } // static Handle TemplateList::Add(Isolate* isolate, Handle list, Handle value) { STATIC_ASSERT(kFirstElementIndex == 1); int index = list->length() + 1; Handle fixed_array = Handle::cast(list); fixed_array = FixedArray::SetAndGrow(isolate, fixed_array, index, value); fixed_array->set(kLengthIndex, Smi::FromInt(index)); return Handle::cast(fixed_array); } // ES6 9.5.1 // static MaybeHandle JSProxy::GetPrototype(Handle proxy) { Isolate* isolate = proxy->GetIsolate(); Handle trap_name = isolate->factory()->getPrototypeOf_string(); STACK_CHECK(isolate, MaybeHandle()); // 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 target(JSReceiver::cast(proxy->target()), isolate); Handle handler(JSReceiver::cast(proxy->handler()), isolate); // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). Handle 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 argv[] = {target}; Handle 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 is_extensible = JSReceiver::IsExtensible(target); MAYBE_RETURN(is_extensible, MaybeHandle()); // 10. If extensibleTarget is true, return handlerProto. if (is_extensible.FromJust()) return Handle::cast(handler_proto); // 11. Let targetProto be ? target.[[GetPrototypeOf]](). Handle 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::cast(handler_proto); } MaybeHandle Object::GetPropertyWithAccessor(LookupIterator* it) { Isolate* isolate = it->isolate(); Handle structure = it->GetAccessors(); Handle 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 holder = it->GetHolder(); if (structure->IsAccessorInfo()) { Handle name = it->GetName(); Handle info = Handle::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 result = args.CallAccessorGetter(info, name); RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); if (result.is_null()) return isolate->factory()->undefined_value(); Handle reboxed_result = handle(*result, isolate); if (info->replace_on_access() && receiver->IsJSReceiver()) { RETURN_ON_EXCEPTION(isolate, Accessors::ReplaceAccessorWithDataProperty( receiver, holder, name, result), Object); } return reboxed_result; } // AccessorPair with 'cached' private property. if (it->TryLookupCachedProperty()) { return Object::GetProperty(it); } // Regular accessor. Handle getter(AccessorPair::cast(*structure).getter(), isolate); if (getter->IsFunctionTemplateInfo()) { SaveAndSwitchContext save(isolate, *holder->GetCreationContext()); return Builtins::InvokeApiFunction( isolate, false, Handle::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::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
(getter()); if (accessor == kNullAddress) return kNullAddress; return redirect(accessor, ACCESSOR_GETTER); } Address CallHandlerInfo::redirected_callback() const { Address address = v8::ToCData
(callback()); ApiFunction fun(address); ExternalReference::Type type = ExternalReference::DIRECT_API_CALL; return ExternalReference::Create(&fun, type).address(); } bool AccessorInfo::IsCompatibleReceiverMap(Handle info, Handle map) { if (!info->HasExpectedReceiverType()) return true; if (!map->IsJSObjectMap()) return false; return FunctionTemplateInfo::cast(info->expected_receiver_type()) .IsTemplateFor(*map); } Maybe Object::SetPropertyWithAccessor( LookupIterator* it, Handle value, Maybe maybe_should_throw) { Isolate* isolate = it->isolate(); Handle structure = it->GetAccessors(); Handle 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 holder = it->GetHolder(); if (structure->IsAccessorInfo()) { Handle name = it->GetName(); Handle info = Handle::cast(structure); if (!info->IsCompatibleReceiver(*receiver)) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kIncompatibleMethodReceiver, name, receiver)); return Nothing(); } 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()); } // 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 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()); 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 setter(AccessorPair::cast(*structure).setter(), isolate); if (setter->IsFunctionTemplateInfo()) { SaveAndSwitchContext save(isolate, *holder->GetCreationContext()); Handle argv[] = {value}; RETURN_ON_EXCEPTION_VALUE( isolate, Builtins::InvokeApiFunction(isolate, false, Handle::cast(setter), receiver, arraysize(argv), argv, isolate->factory()->undefined_value()), Nothing()); return Just(true); } else if (setter->IsCallable()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... return SetPropertyWithDefinedSetter( receiver, Handle::cast(setter), value, maybe_should_throw); } RETURN_FAILURE(isolate, GetShouldThrow(isolate, maybe_should_throw), NewTypeError(MessageTemplate::kNoSetterInCallback, it->GetName(), it->GetHolder())); } MaybeHandle Object::GetPropertyWithDefinedGetter( Handle receiver, Handle 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(); } return Execution::Call(isolate, getter, receiver, 0, nullptr); } Maybe Object::SetPropertyWithDefinedSetter( Handle receiver, Handle setter, Handle value, Maybe should_throw) { Isolate* isolate = setter->GetIsolate(); Handle argv[] = {value}; RETURN_ON_EXCEPTION_VALUE( isolate, Execution::Call(isolate, setter, receiver, arraysize(argv), argv), Nothing()); 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::ArraySpeciesConstructor( Isolate* isolate, Handle original_array) { Handle default_species = isolate->array_function(); if (original_array->IsJSArray() && Handle::cast(original_array)->HasArrayPrototype(isolate) && Protectors::IsArraySpeciesLookupChainIntact(isolate)) { return default_species; } Handle constructor = isolate->factory()->undefined_value(); Maybe 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 constructor_context; ASSIGN_RETURN_ON_EXCEPTION( isolate, constructor_context, JSReceiver::GetFunctionRealm(Handle::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::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::SpeciesConstructor( Isolate* isolate, Handle recv, Handle default_ctor) { Handle 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 ctor = Handle::cast(ctor_obj); Handle 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 (!isolate->IsArrayIteratorLookupChainIntact()) 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) && isolate->IsNoElementsProtectorIntact()) { return false; } return true; } 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 << ""; } break; case AWAIT_CONTEXT_TYPE: { os << ""; break; case CATCH_CONTEXT_TYPE: os << ""; break; case DEBUG_EVALUATE_CONTEXT_TYPE: os << ""; break; case EVAL_CONTEXT_TYPE: os << ""; break; case FUNCTION_CONTEXT_TYPE: os << ""; break; case MODULE_CONTEXT_TYPE: os << ""; break; case NATIVE_CONTEXT_TYPE: os << ""; break; case SCRIPT_CONTEXT_TYPE: os << ""; break; case WITH_CONTEXT_TYPE: os << ""; break; case SCRIPT_CONTEXT_TABLE_TYPE: os << ""; break; case HASH_TABLE_TYPE: os << ""; break; case ORDERED_HASH_MAP_TYPE: os << ""; break; case ORDERED_HASH_SET_TYPE: os << ""; break; case ORDERED_NAME_DICTIONARY_TYPE: os << ""; break; case NAME_DICTIONARY_TYPE: os << ""; break; case GLOBAL_DICTIONARY_TYPE: os << ""; break; case NUMBER_DICTIONARY_TYPE: os << ""; break; case SIMPLE_NUMBER_DICTIONARY_TYPE: os << ""; break; case STRING_TABLE_TYPE: os << ""; break; case FIXED_ARRAY_TYPE: os << ""; break; case OBJECT_BOILERPLATE_DESCRIPTION_TYPE: os << ""; break; case FIXED_DOUBLE_ARRAY_TYPE: os << ""; break; case BYTE_ARRAY_TYPE: os << ""; break; case BYTECODE_ARRAY_TYPE: os << ""; break; case DESCRIPTOR_ARRAY_TYPE: os << ""; break; case TRANSITION_ARRAY_TYPE: os << ""; break; case PROPERTY_ARRAY_TYPE: os << ""; break; case FEEDBACK_CELL_TYPE: { { ReadOnlyRoots roots = GetReadOnlyRoots(); os << ""; } break; } case CLOSURE_FEEDBACK_CELL_ARRAY_TYPE: os << ""; break; case FEEDBACK_VECTOR_TYPE: os << ""; break; case FREE_SPACE_TYPE: os << ""; break; case PREPARSE_DATA_TYPE: { PreparseData data = PreparseData::cast(*this); os << ""; break; } case UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE: { UncompiledDataWithoutPreparseData data = UncompiledDataWithoutPreparseData::cast(*this); os << ""; break; } case UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE: { UncompiledDataWithPreparseData data = UncompiledDataWithPreparseData::cast(*this); os << ""; break; } case SHARED_FUNCTION_INFO_TYPE: { SharedFunctionInfo shared = SharedFunctionInfo::cast(*this); std::unique_ptr debug_name = shared.DebugName().ToCString(); if (debug_name[0] != 0) { os << ""; } else { os << ""; } break; } case JS_MESSAGE_OBJECT_TYPE: os << ""; 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 << ""; break; } case SCOPE_INFO_TYPE: { ScopeInfo scope = ScopeInfo::cast(*this); os << ""; break; } case CODE_TYPE: { Code code = Code::cast(*this); os << ""; break; } case ODDBALL_TYPE: { if (IsUndefined()) { os << ""; } else if (IsTheHole()) { os << ""; } else if (IsNull()) { os << ""; } else if (IsTrue()) { os << ""; } else if (IsFalse()) { os << ""; } else { os << ""; } break; } case SYMBOL_TYPE: { Symbol symbol = Symbol::cast(*this); symbol.SymbolShortPrint(os); break; } case HEAP_NUMBER_TYPE: { os << ""; break; } case BIGINT_TYPE: { os << ""; break; } case JS_PROXY_TYPE: os << ""; break; case FOREIGN_TYPE: os << ""; break; case CELL_TYPE: { os << ""; } else { os << ", side_effect_free= false>"; } break; } default: os << ""; break; } } void Struct::BriefPrintDetails(std::ostream& os) {} void Tuple2::BriefPrintDetails(std::ostream& os) { os << " " << Brief(value1()) << ", " << Brief(value2()); } void Tuple3::BriefPrintDetails(std::ostream& os) { os << " " << Brief(value1()) << ", " << Brief(value2()) << ", " << Brief(value3()); } void ClassPositions::BriefPrintDetails(std::ostream& os) { os << " " << start() << ", " << end(); } void ArrayBoilerplateDescription::BriefPrintDetails(std::ostream& os) { os << " " << elements_kind() << ", " << Brief(constant_elements()); } void CallableTask::BriefPrintDetails(std::ostream& os) { os << " callable=" << Brief(callable()); } void HeapObject::Iterate(ObjectVisitor* v) { IterateFast(v); } void HeapObject::IterateBody(ObjectVisitor* v) { Map m = map(); IterateBodyFast(m, SizeFromMap(m), v); } void HeapObject::IterateBody(Map map, int object_size, ObjectVisitor* v) { IterateBodyFast(map, object_size, v); } struct CallIsValidSlot { template 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(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 (IsInRange(instance_type, FIRST_FIXED_ARRAY_TYPE, LAST_FIXED_ARRAY_TYPE)) { return FixedArray::SizeFor( FixedArray::unchecked_cast(*this).synchronized_length()); } if (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 (instance_type == DESCRIPTOR_ARRAY_TYPE) { return DescriptorArray::SizeFor( DescriptorArray::unchecked_cast(*this).number_of_all_descriptors()); } if (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()); } if (instance_type == CODE_TYPE) { return Code::unchecked_cast(*this).CodeSize(); } DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE); return EmbedderDataArray::SizeFor( EmbedderDataArray::unchecked_cast(*this).length()); } bool HeapObject::NeedsRehashing() const { switch (map().instance_type()) { case 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: return OrderedHashMap::cast(*this).NumberOfElements() > 0; case ORDERED_HASH_SET_TYPE: return OrderedHashSet::cast(*this).NumberOfElements() > 0; case NAME_DICTIONARY_TYPE: case GLOBAL_DICTIONARY_TYPE: case NUMBER_DICTIONARY_TYPE: case SIMPLE_NUMBER_DICTIONARY_TYPE: case STRING_TABLE_TYPE: case HASH_TABLE_TYPE: case SMALL_ORDERED_HASH_MAP_TYPE: case SMALL_ORDERED_HASH_SET_TYPE: case SMALL_ORDERED_NAME_DICTIONARY_TYPE: return true; default: return false; } } bool HeapObject::CanBeRehashed() const { DCHECK(NeedsRehashing()); switch (map().instance_type()) { case ORDERED_HASH_MAP_TYPE: case ORDERED_HASH_SET_TYPE: case ORDERED_NAME_DICTIONARY_TYPE: // TODO(yangguo): actually support rehashing OrderedHash{Map,Set}. return false; case NAME_DICTIONARY_TYPE: case GLOBAL_DICTIONARY_TYPE: case NUMBER_DICTIONARY_TYPE: case SIMPLE_NUMBER_DICTIONARY_TYPE: case STRING_TABLE_TYPE: return true; case 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(ReadOnlyRoots roots) { switch (map().instance_type()) { case HASH_TABLE_TYPE: UNREACHABLE(); case NAME_DICTIONARY_TYPE: NameDictionary::cast(*this).Rehash(roots); break; case GLOBAL_DICTIONARY_TYPE: GlobalDictionary::cast(*this).Rehash(roots); break; case NUMBER_DICTIONARY_TYPE: NumberDictionary::cast(*this).Rehash(roots); break; case SIMPLE_NUMBER_DICTIONARY_TYPE: SimpleNumberDictionary::cast(*this).Rehash(roots); break; case STRING_TABLE_TYPE: StringTable::cast(*this).Rehash(roots); 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 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 (int i = 0; i < length; i++) { 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::SetProperty(Isolate* isolate, Handle object, Handle name, Handle value, StoreOrigin store_origin, Maybe should_throw) { LookupIterator it(isolate, object, name); MAYBE_RETURN_NULL(SetProperty(&it, value, store_origin, should_throw)); return value; } Maybe Object::SetPropertyInternal(LookupIterator* it, Handle value, Maybe 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 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(), it->GetName(), value, receiver, should_throw); } case LookupIterator::INTERCEPTOR: { if (it->HolderIsReceiverOrHiddenPrototype()) { Maybe result = JSObject::SetPropertyWithInterceptor(it, should_throw, value); if (result.IsNothing() || result.FromJust()) return result; } else { Maybe maybe_attributes = JSObject::GetPropertyAttributesWithInterceptor(it); if (maybe_attributes.IsNothing()) return Nothing(); if ((maybe_attributes.FromJust() & READ_ONLY) != 0) { return WriteToReadOnlyProperty(it, value, should_throw); } if (maybe_attributes.FromJust() == ABSENT) break; *found = false; return Nothing(); } break; } case LookupIterator::ACCESSOR: { if (it->IsReadOnly()) { return WriteToReadOnlyProperty(it, value, should_throw); } Handle accessors = it->GetAccessors(); if (accessors->IsAccessorInfo() && !it->HolderIsReceiverOrHiddenPrototype() && AccessorInfo::cast(*accessors).is_special_data_property()) { *found = false; return Nothing(); } 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(); Handle throwaway_value; if (holder->type() == kExternalBigInt64Array || holder->type() == kExternalBigUint64Array) { ASSIGN_RETURN_ON_EXCEPTION_VALUE( it->isolate(), throwaway_value, BigInt::FromObject(it->isolate(), value), Nothing()); } else { ASSIGN_RETURN_ON_EXCEPTION_VALUE( it->isolate(), throwaway_value, Object::ToNumber(it->isolate(), value), Nothing()); } // 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(); } it->Next(); } while (it->IsFound()); *found = false; return Nothing(); } Maybe Object::SetProperty(LookupIterator* it, Handle value, StoreOrigin store_origin, Maybe should_throw) { if (it->IsFound()) { bool found = true; Maybe 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(); } return AddDataProperty(it, value, NONE, should_throw, store_origin); } Maybe Object::SetSuperProperty(LookupIterator* it, Handle value, StoreOrigin store_origin, Maybe should_throw) { Isolate* isolate = it->isolate(); if (it->IsFound()) { bool found = true; Maybe 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 receiver = Handle::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); } return Object::SetPropertyWithAccessor(&own_lookup, value, should_throw); } V8_FALLTHROUGH; case LookupIterator::INTEGER_INDEXED_EXOTIC: return RedefineIncompatibleProperty(isolate, it->GetName(), value, should_throw); case LookupIterator::DATA: { if (own_lookup.IsReadOnly()) { return WriteToReadOnlyProperty(&own_lookup, value, should_throw); } return SetDataProperty(&own_lookup, value); } case LookupIterator::INTERCEPTOR: case LookupIterator::JSPROXY: { PropertyDescriptor desc; Maybe owned = JSReceiver::GetOwnPropertyDescriptor(&own_lookup, &desc); MAYBE_RETURN(owned, Nothing()); if (!owned.FromJust()) { return JSReceiver::CreateDataProperty(&own_lookup, value, should_throw); } if (PropertyDescriptor::IsAccessorDescriptor(&desc) || !desc.writable()) { return RedefineIncompatibleProperty(isolate, it->GetName(), value, should_throw); } PropertyDescriptor value_desc; value_desc.set_value(value); return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(), &value_desc, should_throw); } case LookupIterator::NOT_FOUND: case LookupIterator::TRANSITION: UNREACHABLE(); } } return AddDataProperty(&own_lookup, value, NONE, should_throw, store_origin); } Maybe Object::CannotCreateProperty(Isolate* isolate, Handle receiver, Handle name, Handle value, Maybe should_throw) { RETURN_FAILURE( isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kStrictCannotCreateProperty, name, Object::TypeOf(isolate, receiver), receiver)); } Maybe Object::WriteToReadOnlyProperty( LookupIterator* it, Handle value, Maybe maybe_should_throw) { ShouldThrow should_throw = GetShouldThrow(it->isolate(), maybe_should_throw); if (it->IsFound() && !it->HolderIsReceiver()) { // "Override mistake" attempted, record a use count to track this per // v8:8175 v8::Isolate::UseCounterFeature feature = should_throw == kThrowOnError ? v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeStrict : v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeSloppy; it->isolate()->CountUsage(feature); } return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(), it->GetName(), value, should_throw); } Maybe Object::WriteToReadOnlyProperty(Isolate* isolate, Handle receiver, Handle name, Handle value, ShouldThrow should_throw) { RETURN_FAILURE(isolate, should_throw, NewTypeError(MessageTemplate::kStrictReadOnlyProperty, name, Object::TypeOf(isolate, receiver), receiver)); } Maybe Object::RedefineIncompatibleProperty( Isolate* isolate, Handle name, Handle value, Maybe should_throw) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kRedefineDisallowed, name)); } Maybe Object::SetDataProperty(LookupIterator* it, Handle value) { DCHECK_IMPLIES(it->GetReceiver()->IsJSProxy(), it->GetName()->IsPrivateName()); DCHECK_IMPLIES(!it->IsElement() && it->GetName()->IsPrivateName(), it->state() == LookupIterator::DATA); Handle receiver = Handle::cast(it->GetReceiver()); // Store on the holder which may be hidden behind the receiver. DCHECK(it->HolderIsReceiverOrHiddenPrototype()); Handle to_assign = value; // Convert the incoming value to a number for storing into typed arrays. if (it->IsElement() && receiver->IsJSObject() && JSObject::cast(*receiver).HasTypedArrayElements()) { ElementsKind elements_kind = JSObject::cast(*receiver).GetElementsKind(); if (elements_kind == BIGINT64_ELEMENTS || elements_kind == BIGUINT64_ELEMENTS) { ASSIGN_RETURN_ON_EXCEPTION_VALUE(it->isolate(), to_assign, BigInt::FromObject(it->isolate(), value), Nothing()); // We have to recheck the length. However, it can only change if the // underlying buffer was detached, so just check that. if (Handle::cast(receiver)->WasDetached()) { return Just(true); // TODO(neis): According to the spec, this should throw a TypeError. } } else if (!value->IsNumber() && !value->IsUndefined(it->isolate())) { ASSIGN_RETURN_ON_EXCEPTION_VALUE(it->isolate(), to_assign, Object::ToNumber(it->isolate(), value), Nothing()); // We have to recheck the length. However, it can only change if the // underlying buffer was detached, so just check that. if (Handle::cast(receiver)->WasDetached()) { return Just(true); // TODO(neis): According to the spec, this should throw a TypeError. } } } // Possibly migrate to the most up-to-date map that will be able to store // |value| under it->name(). it->PrepareForDataProperty(to_assign); // Write the property value. it->WriteDataValue(to_assign, false); #if VERIFY_HEAP if (FLAG_verify_heap) { receiver->HeapObjectVerify(it->isolate()); } #endif return Just(true); } Maybe Object::AddDataProperty(LookupIterator* it, Handle value, PropertyAttributes attributes, Maybe should_throw, StoreOrigin store_origin) { if (!it->GetReceiver()->IsJSReceiver()) { return CannotCreateProperty(it->isolate(), it->GetReceiver(), it->GetName(), value, should_throw); } // Private symbols should be installed on JSProxy using // JSProxy::SetPrivateSymbol. if (it->GetReceiver()->IsJSProxy() && it->GetName()->IsPrivate() && !it->GetName()->IsPrivateName()) { RETURN_FAILURE(it->isolate(), GetShouldThrow(it->isolate(), should_throw), NewTypeError(MessageTemplate::kProxyPrivate)); } DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, it->state()); Handle receiver = it->GetStoreTarget(); DCHECK_IMPLIES(receiver->IsJSProxy(), it->GetName()->IsPrivateName()); DCHECK_IMPLIES(receiver->IsJSProxy(), it->state() == LookupIterator::NOT_FOUND); // If the receiver is a JSGlobalProxy, store on the prototype (JSGlobalObject) // instead. If the prototype is Null, the proxy is detached. if (receiver->IsJSGlobalProxy()) return Just(true); Isolate* isolate = it->isolate(); if (it->ExtendingNonExtensible(receiver)) { RETURN_FAILURE( isolate, GetShouldThrow(it->isolate(), should_throw), NewTypeError(MessageTemplate::kObjectNotExtensible, it->GetName())); } if (it->IsElement()) { if (receiver->IsJSArray()) { Handle array = Handle::cast(receiver); if (JSArray::WouldChangeReadOnlyLength(array, it->index())) { RETURN_FAILURE(isolate, GetShouldThrow(it->isolate(), should_throw), NewTypeError(MessageTemplate::kStrictReadOnlyProperty, isolate->factory()->length_string(), Object::TypeOf(isolate, array), array)); } } Handle receiver_obj = Handle::cast(receiver); JSObject::AddDataElement(receiver_obj, it->index(), value, attributes); JSObject::ValidateElements(*receiver_obj); return Just(true); } else { it->UpdateProtector(); // Migrate to the most up-to-date map that will be able to store |value| // under it->name() with |attributes|. it->PrepareTransitionToDataProperty(receiver, value, attributes, store_origin); DCHECK_EQ(LookupIterator::TRANSITION, it->state()); it->ApplyTransitionToDataProperty(receiver); // Write the property value. it->WriteDataValue(value, true); #if VERIFY_HEAP if (FLAG_verify_heap) { receiver->HeapObjectVerify(isolate); } #endif } return Just(true); } template static int AppendUniqueCallbacks(Isolate* isolate, Handle callbacks, Handle array, int valid_descriptors) { int nof_callbacks = callbacks->length(); // Fill in new callback descriptors. Process the callbacks from // back to front so that the last callback with a given name takes // precedence over previously added callbacks with that name. for (int i = nof_callbacks - 1; i >= 0; i--) { Handle entry(AccessorInfo::cast(callbacks->get(i)), isolate); Handle key(Name::cast(entry->name()), isolate); DCHECK(key->IsUniqueName()); // Check if a descriptor with this name already exists before writing. if (!T::Contains(key, entry, valid_descriptors, array)) { T::Insert(key, entry, valid_descriptors, array); valid_descriptors++; } } return valid_descriptors; } struct FixedArrayAppender { using Array = FixedArray; static bool Contains(Handle key, Handle entry, int valid_descriptors, Handle array) { for (int i = 0; i < valid_descriptors; i++) { if (*key == AccessorInfo::cast(array->get(i)).name()) return true; } return false; } static void Insert(Handle key, Handle entry, int valid_descriptors, Handle array) { DisallowHeapAllocation no_gc; array->set(valid_descriptors, *entry); } }; int AccessorInfo::AppendUnique(Isolate* isolate, Handle descriptors, Handle array, int valid_descriptors) { Handle callbacks = Handle::cast(descriptors); DCHECK_GE(array->length(), callbacks->length() + valid_descriptors); return AppendUniqueCallbacks(isolate, callbacks, array, valid_descriptors); } void JSProxy::Revoke(Handle proxy) { Isolate* isolate = proxy->GetIsolate(); // ES#sec-proxy-revocation-functions if (!proxy->IsRevoked()) { // 5. Set p.[[ProxyTarget]] to null. proxy->set_target(ReadOnlyRoots(isolate).null_value()); // 6. Set p.[[ProxyHandler]] to null. proxy->set_handler(ReadOnlyRoots(isolate).null_value()); } DCHECK(proxy->IsRevoked()); } // static Maybe JSProxy::IsArray(Handle proxy) { Isolate* isolate = proxy->GetIsolate(); Handle object = Handle::cast(proxy); for (int i = 0; i < JSProxy::kMaxIterationLimit; i++) { Handle proxy = Handle::cast(object); if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyRevoked, isolate->factory()->NewStringFromAsciiChecked("IsArray"))); return Nothing(); } object = handle(JSReceiver::cast(proxy->target()), isolate); if (object->IsJSArray()) return Just(true); if (!object->IsJSProxy()) return Just(false); } // Too deep recursion, throw a RangeError. isolate->StackOverflow(); return Nothing(); } Maybe JSProxy::HasProperty(Isolate* isolate, Handle proxy, Handle name) { DCHECK(!name->IsPrivate()); STACK_CHECK(isolate, Nothing()); // 1. (Assert) // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle handler(proxy->handler(), isolate); // 3. If handler is null, throw a TypeError exception. // 4. Assert: Type(handler) is Object. if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyRevoked, isolate->factory()->has_string())); return Nothing(); } // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. Handle target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "has"). Handle trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(Handle::cast(handler), isolate->factory()->has_string()), Nothing()); // 7. If trap is undefined, then if (trap->IsUndefined(isolate)) { // 7a. Return target.[[HasProperty]](P). return JSReceiver::HasProperty(target, name); } // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, P»)). Handle trap_result_obj; Handle args[] = {target, name}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result_obj, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing()); bool boolean_trap_result = trap_result_obj->BooleanValue(isolate); // 9. If booleanTrapResult is false, then: if (!boolean_trap_result) { MAYBE_RETURN(JSProxy::CheckHasTrap(isolate, name, target), Nothing()); } // 10. Return booleanTrapResult. return Just(boolean_trap_result); } Maybe JSProxy::CheckHasTrap(Isolate* isolate, Handle name, Handle target) { // 9a. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe target_found = JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); MAYBE_RETURN(target_found, Nothing()); // 9b. If targetDesc is not undefined, then: if (target_found.FromJust()) { // 9b i. If targetDesc.[[Configurable]] is false, throw a TypeError // exception. if (!target_desc.configurable()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyHasNonConfigurable, name)); return Nothing(); } // 9b ii. Let extensibleTarget be ? IsExtensible(target). Maybe extensible_target = JSReceiver::IsExtensible(target); MAYBE_RETURN(extensible_target, Nothing()); // 9b iii. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target.FromJust()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyHasNonExtensible, name)); return Nothing(); } } return Just(true); } Maybe JSProxy::SetProperty(Handle proxy, Handle name, Handle value, Handle receiver, Maybe should_throw) { DCHECK(!name->IsPrivate()); Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing()); Factory* factory = isolate->factory(); Handle trap_name = factory->set_string(); if (proxy->IsRevoked()) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing(); } Handle target(JSReceiver::cast(proxy->target()), isolate); Handle handler(JSReceiver::cast(proxy->handler()), isolate); Handle trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing()); if (trap->IsUndefined(isolate)) { LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name, target); return Object::SetSuperProperty(&it, value, StoreOrigin::kMaybeKeyed, should_throw); } Handle trap_result; Handle args[] = {target, name, value, receiver}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing()); if (!trap_result->BooleanValue(isolate)) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor, trap_name, name)); } MaybeHandle result = JSProxy::CheckGetSetTrapResult(isolate, name, target, value, kSet); if (result.is_null()) { return Nothing(); } return Just(true); } Maybe JSProxy::DeletePropertyOrElement(Handle proxy, Handle name, LanguageMode language_mode) { DCHECK(!name->IsPrivate()); ShouldThrow should_throw = is_sloppy(language_mode) ? kDontThrow : kThrowOnError; Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing()); Factory* factory = isolate->factory(); Handle trap_name = factory->deleteProperty_string(); if (proxy->IsRevoked()) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing(); } Handle target(JSReceiver::cast(proxy->target()), isolate); Handle handler(JSReceiver::cast(proxy->handler()), isolate); Handle trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing()); if (trap->IsUndefined(isolate)) { return JSReceiver::DeletePropertyOrElement(target, name, language_mode); } Handle trap_result; Handle args[] = {target, name}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing()); if (!trap_result->BooleanValue(isolate)) { RETURN_FAILURE(isolate, should_throw, NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor, trap_name, name)); } // Enforce the invariant. return JSProxy::CheckDeleteTrap(isolate, name, target); } Maybe JSProxy::CheckDeleteTrap(Isolate* isolate, Handle name, Handle target) { // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe target_found = JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); MAYBE_RETURN(target_found, Nothing()); // 11. If targetDesc is undefined, return true. if (target_found.FromJust()) { // 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception. if (!target_desc.configurable()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDeletePropertyNonConfigurable, name)); return Nothing(); } // 13. Let extensibleTarget be ? IsExtensible(target). Maybe extensible_target = JSReceiver::IsExtensible(target); MAYBE_RETURN(extensible_target, Nothing()); // 14. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target.FromJust()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDeletePropertyNonExtensible, name)); return Nothing(); } } return Just(true); } // static MaybeHandle JSProxy::New(Isolate* isolate, Handle target, Handle handler) { if (!target->IsJSReceiver()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject), JSProxy); } if (target->IsJSProxy() && JSProxy::cast(*target).IsRevoked()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyHandlerOrTargetRevoked), JSProxy); } if (!handler->IsJSReceiver()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject), JSProxy); } if (handler->IsJSProxy() && JSProxy::cast(*handler).IsRevoked()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyHandlerOrTargetRevoked), JSProxy); } return isolate->factory()->NewJSProxy(Handle::cast(target), Handle::cast(handler)); } // static MaybeHandle JSProxy::GetFunctionRealm(Handle proxy) { DCHECK(proxy->map().is_constructor()); if (proxy->IsRevoked()) { THROW_NEW_ERROR(proxy->GetIsolate(), NewTypeError(MessageTemplate::kProxyRevoked), NativeContext); } Handle target(JSReceiver::cast(proxy->target()), proxy->GetIsolate()); return JSReceiver::GetFunctionRealm(target); } Maybe JSProxy::GetPropertyAttributes(LookupIterator* it) { PropertyDescriptor desc; Maybe found = JSProxy::GetOwnPropertyDescriptor( it->isolate(), it->GetHolder(), it->GetName(), &desc); MAYBE_RETURN(found, Nothing()); if (!found.FromJust()) return Just(ABSENT); return Just(desc.ToAttributes()); } // TODO(jkummerow): Consider unification with FastAsArrayLength() in // accessors.cc. bool PropertyKeyToArrayLength(Handle value, uint32_t* length) { DCHECK(value->IsNumber() || value->IsName()); if (value->ToArrayLength(length)) return true; if (value->IsString()) return String::cast(*value).AsArrayIndex(length); return false; } bool PropertyKeyToArrayIndex(Handle index_obj, uint32_t* output) { return PropertyKeyToArrayLength(index_obj, output) && *output != kMaxUInt32; } // ES6 9.4.2.1 // static Maybe JSArray::DefineOwnProperty(Isolate* isolate, Handle o, Handle name, PropertyDescriptor* desc, Maybe should_throw) { // 1. Assert: IsPropertyKey(P) is true. ("P" is |name|.) // 2. If P is "length", then: // TODO(jkummerow): Check if we need slow string comparison. if (*name == ReadOnlyRoots(isolate).length_string()) { // 2a. Return ArraySetLength(A, Desc). return ArraySetLength(isolate, o, desc, should_throw); } // 3. Else if P is an array index, then: uint32_t index = 0; if (PropertyKeyToArrayIndex(name, &index)) { // 3a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). PropertyDescriptor old_len_desc; Maybe success = GetOwnPropertyDescriptor( isolate, o, isolate->factory()->length_string(), &old_len_desc); // 3b. (Assert) DCHECK(success.FromJust()); USE(success); // 3c. Let oldLen be oldLenDesc.[[Value]]. uint32_t old_len = 0; CHECK(old_len_desc.value()->ToArrayLength(&old_len)); // 3d. Let index be ToUint32(P). // (Already done above.) // 3e. (Assert) // 3f. If index >= oldLen and oldLenDesc.[[Writable]] is false, // return false. if (index >= old_len && old_len_desc.has_writable() && !old_len_desc.writable()) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kDefineDisallowed, name)); } // 3g. Let succeeded be OrdinaryDefineOwnProperty(A, P, Desc). Maybe succeeded = OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw); // 3h. Assert: succeeded is not an abrupt completion. // In our case, if should_throw == kThrowOnError, it can be! // 3i. If succeeded is false, return false. if (succeeded.IsNothing() || !succeeded.FromJust()) return succeeded; // 3j. If index >= oldLen, then: if (index >= old_len) { // 3j i. Set oldLenDesc.[[Value]] to index + 1. old_len_desc.set_value(isolate->factory()->NewNumberFromUint(index + 1)); // 3j ii. Let succeeded be // OrdinaryDefineOwnProperty(A, "length", oldLenDesc). succeeded = OrdinaryDefineOwnProperty(isolate, o, isolate->factory()->length_string(), &old_len_desc, should_throw); // 3j iii. Assert: succeeded is true. DCHECK(succeeded.FromJust()); USE(succeeded); } // 3k. Return true. return Just(true); } // 4. Return OrdinaryDefineOwnProperty(A, P, Desc). return OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw); } // Part of ES6 9.4.2.4 ArraySetLength. // static bool JSArray::AnythingToArrayLength(Isolate* isolate, Handle length_object, uint32_t* output) { // Fast path: check numbers and strings that can be converted directly // and unobservably. if (length_object->ToArrayLength(output)) return true; if (length_object->IsString() && Handle::cast(length_object)->AsArrayIndex(output)) { return true; } // Slow path: follow steps in ES6 9.4.2.4 "ArraySetLength". // 3. Let newLen be ToUint32(Desc.[[Value]]). Handle uint32_v; if (!Object::ToUint32(isolate, length_object).ToHandle(&uint32_v)) { // 4. ReturnIfAbrupt(newLen). return false; } // 5. Let numberLen be ToNumber(Desc.[[Value]]). Handle number_v; if (!Object::ToNumber(isolate, length_object).ToHandle(&number_v)) { // 6. ReturnIfAbrupt(newLen). return false; } // 7. If newLen != numberLen, throw a RangeError exception. if (uint32_v->Number() != number_v->Number()) { Handle exception = isolate->factory()->NewRangeError(MessageTemplate::kInvalidArrayLength); isolate->Throw(*exception); return false; } CHECK(uint32_v->ToArrayLength(output)); return true; } // ES6 9.4.2.4 // static Maybe JSArray::ArraySetLength(Isolate* isolate, Handle a, PropertyDescriptor* desc, Maybe should_throw) { // 1. If the [[Value]] field of Desc is absent, then if (!desc->has_value()) { // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc). return OrdinaryDefineOwnProperty( isolate, a, isolate->factory()->length_string(), desc, should_throw); } // 2. Let newLenDesc be a copy of Desc. // (Actual copying is not necessary.) PropertyDescriptor* new_len_desc = desc; // 3. - 7. Convert Desc.[[Value]] to newLen. uint32_t new_len = 0; if (!AnythingToArrayLength(isolate, desc->value(), &new_len)) { DCHECK(isolate->has_pending_exception()); return Nothing(); } // 8. Set newLenDesc.[[Value]] to newLen. // (Done below, if needed.) // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). PropertyDescriptor old_len_desc; Maybe success = GetOwnPropertyDescriptor( isolate, a, isolate->factory()->length_string(), &old_len_desc); // 10. (Assert) DCHECK(success.FromJust()); USE(success); // 11. Let oldLen be oldLenDesc.[[Value]]. uint32_t old_len = 0; CHECK(old_len_desc.value()->ToArrayLength(&old_len)); // 12. If newLen >= oldLen, then if (new_len >= old_len) { // 8. Set newLenDesc.[[Value]] to newLen. // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc). new_len_desc->set_value(isolate->factory()->NewNumberFromUint(new_len)); return OrdinaryDefineOwnProperty(isolate, a, isolate->factory()->length_string(), new_len_desc, should_throw); } // 13. If oldLenDesc.[[Writable]] is false, return false. if (!old_len_desc.writable() || // Also handle the {configurable: true} case since we later use // JSArray::SetLength instead of OrdinaryDefineOwnProperty to change // the length, and it doesn't have access to the descriptor anymore. new_len_desc->configurable()) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kRedefineDisallowed, isolate->factory()->length_string())); } // 14. If newLenDesc.[[Writable]] is absent or has the value true, // let newWritable be true. bool new_writable = false; if (!new_len_desc->has_writable() || new_len_desc->writable()) { new_writable = true; } else { // 15. Else, // 15a. Need to defer setting the [[Writable]] attribute to false in case // any elements cannot be deleted. // 15b. Let newWritable be false. (It's initialized as "false" anyway.) // 15c. Set newLenDesc.[[Writable]] to true. // (Not needed.) } // Most of steps 16 through 19 is implemented by JSArray::SetLength. JSArray::SetLength(a, new_len); // Steps 19d-ii, 20. if (!new_writable) { PropertyDescriptor readonly; readonly.set_writable(false); Maybe success = OrdinaryDefineOwnProperty( isolate, a, isolate->factory()->length_string(), &readonly, should_throw); DCHECK(success.FromJust()); USE(success); } uint32_t actual_new_len = 0; CHECK(a->length().ToArrayLength(&actual_new_len)); // Steps 19d-v, 21. Return false if there were non-deletable elements. bool result = actual_new_len == new_len; if (!result) { RETURN_FAILURE( isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kStrictDeleteProperty, isolate->factory()->NewNumberFromUint(actual_new_len - 1), a)); } return Just(result); } // ES6 9.5.6 // static Maybe JSProxy::DefineOwnProperty(Isolate* isolate, Handle proxy, Handle key, PropertyDescriptor* desc, Maybe should_throw) { STACK_CHECK(isolate, Nothing()); if (key->IsSymbol() && Handle::cast(key)->IsPrivate()) { DCHECK(!Handle::cast(key)->IsPrivateName()); return JSProxy::SetPrivateSymbol(isolate, proxy, Handle::cast(key), desc, should_throw); } Handle trap_name = isolate->factory()->defineProperty_string(); // 1. Assert: IsPropertyKey(P) is true. DCHECK(key->IsName() || key->IsNumber()); // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle handler(proxy->handler(), isolate); // 3. If handler is null, throw a TypeError exception. // 4. Assert: Type(handler) is Object. if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyRevoked, trap_name)); return Nothing(); } // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. Handle target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "defineProperty"). Handle trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(Handle::cast(handler), trap_name), Nothing()); // 7. If trap is undefined, then: if (trap->IsUndefined(isolate)) { // 7a. Return target.[[DefineOwnProperty]](P, Desc). return JSReceiver::DefineOwnProperty(isolate, target, key, desc, should_throw); } // 8. Let descObj be FromPropertyDescriptor(Desc). Handle desc_obj = desc->ToObject(isolate); // 9. Let booleanTrapResult be // ToBoolean(? Call(trap, handler, «target, P, descObj»)). Handle property_name = key->IsName() ? Handle::cast(key) : Handle::cast(isolate->factory()->NumberToString(key)); // Do not leak private property names. DCHECK(!property_name->IsPrivate()); Handle trap_result_obj; Handle args[] = {target, property_name, desc_obj}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result_obj, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing()); // 10. If booleanTrapResult is false, return false. if (!trap_result_obj->BooleanValue(isolate)) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor, trap_name, property_name)); } // 11. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe target_found = JSReceiver::GetOwnPropertyDescriptor(isolate, target, key, &target_desc); MAYBE_RETURN(target_found, Nothing()); // 12. Let extensibleTarget be ? IsExtensible(target). Maybe maybe_extensible = JSReceiver::IsExtensible(target); MAYBE_RETURN(maybe_extensible, Nothing()); bool extensible_target = maybe_extensible.FromJust(); // 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] // is false, then: // 13a. Let settingConfigFalse be true. // 14. Else let settingConfigFalse be false. bool setting_config_false = desc->has_configurable() && !desc->configurable(); // 15. If targetDesc is undefined, then if (!target_found.FromJust()) { // 15a. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDefinePropertyNonExtensible, property_name)); return Nothing(); } // 15b. If settingConfigFalse is true, throw a TypeError exception. if (setting_config_false) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name)); return Nothing(); } } else { // 16. Else targetDesc is not undefined, // 16a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, // targetDesc) is false, throw a TypeError exception. Maybe valid = IsCompatiblePropertyDescriptor( isolate, extensible_target, desc, &target_desc, property_name, Just(kDontThrow)); MAYBE_RETURN(valid, Nothing()); if (!valid.FromJust()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDefinePropertyIncompatible, property_name)); return Nothing(); } // 16b. If settingConfigFalse is true and targetDesc.[[Configurable]] is // true, throw a TypeError exception. if (setting_config_false && target_desc.configurable()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name)); return Nothing(); } // 16c. If IsDataDescriptor(targetDesc) is true, // targetDesc.[[Configurable]] is // false, and targetDesc.[[Writable]] is true, then if (PropertyDescriptor::IsDataDescriptor(&target_desc) && !target_desc.configurable() && target_desc.writable()) { // 16c i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, // throw a TypeError exception. if (desc->has_writable() && !desc->writable()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDefinePropertyNonConfigurableWritable, property_name)); return Nothing(); } } } // 17. Return true. return Just(true); } // static Maybe JSProxy::SetPrivateSymbol(Isolate* isolate, Handle proxy, Handle private_name, PropertyDescriptor* desc, Maybe should_throw) { DCHECK(!private_name->IsPrivateName()); // Despite the generic name, this can only add private data properties. if (!PropertyDescriptor::IsDataDescriptor(desc) || desc->ToAttributes() != DONT_ENUM) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kProxyPrivate)); } DCHECK(proxy->map().is_dictionary_map()); Handle value = desc->has_value() ? desc->value() : Handle::cast(isolate->factory()->undefined_value()); LookupIterator it(proxy, private_name, proxy); if (it.IsFound()) { DCHECK_EQ(LookupIterator::DATA, it.state()); DCHECK_EQ(DONT_ENUM, it.property_attributes()); it.WriteDataValue(value, false); return Just(true); } Handle dict(proxy->property_dictionary(), isolate); PropertyDetails details(kData, DONT_ENUM, PropertyCellType::kNoCell); Handle result = NameDictionary::Add(isolate, dict, private_name, value, details); if (!dict.is_identical_to(result)) proxy->SetProperties(*result); return Just(true); } // ES6 9.5.5 // static Maybe JSProxy::GetOwnPropertyDescriptor(Isolate* isolate, Handle proxy, Handle name, PropertyDescriptor* desc) { DCHECK(!name->IsPrivate()); STACK_CHECK(isolate, Nothing()); Handle trap_name = isolate->factory()->getOwnPropertyDescriptor_string(); // 1. (Assert) // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle handler(proxy->handler(), isolate); // 3. If handler is null, throw a TypeError exception. // 4. Assert: Type(handler) is Object. if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyRevoked, trap_name)); return Nothing(); } // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. Handle target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). Handle trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(Handle::cast(handler), trap_name), Nothing()); // 7. If trap is undefined, then if (trap->IsUndefined(isolate)) { // 7a. Return target.[[GetOwnProperty]](P). return JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, desc); } // 8. Let trapResultObj be ? Call(trap, handler, «target, P»). Handle trap_result_obj; Handle args[] = {target, name}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result_obj, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing()); // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a // TypeError exception. if (!trap_result_obj->IsJSReceiver() && !trap_result_obj->IsUndefined(isolate)) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyGetOwnPropertyDescriptorInvalid, name)); return Nothing(); } // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe found = JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); MAYBE_RETURN(found, Nothing()); // 11. If trapResultObj is undefined, then if (trap_result_obj->IsUndefined(isolate)) { // 11a. If targetDesc is undefined, return undefined. if (!found.FromJust()) return Just(false); // 11b. If targetDesc.[[Configurable]] is false, throw a TypeError // exception. if (!target_desc.configurable()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyGetOwnPropertyDescriptorUndefined, name)); return Nothing(); } // 11c. Let extensibleTarget be ? IsExtensible(target). Maybe extensible_target = JSReceiver::IsExtensible(target); MAYBE_RETURN(extensible_target, Nothing()); // 11d. (Assert) // 11e. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target.FromJust()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyGetOwnPropertyDescriptorNonExtensible, name)); return Nothing(); } // 11f. Return undefined. return Just(false); } // 12. Let extensibleTarget be ? IsExtensible(target). Maybe extensible_target = JSReceiver::IsExtensible(target); MAYBE_RETURN(extensible_target, Nothing()); // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj). if (!PropertyDescriptor::ToPropertyDescriptor(isolate, trap_result_obj, desc)) { DCHECK(isolate->has_pending_exception()); return Nothing(); } // 14. Call CompletePropertyDescriptor(resultDesc). PropertyDescriptor::CompletePropertyDescriptor(isolate, desc); // 15. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget, // resultDesc, targetDesc). Maybe valid = IsCompatiblePropertyDescriptor( isolate, extensible_target.FromJust(), desc, &target_desc, name, Just(kDontThrow)); MAYBE_RETURN(valid, Nothing()); // 16. If valid is false, throw a TypeError exception. if (!valid.FromJust()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyGetOwnPropertyDescriptorIncompatible, name)); return Nothing(); } // 17. If resultDesc.[[Configurable]] is false, then if (!desc->configurable()) { // 17a. If targetDesc is undefined or targetDesc.[[Configurable]] is true: if (target_desc.is_empty() || target_desc.configurable()) { // 17a i. Throw a TypeError exception. isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyGetOwnPropertyDescriptorNonConfigurable, name)); return Nothing(); } // 17b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] // is false, then if (desc->has_writable() && !desc->writable()) { // 17b i. If targetDesc.[[Writable]] is true, throw a TypeError exception. if (target_desc.writable()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate:: kProxyGetOwnPropertyDescriptorNonConfigurableWritable, name)); return Nothing(); } } } // 18. Return resultDesc. return Just(true); } Maybe JSProxy::PreventExtensions(Handle proxy, ShouldThrow should_throw) { Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing()); Factory* factory = isolate->factory(); Handle trap_name = factory->preventExtensions_string(); if (proxy->IsRevoked()) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing(); } Handle target(JSReceiver::cast(proxy->target()), isolate); Handle handler(JSReceiver::cast(proxy->handler()), isolate); Handle trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing()); if (trap->IsUndefined(isolate)) { return JSReceiver::PreventExtensions(target, should_throw); } Handle trap_result; Handle args[] = {target}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing()); if (!trap_result->BooleanValue(isolate)) { RETURN_FAILURE( isolate, should_throw, NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name)); } // Enforce the invariant. Maybe target_result = JSReceiver::IsExtensible(target); MAYBE_RETURN(target_result, Nothing()); if (target_result.FromJust()) { isolate->Throw(*factory->NewTypeError( MessageTemplate::kProxyPreventExtensionsExtensible)); return Nothing(); } return Just(true); } Maybe JSProxy::IsExtensible(Handle proxy) { Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing()); Factory* factory = isolate->factory(); Handle trap_name = factory->isExtensible_string(); if (proxy->IsRevoked()) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing(); } Handle target(JSReceiver::cast(proxy->target()), isolate); Handle handler(JSReceiver::cast(proxy->handler()), isolate); Handle trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing()); if (trap->IsUndefined(isolate)) { return JSReceiver::IsExtensible(target); } Handle trap_result; Handle args[] = {target}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing()); // Enforce the invariant. Maybe target_result = JSReceiver::IsExtensible(target); MAYBE_RETURN(target_result, Nothing()); if (target_result.FromJust() != trap_result->BooleanValue(isolate)) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyIsExtensibleInconsistent, factory->ToBoolean(target_result.FromJust()))); return Nothing(); } return target_result; } Handle DescriptorArray::CopyUpTo(Isolate* isolate, Handle desc, int enumeration_index, int slack) { return DescriptorArray::CopyUpToAddAttributes(isolate, desc, enumeration_index, NONE, slack); } Handle DescriptorArray::CopyUpToAddAttributes( Isolate* isolate, Handle desc, int enumeration_index, PropertyAttributes attributes, int slack) { if (enumeration_index + slack == 0) { return isolate->factory()->empty_descriptor_array(); } int size = enumeration_index; Handle descriptors = DescriptorArray::Allocate(isolate, size, slack); if (attributes != NONE) { for (int i = 0; i < size; ++i) { MaybeObject value_or_field_type = desc->GetValue(i); Name key = desc->GetKey(i); PropertyDetails details = desc->GetDetails(i); // Bulk attribute changes never affect private properties. if (!key.IsPrivate()) { int mask = DONT_DELETE | DONT_ENUM; // READ_ONLY is an invalid attribute for JS setters/getters. HeapObject heap_object; if (details.kind() != kAccessor || !(value_or_field_type->GetHeapObjectIfStrong(&heap_object) && heap_object.IsAccessorPair())) { mask |= READ_ONLY; } details = details.CopyAddAttributes( static_cast(attributes & mask)); } descriptors->Set(i, key, value_or_field_type, details); } } else { for (int i = 0; i < size; ++i) { descriptors->CopyFrom(i, *desc); } } if (desc->number_of_descriptors() != enumeration_index) descriptors->Sort(); return descriptors; } // Create a new descriptor array with only enumerable, configurable, writeable // data properties, but identical field locations. Handle DescriptorArray::CopyForFastObjectClone( Isolate* isolate, Handle src, int enumeration_index, int slack) { if (enumeration_index + slack == 0) { return isolate->factory()->empty_descriptor_array(); } int size = enumeration_index; Handle descriptors = DescriptorArray::Allocate(isolate, size, slack); for (int i = 0; i < size; ++i) { Name key = src->GetKey(i); PropertyDetails details = src->GetDetails(i); DCHECK(!key.IsPrivateName()); DCHECK(details.IsEnumerable()); DCHECK_EQ(details.kind(), kData); // Ensure the ObjectClone property details are NONE, and that all source // details did not contain DONT_ENUM. PropertyDetails new_details(kData, NONE, details.location(), details.constness(), details.representation(), details.field_index()); // Do not propagate the field type of normal object fields from the // original descriptors since FieldType changes don't create new maps. MaybeObject type = src->GetValue(i); if (details.location() == PropertyLocation::kField) { type = MaybeObject::FromObject(FieldType::Any()); // TODO(bmeurer,ishell): Igor suggested to use some kind of dynamic // checks in the fast-path for CloneObjectIC instead to avoid the // need to generalize the descriptors here. That will also enable // us to skip the defensive copying of the target map whenever a // CloneObjectIC misses. if (FLAG_modify_field_representation_inplace && (new_details.representation().IsSmi() || new_details.representation().IsHeapObject())) { new_details = new_details.CopyWithRepresentation(Representation::Tagged()); } } descriptors->Set(i, key, type, new_details); } descriptors->Sort(); return descriptors; } bool DescriptorArray::IsEqualUpTo(DescriptorArray desc, int nof_descriptors) { for (int i = 0; i < nof_descriptors; i++) { if (GetKey(i) != desc.GetKey(i) || GetValue(i) != desc.GetValue(i)) { return false; } PropertyDetails details = GetDetails(i); PropertyDetails other_details = desc.GetDetails(i); if (details.kind() != other_details.kind() || details.location() != other_details.location() || !details.representation().Equals(other_details.representation())) { return false; } } return true; } Handle FixedArray::SetAndGrow(Isolate* isolate, Handle array, int index, Handle value, AllocationType allocation) { if (index < array->length()) { array->set(index, *value); return array; } int capacity = array->length(); do { capacity = JSObject::NewElementsCapacity(capacity); } while (capacity <= index); Handle new_array = isolate->factory()->NewUninitializedFixedArray(capacity, allocation); array->CopyTo(0, *new_array, 0, array->length()); new_array->FillWithHoles(array->length(), new_array->length()); new_array->set(index, *value); return new_array; } Handle FixedArray::ShrinkOrEmpty(Isolate* isolate, Handle array, int new_length) { if (new_length == 0) { return array->GetReadOnlyRoots().empty_fixed_array_handle(); } else { array->Shrink(isolate, new_length); return array; } } void FixedArray::Shrink(Isolate* isolate, int new_length) { DCHECK(0 < new_length && new_length <= length()); if (new_length < length()) { isolate->heap()->RightTrimFixedArray(*this, length() - new_length); } } void FixedArray::CopyTo(int pos, FixedArray dest, int dest_pos, int len) const { DisallowHeapAllocation no_gc; // Return early if len == 0 so that we don't try to read the write barrier off // a canonical read-only empty fixed array. if (len == 0) return; WriteBarrierMode mode = dest.GetWriteBarrierMode(no_gc); for (int index = 0; index < len; index++) { dest.set(dest_pos + index, get(pos + index), mode); } } // static Handle ArrayList::Add(Isolate* isolate, Handle array, Handle obj) { int length = array->Length(); array = EnsureSpace(isolate, array, length + 1); // Check that GC didn't remove elements from the array. DCHECK_EQ(array->Length(), length); array->Set(length, *obj); array->SetLength(length + 1); return array; } // static Handle ArrayList::Add(Isolate* isolate, Handle array, Handle obj1, Handle obj2) { int length = array->Length(); array = EnsureSpace(isolate, array, length + 2); // Check that GC didn't remove elements from the array. DCHECK_EQ(array->Length(), length); array->Set(length, *obj1); array->Set(length + 1, *obj2); array->SetLength(length + 2); return array; } // static Handle ArrayList::New(Isolate* isolate, int size) { Handle fixed_array = isolate->factory()->NewFixedArray(size + kFirstIndex); fixed_array->set_map_no_write_barrier( ReadOnlyRoots(isolate).array_list_map()); Handle result = Handle::cast(fixed_array); result->SetLength(0); return result; } Handle ArrayList::Elements(Isolate* isolate, Handle array) { int length = array->Length(); Handle result = isolate->factory()->NewFixedArray(length); // Do not copy the first entry, i.e., the length. array->CopyTo(kFirstIndex, *result, 0, length); return result; } namespace { Handle EnsureSpaceInFixedArray(Isolate* isolate, Handle array, int length) { int capacity = array->length(); if (capacity < length) { int new_capacity = length; new_capacity = new_capacity + Max(new_capacity / 2, 2); int grow_by = new_capacity - capacity; array = isolate->factory()->CopyFixedArrayAndGrow(array, grow_by); } return array; } } // namespace // static Handle ArrayList::EnsureSpace(Isolate* isolate, Handle array, int length) { const bool empty = (array->length() == 0); Handle ret = EnsureSpaceInFixedArray(isolate, array, kFirstIndex + length); if (empty) { ret->set_map_no_write_barrier(array->GetReadOnlyRoots().array_list_map()); Handle::cast(ret)->SetLength(0); } return Handle::cast(ret); } // static Handle WeakArrayList::AddToEnd(Isolate* isolate, Handle array, const MaybeObjectHandle& value) { int length = array->length(); array = EnsureSpace(isolate, array, length + 1); // Reload length; GC might have removed elements from the array. length = array->length(); array->Set(length, *value); array->set_length(length + 1); return array; } bool WeakArrayList::IsFull() { return length() == capacity(); } // static Handle WeakArrayList::EnsureSpace(Isolate* isolate, Handle array, int length, AllocationType allocation) { int capacity = array->capacity(); if (capacity < length) { int new_capacity = length; new_capacity = new_capacity + Max(new_capacity / 2, 2); int grow_by = new_capacity - capacity; array = isolate->factory()->CopyWeakArrayListAndGrow(array, grow_by, allocation); } return array; } int WeakArrayList::CountLiveWeakReferences() const { int live_weak_references = 0; for (int i = 0; i < length(); i++) { if (Get(i)->IsWeak()) { ++live_weak_references; } } return live_weak_references; } bool WeakArrayList::RemoveOne(const MaybeObjectHandle& value) { if (length() == 0) return false; // Optimize for the most recently added element to be removed again. MaybeObject cleared_weak_ref = HeapObjectReference::ClearedValue(GetIsolate()); int last_index = length() - 1; for (int i = last_index; i >= 0; --i) { if (Get(i) == *value) { // Move the last element into the this slot (or no-op, if this is the // last slot). Set(i, Get(last_index)); Set(last_index, cleared_weak_ref); set_length(last_index); return true; } } return false; } // static Handle PrototypeUsers::Add(Isolate* isolate, Handle array, Handle value, int* assigned_index) { int length = array->length(); if (length == 0) { // Uninitialized WeakArrayList; need to initialize empty_slot_index. array = WeakArrayList::EnsureSpace(isolate, array, kFirstIndex + 1); set_empty_slot_index(*array, kNoEmptySlotsMarker); array->Set(kFirstIndex, HeapObjectReference::Weak(*value)); array->set_length(kFirstIndex + 1); if (assigned_index != nullptr) *assigned_index = kFirstIndex; return array; } // If the array has unfilled space at the end, use it. if (!array->IsFull()) { array->Set(length, HeapObjectReference::Weak(*value)); array->set_length(length + 1); if (assigned_index != nullptr) *assigned_index = length; return array; } // If there are empty slots, use one of them. int empty_slot = Smi::ToInt(empty_slot_index(*array)); if (empty_slot != kNoEmptySlotsMarker) { DCHECK_GE(empty_slot, kFirstIndex); CHECK_LT(empty_slot, array->length()); int next_empty_slot = array->Get(empty_slot).ToSmi().value(); array->Set(empty_slot, HeapObjectReference::Weak(*value)); if (assigned_index != nullptr) *assigned_index = empty_slot; set_empty_slot_index(*array, next_empty_slot); return array; } else { DCHECK_EQ(empty_slot, kNoEmptySlotsMarker); } // Array full and no empty slots. Grow the array. array = WeakArrayList::EnsureSpace(isolate, array, length + 1); array->Set(length, HeapObjectReference::Weak(*value)); array->set_length(length + 1); if (assigned_index != nullptr) *assigned_index = length; return array; } WeakArrayList PrototypeUsers::Compact(Handle array, Heap* heap, CompactionCallback callback, AllocationType allocation) { if (array->length() == 0) { return *array; } int new_length = kFirstIndex + array->CountLiveWeakReferences(); if (new_length == array->length()) { return *array; } Handle new_array = WeakArrayList::EnsureSpace( heap->isolate(), handle(ReadOnlyRoots(heap).empty_weak_array_list(), heap->isolate()), new_length, allocation); // Allocation might have caused GC and turned some of the elements into // cleared weak heap objects. Count the number of live objects again. int copy_to = kFirstIndex; for (int i = kFirstIndex; i < array->length(); i++) { MaybeObject element = array->Get(i); HeapObject value; if (element->GetHeapObjectIfWeak(&value)) { callback(value, i, copy_to); new_array->Set(copy_to++, element); } else { DCHECK(element->IsCleared() || element->IsSmi()); } } new_array->set_length(copy_to); set_empty_slot_index(*new_array, kNoEmptySlotsMarker); return *new_array; } Handle RegExpMatchInfo::ReserveCaptures( Isolate* isolate, Handle match_info, int capture_count) { DCHECK_GE(match_info->length(), kLastMatchOverhead); const int required_length = kFirstCaptureIndex + capture_count; return Handle::cast( EnsureSpaceInFixedArray(isolate, match_info, required_length)); } // static Handle FrameArray::AppendJSFrame(Handle in, Handle receiver, Handle function, Handle code, int offset, int flags, Handle parameters) { const int frame_count = in->FrameCount(); const int new_length = LengthFor(frame_count + 1); Handle array = EnsureSpace(function->GetIsolate(), in, new_length); array->SetReceiver(frame_count, *receiver); array->SetFunction(frame_count, *function); array->SetCode(frame_count, *code); array->SetOffset(frame_count, Smi::FromInt(offset)); array->SetFlags(frame_count, Smi::FromInt(flags)); array->SetParameters(frame_count, *parameters); array->set(kFrameCountIndex, Smi::FromInt(frame_count + 1)); return array; } // static Handle FrameArray::AppendWasmFrame( Handle in, Handle wasm_instance, int wasm_function_index, wasm::WasmCode* code, int offset, int flags) { Isolate* isolate = wasm_instance->GetIsolate(); const int frame_count = in->FrameCount(); const int new_length = LengthFor(frame_count + 1); Handle array = EnsureSpace(isolate, in, new_length); // The {code} will be {nullptr} for interpreted wasm frames. Handle code_ref = isolate->factory()->undefined_value(); if (code) { auto native_module = wasm_instance->module_object().shared_native_module(); code_ref = Managed::Allocate( isolate, 0, code, std::move(native_module)); } array->SetWasmInstance(frame_count, *wasm_instance); array->SetWasmFunctionIndex(frame_count, Smi::FromInt(wasm_function_index)); array->SetWasmCodeObject(frame_count, *code_ref); array->SetOffset(frame_count, Smi::FromInt(offset)); array->SetFlags(frame_count, Smi::FromInt(flags)); array->set(kFrameCountIndex, Smi::FromInt(frame_count + 1)); return array; } void FrameArray::ShrinkToFit(Isolate* isolate) { Shrink(isolate, LengthFor(FrameCount())); } // static Handle FrameArray::EnsureSpace(Isolate* isolate, Handle array, int length) { return Handle::cast( EnsureSpaceInFixedArray(isolate, array, length)); } Handle DescriptorArray::Allocate(Isolate* isolate, int nof_descriptors, int slack, AllocationType allocation) { return nof_descriptors + slack == 0 ? isolate->factory()->empty_descriptor_array() : isolate->factory()->NewDescriptorArray(nof_descriptors, slack, allocation); } void DescriptorArray::Initialize(EnumCache enum_cache, HeapObject undefined_value, int nof_descriptors, int slack) { DCHECK_GE(nof_descriptors, 0); DCHECK_GE(slack, 0); DCHECK_LE(nof_descriptors + slack, kMaxNumberOfDescriptors); set_number_of_all_descriptors(nof_descriptors + slack); set_number_of_descriptors(nof_descriptors); set_raw_number_of_marked_descriptors(0); set_filler16bits(0); set_enum_cache(enum_cache); MemsetTagged(GetDescriptorSlot(0), undefined_value, number_of_all_descriptors() * kEntrySize); } void DescriptorArray::ClearEnumCache() { set_enum_cache(GetReadOnlyRoots().empty_enum_cache()); } void DescriptorArray::Replace(int index, Descriptor* descriptor) { descriptor->SetSortedKeyIndex(GetSortedKeyIndex(index)); Set(index, descriptor); } // static void DescriptorArray::InitializeOrChangeEnumCache( Handle descriptors, Isolate* isolate, Handle keys, Handle indices) { EnumCache enum_cache = descriptors->enum_cache(); if (enum_cache == ReadOnlyRoots(isolate).empty_enum_cache()) { enum_cache = *isolate->factory()->NewEnumCache(keys, indices); descriptors->set_enum_cache(enum_cache); } else { enum_cache.set_keys(*keys); enum_cache.set_indices(*indices); } } void DescriptorArray::CopyFrom(int index, DescriptorArray src) { PropertyDetails details = src.GetDetails(index); Set(index, src.GetKey(index), src.GetValue(index), details); } void DescriptorArray::Sort() { // In-place heap sort. int len = number_of_descriptors(); // Reset sorting since the descriptor array might contain invalid pointers. for (int i = 0; i < len; ++i) SetSortedKey(i, i); // Bottom-up max-heap construction. // Index of the last node with children const int max_parent_index = (len / 2) - 1; for (int i = max_parent_index; i >= 0; --i) { int parent_index = i; const uint32_t parent_hash = GetSortedKey(i).Hash(); while (parent_index <= max_parent_index) { int child_index = 2 * parent_index + 1; uint32_t child_hash = GetSortedKey(child_index).Hash(); if (child_index + 1 < len) { uint32_t right_child_hash = GetSortedKey(child_index + 1).Hash(); if (right_child_hash > child_hash) { child_index++; child_hash = right_child_hash; } } if (child_hash <= parent_hash) break; SwapSortedKeys(parent_index, child_index); // Now element at child_index could be < its children. parent_index = child_index; // parent_hash remains correct. } } // Extract elements and create sorted array. for (int i = len - 1; i > 0; --i) { // Put max element at the back of the array. SwapSortedKeys(0, i); // Shift down the new top element. int parent_index = 0; const uint32_t parent_hash = GetSortedKey(parent_index).Hash(); const int max_parent_index = (i / 2) - 1; while (parent_index <= max_parent_index) { int child_index = parent_index * 2 + 1; uint32_t child_hash = GetSortedKey(child_index).Hash(); if (child_index + 1 < i) { uint32_t right_child_hash = GetSortedKey(child_index + 1).Hash(); if (right_child_hash > child_hash) { child_index++; child_hash = right_child_hash; } } if (child_hash <= parent_hash) break; SwapSortedKeys(parent_index, child_index); parent_index = child_index; } } DCHECK(IsSortedNoDuplicates()); } int16_t DescriptorArray::UpdateNumberOfMarkedDescriptors( unsigned mark_compact_epoch, int16_t new_marked) { STATIC_ASSERT(kMaxNumberOfDescriptors <= NumberOfMarkedDescriptors::kMaxNumberOfMarkedDescriptors); int16_t old_raw_marked = raw_number_of_marked_descriptors(); int16_t old_marked = NumberOfMarkedDescriptors::decode(mark_compact_epoch, old_raw_marked); int16_t new_raw_marked = NumberOfMarkedDescriptors::encode(mark_compact_epoch, new_marked); while (old_marked < new_marked) { int16_t actual_raw_marked = CompareAndSwapRawNumberOfMarkedDescriptors( old_raw_marked, new_raw_marked); if (actual_raw_marked == old_raw_marked) { break; } old_raw_marked = actual_raw_marked; old_marked = NumberOfMarkedDescriptors::decode(mark_compact_epoch, old_raw_marked); } return old_marked; } Handle AccessorPair::Copy(Isolate* isolate, Handle pair) { Handle copy = isolate->factory()->NewAccessorPair(); copy->set_getter(pair->getter()); copy->set_setter(pair->setter()); return copy; } Handle AccessorPair::GetComponent(Isolate* isolate, Handle native_context, Handle accessor_pair, AccessorComponent component) { Object accessor = accessor_pair->get(component); if (accessor.IsFunctionTemplateInfo()) { return ApiNatives::InstantiateFunction( isolate, native_context, handle(FunctionTemplateInfo::cast(accessor), isolate)) .ToHandleChecked(); } if (accessor.IsNull(isolate)) { return isolate->factory()->undefined_value(); } return handle(accessor, isolate); } #ifdef DEBUG bool DescriptorArray::IsEqualTo(DescriptorArray other) { if (number_of_all_descriptors() != other.number_of_all_descriptors()) { return false; } for (int i = 0; i < number_of_descriptors(); ++i) { if (GetKey(i) != other.GetKey(i)) return false; if (GetDetails(i).AsSmi() != other.GetDetails(i).AsSmi()) return false; if (GetValue(i) != other.GetValue(i)) return false; } return true; } #endif // static MaybeHandle Name::ToFunctionName(Isolate* isolate, Handle name) { if (name->IsString()) return Handle::cast(name); // ES6 section 9.2.11 SetFunctionName, step 4. Handle description(Handle::cast(name)->name(), isolate); if (description->IsUndefined(isolate)) { return isolate->factory()->empty_string(); } IncrementalStringBuilder builder(isolate); builder.AppendCharacter('['); builder.AppendString(Handle::cast(description)); builder.AppendCharacter(']'); return builder.Finish(); } // static MaybeHandle Name::ToFunctionName(Isolate* isolate, Handle name, Handle prefix) { Handle name_string; ASSIGN_RETURN_ON_EXCEPTION(isolate, name_string, ToFunctionName(isolate, name), String); IncrementalStringBuilder builder(isolate); builder.AppendString(prefix); builder.AppendCharacter(' '); builder.AppendString(name_string); return builder.Finish(); } void Relocatable::PostGarbageCollectionProcessing(Isolate* isolate) { Relocatable* current = isolate->relocatable_top(); while (current != nullptr) { current->PostGarbageCollection(); current = current->prev_; } } // Reserve space for statics needing saving and restoring. int Relocatable::ArchiveSpacePerThread() { return sizeof(Relocatable*); // NOLINT } // Archive statics that are thread-local. char* Relocatable::ArchiveState(Isolate* isolate, char* to) { *reinterpret_cast(to) = isolate->relocatable_top(); isolate->set_relocatable_top(nullptr); return to + ArchiveSpacePerThread(); } // Restore statics that are thread-local. char* Relocatable::RestoreState(Isolate* isolate, char* from) { isolate->set_relocatable_top(*reinterpret_cast(from)); return from + ArchiveSpacePerThread(); } char* Relocatable::Iterate(RootVisitor* v, char* thread_storage) { Relocatable* top = *reinterpret_cast(thread_storage); Iterate(v, top); return thread_storage + ArchiveSpacePerThread(); } void Relocatable::Iterate(Isolate* isolate, RootVisitor* v) { Iterate(v, isolate->relocatable_top()); } void Relocatable::Iterate(RootVisitor* v, Relocatable* top) { Relocatable* current = top; while (current != nullptr) { current->IterateInstance(v); current = current->prev_; } } namespace { template void WriteFixedArrayToFlat(FixedArray fixed_array, int length, String separator, sinkchar* sink, int sink_length) { DisallowHeapAllocation no_allocation; CHECK_GT(length, 0); CHECK_LE(length, fixed_array.length()); #ifdef DEBUG sinkchar* sink_end = sink + sink_length; #endif const int separator_length = separator.length(); const bool use_one_byte_separator_fast_path = separator_length == 1 && sizeof(sinkchar) == 1 && StringShape(separator).IsSequentialOneByte(); uint8_t separator_one_char; if (use_one_byte_separator_fast_path) { CHECK(StringShape(separator).IsSequentialOneByte()); CHECK_EQ(separator.length(), 1); separator_one_char = SeqOneByteString::cast(separator).GetChars(no_allocation)[0]; } uint32_t num_separators = 0; for (int i = 0; i < length; i++) { Object element = fixed_array.get(i); const bool element_is_separator_sequence = element.IsSmi(); // If element is a Smi, it represents the number of separators to write. if (V8_UNLIKELY(element_is_separator_sequence)) { CHECK(element.ToUint32(&num_separators)); // Verify that Smis (number of separators) only occur when necessary: // 1) at the beginning // 2) at the end // 3) when the number of separators > 1 // - It is assumed that consecutive Strings will have one separator, // so there is no need for a Smi. DCHECK(i == 0 || i == length - 1 || num_separators > 1); } // Write separator(s) if necessary. if (num_separators > 0 && separator_length > 0) { // TODO(pwong): Consider doubling strategy employed by runtime-strings.cc // WriteRepeatToFlat(). // Fast path for single character, single byte separators. if (use_one_byte_separator_fast_path) { DCHECK_LE(sink + num_separators, sink_end); memset(sink, separator_one_char, num_separators); DCHECK_EQ(separator_length, 1); sink += num_separators; } else { for (uint32_t j = 0; j < num_separators; j++) { DCHECK_LE(sink + separator_length, sink_end); String::WriteToFlat(separator, sink, 0, separator_length); sink += separator_length; } } } if (V8_UNLIKELY(element_is_separator_sequence)) { num_separators = 0; } else { DCHECK(element.IsString()); String string = String::cast(element); const int string_length = string.length(); DCHECK(string_length == 0 || sink < sink_end); String::WriteToFlat(string, sink, 0, string_length); sink += string_length; // Next string element, needs at least one separator preceding it. num_separators = 1; } } // Verify we have written to the end of the sink. DCHECK_EQ(sink, sink_end); } } // namespace // static Address JSArray::ArrayJoinConcatToSequentialString(Isolate* isolate, Address raw_fixed_array, intptr_t length, Address raw_separator, Address raw_dest) { DisallowHeapAllocation no_allocation; DisallowJavascriptExecution no_js(isolate); FixedArray fixed_array = FixedArray::cast(Object(raw_fixed_array)); String separator = String::cast(Object(raw_separator)); String dest = String::cast(Object(raw_dest)); DCHECK(fixed_array.IsFixedArray()); DCHECK(StringShape(dest).IsSequentialOneByte() || StringShape(dest).IsSequentialTwoByte()); if (StringShape(dest).IsSequentialOneByte()) { WriteFixedArrayToFlat(fixed_array, static_cast(length), separator, SeqOneByteString::cast(dest).GetChars(no_allocation), dest.length()); } else { DCHECK(StringShape(dest).IsSequentialTwoByte()); WriteFixedArrayToFlat(fixed_array, static_cast(length), separator, SeqTwoByteString::cast(dest).GetChars(no_allocation), dest.length()); } return dest.ptr(); } uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { // For array indexes mix the length into the hash as an array index could // be zero. DCHECK_GT(length, 0); DCHECK_LE(length, String::kMaxArrayIndexSize); DCHECK(TenToThe(String::kMaxCachedArrayIndexLength) < (1 << String::kArrayIndexValueBits)); value <<= String::ArrayIndexValueBits::kShift; value |= length << String::ArrayIndexLengthBits::kShift; DCHECK_EQ(value & String::kIsNotArrayIndexMask, 0); DCHECK_EQ(length <= String::kMaxCachedArrayIndexLength, Name::ContainsCachedArrayIndex(value)); return value; } Handle CacheInitialJSArrayMaps(Isolate* isolate, Handle native_context, Handle initial_map) { // Replace all of the cached initial array maps in the native context with // the appropriate transitioned elements kind maps. Handle current_map = initial_map; ElementsKind kind = current_map->elements_kind(); DCHECK_EQ(GetInitialFastElementsKind(), kind); native_context->set(Context::ArrayMapIndex(kind), *current_map); for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1; i < kFastElementsKindCount; ++i) { Handle new_map; ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i); Map maybe_elements_transition = current_map->ElementsTransitionMap(isolate); if (!maybe_elements_transition.is_null()) { new_map = handle(maybe_elements_transition, isolate); } else { new_map = Map::CopyAsElementsKind(isolate, current_map, next_kind, INSERT_TRANSITION); } DCHECK_EQ(next_kind, new_map->elements_kind()); native_context->set(Context::ArrayMapIndex(next_kind), *new_map); current_map = new_map; } return initial_map; } STATIC_ASSERT_FIELD_OFFSETS_EQUAL(HeapNumber::kValueOffset, Oddball::kToNumberRawOffset); void Oddball::Initialize(Isolate* isolate, Handle oddball, const char* to_string, Handle to_number, const char* type_of, byte kind) { Handle internalized_to_string = isolate->factory()->InternalizeUtf8String(to_string); Handle internalized_type_of = isolate->factory()->InternalizeUtf8String(type_of); if (to_number->IsHeapNumber()) { oddball->set_to_number_raw_as_bits( Handle::cast(to_number)->value_as_bits()); } else { oddball->set_to_number_raw(to_number->Number()); } oddball->set_to_number(*to_number); oddball->set_to_string(*internalized_to_string); oddball->set_type_of(*internalized_type_of); oddball->set_kind(kind); } // static int Script::GetEvalPosition(Isolate* isolate, Handle