// 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.h" #include <algorithm> #include <cmath> #include <memory> #include <sstream> #include <vector> #include "src/objects-inl.h" #include "src/accessors.h" #include "src/allocation-site-scopes.h" #include "src/api-arguments-inl.h" #include "src/api-natives.h" #include "src/api.h" #include "src/arguments.h" #include "src/ast/ast.h" #include "src/ast/scopes.h" #include "src/base/bits.h" #include "src/base/overflowing-math.h" #include "src/base/utils/random-number-generator.h" #include "src/bootstrapper.h" #include "src/builtins/builtins.h" #include "src/compiler.h" #include "src/counters-inl.h" #include "src/counters.h" #include "src/date.h" #include "src/debug/debug.h" #include "src/elements.h" #include "src/execution.h" #include "src/field-index-inl.h" #include "src/field-index.h" #include "src/field-type.h" #include "src/frames-inl.h" #include "src/function-kind.h" #include "src/globals.h" #include "src/heap/heap-inl.h" #include "src/heap/read-only-heap.h" #include "src/ic/ic.h" #include "src/identity-map.h" #include "src/isolate-inl.h" #include "src/keys.h" #include "src/log.h" #include "src/lookup-inl.h" #include "src/map-updater.h" #include "src/message-template.h" #include "src/microtask-queue.h" #include "src/objects-body-descriptors-inl.h" #include "src/objects/allocation-site-inl.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/embedder-data-array-inl.h" #include "src/objects/foreign.h" #include "src/objects/frame-array-inl.h" #include "src/objects/free-space-inl.h" #include "src/objects/hash-table-inl.h" #include "src/objects/js-array-inl.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/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/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/ostreams.h" #include "src/parsing/preparse-data.h" #include "src/property-descriptor.h" #include "src/prototype.h" #include "src/regexp/jsregexp.h" #include "src/source-position-table.h" #include "src/string-builder-inl.h" #include "src/string-search.h" #include "src/string-stream.h" #include "src/transitions-inl.h" #include "src/unicode-decoder.h" #include "src/unicode-inl.h" #include "src/utils-inl.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-objects.h" #include "src/zone/zone.h" namespace v8 { namespace internal { ShouldThrow GetShouldThrow(Isolate* isolate, Maybe<ShouldThrow> should_throw) { if (should_throw.IsJust()) return should_throw.FromJust(); LanguageMode mode = isolate->context()->scope_info()->language_mode(); if (mode == LanguageMode::kStrict) return kThrowOnError; for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { if (!(it.frame()->is_optimized() || it.frame()->is_interpreted())) { continue; } // Get the language mode from closure. JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(it.frame()); std::vector<SharedFunctionInfo> functions; js_frame->GetFunctions(&functions); LanguageMode closure_language_mode = functions.back()->language_mode(); if (closure_language_mode > mode) { mode = closure_language_mode; } break; } return is_sloppy(mode) ? kDontThrow : kThrowOnError; } bool ComparisonResultToBool(Operation op, ComparisonResult result) { switch (op) { case Operation::kLessThan: return result == ComparisonResult::kLessThan; case Operation::kLessThanOrEqual: return result == ComparisonResult::kLessThan || result == ComparisonResult::kEqual; case Operation::kGreaterThan: return result == ComparisonResult::kGreaterThan; case Operation::kGreaterThanOrEqual: return result == ComparisonResult::kGreaterThan || result == ComparisonResult::kEqual; default: break; } UNREACHABLE(); } std::ostream& operator<<(std::ostream& os, InstanceType instance_type) { switch (instance_type) { #define WRITE_TYPE(TYPE) \ case TYPE: \ return os << #TYPE; INSTANCE_TYPE_LIST(WRITE_TYPE) #undef WRITE_TYPE } UNREACHABLE(); } Handle<FieldType> Object::OptimalType(Isolate* isolate, Representation representation) { if (representation.IsNone()) return FieldType::None(isolate); if (FLAG_track_field_types) { if (representation.IsHeapObject() && IsHeapObject()) { // We can track only JavaScript objects with stable maps. Handle<Map> map(HeapObject::cast(*this)->map(), isolate); if (map->is_stable() && map->IsJSReceiverMap()) { return FieldType::Class(map, isolate); } } } return FieldType::Any(isolate); } Handle<Object> Object::NewStorageFor(Isolate* isolate, Handle<Object> object, Representation representation) { if (!representation.IsDouble()) return object; auto result = isolate->factory()->NewMutableHeapNumberWithHoleNaN(); if (object->IsUninitialized(isolate)) { result->set_value_as_bits(kHoleNanInt64); } else if (object->IsMutableHeapNumber()) { // Ensure that all bits of the double value are preserved. result->set_value_as_bits( MutableHeapNumber::cast(*object)->value_as_bits()); } else { result->set_value(object->Number()); } return result; } Handle<Object> Object::WrapForRead(Isolate* isolate, Handle<Object> object, Representation representation) { DCHECK(!object->IsUninitialized(isolate)); if (!representation.IsDouble()) { DCHECK(object->FitsRepresentation(representation)); return object; } return isolate->factory()->NewHeapNumber( MutableHeapNumber::cast(*object)->value()); } MaybeHandle<JSReceiver> Object::ToObjectImpl(Isolate* isolate, Handle<Object> object, const char* method_name) { DCHECK(!object->IsJSReceiver()); // Use ToObject() for fast path. Handle<Context> native_context = isolate->native_context(); Handle<JSFunction> constructor; if (object->IsSmi()) { constructor = handle(native_context->number_function(), isolate); } else { int constructor_function_index = Handle<HeapObject>::cast(object)->map()->GetConstructorFunctionIndex(); if (constructor_function_index == Map::kNoConstructorFunctionIndex) { if (method_name != nullptr) { THROW_NEW_ERROR( isolate, NewTypeError( MessageTemplate::kCalledOnNullOrUndefined, isolate->factory()->NewStringFromAsciiChecked(method_name)), JSReceiver); } THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kUndefinedOrNullToObject), JSReceiver); } constructor = handle( JSFunction::cast(native_context->get(constructor_function_index)), isolate); } Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); Handle<JSValue>::cast(result)->set_value(*object); return result; } // ES6 section 9.2.1.2, OrdinaryCallBindThis for sloppy callee. // static MaybeHandle<JSReceiver> Object::ConvertReceiver(Isolate* isolate, Handle<Object> object) { if (object->IsJSReceiver()) return Handle<JSReceiver>::cast(object); if (object->IsNullOrUndefined(isolate)) { return isolate->global_proxy(); } return Object::ToObject(isolate, object); } // static MaybeHandle<Object> Object::ConvertToNumberOrNumeric(Isolate* isolate, Handle<Object> input, Conversion mode) { while (true) { if (input->IsNumber()) { return input; } if (input->IsString()) { return String::ToNumber(isolate, Handle<String>::cast(input)); } if (input->IsOddball()) { return Oddball::ToNumber(isolate, Handle<Oddball>::cast(input)); } if (input->IsSymbol()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber), Object); } if (input->IsBigInt()) { if (mode == Conversion::kToNumeric) return input; DCHECK_EQ(mode, Conversion::kToNumber); THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kBigIntToNumber), Object); } ASSIGN_RETURN_ON_EXCEPTION( isolate, input, JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input), ToPrimitiveHint::kNumber), Object); } } // static MaybeHandle<Object> Object::ConvertToInteger(Isolate* isolate, Handle<Object> input) { ASSIGN_RETURN_ON_EXCEPTION( isolate, input, ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object); if (input->IsSmi()) return input; return isolate->factory()->NewNumber(DoubleToInteger(input->Number())); } // static MaybeHandle<Object> Object::ConvertToInt32(Isolate* isolate, Handle<Object> input) { ASSIGN_RETURN_ON_EXCEPTION( isolate, input, ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object); if (input->IsSmi()) return input; return isolate->factory()->NewNumberFromInt(DoubleToInt32(input->Number())); } // static MaybeHandle<Object> Object::ConvertToUint32(Isolate* isolate, Handle<Object> input) { ASSIGN_RETURN_ON_EXCEPTION( isolate, input, ConvertToNumberOrNumeric(isolate, input, Conversion::kToNumber), Object); if (input->IsSmi()) return handle(Smi::cast(*input)->ToUint32Smi(), isolate); return isolate->factory()->NewNumberFromUint(DoubleToUint32(input->Number())); } // static MaybeHandle<Name> Object::ConvertToName(Isolate* isolate, Handle<Object> input) { ASSIGN_RETURN_ON_EXCEPTION( isolate, input, Object::ToPrimitive(input, ToPrimitiveHint::kString), Name); if (input->IsName()) return Handle<Name>::cast(input); return ToString(isolate, input); } // ES6 7.1.14 // static MaybeHandle<Object> Object::ConvertToPropertyKey(Isolate* isolate, Handle<Object> value) { // 1. Let key be ToPrimitive(argument, hint String). MaybeHandle<Object> maybe_key = Object::ToPrimitive(value, ToPrimitiveHint::kString); // 2. ReturnIfAbrupt(key). Handle<Object> key; if (!maybe_key.ToHandle(&key)) return key; // 3. If Type(key) is Symbol, then return key. if (key->IsSymbol()) return key; // 4. Return ToString(key). // Extending spec'ed behavior, we'd be happy to return an element index. if (key->IsSmi()) return key; if (key->IsHeapNumber()) { uint32_t uint_value; if (value->ToArrayLength(&uint_value) && uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) { return handle(Smi::FromInt(static_cast<int>(uint_value)), isolate); } } return Object::ToString(isolate, key); } // static MaybeHandle<String> Object::ConvertToString(Isolate* isolate, Handle<Object> input) { while (true) { if (input->IsOddball()) { return handle(Handle<Oddball>::cast(input)->to_string(), isolate); } if (input->IsNumber()) { return isolate->factory()->NumberToString(input); } if (input->IsSymbol()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString), String); } if (input->IsBigInt()) { return BigInt::ToString(isolate, Handle<BigInt>::cast(input)); } ASSIGN_RETURN_ON_EXCEPTION( isolate, input, JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input), ToPrimitiveHint::kString), String); // The previous isString() check happened in Object::ToString and thus we // put it at the end of the loop in this helper. if (input->IsString()) { return Handle<String>::cast(input); } } } namespace { bool IsErrorObject(Isolate* isolate, Handle<Object> object) { if (!object->IsJSReceiver()) return false; Handle<Symbol> symbol = isolate->factory()->stack_trace_symbol(); return JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol) .FromMaybe(false); } Handle<String> AsStringOrEmpty(Isolate* isolate, Handle<Object> object) { return object->IsString() ? Handle<String>::cast(object) : isolate->factory()->empty_string(); } Handle<String> NoSideEffectsErrorToString(Isolate* isolate, Handle<Object> input) { Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input); Handle<Name> name_key = isolate->factory()->name_string(); Handle<Object> name = JSReceiver::GetDataProperty(receiver, name_key); Handle<String> name_str = AsStringOrEmpty(isolate, name); Handle<Name> msg_key = isolate->factory()->message_string(); Handle<Object> msg = JSReceiver::GetDataProperty(receiver, msg_key); Handle<String> msg_str = AsStringOrEmpty(isolate, msg); if (name_str->length() == 0) return msg_str; if (msg_str->length() == 0) return name_str; IncrementalStringBuilder builder(isolate); builder.AppendString(name_str); builder.AppendCString(": "); builder.AppendString(msg_str); return builder.Finish().ToHandleChecked(); } } // namespace // static Handle<String> Object::NoSideEffectsToString(Isolate* isolate, Handle<Object> input) { DisallowJavascriptExecution no_js(isolate); if (input->IsString() || input->IsNumber() || input->IsOddball()) { return Object::ToString(isolate, input).ToHandleChecked(); } else if (input->IsBigInt()) { MaybeHandle<String> maybe_string = BigInt::ToString(isolate, Handle<BigInt>::cast(input), 10, kDontThrow); Handle<String> result; if (maybe_string.ToHandle(&result)) return result; // BigInt-to-String conversion can fail on 32-bit platforms where // String::kMaxLength is too small to fit this BigInt. return isolate->factory()->NewStringFromStaticChars( "<a very large BigInt>"); } else if (input->IsFunction()) { // -- F u n c t i o n Handle<String> fun_str; if (input->IsJSBoundFunction()) { fun_str = JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(input)); } else { DCHECK(input->IsJSFunction()); fun_str = JSFunction::ToString(Handle<JSFunction>::cast(input)); } if (fun_str->length() > 128) { IncrementalStringBuilder builder(isolate); builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111)); builder.AppendCString("...<omitted>..."); builder.AppendString(isolate->factory()->NewSubString( fun_str, fun_str->length() - 2, fun_str->length())); return builder.Finish().ToHandleChecked(); } return fun_str; } else if (input->IsSymbol()) { // -- S y m b o l Handle<Symbol> symbol = Handle<Symbol>::cast(input); if (symbol->is_private_name()) { return Handle<String>(String::cast(symbol->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<JSReceiver> receiver = Handle<JSReceiver>::cast(input); Handle<Object> to_string = JSReceiver::GetDataProperty( receiver, isolate->factory()->toString_string()); if (IsErrorObject(isolate, input) || *to_string == *isolate->error_to_string()) { // When internally formatting error objects, use a side-effects-free // version of Error.prototype.toString independent of the actually // installed toString method. return NoSideEffectsErrorToString(isolate, input); } else if (*to_string == *isolate->object_to_string()) { Handle<Object> ctor = JSReceiver::GetDataProperty( receiver, isolate->factory()->constructor_string()); if (ctor->IsFunction()) { Handle<String> ctor_name; if (ctor->IsJSBoundFunction()) { ctor_name = JSBoundFunction::GetName( isolate, Handle<JSBoundFunction>::cast(ctor)) .ToHandleChecked(); } else if (ctor->IsJSFunction()) { Handle<Object> ctor_name_obj = JSFunction::GetName(isolate, Handle<JSFunction>::cast(ctor)); ctor_name = AsStringOrEmpty(isolate, ctor_name_obj); } if (ctor_name->length() != 0) { IncrementalStringBuilder builder(isolate); builder.AppendCString("#<"); builder.AppendString(ctor_name); builder.AppendCString(">"); return builder.Finish().ToHandleChecked(); } } } } // At this point, input is either none of the above or a JSReceiver. Handle<JSReceiver> receiver; if (input->IsJSReceiver()) { receiver = Handle<JSReceiver>::cast(input); } else { // This is the only case where Object::ToObject throws. DCHECK(!input->IsSmi()); int constructor_function_index = Handle<HeapObject>::cast(input)->map()->GetConstructorFunctionIndex(); if (constructor_function_index == Map::kNoConstructorFunctionIndex) { return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]"); } receiver = Object::ToObjectImpl(isolate, input).ToHandleChecked(); } Handle<String> builtin_tag = handle(receiver->class_name(), isolate); Handle<Object> tag_obj = JSReceiver::GetDataProperty( receiver, isolate->factory()->to_string_tag_symbol()); Handle<String> tag = tag_obj->IsString() ? Handle<String>::cast(tag_obj) : builtin_tag; IncrementalStringBuilder builder(isolate); builder.AppendCString("[object "); builder.AppendString(tag); builder.AppendCString("]"); return builder.Finish().ToHandleChecked(); } // static MaybeHandle<Object> Object::ConvertToLength(Isolate* isolate, Handle<Object> input) { ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(isolate, input), Object); if (input->IsSmi()) { int value = std::max(Smi::ToInt(*input), 0); return handle(Smi::FromInt(value), isolate); } double len = DoubleToInteger(input->Number()); if (len <= 0.0) { return handle(Smi::kZero, isolate); } else if (len >= kMaxSafeInteger) { len = kMaxSafeInteger; } return isolate->factory()->NewNumber(len); } // static MaybeHandle<Object> Object::ConvertToIndex(Isolate* isolate, Handle<Object> input, MessageTemplate error_index) { if (input->IsUndefined(isolate)) return handle(Smi::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<Object> x, Handle<Object> y) { return StrictNumberEquals(*x, *y); } ComparisonResult Reverse(ComparisonResult result) { if (result == ComparisonResult::kLessThan) { return ComparisonResult::kGreaterThan; } if (result == ComparisonResult::kGreaterThan) { return ComparisonResult::kLessThan; } return result; } } // anonymous namespace // static Maybe<ComparisonResult> Object::Compare(Isolate* isolate, Handle<Object> x, Handle<Object> y) { // ES6 section 7.2.11 Abstract Relational Comparison step 3 and 4. if (!Object::ToPrimitive(x, ToPrimitiveHint::kNumber).ToHandle(&x) || !Object::ToPrimitive(y, ToPrimitiveHint::kNumber).ToHandle(&y)) { return Nothing<ComparisonResult>(); } if (x->IsString() && y->IsString()) { // ES6 section 7.2.11 Abstract Relational Comparison step 5. return Just(String::Compare(isolate, Handle<String>::cast(x), Handle<String>::cast(y))); } if (x->IsBigInt() && y->IsString()) { return Just(BigInt::CompareToString(isolate, Handle<BigInt>::cast(x), Handle<String>::cast(y))); } if (x->IsString() && y->IsBigInt()) { return Just(Reverse(BigInt::CompareToString( isolate, Handle<BigInt>::cast(y), Handle<String>::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<ComparisonResult>(); } bool x_is_number = x->IsNumber(); bool y_is_number = y->IsNumber(); if (x_is_number && y_is_number) { return Just(StrictNumberCompare(x->Number(), y->Number())); } else if (!x_is_number && !y_is_number) { return Just(BigInt::CompareToBigInt(Handle<BigInt>::cast(x), Handle<BigInt>::cast(y))); } else if (x_is_number) { return Just(Reverse(BigInt::CompareToNumber(Handle<BigInt>::cast(y), x))); } else { return Just(BigInt::CompareToNumber(Handle<BigInt>::cast(x), y)); } } // static Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x, Handle<Object> y) { // This is the generic version of Abstract Equality Comparison. Must be in // sync with CodeStubAssembler::Equal. while (true) { if (x->IsNumber()) { if (y->IsNumber()) { return Just(StrictNumberEquals(x, y)); } else if (y->IsBoolean()) { return Just( StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); } else if (y->IsString()) { return Just(StrictNumberEquals( x, String::ToNumber(isolate, Handle<String>::cast(y)))); } else if (y->IsBigInt()) { return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x)); } else if (y->IsJSReceiver()) { if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) .ToHandle(&y)) { return Nothing<bool>(); } } else { return Just(false); } } else if (x->IsString()) { if (y->IsString()) { return Just(String::Equals(isolate, Handle<String>::cast(x), Handle<String>::cast(y))); } else if (y->IsNumber()) { x = String::ToNumber(isolate, Handle<String>::cast(x)); return Just(StrictNumberEquals(x, y)); } else if (y->IsBoolean()) { x = String::ToNumber(isolate, Handle<String>::cast(x)); return Just( StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); } else if (y->IsBigInt()) { return Just(BigInt::EqualToString(isolate, Handle<BigInt>::cast(y), Handle<String>::cast(x))); } else if (y->IsJSReceiver()) { if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) .ToHandle(&y)) { return Nothing<bool>(); } } else { return Just(false); } } else if (x->IsBoolean()) { if (y->IsOddball()) { return Just(x.is_identical_to(y)); } else if (y->IsNumber()) { return Just( StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); } else if (y->IsString()) { y = String::ToNumber(isolate, Handle<String>::cast(y)); return Just( StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); } else if (y->IsBigInt()) { x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x)); return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x)); } else if (y->IsJSReceiver()) { if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) .ToHandle(&y)) { return Nothing<bool>(); } x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x)); } else { return Just(false); } } else if (x->IsSymbol()) { if (y->IsSymbol()) { return Just(x.is_identical_to(y)); } else if (y->IsJSReceiver()) { if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) .ToHandle(&y)) { return Nothing<bool>(); } } else { return Just(false); } } else if (x->IsBigInt()) { if (y->IsBigInt()) { return Just(BigInt::EqualToBigInt(BigInt::cast(*x), BigInt::cast(*y))); } return Equals(isolate, y, x); } else if (x->IsJSReceiver()) { if (y->IsJSReceiver()) { return Just(x.is_identical_to(y)); } else if (y->IsUndetectable()) { return Just(x->IsUndetectable()); } else if (y->IsBoolean()) { y = Oddball::ToNumber(isolate, Handle<Oddball>::cast(y)); } else if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(x)) .ToHandle(&x)) { return Nothing<bool>(); } } else { return Just(x->IsUndetectable() && y->IsUndetectable()); } } } bool Object::StrictEquals(Object that) { if (this->IsNumber()) { if (!that->IsNumber()) return false; return StrictNumberEquals(*this, that); } else if (this->IsString()) { if (!that->IsString()) return false; return String::cast(*this)->Equals(String::cast(that)); } else if (this->IsBigInt()) { if (!that->IsBigInt()) return false; return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(that)); } return *this == that; } // static Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) { if (object->IsNumber()) return isolate->factory()->number_string(); if (object->IsOddball()) return handle(Oddball::cast(*object)->type_of(), isolate); if (object->IsUndetectable()) { return isolate->factory()->undefined_string(); } if (object->IsString()) return isolate->factory()->string_string(); if (object->IsSymbol()) return isolate->factory()->symbol_string(); if (object->IsBigInt()) return isolate->factory()->bigint_string(); if (object->IsCallable()) return isolate->factory()->function_string(); return isolate->factory()->object_string(); } // static MaybeHandle<Object> Object::Add(Isolate* isolate, Handle<Object> lhs, Handle<Object> rhs) { if (lhs->IsNumber() && rhs->IsNumber()) { return isolate->factory()->NewNumber(lhs->Number() + rhs->Number()); } else if (lhs->IsString() && rhs->IsString()) { return isolate->factory()->NewConsString(Handle<String>::cast(lhs), Handle<String>::cast(rhs)); } ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToPrimitive(lhs), Object); ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToPrimitive(rhs), Object); if (lhs->IsString() || rhs->IsString()) { ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToString(isolate, rhs), Object); ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToString(isolate, lhs), Object); return isolate->factory()->NewConsString(Handle<String>::cast(lhs), Handle<String>::cast(rhs)); } ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(isolate, rhs), Object); ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(isolate, lhs), Object); return isolate->factory()->NewNumber(lhs->Number() + rhs->Number()); } // static MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate, Handle<Object> callable, Handle<Object> object) { // The {callable} must have a [[Call]] internal method. if (!callable->IsCallable()) return isolate->factory()->false_value(); // Check if {callable} is a bound function, and if so retrieve its // [[BoundTargetFunction]] and use that instead of {callable}. if (callable->IsJSBoundFunction()) { Handle<Object> bound_callable( Handle<JSBoundFunction>::cast(callable)->bound_target_function(), isolate); return Object::InstanceOf(isolate, object, bound_callable); } // If {object} is not a receiver, return false. if (!object->IsJSReceiver()) return isolate->factory()->false_value(); // Get the "prototype" of {callable}; raise an error if it's not a receiver. Handle<Object> prototype; ASSIGN_RETURN_ON_EXCEPTION( isolate, prototype, Object::GetProperty(isolate, callable, isolate->factory()->prototype_string()), Object); if (!prototype->IsJSReceiver()) { THROW_NEW_ERROR( isolate, NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype), Object); } // Return whether or not {prototype} is in the prototype chain of {object}. Maybe<bool> result = JSReceiver::HasInPrototypeChain( isolate, Handle<JSReceiver>::cast(object), prototype); if (result.IsNothing()) return MaybeHandle<Object>(); return isolate->factory()->ToBoolean(result.FromJust()); } // static MaybeHandle<Object> Object::InstanceOf(Isolate* isolate, Handle<Object> object, Handle<Object> callable) { // The {callable} must be a receiver. if (!callable->IsJSReceiver()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck), Object); } // Lookup the @@hasInstance method on {callable}. Handle<Object> inst_of_handler; ASSIGN_RETURN_ON_EXCEPTION( isolate, inst_of_handler, Object::GetMethod(Handle<JSReceiver>::cast(callable), isolate->factory()->has_instance_symbol()), Object); if (!inst_of_handler->IsUndefined(isolate)) { // Call the {inst_of_handler} on the {callable}. Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, Execution::Call(isolate, inst_of_handler, callable, 1, &object), Object); return isolate->factory()->ToBoolean(result->BooleanValue(isolate)); } // The {callable} must have a [[Call]] internal method. if (!callable->IsCallable()) { THROW_NEW_ERROR( isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck), Object); } // Fall back to OrdinaryHasInstance with {callable} and {object}. Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, Object::OrdinaryHasInstance(isolate, callable, object), Object); return result; } // static MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver, Handle<Name> name) { Handle<Object> func; Isolate* isolate = receiver->GetIsolate(); ASSIGN_RETURN_ON_EXCEPTION( isolate, func, JSReceiver::GetProperty(isolate, receiver, name), Object); if (func->IsNullOrUndefined(isolate)) { return isolate->factory()->undefined_value(); } if (!func->IsCallable()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kPropertyNotFunction, func, name, receiver), Object); } return func; } namespace { MaybeHandle<FixedArray> CreateListFromArrayLikeFastPath( Isolate* isolate, Handle<Object> object, ElementTypes element_types) { if (element_types == ElementTypes::kAll) { if (object->IsJSArray()) { Handle<JSArray> array = Handle<JSArray>::cast(object); uint32_t length; if (!array->HasArrayPrototype(isolate) || !array->length()->ToUint32(&length) || !array->HasFastElements() || !JSObject::PrototypeHasNoElements(isolate, *array)) { return MaybeHandle<FixedArray>(); } return array->GetElementsAccessor()->CreateListFromArrayLike( isolate, array, length); } else if (object->IsJSTypedArray()) { Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(object); size_t length = array->length(); if (array->WasDetached() || length > static_cast<size_t>(FixedArray::kMaxLength)) { return MaybeHandle<FixedArray>(); } return array->GetElementsAccessor()->CreateListFromArrayLike( isolate, array, static_cast<uint32_t>(length)); } } return MaybeHandle<FixedArray>(); } } // namespace // static MaybeHandle<FixedArray> Object::CreateListFromArrayLike( Isolate* isolate, Handle<Object> object, ElementTypes element_types) { // Fast-path for JSArray and JSTypedArray. MaybeHandle<FixedArray> fast_result = CreateListFromArrayLikeFastPath(isolate, object, element_types); if (!fast_result.is_null()) return fast_result; // 1. ReturnIfAbrupt(object). // 2. (default elementTypes -- not applicable.) // 3. If Type(obj) is not Object, throw a TypeError exception. if (!object->IsJSReceiver()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, isolate->factory()->NewStringFromAsciiChecked( "CreateListFromArrayLike")), FixedArray); } // 4. Let len be ? ToLength(? Get(obj, "length")). Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object); Handle<Object> raw_length_number; ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number, Object::GetLengthFromArrayLike(isolate, receiver), FixedArray); uint32_t len; if (!raw_length_number->ToUint32(&len) || len > static_cast<uint32_t>(FixedArray::kMaxLength)) { THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidArrayLength), FixedArray); } // 5. Let list be an empty List. Handle<FixedArray> list = isolate->factory()->NewFixedArray(len); // 6. Let index be 0. // 7. Repeat while index < len: for (uint32_t index = 0; index < len; ++index) { // 7a. Let indexName be ToString(index). // 7b. Let next be ? Get(obj, indexName). Handle<Object> next; ASSIGN_RETURN_ON_EXCEPTION(isolate, next, JSReceiver::GetElement(isolate, receiver, index), FixedArray); switch (element_types) { case ElementTypes::kAll: // Nothing to do. break; case ElementTypes::kStringAndSymbol: { // 7c. If Type(next) is not an element of elementTypes, throw a // TypeError exception. if (!next->IsName()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kNotPropertyName, next), FixedArray); } // 7d. Append next as the last element of list. // Internalize on the fly so we can use pointer identity later. next = isolate->factory()->InternalizeName(Handle<Name>::cast(next)); break; } } list->set(index, *next); // 7e. Set index to index + 1. (See loop header.) } // 8. Return list. return list; } // static MaybeHandle<Object> Object::GetLengthFromArrayLike(Isolate* isolate, Handle<JSReceiver> object) { Handle<Object> val; Handle<Name> key = isolate->factory()->length_string(); ASSIGN_RETURN_ON_EXCEPTION( isolate, val, JSReceiver::GetProperty(isolate, object, key), Object); return Object::ToLength(isolate, val); } // static MaybeHandle<Object> Object::GetProperty(LookupIterator* it, 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<Object> receiver = it->GetReceiver(); // In case of global IC, the receiver is the global object. Replace by // the global proxy. if (receiver->IsJSGlobalObject()) { receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), it->isolate()); } MaybeHandle<Object> result = JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(), it->GetName(), receiver, &was_found); if (!was_found) it->NotFound(); return result; } case LookupIterator::INTERCEPTOR: { bool done; Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( it->isolate(), result, JSObject::GetPropertyWithInterceptor(it, &done), Object); if (done) return result; break; } case LookupIterator::ACCESS_CHECK: if (it->HasAccess()) break; return JSObject::GetPropertyWithFailedAccessCheck(it); case LookupIterator::ACCESSOR: return GetPropertyWithAccessor(it); case LookupIterator::INTEGER_INDEXED_EXOTIC: return it->isolate()->factory()->undefined_value(); case LookupIterator::DATA: return it->GetDataValue(); } } 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<Object> JSProxy::GetProperty(Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name, Handle<Object> receiver, bool* was_found) { *was_found = true; DCHECK(!name->IsPrivate()); STACK_CHECK(isolate, MaybeHandle<Object>()); Handle<Name> trap_name = isolate->factory()->get_string(); // 1. Assert: IsPropertyKey(P) is true. // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle<Object> handler(proxy->handler(), isolate); // 3. If handler is null, throw a TypeError exception. // 4. Assert: Type(handler) is Object. if (proxy->IsRevoked()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyRevoked, trap_name), Object); } // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "get"). Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION( isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Object); // 7. If trap is undefined, then if (trap->IsUndefined(isolate)) { // 7.a Return target.[[Get]](P, Receiver). LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name, target); MaybeHandle<Object> result = Object::GetProperty(&it); *was_found = it.IsFound(); return result; } // 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»). Handle<Object> trap_result; Handle<Object> args[] = {target, name, receiver}; ASSIGN_RETURN_ON_EXCEPTION( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Object); MaybeHandle<Object> result = JSProxy::CheckGetSetTrapResult(isolate, name, target, trap_result, kGet); if (result.is_null()) { return result; } // 11. Return trap_result return trap_result; } // static MaybeHandle<Object> JSProxy::CheckGetSetTrapResult(Isolate* isolate, Handle<Name> name, Handle<JSReceiver> target, Handle<Object> trap_result, AccessKind access_kind) { // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe<bool> target_found = JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); MAYBE_RETURN_NULL(target_found); // 10. If targetDesc is not undefined, then if (target_found.FromJust()) { // 10.a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is // false and targetDesc.[[Writable]] is false, then // 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false, // throw a TypeError exception. bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) && !target_desc.configurable() && !target_desc.writable() && !trap_result->SameValue(*target_desc.value()); if (inconsistent) { if (access_kind == kGet) { THROW_NEW_ERROR( isolate, NewTypeError(MessageTemplate::kProxyGetNonConfigurableData, name, target_desc.value(), trap_result), Object); } else { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxySetFrozenData, name)); return MaybeHandle<Object>(); } } // 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] // is false and targetDesc.[[Get]] is undefined, then // 10.b.i. If trapResult is not undefined, throw a TypeError exception. if (access_kind == kGet) { inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && !target_desc.configurable() && target_desc.get()->IsUndefined(isolate) && !trap_result->IsUndefined(isolate); } else { inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && !target_desc.configurable() && target_desc.set()->IsUndefined(isolate); } if (inconsistent) { if (access_kind == kGet) { THROW_NEW_ERROR( isolate, NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor, name, trap_result), Object); } else { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxySetFrozenAccessor, name)); return MaybeHandle<Object>(); } } } return isolate->factory()->undefined_value(); } bool Object::ToInt32(int32_t* value) { if (IsSmi()) { *value = Smi::ToInt(*this); return true; } if (IsHeapNumber()) { double num = HeapNumber::cast(*this)->value(); // Check range before conversion to avoid undefined behavior. if (num >= kMinInt && num <= kMaxInt && FastI2D(FastD2I(num)) == num) { *value = FastD2I(num); return true; } } return false; } // static constexpr object declarations need a definition to make the // compiler happy. constexpr Object Smi::kZero; V8_EXPORT_PRIVATE constexpr Object SharedFunctionInfo::kNoSharedNameSentinel; Handle<SharedFunctionInfo> FunctionTemplateInfo::GetOrCreateSharedFunctionInfo( Isolate* isolate, Handle<FunctionTemplateInfo> info, MaybeHandle<Name> maybe_name) { Object current_info = info->shared_function_info(); if (current_info->IsSharedFunctionInfo()) { return handle(SharedFunctionInfo::cast(current_info), isolate); } Handle<Name> name; Handle<String> name_string; if (maybe_name.ToHandle(&name) && name->IsString()) { name_string = Handle<String>::cast(name); } else if (info->class_name()->IsString()) { name_string = handle(String::cast(info->class_name()), isolate); } else { name_string = isolate->factory()->empty_string(); } FunctionKind function_kind; if (info->remove_prototype()) { function_kind = kConciseMethod; } else { function_kind = kNormalFunction; } Handle<SharedFunctionInfo> result = isolate->factory()->NewSharedFunctionInfoForApiFunction(name_string, info, function_kind); result->set_length(info->length()); result->DontAdaptArguments(); DCHECK(result->IsApiFunction()); info->set_shared_function_info(*result); return result; } bool FunctionTemplateInfo::IsTemplateFor(Map map) { // There is a constraint on the object; check. if (!map->IsJSObjectMap()) return false; // Fetch the constructor function of the object. Object cons_obj = map->GetConstructor(); Object type; if (cons_obj->IsJSFunction()) { JSFunction fun = JSFunction::cast(cons_obj); type = fun->shared()->function_data(); } else if (cons_obj->IsFunctionTemplateInfo()) { type = FunctionTemplateInfo::cast(cons_obj); } else { return false; } // Iterate through the chain of inheriting function templates to // see if the required one occurs. while (type->IsFunctionTemplateInfo()) { if (type == *this) return true; type = FunctionTemplateInfo::cast(type)->GetParentTemplate(); } // Didn't find the required type in the inheritance chain. return false; } // static FunctionTemplateRareData FunctionTemplateInfo::AllocateFunctionTemplateRareData( Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info) { DCHECK(function_template_info->rare_data()->IsUndefined(isolate)); Handle<Struct> struct_obj = isolate->factory()->NewStruct( FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld); Handle<FunctionTemplateRareData> rare_data = i::Handle<FunctionTemplateRareData>::cast(struct_obj); function_template_info->set_rare_data(*rare_data); return *rare_data; } // static Handle<TemplateList> TemplateList::New(Isolate* isolate, int size) { Handle<FixedArray> list = isolate->factory()->NewFixedArray(kLengthIndex + size); list->set(kLengthIndex, Smi::kZero); return Handle<TemplateList>::cast(list); } // static Handle<TemplateList> TemplateList::Add(Isolate* isolate, Handle<TemplateList> list, Handle<i::Object> value) { STATIC_ASSERT(kFirstElementIndex == 1); int index = list->length() + 1; Handle<i::FixedArray> fixed_array = Handle<FixedArray>::cast(list); fixed_array = FixedArray::SetAndGrow(isolate, fixed_array, index, value); fixed_array->set(kLengthIndex, Smi::FromInt(index)); return Handle<TemplateList>::cast(fixed_array); } // ES6 9.5.1 // static MaybeHandle<HeapObject> JSProxy::GetPrototype(Handle<JSProxy> proxy) { Isolate* isolate = proxy->GetIsolate(); Handle<String> trap_name = isolate->factory()->getPrototypeOf_string(); STACK_CHECK(isolate, MaybeHandle<HeapObject>()); // 1. Let handler be the value of the [[ProxyHandler]] internal slot. // 2. If handler is null, throw a TypeError exception. // 3. Assert: Type(handler) is Object. // 4. Let target be the value of the [[ProxyTarget]] internal slot. if (proxy->IsRevoked()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyRevoked, trap_name), HeapObject); } Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION(isolate, trap, Object::GetMethod(handler, trap_name), HeapObject); // 6. If trap is undefined, then return target.[[GetPrototypeOf]](). if (trap->IsUndefined(isolate)) { return JSReceiver::GetPrototype(isolate, target); } // 7. Let handlerProto be ? Call(trap, handler, «target»). Handle<Object> argv[] = {target}; Handle<Object> handler_proto; ASSIGN_RETURN_ON_EXCEPTION( isolate, handler_proto, Execution::Call(isolate, trap, handler, arraysize(argv), argv), HeapObject); // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError. if (!(handler_proto->IsJSReceiver() || handler_proto->IsNull(isolate))) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid), HeapObject); } // 9. Let extensibleTarget be ? IsExtensible(target). Maybe<bool> is_extensible = JSReceiver::IsExtensible(target); MAYBE_RETURN(is_extensible, MaybeHandle<HeapObject>()); // 10. If extensibleTarget is true, return handlerProto. if (is_extensible.FromJust()) return Handle<HeapObject>::cast(handler_proto); // 11. Let targetProto be ? target.[[GetPrototypeOf]](). Handle<HeapObject> target_proto; ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto, JSReceiver::GetPrototype(isolate, target), HeapObject); // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError. if (!handler_proto->SameValue(*target_proto)) { THROW_NEW_ERROR( isolate, NewTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible), HeapObject); } // 13. Return handlerProto. return Handle<HeapObject>::cast(handler_proto); } MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) { Isolate* isolate = it->isolate(); Handle<Object> structure = it->GetAccessors(); Handle<Object> receiver = it->GetReceiver(); // In case of global IC, the receiver is the global object. Replace by the // global proxy. if (receiver->IsJSGlobalObject()) { receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), isolate); } // We should never get here to initialize a const with the hole value since a // const declaration would conflict with the getter. DCHECK(!structure->IsForeign()); // API style callbacks. Handle<JSObject> holder = it->GetHolder<JSObject>(); if (structure->IsAccessorInfo()) { Handle<Name> name = it->GetName(); Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure); if (!info->IsCompatibleReceiver(*receiver)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, name, receiver), Object); } if (!info->has_getter()) return isolate->factory()->undefined_value(); if (info->is_sloppy() && !receiver->IsJSReceiver()) { ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver, Object::ConvertReceiver(isolate, receiver), Object); } PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder, Just(kDontThrow)); Handle<Object> result = args.CallAccessorGetter(info, name); RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); if (result.is_null()) return isolate->factory()->undefined_value(); Handle<Object> reboxed_result = handle(*result, isolate); if (info->replace_on_access() && receiver->IsJSReceiver()) { RETURN_ON_EXCEPTION(isolate, Accessors::ReplaceAccessorWithDataProperty( receiver, holder, name, result), Object); } return reboxed_result; } // AccessorPair with 'cached' private property. if (it->TryLookupCachedProperty()) { return Object::GetProperty(it); } // Regular accessor. Handle<Object> getter(AccessorPair::cast(*structure)->getter(), isolate); if (getter->IsFunctionTemplateInfo()) { SaveAndSwitchContext save(isolate, *holder->GetCreationContext()); return Builtins::InvokeApiFunction( isolate, false, Handle<FunctionTemplateInfo>::cast(getter), receiver, 0, nullptr, isolate->factory()->undefined_value()); } else if (getter->IsCallable()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... return Object::GetPropertyWithDefinedGetter( receiver, Handle<JSReceiver>::cast(getter)); } // Getter is not a function. return isolate->factory()->undefined_value(); } // static Address AccessorInfo::redirect(Address address, AccessorComponent component) { ApiFunction fun(address); DCHECK_EQ(ACCESSOR_GETTER, component); ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL; return ExternalReference::Create(&fun, type).address(); } Address AccessorInfo::redirected_getter() const { Address accessor = v8::ToCData<Address>(getter()); if (accessor == kNullAddress) return kNullAddress; return redirect(accessor, ACCESSOR_GETTER); } Address CallHandlerInfo::redirected_callback() const { Address address = v8::ToCData<Address>(callback()); ApiFunction fun(address); ExternalReference::Type type = ExternalReference::DIRECT_API_CALL; return ExternalReference::Create(&fun, type).address(); } bool AccessorInfo::IsCompatibleReceiverMap(Handle<AccessorInfo> info, Handle<Map> map) { if (!info->HasExpectedReceiverType()) return true; if (!map->IsJSObjectMap()) return false; return FunctionTemplateInfo::cast(info->expected_receiver_type()) ->IsTemplateFor(*map); } Maybe<bool> Object::SetPropertyWithAccessor( LookupIterator* it, Handle<Object> value, Maybe<ShouldThrow> maybe_should_throw) { Isolate* isolate = it->isolate(); Handle<Object> structure = it->GetAccessors(); Handle<Object> receiver = it->GetReceiver(); // In case of global IC, the receiver is the global object. Replace by the // global proxy. if (receiver->IsJSGlobalObject()) { receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), isolate); } // We should never get here to initialize a const with the hole value since a // const declaration would conflict with the setter. DCHECK(!structure->IsForeign()); // API style callbacks. Handle<JSObject> holder = it->GetHolder<JSObject>(); if (structure->IsAccessorInfo()) { Handle<Name> name = it->GetName(); Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure); if (!info->IsCompatibleReceiver(*receiver)) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kIncompatibleMethodReceiver, name, receiver)); return Nothing<bool>(); } if (!info->has_setter()) { // TODO(verwaest): We should not get here anymore once all AccessorInfos // are marked as special_data_property. They cannot both be writable and // not have a setter. return Just(true); } if (info->is_sloppy() && !receiver->IsJSReceiver()) { ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, receiver, Object::ConvertReceiver(isolate, receiver), Nothing<bool>()); } // The actual type of setter callback is either // v8::AccessorNameSetterCallback or // i::Accesors::AccessorNameBooleanSetterCallback, depending on whether the // AccessorInfo was created by the API or internally (see accessors.cc). // Here we handle both cases using GenericNamedPropertySetterCallback and // its Call method. PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder, maybe_should_throw); Handle<Object> result = args.CallAccessorSetter(info, name, value); // In the case of AccessorNameSetterCallback, we know that the result value // cannot have been set, so the result of Call will be null. In the case of // AccessorNameBooleanSetterCallback, the result will either be null // (signalling an exception) or a boolean Oddball. RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); if (result.is_null()) return Just(true); DCHECK(result->BooleanValue(isolate) || GetShouldThrow(isolate, maybe_should_throw) == kDontThrow); return Just(result->BooleanValue(isolate)); } // Regular accessor. Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); if (setter->IsFunctionTemplateInfo()) { SaveAndSwitchContext save(isolate, *holder->GetCreationContext()); Handle<Object> argv[] = {value}; RETURN_ON_EXCEPTION_VALUE( isolate, Builtins::InvokeApiFunction( isolate, false, Handle<FunctionTemplateInfo>::cast(setter), receiver, arraysize(argv), argv, isolate->factory()->undefined_value()), Nothing<bool>()); return Just(true); } else if (setter->IsCallable()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... return SetPropertyWithDefinedSetter( receiver, Handle<JSReceiver>::cast(setter), value, maybe_should_throw); } RETURN_FAILURE(isolate, GetShouldThrow(isolate, maybe_should_throw), NewTypeError(MessageTemplate::kNoSetterInCallback, it->GetName(), it->GetHolder<JSObject>())); } MaybeHandle<Object> Object::GetPropertyWithDefinedGetter( Handle<Object> receiver, Handle<JSReceiver> getter) { Isolate* isolate = getter->GetIsolate(); // Platforms with simulators like arm/arm64 expose a funny issue. If the // simulator has a separate JS stack pointer from the C++ stack pointer, it // can miss C++ stack overflows in the stack guard at the start of JavaScript // functions. It would be very expensive to check the C++ stack pointer at // that location. The best solution seems to be to break the impasse by // adding checks at possible recursion points. What's more, we don't put // this stack check behind the USE_SIMULATOR define in order to keep // behavior the same between hardware and simulators. StackLimitCheck check(isolate); if (check.JsHasOverflowed()) { isolate->StackOverflow(); return MaybeHandle<Object>(); } return Execution::Call(isolate, getter, receiver, 0, nullptr); } Maybe<bool> Object::SetPropertyWithDefinedSetter( Handle<Object> receiver, Handle<JSReceiver> setter, Handle<Object> value, Maybe<ShouldThrow> should_throw) { Isolate* isolate = setter->GetIsolate(); Handle<Object> argv[] = { value }; RETURN_ON_EXCEPTION_VALUE(isolate, Execution::Call(isolate, setter, receiver, arraysize(argv), argv), Nothing<bool>()); return Just(true); } Map Object::GetPrototypeChainRootMap(Isolate* isolate) const { DisallowHeapAllocation no_alloc; if (IsSmi()) { Context native_context = isolate->context()->native_context(); return native_context->number_function()->initial_map(); } const HeapObject heap_object = HeapObject::cast(*this); return heap_object->map()->GetPrototypeChainRootMap(isolate); } Smi Object::GetOrCreateHash(Isolate* isolate) { DisallowHeapAllocation no_gc; Object hash = Object::GetSimpleHash(*this); if (hash->IsSmi()) return Smi::cast(hash); DCHECK(IsJSReceiver()); return JSReceiver::cast(*this)->GetOrCreateIdentityHash(isolate); } bool Object::SameValue(Object other) { if (other == *this) return true; if (IsNumber() && other->IsNumber()) { return SameNumberValue(Number(), other->Number()); } if (IsString() && other->IsString()) { return String::cast(*this)->Equals(String::cast(other)); } if (IsBigInt() && other->IsBigInt()) { return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(other)); } return false; } bool Object::SameValueZero(Object other) { if (other == *this) return true; if (IsNumber() && other->IsNumber()) { double this_value = Number(); double other_value = other->Number(); // +0 == -0 is true return this_value == other_value || (std::isnan(this_value) && std::isnan(other_value)); } if (IsString() && other->IsString()) { return String::cast(*this)->Equals(String::cast(other)); } if (IsBigInt() && other->IsBigInt()) { return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(other)); } return false; } MaybeHandle<Object> Object::ArraySpeciesConstructor( Isolate* isolate, Handle<Object> original_array) { Handle<Object> default_species = isolate->array_function(); if (original_array->IsJSArray() && Handle<JSArray>::cast(original_array)->HasArrayPrototype(isolate) && isolate->IsArraySpeciesLookupChainIntact()) { return default_species; } Handle<Object> constructor = isolate->factory()->undefined_value(); Maybe<bool> is_array = Object::IsArray(original_array); MAYBE_RETURN_NULL(is_array); if (is_array.FromJust()) { ASSIGN_RETURN_ON_EXCEPTION( isolate, constructor, Object::GetProperty(isolate, original_array, isolate->factory()->constructor_string()), Object); if (constructor->IsConstructor()) { Handle<Context> constructor_context; ASSIGN_RETURN_ON_EXCEPTION( isolate, constructor_context, JSReceiver::GetFunctionRealm(Handle<JSReceiver>::cast(constructor)), Object); if (*constructor_context != *isolate->native_context() && *constructor == constructor_context->array_function()) { constructor = isolate->factory()->undefined_value(); } } if (constructor->IsJSReceiver()) { ASSIGN_RETURN_ON_EXCEPTION( isolate, constructor, JSReceiver::GetProperty(isolate, Handle<JSReceiver>::cast(constructor), isolate->factory()->species_symbol()), Object); if (constructor->IsNull(isolate)) { constructor = isolate->factory()->undefined_value(); } } } if (constructor->IsUndefined(isolate)) { return default_species; } else { if (!constructor->IsConstructor()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object); } return constructor; } } // ES6 section 7.3.20 SpeciesConstructor ( O, defaultConstructor ) V8_WARN_UNUSED_RESULT MaybeHandle<Object> Object::SpeciesConstructor( Isolate* isolate, Handle<JSReceiver> recv, Handle<JSFunction> default_ctor) { Handle<Object> ctor_obj; ASSIGN_RETURN_ON_EXCEPTION( isolate, ctor_obj, JSObject::GetProperty(isolate, recv, isolate->factory()->constructor_string()), Object); if (ctor_obj->IsUndefined(isolate)) return default_ctor; if (!ctor_obj->IsJSReceiver()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kConstructorNotReceiver), Object); } Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj); Handle<Object> species; ASSIGN_RETURN_ON_EXCEPTION( isolate, species, JSObject::GetProperty(isolate, ctor, isolate->factory()->species_symbol()), Object); if (species->IsNullOrUndefined(isolate)) { return default_ctor; } if (species->IsConstructor()) return species; THROW_NEW_ERROR( isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object); } bool Object::IterationHasObservableEffects() { // Check that this object is an array. if (!IsJSArray()) return true; JSArray array = JSArray::cast(*this); Isolate* isolate = array->GetIsolate(); #ifdef V8_ENABLE_FORCE_SLOW_PATH if (isolate->force_slow_path()) return true; #endif // Check that we have the original ArrayPrototype. if (!array->map()->prototype()->IsJSObject()) return true; JSObject array_proto = JSObject::cast(array->map()->prototype()); if (!isolate->is_initial_array_prototype(array_proto)) return true; // Check that the ArrayPrototype hasn't been modified in a way that would // affect iteration. if (!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; } void MaybeObject::ShortPrint(FILE* out) { OFStream os(out); os << Brief(*this); } void MaybeObject::ShortPrint(StringStream* accumulator) { std::ostringstream os; os << Brief(*this); accumulator->Add(os.str().c_str()); } void MaybeObject::ShortPrint(std::ostream& os) { os << Brief(*this); } Brief::Brief(const Object v) : value(v->ptr()) {} Brief::Brief(const MaybeObject v) : value(v.ptr()) {} std::ostream& operator<<(std::ostream& os, const Brief& v) { MaybeObject maybe_object(v.value); Smi smi; HeapObject heap_object; if (maybe_object->ToSmi(&smi)) { smi->SmiPrint(os); } else if (maybe_object->IsCleared()) { os << "[cleared]"; } else if (maybe_object->GetHeapObjectIfWeak(&heap_object)) { os << "[weak] "; heap_object->HeapObjectShortPrint(os); } else if (maybe_object->GetHeapObjectIfStrong(&heap_object)) { heap_object->HeapObjectShortPrint(os); } else { UNREACHABLE(); } return os; } void Smi::SmiPrint(std::ostream& os) const { // NOLINT os << value(); } void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT os << AsHex::Address(this->ptr()) << " "; if (IsString()) { HeapStringAllocator allocator; StringStream accumulator(&allocator); String::cast(*this)->StringShortPrint(&accumulator); os << accumulator.ToCString().get(); return; } if (IsJSObject()) { HeapStringAllocator allocator; StringStream accumulator(&allocator); JSObject::cast(*this)->JSObjectShortPrint(&accumulator); os << accumulator.ToCString().get(); return; } switch (map()->instance_type()) { case MAP_TYPE: { os << "<Map"; Map mapInstance = Map::cast(*this); if (mapInstance->IsJSObjectMap()) { os << "(" << ElementsKindToString(mapInstance->elements_kind()) << ")"; } else if (mapInstance->instance_size() != kVariableSizeSentinel) { os << "[" << mapInstance->instance_size() << "]"; } os << ">"; } break; case AWAIT_CONTEXT_TYPE: { os << "<AwaitContext generator= "; HeapStringAllocator allocator; StringStream accumulator(&allocator); Context::cast(*this)->extension()->ShortPrint(&accumulator); os << accumulator.ToCString().get(); os << '>'; break; } case BLOCK_CONTEXT_TYPE: os << "<BlockContext[" << Context::cast(*this)->length() << "]>"; break; case CATCH_CONTEXT_TYPE: os << "<CatchContext[" << Context::cast(*this)->length() << "]>"; break; case DEBUG_EVALUATE_CONTEXT_TYPE: os << "<DebugEvaluateContext[" << Context::cast(*this)->length() << "]>"; break; case EVAL_CONTEXT_TYPE: os << "<EvalContext[" << Context::cast(*this)->length() << "]>"; break; case FUNCTION_CONTEXT_TYPE: os << "<FunctionContext[" << Context::cast(*this)->length() << "]>"; break; case MODULE_CONTEXT_TYPE: os << "<ModuleContext[" << Context::cast(*this)->length() << "]>"; break; case NATIVE_CONTEXT_TYPE: os << "<NativeContext[" << Context::cast(*this)->length() << "]>"; break; case SCRIPT_CONTEXT_TYPE: os << "<ScriptContext[" << Context::cast(*this)->length() << "]>"; break; case WITH_CONTEXT_TYPE: os << "<WithContext[" << Context::cast(*this)->length() << "]>"; break; case SCRIPT_CONTEXT_TABLE_TYPE: os << "<ScriptContextTable[" << FixedArray::cast(*this)->length() << "]>"; break; case HASH_TABLE_TYPE: os << "<HashTable[" << FixedArray::cast(*this)->length() << "]>"; break; case ORDERED_HASH_MAP_TYPE: os << "<OrderedHashMap[" << FixedArray::cast(*this)->length() << "]>"; break; case ORDERED_HASH_SET_TYPE: os << "<OrderedHashSet[" << FixedArray::cast(*this)->length() << "]>"; break; case ORDERED_NAME_DICTIONARY_TYPE: os << "<OrderedNameDictionary[" << FixedArray::cast(*this)->length() << "]>"; break; case NAME_DICTIONARY_TYPE: os << "<NameDictionary[" << FixedArray::cast(*this)->length() << "]>"; break; case GLOBAL_DICTIONARY_TYPE: os << "<GlobalDictionary[" << FixedArray::cast(*this)->length() << "]>"; break; case NUMBER_DICTIONARY_TYPE: os << "<NumberDictionary[" << FixedArray::cast(*this)->length() << "]>"; break; case SIMPLE_NUMBER_DICTIONARY_TYPE: os << "<SimpleNumberDictionary[" << FixedArray::cast(*this)->length() << "]>"; break; case STRING_TABLE_TYPE: os << "<StringTable[" << FixedArray::cast(*this)->length() << "]>"; break; case FIXED_ARRAY_TYPE: os << "<FixedArray[" << FixedArray::cast(*this)->length() << "]>"; break; case OBJECT_BOILERPLATE_DESCRIPTION_TYPE: os << "<ObjectBoilerplateDescription[" << FixedArray::cast(*this)->length() << "]>"; break; case FIXED_DOUBLE_ARRAY_TYPE: os << "<FixedDoubleArray[" << FixedDoubleArray::cast(*this)->length() << "]>"; break; case BYTE_ARRAY_TYPE: os << "<ByteArray[" << ByteArray::cast(*this)->length() << "]>"; break; case BYTECODE_ARRAY_TYPE: os << "<BytecodeArray[" << BytecodeArray::cast(*this)->length() << "]>"; break; case DESCRIPTOR_ARRAY_TYPE: os << "<DescriptorArray[" << DescriptorArray::cast(*this)->number_of_descriptors() << "]>"; break; case TRANSITION_ARRAY_TYPE: os << "<TransitionArray[" << TransitionArray::cast(*this)->length() << "]>"; break; case PROPERTY_ARRAY_TYPE: os << "<PropertyArray[" << PropertyArray::cast(*this)->length() << "]>"; break; case FEEDBACK_CELL_TYPE: { { ReadOnlyRoots roots = GetReadOnlyRoots(); os << "<FeedbackCell["; if (map() == roots.no_closures_cell_map()) { os << "no feedback"; } else if (map() == roots.no_closures_cell_map()) { os << "no closures"; } else if (map() == roots.one_closure_cell_map()) { os << "one closure"; } else if (map() == roots.many_closures_cell_map()) { os << "many closures"; } else { os << "!!!INVALID MAP!!!"; } os << "]>"; } break; } case CLOSURE_FEEDBACK_CELL_ARRAY_TYPE: os << "<ClosureFeedbackCellArray[" << ClosureFeedbackCellArray::cast(*this)->length() << "]>"; break; case FEEDBACK_VECTOR_TYPE: os << "<FeedbackVector[" << FeedbackVector::cast(*this)->length() << "]>"; break; case FREE_SPACE_TYPE: os << "<FreeSpace[" << FreeSpace::cast(*this)->size() << "]>"; break; #define TYPED_ARRAY_SHORT_PRINT(Type, type, TYPE, ctype) \ case FIXED_##TYPE##_ARRAY_TYPE: \ os << "<Fixed" #Type "Array[" << Fixed##Type##Array::cast(*this)->length() \ << "]>"; \ break; TYPED_ARRAYS(TYPED_ARRAY_SHORT_PRINT) #undef TYPED_ARRAY_SHORT_PRINT case PREPARSE_DATA_TYPE: { PreparseData data = PreparseData::cast(*this); os << "<PreparseData[data=" << data->data_length() << " children=" << data->children_length() << "]>"; break; } case UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE: { UncompiledDataWithoutPreparseData data = UncompiledDataWithoutPreparseData::cast(*this); os << "<UncompiledDataWithoutPreparseData (" << data->start_position() << ", " << data->end_position() << ")]>"; break; } case UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE: { UncompiledDataWithPreparseData data = UncompiledDataWithPreparseData::cast(*this); os << "<UncompiledDataWithPreparseData (" << data->start_position() << ", " << data->end_position() << ") preparsed=" << Brief(data->preparse_data()) << ">"; break; } case SHARED_FUNCTION_INFO_TYPE: { SharedFunctionInfo shared = SharedFunctionInfo::cast(*this); std::unique_ptr<char[]> debug_name = shared->DebugName()->ToCString(); if (debug_name[0] != 0) { os << "<SharedFunctionInfo " << debug_name.get() << ">"; } else { os << "<SharedFunctionInfo>"; } break; } case JS_MESSAGE_OBJECT_TYPE: os << "<JSMessageObject>"; break; #define MAKE_STRUCT_CASE(TYPE, Name, name) \ case TYPE: \ os << "<" #Name; \ Name::cast(*this)->BriefPrintDetails(os); \ os << ">"; \ break; STRUCT_LIST(MAKE_STRUCT_CASE) #undef MAKE_STRUCT_CASE case ALLOCATION_SITE_TYPE: { os << "<AllocationSite"; AllocationSite::cast(*this)->BriefPrintDetails(os); os << ">"; break; } case SCOPE_INFO_TYPE: { ScopeInfo scope = ScopeInfo::cast(*this); os << "<ScopeInfo"; if (scope->length()) os << " " << scope->scope_type() << " "; os << "[" << scope->length() << "]>"; break; } case CODE_TYPE: { Code code = Code::cast(*this); os << "<Code " << Code::Kind2String(code->kind()); if (code->is_builtin()) { os << " " << Builtins::name(code->builtin_index()); } os << ">"; break; } case ODDBALL_TYPE: { if (IsUndefined()) { os << "<undefined>"; } else if (IsTheHole()) { os << "<the_hole>"; } else if (IsNull()) { os << "<null>"; } else if (IsTrue()) { os << "<true>"; } else if (IsFalse()) { os << "<false>"; } else { os << "<Odd Oddball: "; os << Oddball::cast(*this)->to_string()->ToCString().get(); os << ">"; } break; } case SYMBOL_TYPE: { Symbol symbol = Symbol::cast(*this); symbol->SymbolShortPrint(os); break; } case HEAP_NUMBER_TYPE: { os << "<HeapNumber "; HeapNumber::cast(*this)->HeapNumberPrint(os); os << ">"; break; } case MUTABLE_HEAP_NUMBER_TYPE: { os << "<MutableHeapNumber "; MutableHeapNumber::cast(*this)->MutableHeapNumberPrint(os); os << '>'; break; } case BIGINT_TYPE: { os << "<BigInt "; BigInt::cast(*this)->BigIntShortPrint(os); os << ">"; break; } case JS_PROXY_TYPE: os << "<JSProxy>"; break; case FOREIGN_TYPE: os << "<Foreign>"; break; case CELL_TYPE: { os << "<Cell value= "; HeapStringAllocator allocator; StringStream accumulator(&allocator); Cell::cast(*this)->value()->ShortPrint(&accumulator); os << accumulator.ToCString().get(); os << '>'; break; } case PROPERTY_CELL_TYPE: { PropertyCell cell = PropertyCell::cast(*this); os << "<PropertyCell name="; cell->name()->ShortPrint(os); os << " value="; HeapStringAllocator allocator; StringStream accumulator(&allocator); cell->value()->ShortPrint(&accumulator); os << accumulator.ToCString().get(); os << '>'; break; } case CALL_HANDLER_INFO_TYPE: { CallHandlerInfo info = CallHandlerInfo::cast(*this); os << "<CallHandlerInfo "; os << "callback= " << Brief(info->callback()); os << ", js_callback= " << Brief(info->js_callback()); os << ", data= " << Brief(info->data()); if (info->IsSideEffectFreeCallHandlerInfo()) { os << ", side_effect_free= true>"; } else { os << ", side_effect_free= false>"; } break; } default: os << "<Other heap object (" << map()->instance_type() << ")>"; break; } } void Struct::BriefPrintDetails(std::ostream& os) {} void Tuple2::BriefPrintDetails(std::ostream& os) { os << " " << Brief(value1()) << ", " << Brief(value2()); } void 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<ObjectVisitor>(v); } void HeapObject::IterateBody(ObjectVisitor* v) { Map m = map(); IterateBodyFast<ObjectVisitor>(m, SizeFromMap(m), v); } void HeapObject::IterateBody(Map map, int object_size, ObjectVisitor* v) { IterateBodyFast<ObjectVisitor>(map, object_size, v); } struct CallIsValidSlot { template <typename BodyDescriptor> static bool apply(Map map, HeapObject obj, int offset, int) { return BodyDescriptor::IsValidSlot(map, obj, offset); } }; bool HeapObject::IsValidSlot(Map map, int offset) { DCHECK_NE(0, offset); return BodyDescriptorApply<CallIsValidSlot, bool>(map->instance_type(), map, *this, offset, 0); } int HeapObject::SizeFromMap(Map map) const { int instance_size = map->instance_size(); if (instance_size != kVariableSizeSentinel) return instance_size; // Only inline the most frequent cases. InstanceType instance_type = map->instance_type(); if (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)) { // Native context has fixed size. DCHECK_NE(instance_type, NATIVE_CONTEXT_TYPE); 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 (IsInRange(instance_type, FIRST_FIXED_TYPED_ARRAY_TYPE, LAST_FIXED_TYPED_ARRAY_TYPE)) { return FixedTypedArrayBase::unchecked_cast(*this)->TypedArraySize( instance_type); } 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(); break; 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, FieldType::Any()); } set(ToDetailsIndex(i), MaybeObject::FromObject(details.AsSmi())); } } MaybeHandle<Object> Object::SetProperty(Isolate* isolate, Handle<Object> object, Handle<Name> name, Handle<Object> value, StoreOrigin store_origin, Maybe<ShouldThrow> should_throw) { LookupIterator it(isolate, object, name); MAYBE_RETURN_NULL(SetProperty(&it, value, store_origin, should_throw)); return value; } Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, Handle<Object> value, Maybe<ShouldThrow> should_throw, StoreOrigin store_origin, bool* found) { it->UpdateProtector(); DCHECK(it->IsFound()); // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc(it->isolate()); do { switch (it->state()) { case LookupIterator::NOT_FOUND: UNREACHABLE(); case LookupIterator::ACCESS_CHECK: if (it->HasAccess()) break; // Check whether it makes sense to reuse the lookup iterator. Here it // might still call into setters up the prototype chain. return JSObject::SetPropertyWithFailedAccessCheck(it, value, should_throw); case LookupIterator::JSPROXY: { Handle<Object> receiver = it->GetReceiver(); // In case of global IC, the receiver is the global object. Replace by // the global proxy. if (receiver->IsJSGlobalObject()) { receiver = handle(JSGlobalObject::cast(*receiver)->global_proxy(), it->isolate()); } return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(), value, receiver, should_throw); } case LookupIterator::INTERCEPTOR: { if (it->HolderIsReceiverOrHiddenPrototype()) { Maybe<bool> result = JSObject::SetPropertyWithInterceptor(it, should_throw, value); if (result.IsNothing() || result.FromJust()) return result; } else { Maybe<PropertyAttributes> maybe_attributes = JSObject::GetPropertyAttributesWithInterceptor(it); if (maybe_attributes.IsNothing()) return Nothing<bool>(); if ((maybe_attributes.FromJust() & READ_ONLY) != 0) { return WriteToReadOnlyProperty(it, value, should_throw); } if (maybe_attributes.FromJust() == ABSENT) break; *found = false; return Nothing<bool>(); } break; } case LookupIterator::ACCESSOR: { if (it->IsReadOnly()) { return WriteToReadOnlyProperty(it, value, should_throw); } Handle<Object> accessors = it->GetAccessors(); if (accessors->IsAccessorInfo() && !it->HolderIsReceiverOrHiddenPrototype() && AccessorInfo::cast(*accessors)->is_special_data_property()) { *found = false; return Nothing<bool>(); } return SetPropertyWithAccessor(it, value, should_throw); } case LookupIterator::INTEGER_INDEXED_EXOTIC: { // IntegerIndexedElementSet converts value to a Number/BigInt prior to // the bounds check. The bounds check has already happened here, but // perform the possibly effectful ToNumber (or ToBigInt) operation // anyways. auto holder = it->GetHolder<JSTypedArray>(); Handle<Object> throwaway_value; if (holder->type() == kExternalBigInt64Array || holder->type() == kExternalBigUint64Array) { ASSIGN_RETURN_ON_EXCEPTION_VALUE( it->isolate(), throwaway_value, BigInt::FromObject(it->isolate(), value), Nothing<bool>()); } else { ASSIGN_RETURN_ON_EXCEPTION_VALUE( it->isolate(), throwaway_value, Object::ToNumber(it->isolate(), value), Nothing<bool>()); } // FIXME: Throw a TypeError if the holder is detached here // (IntegerIndexedElementSpec step 5). // TODO(verwaest): Per spec, we should return false here (steps 6-9 // in IntegerIndexedElementSpec), resulting in an exception being thrown // on OOB accesses in strict code. Historically, v8 has not done made // this change due to uncertainty about web compat. (v8:4901) return Just(true); } case LookupIterator::DATA: if (it->IsReadOnly()) { return WriteToReadOnlyProperty(it, value, should_throw); } if (it->HolderIsReceiverOrHiddenPrototype()) { return SetDataProperty(it, value); } V8_FALLTHROUGH; case LookupIterator::TRANSITION: *found = false; return Nothing<bool>(); } it->Next(); } while (it->IsFound()); *found = false; return Nothing<bool>(); } Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value, StoreOrigin store_origin, Maybe<ShouldThrow> should_throw) { if (it->IsFound()) { bool found = true; Maybe<bool> result = SetPropertyInternal(it, value, should_throw, store_origin, &found); if (found) return result; } // If the receiver is the JSGlobalObject, the store was contextual. In case // the property did not exist yet on the global object itself, we have to // throw a reference error in strict mode. In sloppy mode, we continue. if (it->GetReceiver()->IsJSGlobalObject() && (GetShouldThrow(it->isolate(), should_throw) == ShouldThrow::kThrowOnError)) { it->isolate()->Throw(*it->isolate()->factory()->NewReferenceError( MessageTemplate::kNotDefined, it->name())); return Nothing<bool>(); } return AddDataProperty(it, value, NONE, should_throw, store_origin); } Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, StoreOrigin store_origin, Maybe<ShouldThrow> should_throw) { Isolate* isolate = it->isolate(); if (it->IsFound()) { bool found = true; Maybe<bool> result = SetPropertyInternal(it, value, should_throw, store_origin, &found); if (found) return result; } it->UpdateProtector(); // The property either doesn't exist on the holder or exists there as a data // property. if (!it->GetReceiver()->IsJSReceiver()) { return WriteToReadOnlyProperty(it, value, should_throw); } Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); LookupIterator::Configuration c = LookupIterator::OWN; LookupIterator own_lookup = it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c) : LookupIterator(isolate, receiver, it->name(), c); for (; own_lookup.IsFound(); own_lookup.Next()) { switch (own_lookup.state()) { case LookupIterator::ACCESS_CHECK: if (!own_lookup.HasAccess()) { return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value, should_throw); } break; case LookupIterator::ACCESSOR: if (own_lookup.GetAccessors()->IsAccessorInfo()) { if (own_lookup.IsReadOnly()) { return WriteToReadOnlyProperty(&own_lookup, value, should_throw); } 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<bool> owned = JSReceiver::GetOwnPropertyDescriptor(&own_lookup, &desc); MAYBE_RETURN(owned, Nothing<bool>()); 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<bool> Object::CannotCreateProperty(Isolate* isolate, Handle<Object> receiver, Handle<Object> name, Handle<Object> value, Maybe<ShouldThrow> should_throw) { RETURN_FAILURE( isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kStrictCannotCreateProperty, name, Object::TypeOf(isolate, receiver), receiver)); } Maybe<bool> Object::WriteToReadOnlyProperty( LookupIterator* it, Handle<Object> value, Maybe<ShouldThrow> maybe_should_throw) { ShouldThrow should_throw = GetShouldThrow(it->isolate(), maybe_should_throw); if (it->IsFound() && !it->HolderIsReceiver()) { // "Override mistake" attempted, record a use count to track this per // v8:8175 v8::Isolate::UseCounterFeature feature = should_throw == kThrowOnError ? v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeStrict : v8::Isolate::kAttemptOverrideReadOnlyOnPrototypeSloppy; it->isolate()->CountUsage(feature); } return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(), it->GetName(), value, should_throw); } Maybe<bool> Object::WriteToReadOnlyProperty(Isolate* isolate, Handle<Object> receiver, Handle<Object> name, Handle<Object> value, ShouldThrow should_throw) { RETURN_FAILURE(isolate, should_throw, NewTypeError(MessageTemplate::kStrictReadOnlyProperty, name, Object::TypeOf(isolate, receiver), receiver)); } Maybe<bool> Object::RedefineIncompatibleProperty( Isolate* isolate, Handle<Object> name, Handle<Object> value, Maybe<ShouldThrow> should_throw) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kRedefineDisallowed, name)); } Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) { DCHECK_IMPLIES(it->GetReceiver()->IsJSProxy(), it->GetName()->IsPrivateName()); DCHECK_IMPLIES(!it->IsElement() && it->GetName()->IsPrivateName(), it->state() == LookupIterator::DATA); Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); // Store on the holder which may be hidden behind the receiver. DCHECK(it->HolderIsReceiverOrHiddenPrototype()); Handle<Object> to_assign = value; // Convert the incoming value to a number for storing into typed arrays. if (it->IsElement() && receiver->IsJSObject() && JSObject::cast(*receiver)->HasFixedTypedArrayElements()) { 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<bool>()); // We have to recheck the length. However, it can only change if the // underlying buffer was detached, so just check that. if (Handle<JSArrayBufferView>::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<bool>()); // We have to recheck the length. However, it can only change if the // underlying buffer was detached, so just check that. if (Handle<JSArrayBufferView>::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<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, Maybe<ShouldThrow> 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<JSReceiver> receiver = it->GetStoreTarget<JSReceiver>(); 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<JSArray> array = Handle<JSArray>::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)); } if (FLAG_trace_external_array_abuse && array->HasFixedTypedArrayElements()) { CheckArrayAbuse(array, "typed elements write", it->index(), true); } if (FLAG_trace_js_array_abuse && !array->HasFixedTypedArrayElements()) { CheckArrayAbuse(array, "elements write", it->index(), false); } } Handle<JSObject> receiver_obj = Handle<JSObject>::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 <class T> static int AppendUniqueCallbacks(Isolate* isolate, Handle<TemplateList> callbacks, Handle<typename T::Array> 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<AccessorInfo> entry(AccessorInfo::cast(callbacks->get(i)), isolate); Handle<Name> 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 { typedef FixedArray Array; static bool Contains(Handle<Name> key, Handle<AccessorInfo> entry, int valid_descriptors, Handle<FixedArray> 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<Name> key, Handle<AccessorInfo> entry, int valid_descriptors, Handle<FixedArray> array) { DisallowHeapAllocation no_gc; array->set(valid_descriptors, *entry); } }; int AccessorInfo::AppendUnique(Isolate* isolate, Handle<Object> descriptors, Handle<FixedArray> array, int valid_descriptors) { Handle<TemplateList> callbacks = Handle<TemplateList>::cast(descriptors); DCHECK_GE(array->length(), callbacks->length() + valid_descriptors); return AppendUniqueCallbacks<FixedArrayAppender>(isolate, callbacks, array, valid_descriptors); } void JSProxy::Revoke(Handle<JSProxy> 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<bool> JSProxy::IsArray(Handle<JSProxy> proxy) { Isolate* isolate = proxy->GetIsolate(); Handle<JSReceiver> object = Handle<JSReceiver>::cast(proxy); for (int i = 0; i < JSProxy::kMaxIterationLimit; i++) { Handle<JSProxy> proxy = Handle<JSProxy>::cast(object); if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyRevoked, isolate->factory()->NewStringFromAsciiChecked("IsArray"))); return Nothing<bool>(); } 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<bool>(); } Maybe<bool> JSProxy::HasProperty(Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name) { DCHECK(!name->IsPrivate()); STACK_CHECK(isolate, Nothing<bool>()); // 1. (Assert) // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle<Object> handler(proxy->handler(), isolate); // 3. If handler is null, throw a TypeError exception. // 4. Assert: Type(handler) is Object. if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyRevoked, isolate->factory()->has_string())); return Nothing<bool>(); } // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "has"). Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), isolate->factory()->has_string()), Nothing<bool>()); // 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<Object> trap_result_obj; Handle<Object> args[] = {target, name}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result_obj, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing<bool>()); 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<bool>()); } // 10. Return booleanTrapResult. return Just(boolean_trap_result); } Maybe<bool> JSProxy::CheckHasTrap(Isolate* isolate, Handle<Name> name, Handle<JSReceiver> target) { // 9a. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe<bool> target_found = JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); MAYBE_RETURN(target_found, Nothing<bool>()); // 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<bool>(); } // 9b ii. Let extensibleTarget be ? IsExtensible(target). Maybe<bool> extensible_target = JSReceiver::IsExtensible(target); MAYBE_RETURN(extensible_target, Nothing<bool>()); // 9b iii. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target.FromJust()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyHasNonExtensible, name)); return Nothing<bool>(); } } return Just(true); } Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name, Handle<Object> value, Handle<Object> receiver, Maybe<ShouldThrow> should_throw) { DCHECK(!name->IsPrivate()); Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing<bool>()); Factory* factory = isolate->factory(); Handle<String> trap_name = factory->set_string(); if (proxy->IsRevoked()) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing<bool>(); } Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); if (trap->IsUndefined(isolate)) { LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name, target); return Object::SetSuperProperty(&it, value, StoreOrigin::kMaybeKeyed, should_throw); } Handle<Object> trap_result; Handle<Object> args[] = {target, name, value, receiver}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing<bool>()); if (!trap_result->BooleanValue(isolate)) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor, trap_name, name)); } MaybeHandle<Object> result = JSProxy::CheckGetSetTrapResult(isolate, name, target, value, kSet); if (result.is_null()) { return Nothing<bool>(); } return Just(true); } Maybe<bool> JSProxy::DeletePropertyOrElement(Handle<JSProxy> proxy, Handle<Name> name, LanguageMode language_mode) { DCHECK(!name->IsPrivate()); ShouldThrow should_throw = is_sloppy(language_mode) ? kDontThrow : kThrowOnError; Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing<bool>()); Factory* factory = isolate->factory(); Handle<String> trap_name = factory->deleteProperty_string(); if (proxy->IsRevoked()) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing<bool>(); } Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); if (trap->IsUndefined(isolate)) { return JSReceiver::DeletePropertyOrElement(target, name, language_mode); } Handle<Object> trap_result; Handle<Object> args[] = {target, name}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing<bool>()); if (!trap_result->BooleanValue(isolate)) { RETURN_FAILURE(isolate, should_throw, NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor, trap_name, name)); } // Enforce the invariant. PropertyDescriptor target_desc; Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); MAYBE_RETURN(owned, Nothing<bool>()); if (owned.FromJust() && !target_desc.configurable()) { isolate->Throw(*factory->NewTypeError( MessageTemplate::kProxyDeletePropertyNonConfigurable, name)); return Nothing<bool>(); } return Just(true); } // static MaybeHandle<JSProxy> JSProxy::New(Isolate* isolate, Handle<Object> target, Handle<Object> 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<JSReceiver>::cast(target), Handle<JSReceiver>::cast(handler)); } // static MaybeHandle<NativeContext> JSProxy::GetFunctionRealm(Handle<JSProxy> proxy) { DCHECK(proxy->map()->is_constructor()); if (proxy->IsRevoked()) { THROW_NEW_ERROR(proxy->GetIsolate(), NewTypeError(MessageTemplate::kProxyRevoked), NativeContext); } Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), proxy->GetIsolate()); return JSReceiver::GetFunctionRealm(target); } Maybe<PropertyAttributes> JSProxy::GetPropertyAttributes(LookupIterator* it) { PropertyDescriptor desc; Maybe<bool> found = JSProxy::GetOwnPropertyDescriptor( it->isolate(), it->GetHolder<JSProxy>(), it->GetName(), &desc); MAYBE_RETURN(found, Nothing<PropertyAttributes>()); if (!found.FromJust()) return Just(ABSENT); return Just(desc.ToAttributes()); } // TODO(jkummerow): Consider unification with FastAsArrayLength() in // accessors.cc. bool PropertyKeyToArrayLength(Handle<Object> 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<Object> index_obj, uint32_t* output) { return PropertyKeyToArrayLength(index_obj, output) && *output != kMaxUInt32; } // ES6 9.4.2.1 // static Maybe<bool> JSArray::DefineOwnProperty(Isolate* isolate, Handle<JSArray> o, Handle<Object> name, PropertyDescriptor* desc, Maybe<ShouldThrow> 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<bool> 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<bool> 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<Object> 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<String>::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<Object> uint32_v; if (!Object::ToUint32(isolate, length_object).ToHandle(&uint32_v)) { // 4. ReturnIfAbrupt(newLen). return false; } // 5. Let numberLen be ToNumber(Desc.[[Value]]). Handle<Object> 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<Object> 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<bool> JSArray::ArraySetLength(Isolate* isolate, Handle<JSArray> a, PropertyDescriptor* desc, Maybe<ShouldThrow> 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<bool>(); } // 8. Set newLenDesc.[[Value]] to newLen. // (Done below, if needed.) // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). PropertyDescriptor old_len_desc; Maybe<bool> 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()) { 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<bool> 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<bool> JSProxy::DefineOwnProperty(Isolate* isolate, Handle<JSProxy> proxy, Handle<Object> key, PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw) { STACK_CHECK(isolate, Nothing<bool>()); if (key->IsSymbol() && Handle<Symbol>::cast(key)->IsPrivate()) { DCHECK(!Handle<Symbol>::cast(key)->IsPrivateName()); return JSProxy::SetPrivateSymbol(isolate, proxy, Handle<Symbol>::cast(key), desc, should_throw); } Handle<String> 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<Object> 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<bool>(); } // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "defineProperty"). Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Nothing<bool>()); // 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<Object> desc_obj = desc->ToObject(isolate); // 9. Let booleanTrapResult be // ToBoolean(? Call(trap, handler, «target, P, descObj»)). Handle<Name> property_name = key->IsName() ? Handle<Name>::cast(key) : Handle<Name>::cast(isolate->factory()->NumberToString(key)); // Do not leak private property names. DCHECK(!property_name->IsPrivate()); Handle<Object> trap_result_obj; Handle<Object> args[] = {target, property_name, desc_obj}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result_obj, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing<bool>()); // 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<bool> target_found = JSReceiver::GetOwnPropertyDescriptor(isolate, target, key, &target_desc); MAYBE_RETURN(target_found, Nothing<bool>()); // 12. Let extensibleTarget be ? IsExtensible(target). Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target); MAYBE_RETURN(maybe_extensible, Nothing<bool>()); 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<bool>(); } // 15b. If settingConfigFalse is true, throw a TypeError exception. if (setting_config_false) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name)); return Nothing<bool>(); } } else { // 16. Else targetDesc is not undefined, // 16a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, // targetDesc) is false, throw a TypeError exception. Maybe<bool> valid = IsCompatiblePropertyDescriptor( isolate, extensible_target, desc, &target_desc, property_name, Just(kDontThrow)); MAYBE_RETURN(valid, Nothing<bool>()); if (!valid.FromJust()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyDefinePropertyIncompatible, property_name)); return Nothing<bool>(); } // 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<bool>(); } } // 17. Return true. return Just(true); } // static Maybe<bool> JSProxy::SetPrivateSymbol(Isolate* isolate, Handle<JSProxy> proxy, Handle<Symbol> private_name, PropertyDescriptor* desc, Maybe<ShouldThrow> 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<Object> value = desc->has_value() ? desc->value() : Handle<Object>::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<NameDictionary> dict(proxy->property_dictionary(), isolate); PropertyDetails details(kData, DONT_ENUM, PropertyCellType::kNoCell); Handle<NameDictionary> 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<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name, PropertyDescriptor* desc) { DCHECK(!name->IsPrivate()); STACK_CHECK(isolate, Nothing<bool>()); Handle<String> trap_name = isolate->factory()->getOwnPropertyDescriptor_string(); // 1. (Assert) // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle<Object> handler(proxy->handler(), isolate); // 3. If handler is null, throw a TypeError exception. // 4. Assert: Type(handler) is Object. if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyRevoked, trap_name)); return Nothing<bool>(); } // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Nothing<bool>()); // 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<Object> trap_result_obj; Handle<Object> args[] = {target, name}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result_obj, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing<bool>()); // 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<bool>(); } // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); MAYBE_RETURN(found, Nothing<bool>()); // 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<bool>(); } // 11c. Let extensibleTarget be ? IsExtensible(target). Maybe<bool> extensible_target = JSReceiver::IsExtensible(target); MAYBE_RETURN(extensible_target, Nothing<bool>()); // 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<bool>(); } // 11f. Return undefined. return Just(false); } // 12. Let extensibleTarget be ? IsExtensible(target). Maybe<bool> extensible_target = JSReceiver::IsExtensible(target); MAYBE_RETURN(extensible_target, Nothing<bool>()); // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj). if (!PropertyDescriptor::ToPropertyDescriptor(isolate, trap_result_obj, desc)) { DCHECK(isolate->has_pending_exception()); return Nothing<bool>(); } // 14. Call CompletePropertyDescriptor(resultDesc). PropertyDescriptor::CompletePropertyDescriptor(isolate, desc); // 15. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget, // resultDesc, targetDesc). Maybe<bool> valid = IsCompatiblePropertyDescriptor( isolate, extensible_target.FromJust(), desc, &target_desc, name, Just(kDontThrow)); MAYBE_RETURN(valid, Nothing<bool>()); // 16. If valid is false, throw a TypeError exception. if (!valid.FromJust()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyGetOwnPropertyDescriptorIncompatible, name)); return Nothing<bool>(); } // 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<bool>(); } } // 18. Return resultDesc. return Just(true); } Maybe<bool> JSProxy::PreventExtensions(Handle<JSProxy> proxy, ShouldThrow should_throw) { Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing<bool>()); Factory* factory = isolate->factory(); Handle<String> trap_name = factory->preventExtensions_string(); if (proxy->IsRevoked()) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing<bool>(); } Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); if (trap->IsUndefined(isolate)) { return JSReceiver::PreventExtensions(target, should_throw); } Handle<Object> trap_result; Handle<Object> args[] = {target}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing<bool>()); if (!trap_result->BooleanValue(isolate)) { RETURN_FAILURE( isolate, should_throw, NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name)); } // Enforce the invariant. Maybe<bool> target_result = JSReceiver::IsExtensible(target); MAYBE_RETURN(target_result, Nothing<bool>()); if (target_result.FromJust()) { isolate->Throw(*factory->NewTypeError( MessageTemplate::kProxyPreventExtensionsExtensible)); return Nothing<bool>(); } return Just(true); } Maybe<bool> JSProxy::IsExtensible(Handle<JSProxy> proxy) { Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing<bool>()); Factory* factory = isolate->factory(); Handle<String> trap_name = factory->isExtensible_string(); if (proxy->IsRevoked()) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing<bool>(); } Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); if (trap->IsUndefined(isolate)) { return JSReceiver::IsExtensible(target); } Handle<Object> trap_result; Handle<Object> args[] = {target}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing<bool>()); // Enforce the invariant. Maybe<bool> target_result = JSReceiver::IsExtensible(target); MAYBE_RETURN(target_result, Nothing<bool>()); if (target_result.FromJust() != trap_result->BooleanValue(isolate)) { isolate->Throw( *factory->NewTypeError(MessageTemplate::kProxyIsExtensibleInconsistent, factory->ToBoolean(target_result.FromJust()))); return Nothing<bool>(); } return target_result; } Handle<DescriptorArray> DescriptorArray::CopyUpTo(Isolate* isolate, Handle<DescriptorArray> desc, int enumeration_index, int slack) { return DescriptorArray::CopyUpToAddAttributes(isolate, desc, enumeration_index, NONE, slack); } Handle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes( Isolate* isolate, Handle<DescriptorArray> desc, int enumeration_index, PropertyAttributes attributes, int slack) { if (enumeration_index + slack == 0) { return isolate->factory()->empty_descriptor_array(); } int size = enumeration_index; Handle<DescriptorArray> 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<PropertyAttributes>(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> DescriptorArray::CopyForFastObjectClone( Isolate* isolate, Handle<DescriptorArray> src, int enumeration_index, int slack) { if (enumeration_index + slack == 0) { return isolate->factory()->empty_descriptor_array(); } int size = enumeration_index; Handle<DescriptorArray> 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> FixedArray::SetAndGrow(Isolate* isolate, Handle<FixedArray> array, int index, Handle<Object> 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<FixedArray> 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; } bool FixedArray::ContainsSortedNumbers() { for (int i = 1; i < length(); ++i) { Object a_obj = get(i - 1); Object b_obj = get(i); if (!a_obj->IsNumber() || !b_obj->IsNumber()) return false; uint32_t a = NumberToUint32(a_obj); uint32_t b = NumberToUint32(b_obj); if (a > b) return false; } return true; } Handle<FixedArray> FixedArray::ShrinkOrEmpty(Isolate* isolate, Handle<FixedArray> 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> ArrayList::Add(Isolate* isolate, Handle<ArrayList> array, Handle<Object> 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> ArrayList::Add(Isolate* isolate, Handle<ArrayList> array, Handle<Object> obj1, Handle<Object> 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> ArrayList::New(Isolate* isolate, int size) { Handle<FixedArray> fixed_array = isolate->factory()->NewFixedArray(size + kFirstIndex); fixed_array->set_map_no_write_barrier( ReadOnlyRoots(isolate).array_list_map()); Handle<ArrayList> result = Handle<ArrayList>::cast(fixed_array); result->SetLength(0); return result; } Handle<FixedArray> ArrayList::Elements(Isolate* isolate, Handle<ArrayList> array) { int length = array->Length(); Handle<FixedArray> 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<FixedArray> EnsureSpaceInFixedArray(Isolate* isolate, Handle<FixedArray> 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> ArrayList::EnsureSpace(Isolate* isolate, Handle<ArrayList> array, int length) { const bool empty = (array->length() == 0); Handle<FixedArray> ret = EnsureSpaceInFixedArray(isolate, array, kFirstIndex + length); if (empty) { ret->set_map_no_write_barrier(array->GetReadOnlyRoots().array_list_map()); Handle<ArrayList>::cast(ret)->SetLength(0); } return Handle<ArrayList>::cast(ret); } // static Handle<WeakArrayList> WeakArrayList::AddToEnd(Isolate* isolate, Handle<WeakArrayList> 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> WeakArrayList::EnsureSpace(Isolate* isolate, Handle<WeakArrayList> 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<WeakArrayList> PrototypeUsers::Add(Isolate* isolate, Handle<WeakArrayList> array, Handle<Map> 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<WeakArrayList> 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<WeakArrayList> 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> RegExpMatchInfo::ReserveCaptures( Isolate* isolate, Handle<RegExpMatchInfo> match_info, int capture_count) { DCHECK_GE(match_info->length(), kLastMatchOverhead); const int required_length = kFirstCaptureIndex + capture_count; return Handle<RegExpMatchInfo>::cast( EnsureSpaceInFixedArray(isolate, match_info, required_length)); } // static Handle<FrameArray> FrameArray::AppendJSFrame(Handle<FrameArray> in, Handle<Object> receiver, Handle<JSFunction> function, Handle<AbstractCode> code, int offset, int flags, Handle<FixedArray> parameters) { const int frame_count = in->FrameCount(); const int new_length = LengthFor(frame_count + 1); Handle<FrameArray> 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> FrameArray::AppendWasmFrame( Handle<FrameArray> in, Handle<WasmInstanceObject> 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<FrameArray> array = EnsureSpace(isolate, in, new_length); // The {code} will be {nullptr} for interpreted wasm frames. Handle<Object> code_ref = isolate->factory()->undefined_value(); if (code) { auto native_module = wasm_instance->module_object()->shared_native_module(); code_ref = Managed<wasm::GlobalWasmCodeRef>::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> FrameArray::EnsureSpace(Isolate* isolate, Handle<FrameArray> array, int length) { return Handle<FrameArray>::cast( EnsureSpaceInFixedArray(isolate, array, length)); } Handle<DescriptorArray> 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<DescriptorArray> descriptors, Isolate* isolate, Handle<FixedArray> keys, Handle<FixedArray> 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> AccessorPair::Copy(Isolate* isolate, Handle<AccessorPair> pair) { Handle<AccessorPair> copy = isolate->factory()->NewAccessorPair(); copy->set_getter(pair->getter()); copy->set_setter(pair->setter()); return copy; } Handle<Object> AccessorPair::GetComponent(Isolate* isolate, Handle<AccessorPair> accessor_pair, AccessorComponent component) { Object accessor = accessor_pair->get(component); if (accessor->IsFunctionTemplateInfo()) { return ApiNatives::InstantiateFunction( 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_all_descriptors(); ++i) { if (get(i) != other->get(i)) return false; } return true; } #endif // static MaybeHandle<String> Name::ToFunctionName(Isolate* isolate, Handle<Name> name) { if (name->IsString()) return Handle<String>::cast(name); // ES6 section 9.2.11 SetFunctionName, step 4. Handle<Object> description(Handle<Symbol>::cast(name)->name(), isolate); if (description->IsUndefined(isolate)) { return isolate->factory()->empty_string(); } IncrementalStringBuilder builder(isolate); builder.AppendCharacter('['); builder.AppendString(Handle<String>::cast(description)); builder.AppendCharacter(']'); return builder.Finish(); } // static MaybeHandle<String> Name::ToFunctionName(Isolate* isolate, Handle<Name> name, Handle<String> prefix) { Handle<String> 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<Relocatable**>(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<Relocatable**>(from)); return from + ArchiveSpacePerThread(); } char* Relocatable::Iterate(RootVisitor* v, char* thread_storage) { Relocatable* top = *reinterpret_cast<Relocatable**>(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 <typename sinkchar> 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<int>(length), separator, SeqOneByteString::cast(dest)->GetChars(no_allocation), dest->length()); } else { DCHECK(StringShape(dest).IsSequentialTwoByte()); WriteFixedArrayToFlat(fixed_array, static_cast<int>(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; } uint32_t StringHasher::GetHashField() { if (length_ <= String::kMaxHashCalcLength) { if (is_array_index_) { return MakeArrayIndexHash(array_index_, length_); } return (GetHashCore(raw_running_hash_) << String::kHashShift) | String::kIsNotArrayIndexMask; } else { return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask; } } uint32_t StringHasher::ComputeUtf8Hash(Vector<const char> chars, uint64_t seed, int* utf16_length_out) { int vector_length = chars.length(); // Handle some edge cases if (vector_length <= 1) { DCHECK(vector_length == 0 || static_cast<uint8_t>(chars.start()[0]) <= unibrow::Utf8::kMaxOneByteChar); *utf16_length_out = vector_length; return HashSequentialString(chars.start(), vector_length, seed); } // Start with a fake length which won't affect computation. // It will be updated later. StringHasher hasher(String::kMaxArrayIndexSize, seed); DCHECK(hasher.is_array_index_); unibrow::Utf8Iterator it = unibrow::Utf8Iterator(chars); int utf16_length = 0; bool is_index = true; while (utf16_length < String::kMaxHashCalcLength && !it.Done()) { utf16_length++; uint16_t c = *it; ++it; hasher.AddCharacter(c); if (is_index) is_index = hasher.UpdateIndex(c); } // Now that hashing is done, we just need to calculate utf16_length while (!it.Done()) { ++it; utf16_length++; } *utf16_length_out = utf16_length; // Must set length here so that hash computation is correct. hasher.length_ = utf16_length; return hasher.GetHashField(); } void IteratingStringHasher::VisitConsString(ConsString cons_string) { // Run small ConsStrings through ConsStringIterator. if (cons_string->length() < 64) { ConsStringIterator iter(cons_string); int offset; for (String string = iter.Next(&offset); !string.is_null(); string = iter.Next(&offset)) { DCHECK_EQ(0, offset); String::VisitFlat(this, string, 0); } return; } // Slow case. const int max_length = String::kMaxHashCalcLength; int length = std::min(cons_string->length(), max_length); if (cons_string->IsOneByteRepresentation()) { uint8_t* buffer = new uint8_t[length]; String::WriteToFlat(cons_string, buffer, 0, length); AddCharacters(buffer, length); delete[] buffer; } else { uint16_t* buffer = new uint16_t[length]; String::WriteToFlat(cons_string, buffer, 0, length); AddCharacters(buffer, length); delete[] buffer; } } Handle<Object> CacheInitialJSArrayMaps(Handle<Context> native_context, Handle<Map> initial_map) { // Replace all of the cached initial array maps in the native context with // the appropriate transitioned elements kind maps. Handle<Map> 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<Map> new_map; ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i); Map maybe_elements_transition = current_map->ElementsTransitionMap(); if (!maybe_elements_transition.is_null()) { new_map = handle(maybe_elements_transition, native_context->GetIsolate()); } else { new_map = Map::CopyAsElementsKind(native_context->GetIsolate(), 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> oddball, const char* to_string, Handle<Object> to_number, const char* type_of, byte kind) { Handle<String> internalized_to_string = isolate->factory()->InternalizeUtf8String(to_string); Handle<String> internalized_type_of = isolate->factory()->InternalizeUtf8String(type_of); if (to_number->IsHeapNumber()) { oddball->set_to_number_raw_as_bits( Handle<HeapNumber>::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<Script> script) { DCHECK(script->compilation_type() == Script::COMPILATION_TYPE_EVAL); int position = script->eval_from_position(); if (position < 0) { // Due to laziness, the position may not have been translated from code // offset yet, which would be encoded as negative integer. In that case, // translate and set the position. if (!script->has_eval_from_shared()) { position = 0; } else { Handle<SharedFunctionInfo> shared = handle(script->eval_from_shared(), isolate); SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared); position = shared->abstract_code()->SourcePosition(-position); } DCHECK_GE(position, 0); script->set_eval_from_position(position); } return position; } void Script::InitLineEnds(Handle<Script> script) { Isolate* isolate = script->GetIsolate(); if (!script->line_ends()->IsUndefined(isolate)) return; DCHECK(script->type() != Script::TYPE_WASM || script->source_mapping_url()->IsString()); Object src_obj = script->source(); if (!src_obj->IsString()) { DCHECK(src_obj->IsUndefined(isolate)); script->set_line_ends(ReadOnlyRoots(isolate).empty_fixed_array()); } else { DCHECK(src_obj->IsString()); Handle<String> src(String::cast(src_obj), isolate); Handle<FixedArray> array = String::CalculateLineEnds(isolate, src, true); script->set_line_ends(*array); } DCHECK(script->line_ends()->IsFixedArray()); } bool Script::GetPositionInfo(Handle<Script> script, int position, PositionInfo* info, OffsetFlag offset_flag) { // For wasm, we do not create an artificial line_ends array, but do the // translation directly. if (script->type() != Script::TYPE_WASM) InitLineEnds(script); return script->GetPositionInfo(position, info, offset_flag); } bool Script::IsUserJavaScript() { return type() == Script::TYPE_NORMAL; } bool Script::ContainsAsmModule() { DisallowHeapAllocation no_gc; SharedFunctionInfo::ScriptIterator iter(this->GetIsolate(), *this); for (SharedFunctionInfo info = iter.Next(); !info.is_null(); info = iter.Next()) { if (info->HasAsmWasmData()) return true; } return false; } namespace { bool GetPositionInfoSlow(const Script script, int position, Script::PositionInfo* info) { if (!script->source()->IsString()) return false; if (position < 0) position = 0; String source_string = String::cast(script->source()); int line = 0; int line_start = 0; int len = source_string->length(); for (int pos = 0; pos <= len; ++pos) { if (pos == len || source_string->Get(pos) == '\n') { if (position <= pos) { info->line = line; info->column = position - line_start; info->line_start = line_start; info->line_end = pos; return true; } line++; line_start = pos + 1; } } return false; } } // namespace #define SMI_VALUE(x) (Smi::ToInt(x)) bool Script::GetPositionInfo(int position, PositionInfo* info, OffsetFlag offset_flag) const { DisallowHeapAllocation no_allocation; // For wasm, we do not rely on the line_ends array, but do the translation // directly. if (type() == Script::TYPE_WASM) { DCHECK_LE(0, position); return WasmModuleObject::cast(wasm_module_object()) ->GetPositionInfo(static_cast<uint32_t>(position), info); } if (line_ends()->IsUndefined()) { // Slow mode: we do not have line_ends. We have to iterate through source. if (!GetPositionInfoSlow(*this, position, info)) return false; } else { DCHECK(line_ends()->IsFixedArray()); FixedArray ends = FixedArray::cast(line_ends()); const int ends_len = ends->length(); if (ends_len == 0) return false; // Return early on invalid positions. Negative positions behave as if 0 was // passed, and positions beyond the end of the script return as failure. if (position < 0) { position = 0; } else if (position > SMI_VALUE(ends->get(ends_len - 1))) { return false; } // Determine line number by doing a binary search on the line ends array. if (SMI_VALUE(ends->get(0)) >= position) { info->line = 0; info->line_start = 0; info->column = position; } else { int left = 0; int right = ends_len - 1; while (right > 0) { DCHECK_LE(left, right); const int mid = (left + right) / 2; if (position > SMI_VALUE(ends->get(mid))) { left = mid + 1; } else if (position <= SMI_VALUE(ends->get(mid - 1))) { right = mid - 1; } else { info->line = mid; break; } } DCHECK(SMI_VALUE(ends->get(info->line)) >= position && SMI_VALUE(ends->get(info->line - 1)) < position); info->line_start = SMI_VALUE(ends->get(info->line - 1)) + 1; info->column = position - info->line_start; } // Line end is position of the linebreak character. info->line_end = SMI_VALUE(ends->get(info->line)); if (info->line_end > 0) { DCHECK(source()->IsString()); String src = String::cast(source()); if (src->length() >= info->line_end && src->Get(info->line_end - 1) == '\r') { info->line_end--; } } } // Add offsets if requested. if (offset_flag == WITH_OFFSET) { if (info->line == 0) { info->column += column_offset(); } info->line += line_offset(); } return true; } #undef SMI_VALUE int Script::GetColumnNumber(Handle<Script> script, int code_pos) { PositionInfo info; GetPositionInfo(script, code_pos, &info, WITH_OFFSET); return info.column; } int Script::GetColumnNumber(int code_pos) const { PositionInfo info; GetPositionInfo(code_pos, &info, WITH_OFFSET); return info.column; } int Script::GetLineNumber(Handle<Script> script, int code_pos) { PositionInfo info; GetPositionInfo(script, code_pos, &info, WITH_OFFSET); return info.line; } int Script::GetLineNumber(int code_pos) const { PositionInfo info; GetPositionInfo(code_pos, &info, WITH_OFFSET); return info.line; } Object Script::GetNameOrSourceURL() { // Keep in sync with ScriptNameOrSourceURL in messages.js. if (!source_url()->IsUndefined()) return source_url(); return name(); } MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo( Isolate* isolate, const FunctionLiteral* fun) { CHECK_NE(fun->function_literal_id(), kFunctionLiteralIdInvalid); // If this check fails, the problem is most probably the function id // renumbering done by AstFunctionLiteralIdReindexer; in particular, that // AstTraversalVisitor doesn't recurse properly in the construct which // triggers the mismatch. CHECK_LT(fun->function_literal_id(), shared_function_infos()->length()); MaybeObject shared = shared_function_infos()->Get(fun->function_literal_id()); HeapObject heap_object; if (!shared->GetHeapObject(&heap_object) || heap_object->IsUndefined(isolate)) { return MaybeHandle<SharedFunctionInfo>(); } return handle(SharedFunctionInfo::cast(heap_object), isolate); } std::unique_ptr<v8::tracing::TracedValue> Script::ToTracedValue() { auto value = v8::tracing::TracedValue::Create(); if (name()->IsString()) { value->SetString("name", String::cast(name())->ToCString()); } value->SetInteger("lineOffset", line_offset()); value->SetInteger("columnOffset", column_offset()); if (source_mapping_url()->IsString()) { value->SetString("sourceMappingURL", String::cast(source_mapping_url())->ToCString()); } if (source()->IsString()) { value->SetString("source", String::cast(source())->ToCString()); } return value; } // static const char* Script::kTraceScope = "v8::internal::Script"; uint64_t Script::TraceID() const { return id(); } std::unique_ptr<v8::tracing::TracedValue> Script::TraceIDRef() const { auto value = v8::tracing::TracedValue::Create(); std::ostringstream ost; ost << "0x" << std::hex << TraceID(); value->SetString("id_ref", ost.str()); value->SetString("scope", kTraceScope); return value; } Script::Iterator::Iterator(Isolate* isolate) : iterator_(isolate->heap()->script_list()) {} Script Script::Iterator::Next() { Object o = iterator_.Next(); if (o != Object()) { return Script::cast(o); } return Script(); } uint32_t SharedFunctionInfo::Hash() { // Hash SharedFunctionInfo based on its start position and script id. Note: we // don't use the function's literal id since getting that is slow for compiled // funcitons. int start_pos = StartPosition(); int script_id = script()->IsScript() ? Script::cast(script())->id() : 0; return static_cast<uint32_t>(base::hash_combine(start_pos, script_id)); } std::unique_ptr<v8::tracing::TracedValue> SharedFunctionInfo::ToTracedValue() { auto value = v8::tracing::TracedValue::Create(); if (HasSharedName()) { value->SetString("name", Name()->ToCString()); } if (HasInferredName()) { value->SetString("inferredName", inferred_name()->ToCString()); } if (is_toplevel()) { value->SetBoolean("isToplevel", true); } value->SetInteger("formalParameterCount", internal_formal_parameter_count()); value->SetString("languageMode", LanguageMode2String(language_mode())); value->SetString("kind", FunctionKind2String(kind())); if (script()->IsScript()) { value->SetValue("script", Script::cast(script())->TraceIDRef()); value->BeginDictionary("sourcePosition"); Script::PositionInfo info; if (Script::cast(script())->GetPositionInfo(StartPosition(), &info, Script::WITH_OFFSET)) { value->SetInteger("line", info.line + 1); value->SetInteger("column", info.column + 1); } value->EndDictionary(); } return value; } // static const char* SharedFunctionInfo::kTraceScope = "v8::internal::SharedFunctionInfo"; uint64_t SharedFunctionInfo::TraceID() const { // TODO(bmeurer): We use a combination of Script ID and function literal // ID (within the Script) to uniquely identify SharedFunctionInfos. This // can add significant overhead, and we should probably find a better way // to uniquely identify SharedFunctionInfos over time. Script script = Script::cast(this->script()); WeakFixedArray script_functions = script->shared_function_infos(); for (int i = 0; i < script_functions->length(); ++i) { HeapObject script_function; if (script_functions->Get(i).GetHeapObjectIfWeak(&script_function) && script_function->address() == address()) { return (static_cast<uint64_t>(script->id() + 1) << 32) | (static_cast<uint64_t>(i)); } } UNREACHABLE(); } std::unique_ptr<v8::tracing::TracedValue> SharedFunctionInfo::TraceIDRef() const { auto value = v8::tracing::TracedValue::Create(); std::ostringstream ost; ost << "0x" << std::hex << TraceID(); value->SetString("id_ref", ost.str()); value->SetString("scope", kTraceScope); return value; } Code SharedFunctionInfo::GetCode() const { // ====== // NOTE: This chain of checks MUST be kept in sync with the equivalent CSA // GetSharedFunctionInfoCode method in code-stub-assembler.cc. // ====== Isolate* isolate = GetIsolate(); Object data = function_data(); if (data->IsSmi()) { // Holding a Smi means we are a builtin. DCHECK(HasBuiltinId()); return isolate->builtins()->builtin(builtin_id()); } else if (data->IsBytecodeArray()) { // Having a bytecode array means we are a compiled, interpreted function. DCHECK(HasBytecodeArray()); return isolate->builtins()->builtin(Builtins::kInterpreterEntryTrampoline); } else if (data->IsAsmWasmData()) { // Having AsmWasmData means we are an asm.js/wasm function. DCHECK(HasAsmWasmData()); return isolate->builtins()->builtin(Builtins::kInstantiateAsmJs); } else if (data->IsUncompiledData()) { // Having uncompiled data (with or without scope) means we need to compile. DCHECK(HasUncompiledData()); return isolate->builtins()->builtin(Builtins::kCompileLazy); } else if (data->IsFunctionTemplateInfo()) { // Having a function template info means we are an API function. DCHECK(IsApiFunction()); return isolate->builtins()->builtin(Builtins::kHandleApiCall); } else if (data->IsWasmExportedFunctionData()) { // Having a WasmExportedFunctionData means the code is in there. DCHECK(HasWasmExportedFunctionData()); return wasm_exported_function_data()->wrapper_code(); } else if (data->IsInterpreterData()) { Code code = InterpreterTrampoline(); DCHECK(code->IsCode()); DCHECK(code->is_interpreter_trampoline_builtin()); return code; } UNREACHABLE(); } WasmExportedFunctionData SharedFunctionInfo::wasm_exported_function_data() const { DCHECK(HasWasmExportedFunctionData()); return WasmExportedFunctionData::cast(function_data()); } SharedFunctionInfo::ScriptIterator::ScriptIterator(Isolate* isolate, Script script) : ScriptIterator(isolate, handle(script->shared_function_infos(), isolate)) {} SharedFunctionInfo::ScriptIterator::ScriptIterator( Isolate* isolate, Handle<WeakFixedArray> shared_function_infos) : isolate_(isolate), shared_function_infos_(shared_function_infos), index_(0) {} SharedFunctionInfo SharedFunctionInfo::ScriptIterator::Next() { while (index_ < shared_function_infos_->length()) { MaybeObject raw = shared_function_infos_->Get(index_++); HeapObject heap_object; if (!raw->GetHeapObject(&heap_object) || heap_object->IsUndefined(isolate_)) { continue; } return SharedFunctionInfo::cast(heap_object); } return SharedFunctionInfo(); } void SharedFunctionInfo::ScriptIterator::Reset(Script script) { shared_function_infos_ = handle(script->shared_function_infos(), isolate_); index_ = 0; } SharedFunctionInfo::GlobalIterator::GlobalIterator(Isolate* isolate) : script_iterator_(isolate), noscript_sfi_iterator_(isolate->heap()->noscript_shared_function_infos()), sfi_iterator_(isolate, script_iterator_.Next()) {} SharedFunctionInfo SharedFunctionInfo::GlobalIterator::Next() { HeapObject next = noscript_sfi_iterator_.Next(); if (!next.is_null()) return SharedFunctionInfo::cast(next); for (;;) { next = sfi_iterator_.Next(); if (!next.is_null()) return SharedFunctionInfo::cast(next); Script next_script = script_iterator_.Next(); if (next_script.is_null()) return SharedFunctionInfo(); sfi_iterator_.Reset(next_script); } } void SharedFunctionInfo::SetScript(Handle<SharedFunctionInfo> shared, Handle<Object> script_object, int function_literal_id, bool reset_preparsed_scope_data) { if (shared->script() == *script_object) return; Isolate* isolate = shared->GetIsolate(); if (reset_preparsed_scope_data && shared->HasUncompiledDataWithPreparseData()) { shared->ClearPreparseData(); } // Add shared function info to new script's list. If a collection occurs, // the shared function info may be temporarily in two lists. // This is okay because the gc-time processing of these lists can tolerate // duplicates. if (script_object->IsScript()) { DCHECK(!shared->script()->IsScript()); Handle<Script> script = Handle<Script>::cast(script_object); Handle<WeakFixedArray> list = handle(script->shared_function_infos(), isolate); #ifdef DEBUG DCHECK_LT(function_literal_id, list->length()); MaybeObject maybe_object = list->Get(function_literal_id); HeapObject heap_object; if (maybe_object->GetHeapObjectIfWeak(&heap_object)) { DCHECK_EQ(heap_object, *shared); } #endif list->Set(function_literal_id, HeapObjectReference::Weak(*shared)); // Remove shared function info from root array. WeakArrayList noscript_list = isolate->heap()->noscript_shared_function_infos(); CHECK(noscript_list->RemoveOne(MaybeObjectHandle::Weak(shared))); } else { DCHECK(shared->script()->IsScript()); Handle<WeakArrayList> list = isolate->factory()->noscript_shared_function_infos(); #ifdef DEBUG if (FLAG_enable_slow_asserts) { WeakArrayList::Iterator iterator(*list); for (HeapObject next = iterator.Next(); !next.is_null(); next = iterator.Next()) { DCHECK_NE(next, *shared); } } #endif // DEBUG list = WeakArrayList::AddToEnd(isolate, list, MaybeObjectHandle::Weak(shared)); isolate->heap()->SetRootNoScriptSharedFunctionInfos(*list); // Remove shared function info from old script's list. Script old_script = Script::cast(shared->script()); // Due to liveedit, it might happen that the old_script doesn't know // about the SharedFunctionInfo, so we have to guard against that. Handle<WeakFixedArray> infos(old_script->shared_function_infos(), isolate); if (function_literal_id < infos->length()) { MaybeObject raw = old_script->shared_function_infos()->Get(function_literal_id); HeapObject heap_object; if (raw->GetHeapObjectIfWeak(&heap_object) && heap_object == *shared) { old_script->shared_function_infos()->Set( function_literal_id, HeapObjectReference::Strong( ReadOnlyRoots(isolate).undefined_value())); } } } // Finally set new script. shared->set_script(*script_object); } bool SharedFunctionInfo::HasBreakInfo() const { if (!HasDebugInfo()) return false; DebugInfo info = GetDebugInfo(); bool has_break_info = info->HasBreakInfo(); return has_break_info; } bool SharedFunctionInfo::BreakAtEntry() const { if (!HasDebugInfo()) return false; DebugInfo info = GetDebugInfo(); bool break_at_entry = info->BreakAtEntry(); return break_at_entry; } bool SharedFunctionInfo::HasCoverageInfo() const { if (!HasDebugInfo()) return false; DebugInfo info = GetDebugInfo(); bool has_coverage_info = info->HasCoverageInfo(); return has_coverage_info; } CoverageInfo SharedFunctionInfo::GetCoverageInfo() const { DCHECK(HasCoverageInfo()); return CoverageInfo::cast(GetDebugInfo()->coverage_info()); } String SharedFunctionInfo::DebugName() { DisallowHeapAllocation no_gc; String function_name = Name(); if (function_name->length() > 0) return function_name; return inferred_name(); } bool SharedFunctionInfo::PassesFilter(const char* raw_filter) { Vector<const char> filter = CStrVector(raw_filter); std::unique_ptr<char[]> cstrname(DebugName()->ToCString()); return v8::internal::PassesFilter(CStrVector(cstrname.get()), filter); } bool SharedFunctionInfo::HasSourceCode() const { Isolate* isolate = GetIsolate(); return !script()->IsUndefined(isolate) && !Script::cast(script())->source()->IsUndefined(isolate); } void SharedFunctionInfo::DiscardCompiledMetadata( Isolate* isolate, std::function<void(HeapObject object, ObjectSlot slot, HeapObject target)> gc_notify_updated_slot) { DisallowHeapAllocation no_gc; if (is_compiled()) { HeapObject outer_scope_info; if (scope_info()->HasOuterScopeInfo()) { outer_scope_info = scope_info()->OuterScopeInfo(); } else { outer_scope_info = ReadOnlyRoots(isolate).the_hole_value(); } // Raw setter to avoid validity checks, since we're performing the unusual // task of decompiling. set_raw_outer_scope_info_or_feedback_metadata(outer_scope_info); gc_notify_updated_slot( *this, RawField(SharedFunctionInfo::kOuterScopeInfoOrFeedbackMetadataOffset), outer_scope_info); } else { DCHECK(outer_scope_info()->IsScopeInfo() || outer_scope_info()->IsTheHole()); } // TODO(rmcilroy): Possibly discard ScopeInfo here as well. } // static void SharedFunctionInfo::DiscardCompiled( Isolate* isolate, Handle<SharedFunctionInfo> shared_info) { DCHECK(shared_info->CanDiscardCompiled()); Handle<String> inferred_name_val = handle(shared_info->inferred_name(), isolate); int start_position = shared_info->StartPosition(); int end_position = shared_info->EndPosition(); int function_literal_id = shared_info->FunctionLiteralId(isolate); shared_info->DiscardCompiledMetadata(isolate); // Replace compiled data with a new UncompiledData object. if (shared_info->HasUncompiledDataWithPreparseData()) { // If this is uncompiled data with a pre-parsed scope data, we can just // clear out the scope data and keep the uncompiled data. shared_info->ClearPreparseData(); } else { // Create a new UncompiledData, without pre-parsed scope, and update the // function data to point to it. Use the raw function data setter to avoid // validity checks, since we're performing the unusual task of decompiling. Handle<UncompiledData> data = isolate->factory()->NewUncompiledDataWithoutPreparseData( inferred_name_val, start_position, end_position, function_literal_id); shared_info->set_function_data(*data); } } // static Handle<Object> SharedFunctionInfo::GetSourceCode( Handle<SharedFunctionInfo> shared) { Isolate* isolate = shared->GetIsolate(); if (!shared->HasSourceCode()) return isolate->factory()->undefined_value(); Handle<String> source(String::cast(Script::cast(shared->script())->source()), isolate); return isolate->factory()->NewSubString(source, shared->StartPosition(), shared->EndPosition()); } // static Handle<Object> SharedFunctionInfo::GetSourceCodeHarmony( Handle<SharedFunctionInfo> shared) { Isolate* isolate = shared->GetIsolate(); if (!shared->HasSourceCode()) return isolate->factory()->undefined_value(); Handle<String> script_source( String::cast(Script::cast(shared->script())->source()), isolate); int start_pos = shared->function_token_position(); DCHECK_NE(start_pos, kNoSourcePosition); Handle<String> source = isolate->factory()->NewSubString( script_source, start_pos, shared->EndPosition()); if (!shared->is_wrapped()) return source; DCHECK(!shared->name_should_print_as_anonymous()); IncrementalStringBuilder builder(isolate); builder.AppendCString("function "); builder.AppendString(Handle<String>(shared->Name(), isolate)); builder.AppendCString("("); Handle<FixedArray> args(Script::cast(shared->script())->wrapped_arguments(), isolate); int argc = args->length(); for (int i = 0; i < argc; i++) { if (i > 0) builder.AppendCString(", "); builder.AppendString(Handle<String>(String::cast(args->get(i)), isolate)); } builder.AppendCString(") {\n"); builder.AppendString(source); builder.AppendCString("\n}"); return builder.Finish().ToHandleChecked(); } namespace { void TraceInlining(SharedFunctionInfo shared, const char* msg) { if (FLAG_trace_turbo_inlining) { StdoutStream os; os << Brief(shared) << ": IsInlineable? " << msg << "\n"; } } } // namespace bool SharedFunctionInfo::IsInlineable() { if (!script()->IsScript()) { TraceInlining(*this, "false (no Script associated with it)"); return false; } if (GetIsolate()->is_binary_code_coverage() && !has_reported_binary_coverage()) { // We may miss invocations if this function is inlined. TraceInlining(*this, "false (requires reported binary coverage)"); return false; } if (optimization_disabled()) { TraceInlining(*this, "false (optimization disabled)"); return false; } // Built-in functions are handled by the JSCallReducer. if (HasBuiltinId()) { TraceInlining(*this, "false (is a builtin)"); return false; } if (!IsUserJavaScript()) { TraceInlining(*this, "false (is not user code)"); return false; } // If there is no bytecode array, it is either not compiled or it is compiled // with WebAssembly for the asm.js pipeline. In either case we don't want to // inline. if (!HasBytecodeArray()) { TraceInlining(*this, "false (has no BytecodeArray)"); return false; } if (GetBytecodeArray()->length() > FLAG_max_inlined_bytecode_size) { TraceInlining(*this, "false (length > FLAG_max_inlined_bytecode_size)"); return false; } if (HasBreakInfo()) { TraceInlining(*this, "false (may contain break points)"); return false; } TraceInlining(*this, "true"); return true; } int SharedFunctionInfo::SourceSize() { return EndPosition() - StartPosition(); } int SharedFunctionInfo::FindIndexInScript(Isolate* isolate) const { DisallowHeapAllocation no_gc; Object script_obj = script(); if (!script_obj->IsScript()) return kFunctionLiteralIdInvalid; WeakFixedArray shared_info_list = Script::cast(script_obj)->shared_function_infos(); SharedFunctionInfo::ScriptIterator iterator( isolate, Handle<WeakFixedArray>(reinterpret_cast<Address*>(&shared_info_list))); for (SharedFunctionInfo shared = iterator.Next(); !shared.is_null(); shared = iterator.Next()) { if (shared == *this) { return iterator.CurrentIndex(); } } return kFunctionLiteralIdInvalid; } // Output the source code without any allocation in the heap. std::ostream& operator<<(std::ostream& os, const SourceCodeOf& v) { const SharedFunctionInfo s = v.value; // For some native functions there is no source. if (!s->HasSourceCode()) return os << "<No Source>"; // Get the source for the script which this function came from. // Don't use String::cast because we don't want more assertion errors while // we are already creating a stack dump. String script_source = String::unchecked_cast(Script::cast(s->script())->source()); if (!script_source->LooksValid()) return os << "<Invalid Source>"; if (!s->is_toplevel()) { os << "function "; String name = s->Name(); if (name->length() > 0) { name->PrintUC16(os); } } int len = s->EndPosition() - s->StartPosition(); if (len <= v.max_length || v.max_length < 0) { script_source->PrintUC16(os, s->StartPosition(), s->EndPosition()); return os; } else { script_source->PrintUC16(os, s->StartPosition(), s->StartPosition() + v.max_length); return os << "...\n"; } } void SharedFunctionInfo::DisableOptimization(BailoutReason reason) { DCHECK_NE(reason, BailoutReason::kNoReason); set_flags(DisabledOptimizationReasonBits::update(flags(), reason)); // Code should be the lazy compilation stub or else interpreted. DCHECK(abstract_code()->kind() == AbstractCode::INTERPRETED_FUNCTION || abstract_code()->kind() == AbstractCode::BUILTIN); PROFILE(GetIsolate(), CodeDisableOptEvent(abstract_code(), *this)); if (FLAG_trace_opt) { PrintF("[disabled optimization for "); ShortPrint(); PrintF(", reason: %s]\n", GetBailoutReason(reason)); } } void SharedFunctionInfo::InitFromFunctionLiteral( Handle<SharedFunctionInfo> shared_info, FunctionLiteral* lit, bool is_toplevel) { Isolate* isolate = shared_info->GetIsolate(); bool needs_position_info = true; // When adding fields here, make sure DeclarationScope::AnalyzePartially is // updated accordingly. shared_info->set_internal_formal_parameter_count(lit->parameter_count()); shared_info->SetFunctionTokenPosition(lit->function_token_position(), lit->start_position()); if (shared_info->scope_info()->HasPositionInfo()) { shared_info->scope_info()->SetPositionInfo(lit->start_position(), lit->end_position()); needs_position_info = false; } shared_info->set_is_declaration(lit->is_declaration()); shared_info->set_is_named_expression(lit->is_named_expression()); shared_info->set_is_anonymous_expression(lit->is_anonymous_expression()); shared_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation()); shared_info->set_language_mode(lit->language_mode()); shared_info->set_is_wrapped(lit->is_wrapped()); // shared_info->set_kind(lit->kind()); // FunctionKind must have already been set. DCHECK(lit->kind() == shared_info->kind()); shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject()); DCHECK_IMPLIES(lit->requires_instance_members_initializer(), IsClassConstructor(lit->kind())); shared_info->set_requires_instance_members_initializer( lit->requires_instance_members_initializer()); shared_info->set_is_toplevel(is_toplevel); DCHECK(shared_info->outer_scope_info()->IsTheHole()); if (!is_toplevel) { Scope* outer_scope = lit->scope()->GetOuterScopeWithContext(); if (outer_scope) { shared_info->set_outer_scope_info(*outer_scope->scope_info()); } } shared_info->set_length(lit->function_length()); // For lazy parsed functions, the following flags will be inaccurate since we // don't have the information yet. They're set later in // SetSharedFunctionFlagsFromLiteral (compiler.cc), when the function is // really parsed and compiled. if (lit->ShouldEagerCompile()) { shared_info->set_has_duplicate_parameters(lit->has_duplicate_parameters()); shared_info->UpdateAndFinalizeExpectedNofPropertiesFromEstimate(lit); shared_info->set_is_safe_to_skip_arguments_adaptor( lit->SafeToSkipArgumentsAdaptor()); DCHECK_NULL(lit->produced_preparse_data()); // If we're about to eager compile, we'll have the function literal // available, so there's no need to wastefully allocate an uncompiled data. // TODO(leszeks): This should be explicitly passed as a parameter, rather // than relying on a property of the literal. needs_position_info = false; } else { shared_info->set_is_safe_to_skip_arguments_adaptor(false); ProducedPreparseData* scope_data = lit->produced_preparse_data(); if (scope_data != nullptr) { Handle<PreparseData> preparse_data = scope_data->Serialize(shared_info->GetIsolate()); Handle<UncompiledData> data = isolate->factory()->NewUncompiledDataWithPreparseData( lit->inferred_name(), lit->start_position(), lit->end_position(), lit->function_literal_id(), preparse_data); shared_info->set_uncompiled_data(*data); needs_position_info = false; } shared_info->UpdateExpectedNofPropertiesFromEstimate(lit); } if (needs_position_info) { Handle<UncompiledData> data = isolate->factory()->NewUncompiledDataWithoutPreparseData( lit->inferred_name(), lit->start_position(), lit->end_position(), lit->function_literal_id()); shared_info->set_uncompiled_data(*data); } } uint16_t SharedFunctionInfo::get_property_estimate_from_literal( FunctionLiteral* literal) { int estimate = literal->expected_property_count(); // If this is a class constructor, we may have already parsed fields. if (is_class_constructor()) { estimate += expected_nof_properties(); } return estimate; } void SharedFunctionInfo::UpdateExpectedNofPropertiesFromEstimate( FunctionLiteral* literal) { set_expected_nof_properties(get_property_estimate_from_literal(literal)); } void SharedFunctionInfo::UpdateAndFinalizeExpectedNofPropertiesFromEstimate( FunctionLiteral* literal) { DCHECK(literal->ShouldEagerCompile()); if (are_properties_final()) { return; } int estimate = get_property_estimate_from_literal(literal); // If no properties are added in the constructor, they are more likely // to be added later. if (estimate == 0) estimate = 2; // Limit actual estimate to fit in a 8 bit field, we will never allocate // more than this in any case. STATIC_ASSERT(JSObject::kMaxInObjectProperties <= kMaxUInt8); estimate = std::min(estimate, kMaxUInt8); set_expected_nof_properties(estimate); set_are_properties_final(true); } void SharedFunctionInfo::SetFunctionTokenPosition(int function_token_position, int start_position) { int offset; if (function_token_position == kNoSourcePosition) { offset = 0; } else { offset = start_position - function_token_position; } if (offset > kMaximumFunctionTokenOffset) { offset = kFunctionTokenOutOfRange; } set_raw_function_token_offset(offset); } int SharedFunctionInfo::StartPosition() const { Object maybe_scope_info = name_or_scope_info(); if (maybe_scope_info->IsScopeInfo()) { ScopeInfo info = ScopeInfo::cast(maybe_scope_info); if (info->HasPositionInfo()) { return info->StartPosition(); } } else if (HasUncompiledData()) { // Works with or without scope. return uncompiled_data()->start_position(); } else if (IsApiFunction() || HasBuiltinId()) { DCHECK_IMPLIES(HasBuiltinId(), builtin_id() != Builtins::kCompileLazy); return 0; } return kNoSourcePosition; } int SharedFunctionInfo::EndPosition() const { Object maybe_scope_info = name_or_scope_info(); if (maybe_scope_info->IsScopeInfo()) { ScopeInfo info = ScopeInfo::cast(maybe_scope_info); if (info->HasPositionInfo()) { return info->EndPosition(); } } else if (HasUncompiledData()) { // Works with or without scope. return uncompiled_data()->end_position(); } else if (IsApiFunction() || HasBuiltinId()) { DCHECK_IMPLIES(HasBuiltinId(), builtin_id() != Builtins::kCompileLazy); return 0; } return kNoSourcePosition; } int SharedFunctionInfo::FunctionLiteralId(Isolate* isolate) const { // Fast path for the common case when the SFI is uncompiled and so the // function literal id is already in the uncompiled data. if (HasUncompiledData() && uncompiled_data()->has_function_literal_id()) { int id = uncompiled_data()->function_literal_id(); // Make sure the id is what we should have found with the slow path. DCHECK_EQ(id, FindIndexInScript(isolate)); return id; } // Otherwise, search for the function in the SFI's script's function list, // and return its index in that list. return FindIndexInScript(isolate); } void SharedFunctionInfo::SetPosition(int start_position, int end_position) { Object maybe_scope_info = name_or_scope_info(); if (maybe_scope_info->IsScopeInfo()) { ScopeInfo info = ScopeInfo::cast(maybe_scope_info); if (info->HasPositionInfo()) { info->SetPositionInfo(start_position, end_position); } } else if (HasUncompiledData()) { if (HasUncompiledDataWithPreparseData()) { // Clear out preparsed scope data, since the position setter invalidates // any scope data. ClearPreparseData(); } uncompiled_data()->set_start_position(start_position); uncompiled_data()->set_end_position(end_position); } else { UNREACHABLE(); } } // static void SharedFunctionInfo::EnsureSourcePositionsAvailable( Isolate* isolate, Handle<SharedFunctionInfo> shared_info) { if (FLAG_enable_lazy_source_positions && shared_info->HasBytecodeArray() && !shared_info->GetBytecodeArray()->HasSourcePositionTable()) { Compiler::CollectSourcePositions(isolate, shared_info); } } bool BytecodeArray::IsBytecodeEqual(const BytecodeArray other) const { if (length() != other->length()) return false; for (int i = 0; i < length(); ++i) { if (get(i) != other->get(i)) return false; } return true; } // static void JSArray::Initialize(Handle<JSArray> array, int capacity, int length) { DCHECK_GE(capacity, 0); array->GetIsolate()->factory()->NewJSArrayStorage( array, length, capacity, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); } void JSArray::SetLength(Handle<JSArray> array, uint32_t new_length) { // We should never end in here with a pixel or external array. DCHECK(array->AllowsSetLength()); if (array->SetLengthWouldNormalize(new_length)) { JSObject::NormalizeElements(array); } array->GetElementsAccessor()->SetLength(array, new_length); } // ES6: 9.5.2 [[SetPrototypeOf]] (V) // static Maybe<bool> JSProxy::SetPrototype(Handle<JSProxy> proxy, Handle<Object> value, bool from_javascript, ShouldThrow should_throw) { Isolate* isolate = proxy->GetIsolate(); STACK_CHECK(isolate, Nothing<bool>()); Handle<Name> trap_name = isolate->factory()->setPrototypeOf_string(); // 1. Assert: Either Type(V) is Object or Type(V) is Null. DCHECK(value->IsJSReceiver() || value->IsNull(isolate)); // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle<Object> handler(proxy->handler(), isolate); // 3. If handler is null, throw a TypeError exception. // 4. Assert: Type(handler) is Object. if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyRevoked, trap_name)); return Nothing<bool>(); } // 5. Let target be the value of the [[ProxyTarget]] internal slot. Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate); // 6. Let trap be ? GetMethod(handler, "getPrototypeOf"). Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Nothing<bool>()); // 7. If trap is undefined, then return target.[[SetPrototypeOf]](). if (trap->IsUndefined(isolate)) { return JSReceiver::SetPrototype(target, value, from_javascript, should_throw); } // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, V»)). Handle<Object> argv[] = {target, value}; Handle<Object> trap_result; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(argv), argv), Nothing<bool>()); bool bool_trap_result = trap_result->BooleanValue(isolate); // 9. If booleanTrapResult is false, return false. if (!bool_trap_result) { RETURN_FAILURE( isolate, should_throw, NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name)); } // 10. Let extensibleTarget be ? IsExtensible(target). Maybe<bool> is_extensible = JSReceiver::IsExtensible(target); if (is_extensible.IsNothing()) return Nothing<bool>(); // 11. If extensibleTarget is true, return true. if (is_extensible.FromJust()) { if (bool_trap_result) return Just(true); RETURN_FAILURE( isolate, should_throw, NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name)); } // 12. Let targetProto be ? target.[[GetPrototypeOf]](). Handle<Object> target_proto; ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_proto, JSReceiver::GetPrototype(isolate, target), Nothing<bool>()); // 13. If SameValue(V, targetProto) is false, throw a TypeError exception. if (bool_trap_result && !value->SameValue(*target_proto)) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxySetPrototypeOfNonExtensible)); return Nothing<bool>(); } // 14. Return true. return Just(true); } bool JSArray::SetLengthWouldNormalize(uint32_t new_length) { if (!HasFastElements()) return false; uint32_t capacity = static_cast<uint32_t>(elements()->length()); uint32_t new_capacity; return JSArray::SetLengthWouldNormalize(GetHeap(), new_length) && ShouldConvertToSlowElements(*this, capacity, new_length - 1, &new_capacity); } const double AllocationSite::kPretenureRatio = 0.85; void AllocationSite::ResetPretenureDecision() { set_pretenure_decision(kUndecided); set_memento_found_count(0); set_memento_create_count(0); } AllocationType AllocationSite::GetAllocationType() const { PretenureDecision mode = pretenure_decision(); // Zombie objects "decide" to be untenured. return mode == kTenure ? AllocationType::kOld : AllocationType::kYoung; } bool AllocationSite::IsNested() { DCHECK(FLAG_trace_track_allocation_sites); Object current = boilerplate()->GetHeap()->allocation_sites_list(); while (current->IsAllocationSite()) { AllocationSite current_site = AllocationSite::cast(current); if (current_site->nested_site() == *this) { return true; } current = current_site->weak_next(); } return false; } bool AllocationSite::ShouldTrack(ElementsKind from, ElementsKind to) { return IsSmiElementsKind(from) && IsMoreGeneralElementsKindTransition(from, to); } const char* AllocationSite::PretenureDecisionName(PretenureDecision decision) { switch (decision) { case kUndecided: return "undecided"; case kDontTenure: return "don't tenure"; case kMaybeTenure: return "maybe tenure"; case kTenure: return "tenure"; case kZombie: return "zombie"; default: UNREACHABLE(); } return nullptr; } bool JSArray::HasReadOnlyLength(Handle<JSArray> array) { Map map = array->map(); // Fast path: "length" is the first fast property of arrays. Since it's not // configurable, it's guaranteed to be the first in the descriptor array. if (!map->is_dictionary_map()) { DCHECK(map->instance_descriptors()->GetKey(0) == array->GetReadOnlyRoots().length_string()); return map->instance_descriptors()->GetDetails(0).IsReadOnly(); } Isolate* isolate = array->GetIsolate(); LookupIterator it(array, isolate->factory()->length_string(), array, LookupIterator::OWN_SKIP_INTERCEPTOR); CHECK_EQ(LookupIterator::ACCESSOR, it.state()); return it.IsReadOnly(); } bool JSArray::WouldChangeReadOnlyLength(Handle<JSArray> array, uint32_t index) { uint32_t length = 0; CHECK(array->length()->ToArrayLength(&length)); if (length <= index) return HasReadOnlyLength(array); return false; } // Certain compilers request function template instantiation when they // see the definition of the other template functions in the // class. This requires us to have the template functions put // together, so even though this function belongs in objects-debug.cc, // we keep it here instead to satisfy certain compilers. #ifdef OBJECT_PRINT template <typename Derived, typename Shape> void Dictionary<Derived, Shape>::Print(std::ostream& os) { DisallowHeapAllocation no_gc; ReadOnlyRoots roots = this->GetReadOnlyRoots(); Derived dictionary = Derived::cast(*this); int capacity = dictionary->Capacity(); for (int i = 0; i < capacity; i++) { Object k = dictionary->KeyAt(i); if (!dictionary->ToKey(roots, i, &k)) continue; os << "\n "; if (k->IsString()) { String::cast(k)->StringPrint(os); } else { os << Brief(k); } os << ": " << Brief(dictionary->ValueAt(i)) << " "; dictionary->DetailsAt(i).PrintAsSlowTo(os); } } template <typename Derived, typename Shape> void Dictionary<Derived, Shape>::Print() { StdoutStream os; Print(os); os << std::endl; } #endif int FixedArrayBase::GetMaxLengthForNewSpaceAllocation(ElementsKind kind) { return ((kMaxRegularHeapObjectSize - FixedArrayBase::kHeaderSize) >> ElementsKindToShiftSize(kind)); } bool FixedArrayBase::IsCowArray() const { return map() == GetReadOnlyRoots().fixed_cow_array_map(); } const char* Symbol::PrivateSymbolToName() const { ReadOnlyRoots roots = GetReadOnlyRoots(); #define SYMBOL_CHECK_AND_PRINT(_, name) \ if (*this == roots.name()) return #name; PRIVATE_SYMBOL_LIST_GENERATOR(SYMBOL_CHECK_AND_PRINT, /* not used */) #undef SYMBOL_CHECK_AND_PRINT return "UNKNOWN"; } void Symbol::SymbolShortPrint(std::ostream& os) { os << "<Symbol:"; if (!name()->IsUndefined()) { os << " "; HeapStringAllocator allocator; StringStream accumulator(&allocator); String::cast(name())->StringShortPrint(&accumulator, false); os << accumulator.ToCString().get(); } else { os << " (" << PrivateSymbolToName() << ")"; } os << ">"; } // StringSharedKeys are used as keys in the eval cache. class StringSharedKey : public HashTableKey { public: // This tuple unambiguously identifies calls to eval() or // CreateDynamicFunction() (such as through the Function() constructor). // * source is the string passed into eval(). For dynamic functions, this is // the effective source for the function, some of which is implicitly // generated. // * shared is the shared function info for the function containing the call // to eval(). for dynamic functions, shared is the native context closure. // * When positive, position is the position in the source where eval is // called. When negative, position is the negation of the position in the // dynamic function's effective source where the ')' ends the parameters. StringSharedKey(Handle<String> source, Handle<SharedFunctionInfo> shared, LanguageMode language_mode, int position) : HashTableKey(CompilationCacheShape::StringSharedHash( *source, *shared, language_mode, position)), source_(source), shared_(shared), language_mode_(language_mode), position_(position) {} bool IsMatch(Object other) override { DisallowHeapAllocation no_allocation; if (!other->IsFixedArray()) { DCHECK(other->IsNumber()); uint32_t other_hash = static_cast<uint32_t>(other->Number()); return Hash() == other_hash; } FixedArray other_array = FixedArray::cast(other); SharedFunctionInfo shared = SharedFunctionInfo::cast(other_array->get(0)); if (shared != *shared_) return false; int language_unchecked = Smi::ToInt(other_array->get(2)); DCHECK(is_valid_language_mode(language_unchecked)); LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked); if (language_mode != language_mode_) return false; int position = Smi::ToInt(other_array->get(3)); if (position != position_) return false; String source = String::cast(other_array->get(1)); return source->Equals(*source_); } Handle<Object> AsHandle(Isolate* isolate) { Handle<FixedArray> array = isolate->factory()->NewFixedArray(4); array->set(0, *shared_); array->set(1, *source_); array->set(2, Smi::FromEnum(language_mode_)); array->set(3, Smi::FromInt(position_)); array->set_map(ReadOnlyRoots(isolate).fixed_cow_array_map()); return array; } private: Handle<String> source_; Handle<SharedFunctionInfo> shared_; LanguageMode language_mode_; int position_; }; v8::Promise::PromiseState JSPromise::status() const { int value = flags() & kStatusMask; DCHECK(value == 0 || value == 1 || value == 2); return static_cast<v8::Promise::PromiseState>(value); } void JSPromise::set_status(Promise::PromiseState status) { int value = flags() & ~kStatusMask; set_flags(value | status); } // static const char* JSPromise::Status(v8::Promise::PromiseState status) { switch (status) { case v8::Promise::kFulfilled: return "resolved"; case v8::Promise::kPending: return "pending"; case v8::Promise::kRejected: return "rejected"; } UNREACHABLE(); } int JSPromise::async_task_id() const { return AsyncTaskIdField::decode(flags()); } void JSPromise::set_async_task_id(int id) { set_flags(AsyncTaskIdField::update(flags(), id)); } // static Handle<Object> JSPromise::Fulfill(Handle<JSPromise> promise, Handle<Object> value) { Isolate* const isolate = promise->GetIsolate(); // 1. Assert: The value of promise.[[PromiseState]] is "pending". CHECK_EQ(Promise::kPending, promise->status()); // 2. Let reactions be promise.[[PromiseFulfillReactions]]. Handle<Object> reactions(promise->reactions(), isolate); // 3. Set promise.[[PromiseResult]] to value. // 4. Set promise.[[PromiseFulfillReactions]] to undefined. // 5. Set promise.[[PromiseRejectReactions]] to undefined. promise->set_reactions_or_result(*value); // 6. Set promise.[[PromiseState]] to "fulfilled". promise->set_status(Promise::kFulfilled); // 7. Return TriggerPromiseReactions(reactions, value). return TriggerPromiseReactions(isolate, reactions, value, PromiseReaction::kFulfill); } // static Handle<Object> JSPromise::Reject(Handle<JSPromise> promise, Handle<Object> reason, bool debug_event) { Isolate* const isolate = promise->GetIsolate(); if (debug_event) isolate->debug()->OnPromiseReject(promise, reason); isolate->RunPromiseHook(PromiseHookType::kResolve, promise, isolate->factory()->undefined_value()); // 1. Assert: The value of promise.[[PromiseState]] is "pending". CHECK_EQ(Promise::kPending, promise->status()); // 2. Let reactions be promise.[[PromiseRejectReactions]]. Handle<Object> reactions(promise->reactions(), isolate); // 3. Set promise.[[PromiseResult]] to reason. // 4. Set promise.[[PromiseFulfillReactions]] to undefined. // 5. Set promise.[[PromiseRejectReactions]] to undefined. promise->set_reactions_or_result(*reason); // 6. Set promise.[[PromiseState]] to "rejected". promise->set_status(Promise::kRejected); // 7. If promise.[[PromiseIsHandled]] is false, perform // HostPromiseRejectionTracker(promise, "reject"). if (!promise->has_handler()) { isolate->ReportPromiseReject(promise, reason, kPromiseRejectWithNoHandler); } // 8. Return TriggerPromiseReactions(reactions, reason). return TriggerPromiseReactions(isolate, reactions, reason, PromiseReaction::kReject); } // static MaybeHandle<Object> JSPromise::Resolve(Handle<JSPromise> promise, Handle<Object> resolution) { Isolate* const isolate = promise->GetIsolate(); isolate->RunPromiseHook(PromiseHookType::kResolve, promise, isolate->factory()->undefined_value()); // 6. If SameValue(resolution, promise) is true, then if (promise.is_identical_to(resolution)) { // a. Let selfResolutionError be a newly created TypeError object. Handle<Object> self_resolution_error = isolate->factory()->NewTypeError( MessageTemplate::kPromiseCyclic, resolution); // b. Return RejectPromise(promise, selfResolutionError). return Reject(promise, self_resolution_error); } // 7. If Type(resolution) is not Object, then if (!resolution->IsJSReceiver()) { // a. Return FulfillPromise(promise, resolution). return Fulfill(promise, resolution); } // 8. Let then be Get(resolution, "then"). MaybeHandle<Object> then; if (isolate->IsPromiseThenLookupChainIntact( Handle<JSReceiver>::cast(resolution))) { // We can skip the "then" lookup on {resolution} if its [[Prototype]] // is the (initial) Promise.prototype and the Promise#then protector // is intact, as that guards the lookup path for the "then" property // on JSPromise instances which have the (initial) %PromisePrototype%. then = isolate->promise_then(); } else { then = JSReceiver::GetProperty(isolate, Handle<JSReceiver>::cast(resolution), isolate->factory()->then_string()); } // 9. If then is an abrupt completion, then Handle<Object> then_action; if (!then.ToHandle(&then_action)) { // a. Return RejectPromise(promise, then.[[Value]]). Handle<Object> reason(isolate->pending_exception(), isolate); isolate->clear_pending_exception(); return Reject(promise, reason, false); } // 10. Let thenAction be then.[[Value]]. // 11. If IsCallable(thenAction) is false, then if (!then_action->IsCallable()) { // a. Return FulfillPromise(promise, resolution). return Fulfill(promise, resolution); } // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, // «promise, resolution, thenAction»). Handle<PromiseResolveThenableJobTask> task = isolate->factory()->NewPromiseResolveThenableJobTask( promise, Handle<JSReceiver>::cast(then_action), Handle<JSReceiver>::cast(resolution), isolate->native_context()); if (isolate->debug()->is_active() && resolution->IsJSPromise()) { // Mark the dependency of the new {promise} on the {resolution}. Object::SetProperty(isolate, resolution, isolate->factory()->promise_handled_by_symbol(), promise) .Check(); } MicrotaskQueue* microtask_queue = isolate->native_context()->microtask_queue(); if (microtask_queue) microtask_queue->EnqueueMicrotask(*task); // 13. Return undefined. return isolate->factory()->undefined_value(); } // static Handle<Object> JSPromise::TriggerPromiseReactions(Isolate* isolate, Handle<Object> reactions, Handle<Object> argument, PromiseReaction::Type type) { CHECK(reactions->IsSmi() || reactions->IsPromiseReaction()); // We need to reverse the {reactions} here, since we record them // on the JSPromise in the reverse order. { DisallowHeapAllocation no_gc; Object current = *reactions; Object reversed = Smi::kZero; while (!current->IsSmi()) { Object next = PromiseReaction::cast(current)->next(); PromiseReaction::cast(current)->set_next(reversed); reversed = current; current = next; } reactions = handle(reversed, isolate); } // Morph the {reactions} into PromiseReactionJobTasks // and push them onto the microtask queue. while (!reactions->IsSmi()) { Handle<HeapObject> task = Handle<HeapObject>::cast(reactions); Handle<PromiseReaction> reaction = Handle<PromiseReaction>::cast(task); reactions = handle(reaction->next(), isolate); Handle<NativeContext> handler_context; Handle<HeapObject> primary_handler; Handle<HeapObject> secondary_handler; if (type == PromiseReaction::kFulfill) { primary_handler = handle(reaction->fulfill_handler(), isolate); secondary_handler = handle(reaction->reject_handler(), isolate); } else { primary_handler = handle(reaction->reject_handler(), isolate); secondary_handler = handle(reaction->fulfill_handler(), isolate); } if (primary_handler->IsJSReceiver()) { JSReceiver::GetContextForMicrotask( Handle<JSReceiver>::cast(primary_handler)) .ToHandle(&handler_context); } if (handler_context.is_null() && secondary_handler->IsJSReceiver()) { JSReceiver::GetContextForMicrotask( Handle<JSReceiver>::cast(secondary_handler)) .ToHandle(&handler_context); } if (handler_context.is_null()) handler_context = isolate->native_context(); STATIC_ASSERT(static_cast<int>(PromiseReaction::kSize) == static_cast<int>(PromiseReactionJobTask::kSize)); if (type == PromiseReaction::kFulfill) { task->synchronized_set_map( ReadOnlyRoots(isolate).promise_fulfill_reaction_job_task_map()); Handle<PromiseFulfillReactionJobTask>::cast(task)->set_argument( *argument); Handle<PromiseFulfillReactionJobTask>::cast(task)->set_context( *handler_context); STATIC_ASSERT( static_cast<int>(PromiseReaction::kFulfillHandlerOffset) == static_cast<int>(PromiseFulfillReactionJobTask::kHandlerOffset)); STATIC_ASSERT( static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) == static_cast<int>( PromiseFulfillReactionJobTask::kPromiseOrCapabilityOffset)); } else { DisallowHeapAllocation no_gc; task->synchronized_set_map( ReadOnlyRoots(isolate).promise_reject_reaction_job_task_map()); Handle<PromiseRejectReactionJobTask>::cast(task)->set_argument(*argument); Handle<PromiseRejectReactionJobTask>::cast(task)->set_context( *handler_context); Handle<PromiseRejectReactionJobTask>::cast(task)->set_handler( *primary_handler); STATIC_ASSERT( static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) == static_cast<int>( PromiseRejectReactionJobTask::kPromiseOrCapabilityOffset)); } MicrotaskQueue* microtask_queue = handler_context->microtask_queue(); if (microtask_queue) { microtask_queue->EnqueueMicrotask( *Handle<PromiseReactionJobTask>::cast(task)); } } return isolate->factory()->undefined_value(); } namespace { constexpr JSRegExp::Flag kCharFlagValues[] = { JSRegExp::kGlobal, // g JSRegExp::kInvalid, // h JSRegExp::kIgnoreCase, // i JSRegExp::kInvalid, // j JSRegExp::kInvalid, // k JSRegExp::kInvalid, // l JSRegExp::kMultiline, // m JSRegExp::kInvalid, // n JSRegExp::kInvalid, // o JSRegExp::kInvalid, // p JSRegExp::kInvalid, // q JSRegExp::kInvalid, // r JSRegExp::kDotAll, // s JSRegExp::kInvalid, // t JSRegExp::kUnicode, // u JSRegExp::kInvalid, // v JSRegExp::kInvalid, // w JSRegExp::kInvalid, // x JSRegExp::kSticky, // y }; constexpr JSRegExp::Flag CharToFlag(uc16 flag_char) { return (flag_char < 'g' || flag_char > 'y') ? JSRegExp::kInvalid : kCharFlagValues[flag_char - 'g']; } JSRegExp::Flags RegExpFlagsFromString(Isolate* isolate, Handle<String> flags, bool* success) { STATIC_ASSERT(CharToFlag('g') == JSRegExp::kGlobal); STATIC_ASSERT(CharToFlag('i') == JSRegExp::kIgnoreCase); STATIC_ASSERT(CharToFlag('m') == JSRegExp::kMultiline); STATIC_ASSERT(CharToFlag('s') == JSRegExp::kDotAll); STATIC_ASSERT(CharToFlag('u') == JSRegExp::kUnicode); STATIC_ASSERT(CharToFlag('y') == JSRegExp::kSticky); int length = flags->length(); if (length == 0) { *success = true; return JSRegExp::kNone; } // A longer flags string cannot be valid. if (length > JSRegExp::FlagCount()) return JSRegExp::Flags(0); // Initialize {value} to {kInvalid} to allow 2-in-1 duplicate/invalid check. JSRegExp::Flags value = JSRegExp::kInvalid; if (flags->IsSeqOneByteString()) { DisallowHeapAllocation no_gc; SeqOneByteString seq_flags = SeqOneByteString::cast(*flags); for (int i = 0; i < length; i++) { JSRegExp::Flag flag = CharToFlag(seq_flags.Get(i)); // Duplicate or invalid flag. if (value & flag) return JSRegExp::Flags(0); value |= flag; } } else { flags = String::Flatten(isolate, flags); DisallowHeapAllocation no_gc; String::FlatContent flags_content = flags->GetFlatContent(no_gc); for (int i = 0; i < length; i++) { JSRegExp::Flag flag = CharToFlag(flags_content.Get(i)); // Duplicate or invalid flag. if (value & flag) return JSRegExp::Flags(0); value |= flag; } } *success = true; // Drop the initially set {kInvalid} bit. value ^= JSRegExp::kInvalid; return value; } } // namespace // static MaybeHandle<JSRegExp> JSRegExp::New(Isolate* isolate, Handle<String> pattern, Flags flags) { Handle<JSFunction> constructor = isolate->regexp_function(); Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(isolate->factory()->NewJSObject(constructor)); return JSRegExp::Initialize(regexp, pattern, flags); } // static Handle<JSRegExp> JSRegExp::Copy(Handle<JSRegExp> regexp) { Isolate* const isolate = regexp->GetIsolate(); return Handle<JSRegExp>::cast(isolate->factory()->CopyJSObject(regexp)); } namespace { template <typename Char> int CountRequiredEscapes(Handle<String> source) { DisallowHeapAllocation no_gc; int escapes = 0; Vector<const Char> src = source->GetCharVector<Char>(no_gc); for (int i = 0; i < src.length(); i++) { const Char c = src[i]; if (c == '\\') { // Escape. Skip next character; i++; } else if (c == '/') { // Not escaped forward-slash needs escape. escapes++; } else if (c == '\n') { escapes++; } else if (c == '\r') { escapes++; } else if (static_cast<int>(c) == 0x2028) { escapes += std::strlen("\\u2028") - 1; } else if (static_cast<int>(c) == 0x2029) { escapes += std::strlen("\\u2029") - 1; } else { DCHECK(!unibrow::IsLineTerminator(static_cast<unibrow::uchar>(c))); } } return escapes; } template <typename Char> void WriteStringToCharVector(Vector<Char> v, int* d, const char* string) { int s = 0; while (string[s] != '\0') v[(*d)++] = string[s++]; } template <typename Char, typename StringType> Handle<StringType> WriteEscapedRegExpSource(Handle<String> source, Handle<StringType> result) { DisallowHeapAllocation no_gc; Vector<const Char> src = source->GetCharVector<Char>(no_gc); Vector<Char> dst(result->GetChars(no_gc), result->length()); int s = 0; int d = 0; // TODO(v8:1982): Fully implement // https://tc39.github.io/ecma262/#sec-escaperegexppattern while (s < src.length()) { if (src[s] == '\\') { // Escape. Copy this and next character. dst[d++] = src[s++]; if (s == src.length()) break; } else if (src[s] == '/') { // Not escaped forward-slash needs escape. dst[d++] = '\\'; } else if (src[s] == '\n') { WriteStringToCharVector(dst, &d, "\\n"); s++; continue; } else if (src[s] == '\r') { WriteStringToCharVector(dst, &d, "\\r"); s++; continue; } else if (static_cast<int>(src[s]) == 0x2028) { WriteStringToCharVector(dst, &d, "\\u2028"); s++; continue; } else if (static_cast<int>(src[s]) == 0x2029) { WriteStringToCharVector(dst, &d, "\\u2029"); s++; continue; } dst[d++] = src[s++]; } DCHECK_EQ(result->length(), d); return result; } MaybeHandle<String> EscapeRegExpSource(Isolate* isolate, Handle<String> source) { DCHECK(source->IsFlat()); if (source->length() == 0) return isolate->factory()->query_colon_string(); bool one_byte = String::IsOneByteRepresentationUnderneath(*source); int escapes = one_byte ? CountRequiredEscapes<uint8_t>(source) : CountRequiredEscapes<uc16>(source); if (escapes == 0) return source; int length = source->length() + escapes; if (one_byte) { Handle<SeqOneByteString> result; ASSIGN_RETURN_ON_EXCEPTION(isolate, result, isolate->factory()->NewRawOneByteString(length), String); return WriteEscapedRegExpSource<uint8_t>(source, result); } else { Handle<SeqTwoByteString> result; ASSIGN_RETURN_ON_EXCEPTION(isolate, result, isolate->factory()->NewRawTwoByteString(length), String); return WriteEscapedRegExpSource<uc16>(source, result); } } } // namespace // static MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp, Handle<String> source, Handle<String> flags_string) { Isolate* isolate = regexp->GetIsolate(); bool success = false; Flags flags = RegExpFlagsFromString(isolate, flags_string, &success); if (!success) { THROW_NEW_ERROR( isolate, NewSyntaxError(MessageTemplate::kInvalidRegExpFlags, flags_string), JSRegExp); } return Initialize(regexp, source, flags); } // static MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp, Handle<String> source, Flags flags) { Isolate* isolate = regexp->GetIsolate(); Factory* factory = isolate->factory(); // If source is the empty string we set it to "(?:)" instead as // suggested by ECMA-262, 5th, section 15.10.4.1. if (source->length() == 0) source = factory->query_colon_string(); source = String::Flatten(isolate, source); Handle<String> escaped_source; ASSIGN_RETURN_ON_EXCEPTION(isolate, escaped_source, EscapeRegExpSource(isolate, source), JSRegExp); RETURN_ON_EXCEPTION( isolate, RegExpImpl::Compile(isolate, regexp, source, flags), JSRegExp); regexp->set_source(*escaped_source); regexp->set_flags(Smi::FromInt(flags)); Map map = regexp->map(); Object constructor = map->GetConstructor(); if (constructor->IsJSFunction() && JSFunction::cast(constructor)->initial_map() == map) { // If we still have the original map, set in-object properties directly. regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, Smi::kZero, SKIP_WRITE_BARRIER); } else { // Map has changed, so use generic, but slower, method. RETURN_ON_EXCEPTION( isolate, Object::SetProperty(isolate, regexp, factory->lastIndex_string(), Handle<Smi>(Smi::zero(), isolate)), JSRegExp); } return regexp; } // RegExpKey carries the source and flags of a regular expression as key. class RegExpKey : public HashTableKey { public: RegExpKey(Handle<String> string, JSRegExp::Flags flags) : HashTableKey( CompilationCacheShape::RegExpHash(*string, Smi::FromInt(flags))), string_(string), flags_(Smi::FromInt(flags)) {} // Rather than storing the key in the hash table, a pointer to the // stored value is stored where the key should be. IsMatch then // compares the search key to the found object, rather than comparing // a key to a key. bool IsMatch(Object obj) override { FixedArray val = FixedArray::cast(obj); return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex))) && (flags_ == val->get(JSRegExp::kFlagsIndex)); } Handle<String> string_; Smi flags_; }; Handle<String> OneByteStringKey::AsHandle(Isolate* isolate) { return isolate->factory()->NewOneByteInternalizedString(string_, HashField()); } Handle<String> TwoByteStringKey::AsHandle(Isolate* isolate) { return isolate->factory()->NewTwoByteInternalizedString(string_, HashField()); } Handle<String> SeqOneByteSubStringKey::AsHandle(Isolate* isolate) { return isolate->factory()->NewOneByteInternalizedSubString( string_, from_, length_, HashField()); } bool SeqOneByteSubStringKey::IsMatch(Object string) { DisallowHeapAllocation no_gc; Vector<const uint8_t> chars(string_->GetChars(no_gc) + from_, length_); return String::cast(string)->IsOneByteEqualTo(chars); } // InternalizedStringKey carries a string/internalized-string object as key. class InternalizedStringKey : public StringTableKey { public: explicit InternalizedStringKey(Handle<String> string) : StringTableKey(0), string_(string) { DCHECK(!string->IsInternalizedString()); DCHECK(string->IsFlat()); // Make sure hash_field is computed. string->Hash(); set_hash_field(string->hash_field()); } bool IsMatch(Object string) override { return string_->SlowEquals(String::cast(string)); } Handle<String> AsHandle(Isolate* isolate) override { // Internalize the string if possible. MaybeHandle<Map> maybe_map = isolate->factory()->InternalizedStringMapForString(string_); Handle<Map> map; if (maybe_map.ToHandle(&map)) { string_->set_map_no_write_barrier(*map); DCHECK(string_->IsInternalizedString()); return string_; } if (FLAG_thin_strings) { // External strings get special treatment, to avoid copying their // contents. if (string_->IsExternalOneByteString()) { return isolate->factory() ->InternalizeExternalString<ExternalOneByteString>(string_); } else if (string_->IsExternalTwoByteString()) { return isolate->factory() ->InternalizeExternalString<ExternalTwoByteString>(string_); } } // Otherwise allocate a new internalized string. return isolate->factory()->NewInternalizedStringImpl( string_, string_->length(), string_->hash_field()); } private: Handle<String> string_; }; template <typename Derived, typename Shape> void HashTable<Derived, Shape>::IteratePrefix(ObjectVisitor* v) { BodyDescriptorBase::IteratePointers(*this, 0, kElementsStartOffset, v); } template <typename Derived, typename Shape> void HashTable<Derived, Shape>::IterateElements(ObjectVisitor* v) { BodyDescriptorBase::IteratePointers(*this, kElementsStartOffset, SizeFor(length()), v); } template <typename Derived, typename Shape> Handle<Derived> HashTable<Derived, Shape>::New( Isolate* isolate, int at_least_space_for, AllocationType allocation, MinimumCapacity capacity_option) { DCHECK_LE(0, at_least_space_for); DCHECK_IMPLIES(capacity_option == USE_CUSTOM_MINIMUM_CAPACITY, base::bits::IsPowerOfTwo(at_least_space_for)); int capacity = (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY) ? at_least_space_for : ComputeCapacity(at_least_space_for); if (capacity > HashTable::kMaxCapacity) { isolate->heap()->FatalProcessOutOfMemory("invalid table size"); } return NewInternal(isolate, capacity, allocation); } template <typename Derived, typename Shape> Handle<Derived> HashTable<Derived, Shape>::NewInternal( Isolate* isolate, int capacity, AllocationType allocation) { Factory* factory = isolate->factory(); int length = EntryToIndex(capacity); RootIndex map_root_index = Shape::GetMapRootIndex(); Handle<FixedArray> array = factory->NewFixedArrayWithMap(map_root_index, length, allocation); Handle<Derived> table = Handle<Derived>::cast(array); table->SetNumberOfElements(0); table->SetNumberOfDeletedElements(0); table->SetCapacity(capacity); return table; } template <typename Derived, typename Shape> void HashTable<Derived, Shape>::Rehash(ReadOnlyRoots roots, Derived new_table) { DisallowHeapAllocation no_gc; WriteBarrierMode mode = new_table->GetWriteBarrierMode(no_gc); DCHECK_LT(NumberOfElements(), new_table->Capacity()); // Copy prefix to new array. for (int i = kPrefixStartIndex; i < kElementsStartIndex; i++) { new_table->set(i, get(i), mode); } // Rehash the elements. int capacity = this->Capacity(); for (int i = 0; i < capacity; i++) { uint32_t from_index = EntryToIndex(i); Object k = this->get(from_index); if (!Shape::IsLive(roots, k)) continue; uint32_t hash = Shape::HashForObject(roots, k); uint32_t insertion_index = EntryToIndex(new_table->FindInsertionEntry(hash)); new_table->set_key(insertion_index, get(from_index), mode); for (int j = 1; j < Shape::kEntrySize; j++) { new_table->set(insertion_index + j, get(from_index + j), mode); } } new_table->SetNumberOfElements(NumberOfElements()); new_table->SetNumberOfDeletedElements(0); } template <typename Derived, typename Shape> uint32_t HashTable<Derived, Shape>::EntryForProbe(ReadOnlyRoots roots, Object k, int probe, uint32_t expected) { uint32_t hash = Shape::HashForObject(roots, k); uint32_t capacity = this->Capacity(); uint32_t entry = FirstProbe(hash, capacity); for (int i = 1; i < probe; i++) { if (entry == expected) return expected; entry = NextProbe(entry, i, capacity); } return entry; } template <typename Derived, typename Shape> void HashTable<Derived, Shape>::Swap(uint32_t entry1, uint32_t entry2, WriteBarrierMode mode) { int index1 = EntryToIndex(entry1); int index2 = EntryToIndex(entry2); Object temp[Shape::kEntrySize]; Derived* self = static_cast<Derived*>(this); for (int j = 0; j < Shape::kEntrySize; j++) { temp[j] = get(index1 + j); } self->set_key(index1, get(index2), mode); for (int j = 1; j < Shape::kEntrySize; j++) { set(index1 + j, get(index2 + j), mode); } self->set_key(index2, temp[0], mode); for (int j = 1; j < Shape::kEntrySize; j++) { set(index2 + j, temp[j], mode); } } template <typename Derived, typename Shape> void HashTable<Derived, Shape>::Rehash(ReadOnlyRoots roots) { DisallowHeapAllocation no_gc; WriteBarrierMode mode = GetWriteBarrierMode(no_gc); uint32_t capacity = Capacity(); bool done = false; for (int probe = 1; !done; probe++) { // All elements at entries given by one of the first _probe_ probes // are placed correctly. Other elements might need to be moved. done = true; for (uint32_t current = 0; current < capacity; current++) { Object current_key = KeyAt(current); if (!Shape::IsLive(roots, current_key)) continue; uint32_t target = EntryForProbe(roots, current_key, probe, current); if (current == target) continue; Object target_key = KeyAt(target); if (!Shape::IsLive(roots, target_key) || EntryForProbe(roots, target_key, probe, target) != target) { // Put the current element into the correct position. Swap(current, target, mode); // The other element will be processed on the next iteration. current--; } else { // The place for the current element is occupied. Leave the element // for the next probe. done = false; } } } // Wipe deleted entries. Object the_hole = roots.the_hole_value(); HeapObject undefined = roots.undefined_value(); Derived* self = static_cast<Derived*>(this); for (uint32_t current = 0; current < capacity; current++) { if (KeyAt(current) == the_hole) { self->set_key(EntryToIndex(current) + kEntryKeyIndex, undefined, SKIP_WRITE_BARRIER); } } SetNumberOfDeletedElements(0); } template <typename Derived, typename Shape> Handle<Derived> HashTable<Derived, Shape>::EnsureCapacity( Isolate* isolate, Handle<Derived> table, int n, AllocationType allocation) { if (table->HasSufficientCapacityToAdd(n)) return table; int capacity = table->Capacity(); int new_nof = table->NumberOfElements() + n; const int kMinCapacityForPretenure = 256; bool should_pretenure = allocation == AllocationType::kOld || ((capacity > kMinCapacityForPretenure) && !Heap::InYoungGeneration(*table)); Handle<Derived> new_table = HashTable::New( isolate, new_nof, should_pretenure ? AllocationType::kOld : AllocationType::kYoung); table->Rehash(ReadOnlyRoots(isolate), *new_table); return new_table; } template bool HashTable<NameDictionary, NameDictionaryShape>::HasSufficientCapacityToAdd(int); template <typename Derived, typename Shape> bool HashTable<Derived, Shape>::HasSufficientCapacityToAdd( int number_of_additional_elements) { int capacity = Capacity(); int nof = NumberOfElements() + number_of_additional_elements; int nod = NumberOfDeletedElements(); // Return true if: // 50% is still free after adding number_of_additional_elements elements and // at most 50% of the free elements are deleted elements. if ((nof < capacity) && ((nod <= (capacity - nof) >> 1))) { int needed_free = nof >> 1; if (nof + needed_free <= capacity) return true; } return false; } template <typename Derived, typename Shape> Handle<Derived> HashTable<Derived, Shape>::Shrink(Isolate* isolate, Handle<Derived> table, int additionalCapacity) { int capacity = table->Capacity(); int nof = table->NumberOfElements(); // Shrink to fit the number of elements if only a quarter of the // capacity is filled with elements. if (nof > (capacity >> 2)) return table; // Allocate a new dictionary with room for at least the current number of // elements + {additionalCapacity}. The allocation method will make sure that // there is extra room in the dictionary for additions. Don't go lower than // room for {kMinShrinkCapacity} elements. int at_least_room_for = nof + additionalCapacity; int new_capacity = ComputeCapacity(at_least_room_for); if (new_capacity < Derived::kMinShrinkCapacity) return table; if (new_capacity == capacity) return table; const int kMinCapacityForPretenure = 256; bool pretenure = (at_least_room_for > kMinCapacityForPretenure) && !Heap::InYoungGeneration(*table); Handle<Derived> new_table = HashTable::New(isolate, new_capacity, pretenure ? AllocationType::kOld : AllocationType::kYoung, USE_CUSTOM_MINIMUM_CAPACITY); table->Rehash(ReadOnlyRoots(isolate), *new_table); return new_table; } template <typename Derived, typename Shape> uint32_t HashTable<Derived, Shape>::FindInsertionEntry(uint32_t hash) { uint32_t capacity = Capacity(); uint32_t entry = FirstProbe(hash, capacity); uint32_t count = 1; // EnsureCapacity will guarantee the hash table is never full. ReadOnlyRoots roots = GetReadOnlyRoots(); while (true) { if (!Shape::IsLive(roots, KeyAt(entry))) break; entry = NextProbe(entry, count++, capacity); } return entry; } // This class is used for looking up two character strings in the string table. // If we don't have a hit we don't want to waste much time so we unroll the // string hash calculation loop here for speed. Doesn't work if the two // characters form a decimal integer, since such strings have a different hash // algorithm. class TwoCharHashTableKey : public StringTableKey { public: TwoCharHashTableKey(uint16_t c1, uint16_t c2, uint64_t seed) : StringTableKey(ComputeHashField(c1, c2, seed)), c1_(c1), c2_(c2) {} bool IsMatch(Object o) override { String other = String::cast(o); if (other->length() != 2) return false; if (other->Get(0) != c1_) return false; return other->Get(1) == c2_; } Handle<String> AsHandle(Isolate* isolate) override { // The TwoCharHashTableKey is only used for looking in the string // table, not for adding to it. UNREACHABLE(); } private: uint32_t ComputeHashField(uint16_t c1, uint16_t c2, uint64_t seed) { // Char 1. uint32_t hash = static_cast<uint32_t>(seed); hash += c1; hash += hash << 10; hash ^= hash >> 6; // Char 2. hash += c2; hash += hash << 10; hash ^= hash >> 6; // GetHash. hash += hash << 3; hash ^= hash >> 11; hash += hash << 15; if ((hash & String::kHashBitMask) == 0) hash = StringHasher::kZeroHash; hash = (hash << String::kHashShift) | String::kIsNotArrayIndexMask; #ifdef DEBUG // If this assert fails then we failed to reproduce the two-character // version of the string hashing algorithm above. One reason could be // that we were passed two digits as characters, since the hash // algorithm is different in that case. uint16_t chars[2] = {c1, c2}; uint32_t check_hash = StringHasher::HashSequentialString(chars, 2, seed); DCHECK_EQ(hash, check_hash); #endif return hash; } uint16_t c1_; uint16_t c2_; }; MaybeHandle<String> StringTable::LookupTwoCharsStringIfExists( Isolate* isolate, uint16_t c1, uint16_t c2) { TwoCharHashTableKey key(c1, c2, HashSeed(isolate)); Handle<StringTable> string_table = isolate->factory()->string_table(); int entry = string_table->FindEntry(isolate, &key); if (entry == kNotFound) return MaybeHandle<String>(); Handle<String> result(String::cast(string_table->KeyAt(entry)), isolate); DCHECK(StringShape(*result).IsInternalized()); DCHECK_EQ(result->Hash(), key.Hash()); return result; } void StringTable::EnsureCapacityForDeserialization(Isolate* isolate, int expected) { Handle<StringTable> table = isolate->factory()->string_table(); // We need a key instance for the virtual hash function. table = StringTable::EnsureCapacity(isolate, table, expected); isolate->heap()->SetRootStringTable(*table); } namespace { template <class StringClass> void MigrateExternalStringResource(Isolate* isolate, String from, String to) { StringClass cast_from = StringClass::cast(from); StringClass cast_to = StringClass::cast(to); const typename StringClass::Resource* to_resource = cast_to->resource(); if (to_resource == nullptr) { // |to| is a just-created internalized copy of |from|. Migrate the resource. cast_to->SetResource(isolate, cast_from->resource()); // Zap |from|'s resource pointer to reflect the fact that |from| has // relinquished ownership of its resource. isolate->heap()->UpdateExternalString( from, ExternalString::cast(from)->ExternalPayloadSize(), 0); cast_from->SetResource(isolate, nullptr); } else if (to_resource != cast_from->resource()) { // |to| already existed and has its own resource. Finalize |from|. isolate->heap()->FinalizeExternalString(from); } } void MakeStringThin(String string, String internalized, Isolate* isolate) { DCHECK_NE(string, internalized); DCHECK(internalized->IsInternalizedString()); if (string->IsExternalString()) { if (internalized->IsExternalOneByteString()) { MigrateExternalStringResource<ExternalOneByteString>(isolate, string, internalized); } else if (internalized->IsExternalTwoByteString()) { MigrateExternalStringResource<ExternalTwoByteString>(isolate, string, internalized); } else { // If the external string is duped into an existing non-external // internalized string, free its resource (it's about to be rewritten // into a ThinString below). isolate->heap()->FinalizeExternalString(string); } } DisallowHeapAllocation no_gc; int old_size = string->Size(); isolate->heap()->NotifyObjectLayoutChange(string, old_size, no_gc); bool one_byte = internalized->IsOneByteRepresentation(); Handle<Map> map = one_byte ? isolate->factory()->thin_one_byte_string_map() : isolate->factory()->thin_string_map(); DCHECK_GE(old_size, ThinString::kSize); string->synchronized_set_map(*map); ThinString thin = ThinString::cast(string); thin->set_actual(internalized); Address thin_end = thin->address() + ThinString::kSize; int size_delta = old_size - ThinString::kSize; if (size_delta != 0) { Heap* heap = isolate->heap(); heap->CreateFillerObjectAt(thin_end, size_delta, ClearRecordedSlots::kNo); } } } // namespace // static Handle<String> StringTable::LookupString(Isolate* isolate, Handle<String> string) { string = String::Flatten(isolate, string); if (string->IsInternalizedString()) return string; InternalizedStringKey key(string); Handle<String> result = LookupKey(isolate, &key); if (FLAG_thin_strings) { if (!string->IsInternalizedString()) { MakeStringThin(*string, *result, isolate); } } else { // !FLAG_thin_strings if (string->IsConsString()) { Handle<ConsString> cons = Handle<ConsString>::cast(string); cons->set_first(isolate, *result); cons->set_second(isolate, ReadOnlyRoots(isolate).empty_string()); } else if (string->IsSlicedString()) { STATIC_ASSERT(static_cast<int>(ConsString::kSize) == static_cast<int>(SlicedString::kSize)); DisallowHeapAllocation no_gc; bool one_byte = result->IsOneByteRepresentation(); Handle<Map> map = one_byte ? isolate->factory()->cons_one_byte_string_map() : isolate->factory()->cons_string_map(); string->set_map(*map); Handle<ConsString> cons = Handle<ConsString>::cast(string); cons->set_first(isolate, *result); cons->set_second(isolate, ReadOnlyRoots(isolate).empty_string()); } } return result; } // static Handle<String> StringTable::LookupKey(Isolate* isolate, StringTableKey* key) { Handle<StringTable> table = isolate->factory()->string_table(); int entry = table->FindEntry(isolate, key); // String already in table. if (entry != kNotFound) { return handle(String::cast(table->KeyAt(entry)), isolate); } table = StringTable::CautiousShrink(isolate, table); // Adding new string. Grow table if needed. table = StringTable::EnsureCapacity(isolate, table, 1); isolate->heap()->SetRootStringTable(*table); return AddKeyNoResize(isolate, key); } Handle<String> StringTable::AddKeyNoResize(Isolate* isolate, StringTableKey* key) { Handle<StringTable> table = isolate->factory()->string_table(); DCHECK(table->HasSufficientCapacityToAdd(1)); // Create string object. Handle<String> string = key->AsHandle(isolate); // There must be no attempts to internalize strings that could throw // InvalidStringLength error. CHECK(!string.is_null()); DCHECK(string->HasHashCode()); DCHECK_EQ(table->FindEntry(isolate, key), kNotFound); // Add the new string and return it along with the string table. int entry = table->FindInsertionEntry(key->Hash()); table->set(EntryToIndex(entry), *string); table->ElementAdded(); return Handle<String>::cast(string); } Handle<StringTable> StringTable::CautiousShrink(Isolate* isolate, Handle<StringTable> table) { // Only shrink if the table is very empty to avoid performance penalty. int capacity = table->Capacity(); int nof = table->NumberOfElements(); if (capacity <= StringTable::kMinCapacity) return table; if (nof > (capacity / kMaxEmptyFactor)) return table; // Keep capacity for at least half of the current nof elements. int slack_capacity = nof >> 2; return Shrink(isolate, table, slack_capacity); } namespace { class StringTableNoAllocateKey : public StringTableKey { public: StringTableNoAllocateKey(String string, uint64_t seed) : StringTableKey(0), string_(string) { StringShape shape(string); one_byte_ = shape.encoding_tag() == kOneByteStringTag; DCHECK(!shape.IsInternalized()); DCHECK(!shape.IsThin()); int length = string->length(); if (shape.IsCons() && length <= String::kMaxHashCalcLength) { special_flattening_ = true; uint32_t hash_field = 0; if (one_byte_) { if (V8_LIKELY(length <= static_cast<int>(arraysize(one_byte_buffer_)))) { one_byte_content_ = one_byte_buffer_; } else { one_byte_content_ = new uint8_t[length]; } String::WriteToFlat(string, one_byte_content_, 0, length); hash_field = StringHasher::HashSequentialString(one_byte_content_, length, seed); } else { if (V8_LIKELY(length <= static_cast<int>(arraysize(two_byte_buffer_)))) { two_byte_content_ = two_byte_buffer_; } else { two_byte_content_ = new uint16_t[length]; } String::WriteToFlat(string, two_byte_content_, 0, length); hash_field = StringHasher::HashSequentialString(two_byte_content_, length, seed); } string->set_hash_field(hash_field); } else { special_flattening_ = false; one_byte_content_ = nullptr; string->Hash(); } DCHECK(string->HasHashCode()); set_hash_field(string->hash_field()); } ~StringTableNoAllocateKey() override { if (one_byte_) { if (one_byte_content_ != one_byte_buffer_) delete[] one_byte_content_; } else { if (two_byte_content_ != two_byte_buffer_) delete[] two_byte_content_; } } bool IsMatch(Object otherstring) override { String other = String::cast(otherstring); DCHECK(other->IsInternalizedString()); DCHECK(other->IsFlat()); if (Hash() != other->Hash()) return false; int len = string_->length(); if (len != other->length()) return false; DisallowHeapAllocation no_gc; if (!special_flattening_) { if (string_->Get(0) != other->Get(0)) return false; if (string_->IsFlat()) { StringShape shape1(string_); StringShape shape2(other); if (shape1.encoding_tag() == kOneByteStringTag && shape2.encoding_tag() == kOneByteStringTag) { String::FlatContent flat1 = string_->GetFlatContent(no_gc); String::FlatContent flat2 = other->GetFlatContent(no_gc); return CompareRawStringContents(flat1.ToOneByteVector().start(), flat2.ToOneByteVector().start(), len); } if (shape1.encoding_tag() == kTwoByteStringTag && shape2.encoding_tag() == kTwoByteStringTag) { String::FlatContent flat1 = string_->GetFlatContent(no_gc); String::FlatContent flat2 = other->GetFlatContent(no_gc); return CompareRawStringContents(flat1.ToUC16Vector().start(), flat2.ToUC16Vector().start(), len); } } StringComparator comparator; return comparator.Equals(string_, other); } String::FlatContent flat_content = other->GetFlatContent(no_gc); if (one_byte_) { if (flat_content.IsOneByte()) { return CompareRawStringContents( one_byte_content_, flat_content.ToOneByteVector().start(), len); } else { DCHECK(flat_content.IsTwoByte()); for (int i = 0; i < len; i++) { if (flat_content.Get(i) != one_byte_content_[i]) return false; } return true; } } else { if (flat_content.IsTwoByte()) { return CompareRawStringContents( two_byte_content_, flat_content.ToUC16Vector().start(), len); } else { DCHECK(flat_content.IsOneByte()); for (int i = 0; i < len; i++) { if (flat_content.Get(i) != two_byte_content_[i]) return false; } return true; } } } V8_WARN_UNUSED_RESULT Handle<String> AsHandle(Isolate* isolate) override { UNREACHABLE(); } private: String string_; bool one_byte_; bool special_flattening_; union { uint8_t* one_byte_content_; uint16_t* two_byte_content_; }; union { uint8_t one_byte_buffer_[256]; uint16_t two_byte_buffer_[128]; }; }; } // namespace // static Address StringTable::LookupStringIfExists_NoAllocate(Isolate* isolate, Address raw_string) { DisallowHeapAllocation no_gc; String string = String::cast(Object(raw_string)); Heap* heap = isolate->heap(); StringTable table = heap->string_table(); StringTableNoAllocateKey key(string, HashSeed(isolate)); // String could be an array index. uint32_t hash = string->hash_field(); // Valid array indices are >= 0, so they cannot be mixed up with any of // the result sentinels, which are negative. STATIC_ASSERT( !String::ArrayIndexValueBits::is_valid(ResultSentinel::kUnsupported)); STATIC_ASSERT( !String::ArrayIndexValueBits::is_valid(ResultSentinel::kNotFound)); if (Name::ContainsCachedArrayIndex(hash)) { return Smi::FromInt(String::ArrayIndexValueBits::decode(hash)).ptr(); } if ((hash & Name::kIsNotArrayIndexMask) == 0) { // It is an indexed, but it's not cached. return Smi::FromInt(ResultSentinel::kUnsupported).ptr(); } DCHECK(!string->IsInternalizedString()); int entry = table->FindEntry(ReadOnlyRoots(isolate), &key, key.Hash()); if (entry != kNotFound) { String internalized = String::cast(table->KeyAt(entry)); if (FLAG_thin_strings) { MakeStringThin(string, internalized, isolate); } return internalized.ptr(); } // A string that's not an array index, and not in the string table, // cannot have been used as a property name before. return Smi::FromInt(ResultSentinel::kNotFound).ptr(); } String StringTable::ForwardStringIfExists(Isolate* isolate, StringTableKey* key, String string) { Handle<StringTable> table = isolate->factory()->string_table(); int entry = table->FindEntry(isolate, key); if (entry == kNotFound) return String(); String canonical = String::cast(table->KeyAt(entry)); if (canonical != string) MakeStringThin(string, canonical, isolate); return canonical; } Handle<StringSet> StringSet::New(Isolate* isolate) { return HashTable::New(isolate, 0); } Handle<StringSet> StringSet::Add(Isolate* isolate, Handle<StringSet> stringset, Handle<String> name) { if (!stringset->Has(isolate, name)) { stringset = EnsureCapacity(isolate, stringset, 1); uint32_t hash = ShapeT::Hash(isolate, *name); int entry = stringset->FindInsertionEntry(hash); stringset->set(EntryToIndex(entry), *name); stringset->ElementAdded(); } return stringset; } bool StringSet::Has(Isolate* isolate, Handle<String> name) { return FindEntry(isolate, *name) != kNotFound; } Handle<ObjectHashSet> ObjectHashSet::Add(Isolate* isolate, Handle<ObjectHashSet> set, Handle<Object> key) { int32_t hash = key->GetOrCreateHash(isolate)->value(); if (!set->Has(isolate, key, hash)) { set = EnsureCapacity(isolate, set, 1); int entry = set->FindInsertionEntry(hash); set->set(EntryToIndex(entry), *key); set->ElementAdded(); } return set; } namespace { const int kLiteralEntryLength = 2; const int kLiteralInitialLength = 2; const int kLiteralContextOffset = 0; const int kLiteralLiteralsOffset = 1; int SearchLiteralsMapEntry(CompilationCacheTable cache, int cache_entry, Context native_context) { DisallowHeapAllocation no_gc; DCHECK(native_context->IsNativeContext()); Object obj = cache->get(cache_entry); // Check that there's no confusion between FixedArray and WeakFixedArray (the // object used to be a FixedArray here). DCHECK(!obj->IsFixedArray()); if (obj->IsWeakFixedArray()) { WeakFixedArray literals_map = WeakFixedArray::cast(obj); int length = literals_map->length(); for (int i = 0; i < length; i += kLiteralEntryLength) { DCHECK(literals_map->Get(i + kLiteralContextOffset)->IsWeakOrCleared()); if (literals_map->Get(i + kLiteralContextOffset) == HeapObjectReference::Weak(native_context)) { return i; } } } return -1; } void AddToFeedbackCellsMap(Handle<CompilationCacheTable> cache, int cache_entry, Handle<Context> native_context, Handle<FeedbackCell> feedback_cell) { Isolate* isolate = native_context->GetIsolate(); DCHECK(native_context->IsNativeContext()); STATIC_ASSERT(kLiteralEntryLength == 2); Handle<WeakFixedArray> new_literals_map; int entry; Object obj = cache->get(cache_entry); // Check that there's no confusion between FixedArray and WeakFixedArray (the // object used to be a FixedArray here). DCHECK(!obj->IsFixedArray()); if (!obj->IsWeakFixedArray() || WeakFixedArray::cast(obj)->length() == 0) { new_literals_map = isolate->factory()->NewWeakFixedArray( kLiteralInitialLength, AllocationType::kOld); entry = 0; } else { Handle<WeakFixedArray> old_literals_map(WeakFixedArray::cast(obj), isolate); entry = SearchLiteralsMapEntry(*cache, cache_entry, *native_context); if (entry >= 0) { // Just set the code of the entry. old_literals_map->Set(entry + kLiteralLiteralsOffset, HeapObjectReference::Weak(*feedback_cell)); return; } // Can we reuse an entry? DCHECK_LT(entry, 0); int length = old_literals_map->length(); for (int i = 0; i < length; i += kLiteralEntryLength) { if (old_literals_map->Get(i + kLiteralContextOffset)->IsCleared()) { new_literals_map = old_literals_map; entry = i; break; } } if (entry < 0) { // Copy old optimized code map and append one new entry. new_literals_map = isolate->factory()->CopyWeakFixedArrayAndGrow( old_literals_map, kLiteralEntryLength, AllocationType::kOld); entry = old_literals_map->length(); } } new_literals_map->Set(entry + kLiteralContextOffset, HeapObjectReference::Weak(*native_context)); new_literals_map->Set(entry + kLiteralLiteralsOffset, HeapObjectReference::Weak(*feedback_cell)); #ifdef DEBUG for (int i = 0; i < new_literals_map->length(); i += kLiteralEntryLength) { MaybeObject object = new_literals_map->Get(i + kLiteralContextOffset); DCHECK(object->IsCleared() || object->GetHeapObjectAssumeWeak()->IsNativeContext()); object = new_literals_map->Get(i + kLiteralLiteralsOffset); DCHECK(object->IsCleared() || object->GetHeapObjectAssumeWeak()->IsFeedbackCell()); } #endif Object old_literals_map = cache->get(cache_entry); if (old_literals_map != *new_literals_map) { cache->set(cache_entry, *new_literals_map); } } FeedbackCell SearchLiteralsMap(CompilationCacheTable cache, int cache_entry, Context native_context) { FeedbackCell result; int entry = SearchLiteralsMapEntry(cache, cache_entry, native_context); if (entry >= 0) { WeakFixedArray literals_map = WeakFixedArray::cast(cache->get(cache_entry)); DCHECK_LE(entry + kLiteralEntryLength, literals_map->length()); MaybeObject object = literals_map->Get(entry + kLiteralLiteralsOffset); if (!object->IsCleared()) { result = FeedbackCell::cast(object->GetHeapObjectAssumeWeak()); } } DCHECK(result.is_null() || result->IsFeedbackCell()); return result; } } // namespace MaybeHandle<SharedFunctionInfo> CompilationCacheTable::LookupScript( Handle<CompilationCacheTable> table, Handle<String> src, Handle<Context> native_context, LanguageMode language_mode) { // We use the empty function SFI as part of the key. Although the // empty_function is native context dependent, the SFI is de-duped on // snapshot builds by the PartialSnapshotCache, and so this does not prevent // reuse of scripts in the compilation cache across native contexts. Handle<SharedFunctionInfo> shared(native_context->empty_function()->shared(), native_context->GetIsolate()); Isolate* isolate = native_context->GetIsolate(); src = String::Flatten(isolate, src); StringSharedKey key(src, shared, language_mode, kNoSourcePosition); int entry = table->FindEntry(isolate, &key); if (entry == kNotFound) return MaybeHandle<SharedFunctionInfo>(); int index = EntryToIndex(entry); if (!table->get(index)->IsFixedArray()) { return MaybeHandle<SharedFunctionInfo>(); } Object obj = table->get(index + 1); if (obj->IsSharedFunctionInfo()) { return handle(SharedFunctionInfo::cast(obj), native_context->GetIsolate()); } return MaybeHandle<SharedFunctionInfo>(); } InfoCellPair CompilationCacheTable::LookupEval( Handle<CompilationCacheTable> table, Handle<String> src, Handle<SharedFunctionInfo> outer_info, Handle<Context> native_context, LanguageMode language_mode, int position) { InfoCellPair empty_result; Isolate* isolate = native_context->GetIsolate(); src = String::Flatten(isolate, src); StringSharedKey key(src, outer_info, language_mode, position); int entry = table->FindEntry(isolate, &key); if (entry == kNotFound) return empty_result; int index = EntryToIndex(entry); if (!table->get(index)->IsFixedArray()) return empty_result; Object obj = table->get(EntryToIndex(entry) + 1); if (obj->IsSharedFunctionInfo()) { FeedbackCell feedback_cell = SearchLiteralsMap(*table, EntryToIndex(entry) + 2, *native_context); return InfoCellPair(SharedFunctionInfo::cast(obj), feedback_cell); } return empty_result; } Handle<Object> CompilationCacheTable::LookupRegExp(Handle<String> src, JSRegExp::Flags flags) { Isolate* isolate = GetIsolate(); DisallowHeapAllocation no_allocation; RegExpKey key(src, flags); int entry = FindEntry(isolate, &key); if (entry == kNotFound) return isolate->factory()->undefined_value(); return Handle<Object>(get(EntryToIndex(entry) + 1), isolate); } Handle<CompilationCacheTable> CompilationCacheTable::PutScript( Handle<CompilationCacheTable> cache, Handle<String> src, Handle<Context> native_context, LanguageMode language_mode, Handle<SharedFunctionInfo> value) { Isolate* isolate = native_context->GetIsolate(); // We use the empty function SFI as part of the key. Although the // empty_function is native context dependent, the SFI is de-duped on // snapshot builds by the PartialSnapshotCache, and so this does not prevent // reuse of scripts in the compilation cache across native contexts. Handle<SharedFunctionInfo> shared(native_context->empty_function()->shared(), isolate); src = String::Flatten(isolate, src); StringSharedKey key(src, shared, language_mode, kNoSourcePosition); Handle<Object> k = key.AsHandle(isolate); cache = EnsureCapacity(isolate, cache, 1); int entry = cache->FindInsertionEntry(key.Hash()); cache->set(EntryToIndex(entry), *k); cache->set(EntryToIndex(entry) + 1, *value); cache->ElementAdded(); return cache; } Handle<CompilationCacheTable> CompilationCacheTable::PutEval( Handle<CompilationCacheTable> cache, Handle<String> src, Handle<SharedFunctionInfo> outer_info, Handle<SharedFunctionInfo> value, Handle<Context> native_context, Handle<FeedbackCell> feedback_cell, int position) { Isolate* isolate = native_context->GetIsolate(); src = String::Flatten(isolate, src); StringSharedKey key(src, outer_info, value->language_mode(), position); { Handle<Object> k = key.AsHandle(isolate); int entry = cache->FindEntry(isolate, &key); if (entry != kNotFound) { cache->set(EntryToIndex(entry), *k); cache->set(EntryToIndex(entry) + 1, *value); // AddToFeedbackCellsMap may allocate a new sub-array to live in the // entry, but it won't change the cache array. Therefore EntryToIndex // and entry remains correct. AddToFeedbackCellsMap(cache, EntryToIndex(entry) + 2, native_context, feedback_cell); // Add hash again even on cache hit to avoid unnecessary cache delay in // case of hash collisions. } } cache = EnsureCapacity(isolate, cache, 1); int entry = cache->FindInsertionEntry(key.Hash()); Handle<Object> k = isolate->factory()->NewNumber(static_cast<double>(key.Hash())); cache->set(EntryToIndex(entry), *k); cache->set(EntryToIndex(entry) + 1, Smi::FromInt(kHashGenerations)); cache->ElementAdded(); return cache; } Handle<CompilationCacheTable> CompilationCacheTable::PutRegExp( Isolate* isolate, Handle<CompilationCacheTable> cache, Handle<String> src, JSRegExp::Flags flags, Handle<FixedArray> value) { RegExpKey key(src, flags); cache = EnsureCapacity(isolate, cache, 1); int entry = cache->FindInsertionEntry(key.Hash()); // We store the value in the key slot, and compare the search key // to the stored value with a custon IsMatch function during lookups. cache->set(EntryToIndex(entry), *value); cache->set(EntryToIndex(entry) + 1, *value); cache->ElementAdded(); return cache; } void CompilationCacheTable::Age() { DisallowHeapAllocation no_allocation; Object the_hole_value = GetReadOnlyRoots().the_hole_value(); for (int entry = 0, size = Capacity(); entry < size; entry++) { int entry_index = EntryToIndex(entry); int value_index = entry_index + 1; if (get(entry_index)->IsNumber()) { Smi count = Smi::cast(get(value_index)); count = Smi::FromInt(count->value() - 1); if (count->value() == 0) { NoWriteBarrierSet(*this, entry_index, the_hole_value); NoWriteBarrierSet(*this, value_index, the_hole_value); ElementRemoved(); } else { NoWriteBarrierSet(*this, value_index, count); } } else if (get(entry_index)->IsFixedArray()) { SharedFunctionInfo info = SharedFunctionInfo::cast(get(value_index)); if (info->IsInterpreted() && info->GetBytecodeArray()->IsOld()) { for (int i = 0; i < kEntrySize; i++) { NoWriteBarrierSet(*this, entry_index + i, the_hole_value); } ElementRemoved(); } } } } void CompilationCacheTable::Remove(Object value) { DisallowHeapAllocation no_allocation; Object the_hole_value = GetReadOnlyRoots().the_hole_value(); for (int entry = 0, size = Capacity(); entry < size; entry++) { int entry_index = EntryToIndex(entry); int value_index = entry_index + 1; if (get(value_index) == value) { for (int i = 0; i < kEntrySize; i++) { NoWriteBarrierSet(*this, entry_index + i, the_hole_value); } ElementRemoved(); } } return; } template <typename Derived, typename Shape> Handle<Derived> BaseNameDictionary<Derived, Shape>::New( Isolate* isolate, int at_least_space_for, AllocationType allocation, MinimumCapacity capacity_option) { DCHECK_LE(0, at_least_space_for); Handle<Derived> dict = Dictionary<Derived, Shape>::New( isolate, at_least_space_for, allocation, capacity_option); dict->SetHash(PropertyArray::kNoHashSentinel); dict->SetNextEnumerationIndex(PropertyDetails::kInitialIndex); return dict; } template <typename Derived, typename Shape> Handle<Derived> BaseNameDictionary<Derived, Shape>::EnsureCapacity( Isolate* isolate, Handle<Derived> dictionary, int n) { // Check whether there are enough enumeration indices to add n elements. if (!PropertyDetails::IsValidIndex(dictionary->NextEnumerationIndex() + n)) { // If not, we generate new indices for the properties. int length = dictionary->NumberOfElements(); Handle<FixedArray> iteration_order = IterationIndices(isolate, dictionary); DCHECK_EQ(length, iteration_order->length()); // Iterate over the dictionary using the enumeration order and update // the dictionary with new enumeration indices. for (int i = 0; i < length; i++) { int index = Smi::ToInt(iteration_order->get(i)); DCHECK(dictionary->IsKey(dictionary->GetReadOnlyRoots(), dictionary->KeyAt(index))); int enum_index = PropertyDetails::kInitialIndex + i; PropertyDetails details = dictionary->DetailsAt(index); PropertyDetails new_details = details.set_index(enum_index); dictionary->DetailsAtPut(isolate, index, new_details); } // Set the next enumeration index. dictionary->SetNextEnumerationIndex(PropertyDetails::kInitialIndex + length); } return HashTable<Derived, Shape>::EnsureCapacity(isolate, dictionary, n); } template <typename Derived, typename Shape> Handle<Derived> Dictionary<Derived, Shape>::DeleteEntry( Isolate* isolate, Handle<Derived> dictionary, int entry) { DCHECK(Shape::kEntrySize != 3 || dictionary->DetailsAt(entry).IsConfigurable()); dictionary->ClearEntry(isolate, entry); dictionary->ElementRemoved(); return Shrink(isolate, dictionary); } template <typename Derived, typename Shape> Handle<Derived> Dictionary<Derived, Shape>::AtPut(Isolate* isolate, Handle<Derived> dictionary, Key key, Handle<Object> value, PropertyDetails details) { int entry = dictionary->FindEntry(isolate, key); // If the entry is present set the value; if (entry == Dictionary::kNotFound) { return Derived::Add(isolate, dictionary, key, value, details); } // We don't need to copy over the enumeration index. dictionary->ValueAtPut(entry, *value); if (Shape::kEntrySize == 3) dictionary->DetailsAtPut(isolate, entry, details); return dictionary; } template <typename Derived, typename Shape> Handle<Derived> BaseNameDictionary<Derived, Shape>::AddNoUpdateNextEnumerationIndex( Isolate* isolate, Handle<Derived> dictionary, Key key, Handle<Object> value, PropertyDetails details, int* entry_out) { // Insert element at empty or deleted entry return Dictionary<Derived, Shape>::Add(isolate, dictionary, key, value, details, entry_out); } template <typename Derived, typename Shape> Handle<Derived> BaseNameDictionary<Derived, Shape>::Add( Isolate* isolate, Handle<Derived> dictionary, Key key, Handle<Object> value, PropertyDetails details, int* entry_out) { // Insert element at empty or deleted entry DCHECK_EQ(0, details.dictionary_index()); // Assign an enumeration index to the property and update // SetNextEnumerationIndex. int index = dictionary->NextEnumerationIndex(); details = details.set_index(index); dictionary = AddNoUpdateNextEnumerationIndex(isolate, dictionary, key, value, details, entry_out); // Update enumeration index here in order to avoid potential modification of // the canonical empty dictionary which lives in read only space. dictionary->SetNextEnumerationIndex(index + 1); return dictionary; } template <typename Derived, typename Shape> Handle<Derived> Dictionary<Derived, Shape>::Add(Isolate* isolate, Handle<Derived> dictionary, Key key, Handle<Object> value, PropertyDetails details, int* entry_out) { uint32_t hash = Shape::Hash(isolate, key); // Valdate key is absent. SLOW_DCHECK((dictionary->FindEntry(isolate, key) == Dictionary::kNotFound)); // Check whether the dictionary should be extended. dictionary = Derived::EnsureCapacity(isolate, dictionary, 1); // Compute the key object. Handle<Object> k = Shape::AsHandle(isolate, key); uint32_t entry = dictionary->FindInsertionEntry(hash); dictionary->SetEntry(isolate, entry, *k, *value, details); DCHECK(dictionary->KeyAt(entry)->IsNumber() || Shape::Unwrap(dictionary->KeyAt(entry))->IsUniqueName()); dictionary->ElementAdded(); if (entry_out) *entry_out = entry; return dictionary; } // static Handle<SimpleNumberDictionary> SimpleNumberDictionary::Set( Isolate* isolate, Handle<SimpleNumberDictionary> dictionary, uint32_t key, Handle<Object> value) { return AtPut(isolate, dictionary, key, value, PropertyDetails::Empty()); } bool NumberDictionary::HasComplexElements() { if (!requires_slow_elements()) return false; ReadOnlyRoots roots = GetReadOnlyRoots(); int capacity = this->Capacity(); for (int i = 0; i < capacity; i++) { Object k; if (!this->ToKey(roots, i, &k)) continue; PropertyDetails details = this->DetailsAt(i); if (details.kind() == kAccessor) return true; PropertyAttributes attr = details.attributes(); if (attr & ALL_ATTRIBUTES_MASK) return true; } return false; } void NumberDictionary::UpdateMaxNumberKey(uint32_t key, Handle<JSObject> dictionary_holder) { DisallowHeapAllocation no_allocation; // If the dictionary requires slow elements an element has already // been added at a high index. if (requires_slow_elements()) return; // Check if this index is high enough that we should require slow // elements. if (key > kRequiresSlowElementsLimit) { if (!dictionary_holder.is_null()) { dictionary_holder->RequireSlowElements(*this); } set_requires_slow_elements(); return; } // Update max key value. Object max_index_object = get(kMaxNumberKeyIndex); if (!max_index_object->IsSmi() || max_number_key() < key) { FixedArray::set(kMaxNumberKeyIndex, Smi::FromInt(key << kRequiresSlowElementsTagSize)); } } Handle<NumberDictionary> NumberDictionary::Set( Isolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key, Handle<Object> value, Handle<JSObject> dictionary_holder, PropertyDetails details) { dictionary->UpdateMaxNumberKey(key, dictionary_holder); return AtPut(isolate, dictionary, key, value, details); } void NumberDictionary::CopyValuesTo(FixedArray elements) { ReadOnlyRoots roots = GetReadOnlyRoots(); int pos = 0; int capacity = this->Capacity(); DisallowHeapAllocation no_gc; WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); for (int i = 0; i < capacity; i++) { Object k; if (this->ToKey(roots, i, &k)) { elements->set(pos++, this->ValueAt(i), mode); } } DCHECK_EQ(pos, elements->length()); } template <typename Derived, typename Shape> int Dictionary<Derived, Shape>::NumberOfEnumerableProperties() { ReadOnlyRoots roots = this->GetReadOnlyRoots(); int capacity = this->Capacity(); int result = 0; for (int i = 0; i < capacity; i++) { Object k; if (!this->ToKey(roots, i, &k)) continue; if (k->FilterKey(ENUMERABLE_STRINGS)) continue; PropertyDetails details = this->DetailsAt(i); PropertyAttributes attr = details.attributes(); if ((attr & ONLY_ENUMERABLE) == 0) result++; } return result; } template <typename Dictionary> struct EnumIndexComparator { explicit EnumIndexComparator(Dictionary dict) : dict(dict) {} bool operator()(Tagged_t a, Tagged_t b) { PropertyDetails da(dict->DetailsAt(Smi(static_cast<Address>(a)).value())); PropertyDetails db(dict->DetailsAt(Smi(static_cast<Address>(b)).value())); return da.dictionary_index() < db.dictionary_index(); } Dictionary dict; }; template <typename Derived, typename Shape> void BaseNameDictionary<Derived, Shape>::CopyEnumKeysTo( Isolate* isolate, Handle<Derived> dictionary, Handle<FixedArray> storage, KeyCollectionMode mode, KeyAccumulator* accumulator) { DCHECK_IMPLIES(mode != KeyCollectionMode::kOwnOnly, accumulator != nullptr); int length = storage->length(); int capacity = dictionary->Capacity(); int properties = 0; ReadOnlyRoots roots(isolate); for (int i = 0; i < capacity; i++) { Object key; if (!dictionary->ToKey(roots, i, &key)) continue; bool is_shadowing_key = false; if (key->IsSymbol()) continue; PropertyDetails details = dictionary->DetailsAt(i); if (details.IsDontEnum()) { if (mode == KeyCollectionMode::kIncludePrototypes) { is_shadowing_key = true; } else { continue; } } if (is_shadowing_key) { accumulator->AddShadowingKey(key); continue; } else { storage->set(properties, Smi::FromInt(i)); } properties++; if (mode == KeyCollectionMode::kOwnOnly && properties == length) break; } CHECK_EQ(length, properties); DisallowHeapAllocation no_gc; Derived raw_dictionary = *dictionary; FixedArray raw_storage = *storage; EnumIndexComparator<Derived> cmp(raw_dictionary); // Use AtomicSlot wrapper to ensure that std::sort uses atomic load and // store operations that are safe for concurrent marking. AtomicSlot start(storage->GetFirstElementAddress()); std::sort(start, start + length, cmp); for (int i = 0; i < length; i++) { int index = Smi::ToInt(raw_storage->get(i)); raw_storage->set(i, raw_dictionary->NameAt(index)); } } template <typename Derived, typename Shape> Handle<FixedArray> BaseNameDictionary<Derived, Shape>::IterationIndices( Isolate* isolate, Handle<Derived> dictionary) { int capacity = dictionary->Capacity(); int length = dictionary->NumberOfElements(); Handle<FixedArray> array = isolate->factory()->NewFixedArray(length); ReadOnlyRoots roots(isolate); int array_size = 0; { DisallowHeapAllocation no_gc; Derived raw_dictionary = *dictionary; for (int i = 0; i < capacity; i++) { Object k; if (!raw_dictionary->ToKey(roots, i, &k)) continue; array->set(array_size++, Smi::FromInt(i)); } DCHECK_EQ(array_size, length); EnumIndexComparator<Derived> cmp(raw_dictionary); // Use AtomicSlot wrapper to ensure that std::sort uses atomic load and // store operations that are safe for concurrent marking. AtomicSlot start(array->GetFirstElementAddress()); std::sort(start, start + array_size, cmp); } return FixedArray::ShrinkOrEmpty(isolate, array, array_size); } template <typename Derived, typename Shape> void BaseNameDictionary<Derived, Shape>::CollectKeysTo( Handle<Derived> dictionary, KeyAccumulator* keys) { Isolate* isolate = keys->isolate(); ReadOnlyRoots roots(isolate); int capacity = dictionary->Capacity(); Handle<FixedArray> array = isolate->factory()->NewFixedArray(dictionary->NumberOfElements()); int array_size = 0; PropertyFilter filter = keys->filter(); { DisallowHeapAllocation no_gc; Derived raw_dictionary = *dictionary; for (int i = 0; i < capacity; i++) { Object k; if (!raw_dictionary->ToKey(roots, i, &k)) continue; if (k->FilterKey(filter)) continue; PropertyDetails details = raw_dictionary->DetailsAt(i); if ((details.attributes() & filter) != 0) { keys->AddShadowingKey(k); continue; } if (filter & ONLY_ALL_CAN_READ) { if (details.kind() != kAccessor) continue; Object accessors = raw_dictionary->ValueAt(i); if (!accessors->IsAccessorInfo()) continue; if (!AccessorInfo::cast(accessors)->all_can_read()) continue; } array->set(array_size++, Smi::FromInt(i)); } EnumIndexComparator<Derived> cmp(raw_dictionary); // Use AtomicSlot wrapper to ensure that std::sort uses atomic load and // store operations that are safe for concurrent marking. AtomicSlot start(array->GetFirstElementAddress()); std::sort(start, start + array_size, cmp); } bool has_seen_symbol = false; for (int i = 0; i < array_size; i++) { int index = Smi::ToInt(array->get(i)); Object key = dictionary->NameAt(index); if (key->IsSymbol()) { has_seen_symbol = true; continue; } keys->AddKey(key, DO_NOT_CONVERT); } if (has_seen_symbol) { for (int i = 0; i < array_size; i++) { int index = Smi::ToInt(array->get(i)); Object key = dictionary->NameAt(index); if (!key->IsSymbol()) continue; keys->AddKey(key, DO_NOT_CONVERT); } } } // Backwards lookup (slow). template <typename Derived, typename Shape> Object Dictionary<Derived, Shape>::SlowReverseLookup(Object value) { Derived dictionary = Derived::cast(*this); ReadOnlyRoots roots = dictionary->GetReadOnlyRoots(); int capacity = dictionary->Capacity(); for (int i = 0; i < capacity; i++) { Object k; if (!dictionary->ToKey(roots, i, &k)) continue; Object e = dictionary->ValueAt(i); if (e == value) return k; } return roots.undefined_value(); } template <typename Derived, typename Shape> void ObjectHashTableBase<Derived, Shape>::FillEntriesWithHoles( Handle<Derived> table) { int length = table->length(); for (int i = Derived::EntryToIndex(0); i < length; i++) { table->set_the_hole(i); } } template <typename Derived, typename Shape> Object ObjectHashTableBase<Derived, Shape>::Lookup(ReadOnlyRoots roots, Handle<Object> key, int32_t hash) { DisallowHeapAllocation no_gc; DCHECK(this->IsKey(roots, *key)); int entry = this->FindEntry(roots, key, hash); if (entry == kNotFound) return roots.the_hole_value(); return this->get(Derived::EntryToIndex(entry) + 1); } template <typename Derived, typename Shape> Object ObjectHashTableBase<Derived, Shape>::Lookup(Handle<Object> key) { DisallowHeapAllocation no_gc; ReadOnlyRoots roots = this->GetReadOnlyRoots(); DCHECK(this->IsKey(roots, *key)); // If the object does not have an identity hash, it was never used as a key. Object hash = key->GetHash(); if (hash->IsUndefined(roots)) { return roots.the_hole_value(); } return Lookup(roots, key, Smi::ToInt(hash)); } template <typename Derived, typename Shape> Object ObjectHashTableBase<Derived, Shape>::Lookup(Handle<Object> key, int32_t hash) { return Lookup(this->GetReadOnlyRoots(), key, hash); } template <typename Derived, typename Shape> Object ObjectHashTableBase<Derived, Shape>::ValueAt(int entry) { return this->get(EntryToValueIndex(entry)); } template <typename Derived, typename Shape> Handle<Derived> ObjectHashTableBase<Derived, Shape>::Put(Handle<Derived> table, Handle<Object> key, Handle<Object> value) { Isolate* isolate = Heap::FromWritableHeapObject(*table)->isolate(); DCHECK(table->IsKey(ReadOnlyRoots(isolate), *key)); DCHECK(!value->IsTheHole(ReadOnlyRoots(isolate))); // Make sure the key object has an identity hash code. int32_t hash = key->GetOrCreateHash(isolate)->value(); return ObjectHashTableBase<Derived, Shape>::Put(isolate, table, key, value, hash); } template <typename Derived, typename Shape> Handle<Derived> ObjectHashTableBase<Derived, Shape>::Put(Isolate* isolate, Handle<Derived> table, Handle<Object> key, Handle<Object> value, int32_t hash) { ReadOnlyRoots roots(isolate); DCHECK(table->IsKey(roots, *key)); DCHECK(!value->IsTheHole(roots)); int entry = table->FindEntry(roots, key, hash); // Key is already in table, just overwrite value. if (entry != kNotFound) { table->set(Derived::EntryToValueIndex(entry), *value); return table; } // Rehash if more than 33% of the entries are deleted entries. // TODO(jochen): Consider to shrink the fixed array in place. if ((table->NumberOfDeletedElements() << 1) > table->NumberOfElements()) { table->Rehash(roots); } // If we're out of luck, we didn't get a GC recently, and so rehashing // isn't enough to avoid a crash. if (!table->HasSufficientCapacityToAdd(1)) { int nof = table->NumberOfElements() + 1; int capacity = ObjectHashTable::ComputeCapacity(nof * 2); if (capacity > ObjectHashTable::kMaxCapacity) { for (size_t i = 0; i < 2; ++i) { isolate->heap()->CollectAllGarbage( Heap::kNoGCFlags, GarbageCollectionReason::kFullHashtable); } table->Rehash(roots); } } // Check whether the hash table should be extended. table = Derived::EnsureCapacity(isolate, table, 1); table->AddEntry(table->FindInsertionEntry(hash), *key, *value); return table; } template <typename Derived, typename Shape> Handle<Derived> ObjectHashTableBase<Derived, Shape>::Remove( Isolate* isolate, Handle<Derived> table, Handle<Object> key, bool* was_present) { DCHECK(table->IsKey(table->GetReadOnlyRoots(), *key)); Object hash = key->GetHash(); if (hash->IsUndefined()) { *was_present = false; return table; } return Remove(isolate, table, key, was_present, Smi::ToInt(hash)); } template <typename Derived, typename Shape> Handle<Derived> ObjectHashTableBase<Derived, Shape>::Remove( Isolate* isolate, Handle<Derived> table, Handle<Object> key, bool* was_present, int32_t hash) { ReadOnlyRoots roots = table->GetReadOnlyRoots(); DCHECK(table->IsKey(roots, *key)); int entry = table->FindEntry(roots, key, hash); if (entry == kNotFound) { *was_present = false; return table; } *was_present = true; table->RemoveEntry(entry); return Derived::Shrink(isolate, table); } template <typename Derived, typename Shape> void ObjectHashTableBase<Derived, Shape>::AddEntry(int entry, Object key, Object value) { Derived* self = static_cast<Derived*>(this); self->set_key(Derived::EntryToIndex(entry), key); self->set(Derived::EntryToValueIndex(entry), value); self->ElementAdded(); } template <typename Derived, typename Shape> void ObjectHashTableBase<Derived, Shape>::RemoveEntry(int entry) { this->set_the_hole(Derived::EntryToIndex(entry)); this->set_the_hole(Derived::EntryToValueIndex(entry)); this->ElementRemoved(); } void JSSet::Initialize(Handle<JSSet> set, Isolate* isolate) { Handle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet(); set->set_table(*table); } void JSSet::Clear(Isolate* isolate, Handle<JSSet> set) { Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()), isolate); table = OrderedHashSet::Clear(isolate, table); set->set_table(*table); } void JSMap::Initialize(Handle<JSMap> map, Isolate* isolate) { Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap(); map->set_table(*table); } void JSMap::Clear(Isolate* isolate, Handle<JSMap> map) { Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()), isolate); table = OrderedHashMap::Clear(isolate, table); map->set_table(*table); } void JSWeakCollection::Initialize(Handle<JSWeakCollection> weak_collection, Isolate* isolate) { Handle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0); weak_collection->set_table(*table); } void JSWeakCollection::Set(Handle<JSWeakCollection> weak_collection, Handle<Object> key, Handle<Object> value, int32_t hash) { DCHECK(key->IsJSReceiver() || key->IsSymbol()); Handle<EphemeronHashTable> table( EphemeronHashTable::cast(weak_collection->table()), weak_collection->GetIsolate()); DCHECK(table->IsKey(weak_collection->GetReadOnlyRoots(), *key)); Handle<EphemeronHashTable> new_table = EphemeronHashTable::Put( weak_collection->GetIsolate(), table, key, value, hash); weak_collection->set_table(*new_table); if (*table != *new_table) { // Zap the old table since we didn't record slots for its elements. EphemeronHashTable::FillEntriesWithHoles(table); } } bool JSWeakCollection::Delete(Handle<JSWeakCollection> weak_collection, Handle<Object> key, int32_t hash) { DCHECK(key->IsJSReceiver() || key->IsSymbol()); Handle<EphemeronHashTable> table( EphemeronHashTable::cast(weak_collection->table()), weak_collection->GetIsolate()); DCHECK(table->IsKey(weak_collection->GetReadOnlyRoots(), *key)); bool was_present = false; Handle<EphemeronHashTable> new_table = EphemeronHashTable::Remove( weak_collection->GetIsolate(), table, key, &was_present, hash); weak_collection->set_table(*new_table); if (*table != *new_table) { // Zap the old table since we didn't record slots for its elements. EphemeronHashTable::FillEntriesWithHoles(table); } return was_present; } Handle<JSArray> JSWeakCollection::GetEntries(Handle<JSWeakCollection> holder, int max_entries) { Isolate* isolate = holder->GetIsolate(); Handle<EphemeronHashTable> table(EphemeronHashTable::cast(holder->table()), isolate); if (max_entries == 0 || max_entries > table->NumberOfElements()) { max_entries = table->NumberOfElements(); } int values_per_entry = holder->IsJSWeakMap() ? 2 : 1; Handle<FixedArray> entries = isolate->factory()->NewFixedArray(max_entries * values_per_entry); // Recompute max_values because GC could have removed elements from the table. if (max_entries > table->NumberOfElements()) { max_entries = table->NumberOfElements(); } { DisallowHeapAllocation no_gc; ReadOnlyRoots roots = ReadOnlyRoots(isolate); int count = 0; for (int i = 0; count / values_per_entry < max_entries && i < table->Capacity(); i++) { Object key; if (table->ToKey(roots, i, &key)) { entries->set(count++, key); if (values_per_entry > 1) { Object value = table->Lookup(handle(key, isolate)); entries->set(count++, value); } } } DCHECK_EQ(max_entries * values_per_entry, count); } return isolate->factory()->NewJSArrayWithElements(entries); } Handle<PropertyCell> PropertyCell::InvalidateEntry( Isolate* isolate, Handle<GlobalDictionary> dictionary, int entry) { // Swap with a copy. Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate); Handle<Name> name(cell->name(), isolate); Handle<PropertyCell> new_cell = isolate->factory()->NewPropertyCell(name); new_cell->set_value(cell->value()); dictionary->ValueAtPut(entry, *new_cell); bool is_the_hole = cell->value()->IsTheHole(isolate); // Cell is officially mutable henceforth. PropertyDetails details = cell->property_details(); details = details.set_cell_type(is_the_hole ? PropertyCellType::kUninitialized : PropertyCellType::kMutable); new_cell->set_property_details(details); // Old cell is ready for invalidation. if (is_the_hole) { cell->set_value(ReadOnlyRoots(isolate).undefined_value()); } else { cell->set_value(ReadOnlyRoots(isolate).the_hole_value()); } details = details.set_cell_type(PropertyCellType::kInvalidated); cell->set_property_details(details); cell->dependent_code()->DeoptimizeDependentCodeGroup( isolate, DependentCode::kPropertyCellChangedGroup); return new_cell; } PropertyCellConstantType PropertyCell::GetConstantType() { if (value()->IsSmi()) return PropertyCellConstantType::kSmi; return PropertyCellConstantType::kStableMap; } static bool RemainsConstantType(Handle<PropertyCell> cell, Handle<Object> value) { // TODO(dcarney): double->smi and smi->double transition from kConstant if (cell->value()->IsSmi() && value->IsSmi()) { return true; } else if (cell->value()->IsHeapObject() && value->IsHeapObject()) { return HeapObject::cast(cell->value())->map() == HeapObject::cast(*value)->map() && HeapObject::cast(*value)->map()->is_stable(); } return false; } PropertyCellType PropertyCell::UpdatedType(Isolate* isolate, Handle<PropertyCell> cell, Handle<Object> value, PropertyDetails details) { PropertyCellType type = details.cell_type(); DCHECK(!value->IsTheHole(isolate)); if (cell->value()->IsTheHole(isolate)) { switch (type) { // Only allow a cell to transition once into constant state. case PropertyCellType::kUninitialized: if (value->IsUndefined(isolate)) return PropertyCellType::kUndefined; return PropertyCellType::kConstant; case PropertyCellType::kInvalidated: return PropertyCellType::kMutable; default: UNREACHABLE(); } } switch (type) { case PropertyCellType::kUndefined: return PropertyCellType::kConstant; case PropertyCellType::kConstant: if (*value == cell->value()) return PropertyCellType::kConstant; V8_FALLTHROUGH; case PropertyCellType::kConstantType: if (RemainsConstantType(cell, value)) { return PropertyCellType::kConstantType; } V8_FALLTHROUGH; case PropertyCellType::kMutable: return PropertyCellType::kMutable; } UNREACHABLE(); } Handle<PropertyCell> PropertyCell::PrepareForValue( Isolate* isolate, Handle<GlobalDictionary> dictionary, int entry, Handle<Object> value, PropertyDetails details) { DCHECK(!value->IsTheHole(isolate)); Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate); const PropertyDetails original_details = cell->property_details(); // Data accesses could be cached in ics or optimized code. bool invalidate = (original_details.kind() == kData && details.kind() == kAccessor) || (!original_details.IsReadOnly() && details.IsReadOnly()); int index; PropertyCellType old_type = original_details.cell_type(); // Preserve the enumeration index unless the property was deleted or never // initialized. if (cell->value()->IsTheHole(isolate)) { index = dictionary->NextEnumerationIndex(); dictionary->SetNextEnumerationIndex(index + 1); } else { index = original_details.dictionary_index(); } DCHECK_LT(0, index); details = details.set_index(index); PropertyCellType new_type = UpdatedType(isolate, cell, value, original_details); if (invalidate) { cell = PropertyCell::InvalidateEntry(isolate, dictionary, entry); } // Install new property details. details = details.set_cell_type(new_type); cell->set_property_details(details); if (new_type == PropertyCellType::kConstant || new_type == PropertyCellType::kConstantType) { // Store the value now to ensure that the cell contains the constant or // type information. Otherwise subsequent store operation will turn // the cell to mutable. cell->set_value(*value); } // Deopt when transitioning from a constant type. if (!invalidate && (old_type != new_type || original_details.IsReadOnly() != details.IsReadOnly())) { cell->dependent_code()->DeoptimizeDependentCodeGroup( isolate, DependentCode::kPropertyCellChangedGroup); } return cell; } // static void PropertyCell::SetValueWithInvalidation(Isolate* isolate, Handle<PropertyCell> cell, Handle<Object> new_value) { if (cell->value() != *new_value) { cell->set_value(*new_value); cell->dependent_code()->DeoptimizeDependentCodeGroup( isolate, DependentCode::kPropertyCellChangedGroup); } } int JSGeneratorObject::source_position() const { CHECK(is_suspended()); DCHECK(function()->shared()->HasBytecodeArray()); DCHECK(function()->shared()->GetBytecodeArray()->HasSourcePositionTable()); int code_offset = Smi::ToInt(input_or_debug_pos()); // The stored bytecode offset is relative to a different base than what // is used in the source position table, hence the subtraction. code_offset -= BytecodeArray::kHeaderSize - kHeapObjectTag; AbstractCode code = AbstractCode::cast(function()->shared()->GetBytecodeArray()); return code->SourcePosition(code_offset); } // static AccessCheckInfo AccessCheckInfo::Get(Isolate* isolate, Handle<JSObject> receiver) { DisallowHeapAllocation no_gc; DCHECK(receiver->map()->is_access_check_needed()); Object maybe_constructor = receiver->map()->GetConstructor(); if (maybe_constructor->IsFunctionTemplateInfo()) { Object data_obj = FunctionTemplateInfo::cast(maybe_constructor)->GetAccessCheckInfo(); if (data_obj->IsUndefined(isolate)) return AccessCheckInfo(); return AccessCheckInfo::cast(data_obj); } // Might happen for a detached context. if (!maybe_constructor->IsJSFunction()) return AccessCheckInfo(); JSFunction constructor = JSFunction::cast(maybe_constructor); // Might happen for the debug context. if (!constructor->shared()->IsApiFunction()) return AccessCheckInfo(); Object data_obj = constructor->shared()->get_api_func_data()->GetAccessCheckInfo(); if (data_obj->IsUndefined(isolate)) return AccessCheckInfo(); return AccessCheckInfo::cast(data_obj); } MaybeHandle<Name> FunctionTemplateInfo::TryGetCachedPropertyName( Isolate* isolate, Handle<Object> getter) { if (getter->IsFunctionTemplateInfo()) { Handle<FunctionTemplateInfo> fti = Handle<FunctionTemplateInfo>::cast(getter); // Check if the accessor uses a cached property. if (!fti->cached_property_name()->IsTheHole(isolate)) { return handle(Name::cast(fti->cached_property_name()), isolate); } } return MaybeHandle<Name>(); } Address Smi::LexicographicCompare(Isolate* isolate, Smi x, Smi y) { DisallowHeapAllocation no_allocation; DisallowJavascriptExecution no_js(isolate); int x_value = Smi::ToInt(x); int y_value = Smi::ToInt(y); // If the integers are equal so are the string representations. if (x_value == y_value) return Smi::FromInt(0).ptr(); // If one of the integers is zero the normal integer order is the // same as the lexicographic order of the string representations. if (x_value == 0 || y_value == 0) { return Smi::FromInt(x_value < y_value ? -1 : 1).ptr(); } // If only one of the integers is negative the negative number is // smallest because the char code of '-' is less than the char code // of any digit. Otherwise, we make both values positive. // Use unsigned values otherwise the logic is incorrect for -MIN_INT on // architectures using 32-bit Smis. uint32_t x_scaled = x_value; uint32_t y_scaled = y_value; if (x_value < 0) { if (y_value >= 0) { return Smi::FromInt(-1).ptr(); } else { y_scaled = base::NegateWithWraparound(y_value); } x_scaled = base::NegateWithWraparound(x_value); } else if (y_value < 0) { return Smi::FromInt(1).ptr(); } // clang-format off static const uint32_t kPowersOf10[] = { 1, 10, 100, 1000, 10 * 1000, 100 * 1000, 1000 * 1000, 10 * 1000 * 1000, 100 * 1000 * 1000, 1000 * 1000 * 1000}; // clang-format on // If the integers have the same number of decimal digits they can be // compared directly as the numeric order is the same as the // lexicographic order. If one integer has fewer digits, it is scaled // by some power of 10 to have the same number of digits as the longer // integer. If the scaled integers are equal it means the shorter // integer comes first in the lexicographic order. // From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 int x_log2 = 31 - base::bits::CountLeadingZeros(x_scaled); int x_log10 = ((x_log2 + 1) * 1233) >> 12; x_log10 -= x_scaled < kPowersOf10[x_log10]; int y_log2 = 31 - base::bits::CountLeadingZeros(y_scaled); int y_log10 = ((y_log2 + 1) * 1233) >> 12; y_log10 -= y_scaled < kPowersOf10[y_log10]; int tie = 0; if (x_log10 < y_log10) { // X has fewer digits. We would like to simply scale up X but that // might overflow, e.g when comparing 9 with 1_000_000_000, 9 would // be scaled up to 9_000_000_000. So we scale up by the next // smallest power and scale down Y to drop one digit. It is OK to // drop one digit from the longer integer since the final digit is // past the length of the shorter integer. x_scaled *= kPowersOf10[y_log10 - x_log10 - 1]; y_scaled /= 10; tie = -1; } else if (y_log10 < x_log10) { y_scaled *= kPowersOf10[x_log10 - y_log10 - 1]; x_scaled /= 10; tie = 1; } if (x_scaled < y_scaled) return Smi::FromInt(-1).ptr(); if (x_scaled > y_scaled) return Smi::FromInt(1).ptr(); return Smi::FromInt(tie).ptr(); } // Force instantiation of template instances class. // Please note this list is compiler dependent. // Keep this at the end of this file template class HashTable<StringTable, StringTableShape>; template class EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) HashTable<CompilationCacheTable, CompilationCacheShape>; template class EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) HashTable<ObjectHashTable, ObjectHashTableShape>; template class EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) HashTable<ObjectHashSet, ObjectHashSetShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) ObjectHashTableBase<ObjectHashTable, ObjectHashTableShape>; template class EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) HashTable<EphemeronHashTable, EphemeronHashTableShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) ObjectHashTableBase<EphemeronHashTable, EphemeronHashTableShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) BaseNameDictionary<NameDictionary, NameDictionaryShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) BaseNameDictionary<GlobalDictionary, GlobalDictionaryShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Dictionary<NameDictionary, NameDictionaryShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Dictionary<GlobalDictionary, GlobalDictionaryShape>; template class EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) HashTable<NumberDictionary, NumberDictionaryShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Dictionary<NumberDictionary, NumberDictionaryShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) HashTable<SimpleNumberDictionary, SimpleNumberDictionaryShape>; template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Dictionary<SimpleNumberDictionary, SimpleNumberDictionaryShape>; template Handle<NameDictionary> HashTable<NameDictionary, NameDictionaryShape>::New(Isolate*, int, AllocationType, MinimumCapacity); template V8_EXPORT_PRIVATE Handle<NameDictionary> HashTable<NameDictionary, NameDictionaryShape>::Shrink(Isolate* isolate, Handle<NameDictionary>, int additionalCapacity); void JSFinalizationGroup::Cleanup( Handle<JSFinalizationGroup> finalization_group, Isolate* isolate) { // It's possible that the cleared_cells list is empty, since // FinalizationGroup.unregister() removed all its elements before this task // ran. In that case, don't call the cleanup function. if (!finalization_group->cleared_cells()->IsUndefined(isolate)) { // Construct the iterator. Handle<JSFinalizationGroupCleanupIterator> iterator; { Handle<Map> cleanup_iterator_map( isolate->native_context() ->js_finalization_group_cleanup_iterator_map(), isolate); iterator = Handle<JSFinalizationGroupCleanupIterator>::cast( isolate->factory()->NewJSObjectFromMap( cleanup_iterator_map, AllocationType::kYoung, Handle<AllocationSite>::null())); iterator->set_finalization_group(*finalization_group); } Handle<Object> cleanup(finalization_group->cleanup(), isolate); v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate)); v8::Local<v8::Value> result; MaybeHandle<Object> exception; Handle<Object> args[] = {iterator}; bool has_pending_exception = !ToLocal<Value>( Execution::TryCall( isolate, cleanup, handle(ReadOnlyRoots(isolate).undefined_value(), isolate), 1, args, Execution::MessageHandling::kReport, &exception), &result); // TODO(marja): (spec): What if there's an exception? USE(has_pending_exception); // TODO(marja): (spec): Should the iterator be invalidated after the // function returns? } } MaybeHandle<FixedArray> JSReceiver::GetPrivateEntries( Isolate* isolate, Handle<JSReceiver> receiver) { PropertyFilter key_filter = static_cast<PropertyFilter>(PRIVATE_NAMES_ONLY); Handle<FixedArray> keys; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, keys, KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, key_filter, GetKeysConversion::kConvertToString), MaybeHandle<FixedArray>()); Handle<FixedArray> entries = isolate->factory()->NewFixedArray(keys->length() * 2); int length = 0; for (int i = 0; i < keys->length(); ++i) { Handle<Object> obj_key = handle(keys->get(i), isolate); Handle<Symbol> key(Symbol::cast(*obj_key), isolate); CHECK(key->is_private_name()); Handle<Object> value; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, value, Object::GetProperty(isolate, receiver, key), MaybeHandle<FixedArray>()); entries->set(length++, *key); entries->set(length++, *value); } DCHECK_EQ(length, entries->length()); return FixedArray::ShrinkOrEmpty(isolate, entries, length); } } // namespace internal } // namespace v8