diff options
author | Michaël Zasso <targos@protonmail.com> | 2019-08-01 08:38:30 +0200 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2019-08-01 12:53:56 +0200 |
commit | 2dcc3665abf57c3607cebffdeeca062f5894885d (patch) | |
tree | 4f560748132edcfb4c22d6f967a7e80d23d7ea2c /deps/v8/src/objects/objects.cc | |
parent | 1ee47d550c6de132f06110aa13eceb7551d643b3 (diff) | |
download | node-new-2dcc3665abf57c3607cebffdeeca062f5894885d.tar.gz |
deps: update V8 to 7.6.303.28
PR-URL: https://github.com/nodejs/node/pull/28016
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Refael Ackermann (רפאל פלחי) <refack@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Diffstat (limited to 'deps/v8/src/objects/objects.cc')
-rw-r--r-- | deps/v8/src/objects/objects.cc | 8200 |
1 files changed, 8200 insertions, 0 deletions
diff --git a/deps/v8/src/objects/objects.cc b/deps/v8/src/objects/objects.cc new file mode 100644 index 0000000000..8cc22fa0e5 --- /dev/null +++ b/deps/v8/src/objects/objects.cc @@ -0,0 +1,8200 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/objects/objects.h" + +#include <algorithm> +#include <cmath> +#include <memory> +#include <sstream> +#include <vector> + +#include "src/objects/objects-inl.h" + +#include "src/api/api-arguments-inl.h" +#include "src/api/api-natives.h" +#include "src/api/api.h" +#include "src/ast/ast.h" +#include "src/ast/scopes.h" +#include "src/base/bits.h" +#include "src/base/debug/stack_trace.h" +#include "src/base/overflowing-math.h" +#include "src/base/utils/random-number-generator.h" +#include "src/builtins/accessors.h" +#include "src/builtins/builtins.h" +#include "src/codegen/compiler.h" +#include "src/common/globals.h" +#include "src/date/date.h" +#include "src/debug/debug.h" +#include "src/execution/arguments.h" +#include "src/execution/execution.h" +#include "src/execution/frames-inl.h" +#include "src/execution/isolate-inl.h" +#include "src/execution/message-template.h" +#include "src/execution/microtask-queue.h" +#include "src/heap/heap-inl.h" +#include "src/heap/read-only-heap.h" +#include "src/ic/ic.h" +#include "src/init/bootstrapper.h" +#include "src/logging/counters-inl.h" +#include "src/logging/counters.h" +#include "src/logging/log.h" +#include "src/objects/allocation-site-inl.h" +#include "src/objects/allocation-site-scopes.h" +#include "src/objects/api-callbacks.h" +#include "src/objects/arguments-inl.h" +#include "src/objects/bigint.h" +#include "src/objects/cell-inl.h" +#include "src/objects/code-inl.h" +#include "src/objects/compilation-cache-inl.h" +#include "src/objects/debug-objects-inl.h" +#include "src/objects/elements.h" +#include "src/objects/embedder-data-array-inl.h" +#include "src/objects/field-index-inl.h" +#include "src/objects/field-index.h" +#include "src/objects/field-type.h" +#include "src/objects/foreign.h" +#include "src/objects/frame-array-inl.h" +#include "src/objects/free-space-inl.h" +#include "src/objects/function-kind.h" +#include "src/objects/hash-table-inl.h" +#include "src/objects/js-array-inl.h" +#include "src/objects/keys.h" +#include "src/objects/lookup-inl.h" +#include "src/objects/map-updater.h" +#include "src/objects/objects-body-descriptors-inl.h" +#include "src/utils/identity-map.h" +#ifdef V8_INTL_SUPPORT +#include "src/objects/js-break-iterator.h" +#include "src/objects/js-collator.h" +#endif // V8_INTL_SUPPORT +#include "src/objects/js-collection-inl.h" +#ifdef V8_INTL_SUPPORT +#include "src/objects/js-date-time-format.h" +#endif // V8_INTL_SUPPORT +#include "src/objects/js-generator-inl.h" +#ifdef V8_INTL_SUPPORT +#include "src/objects/js-list-format.h" +#include "src/objects/js-locale.h" +#include "src/objects/js-number-format.h" +#include "src/objects/js-plural-rules.h" +#endif // V8_INTL_SUPPORT +#include "src/objects/js-regexp-inl.h" +#include "src/objects/js-regexp-string-iterator.h" +#ifdef V8_INTL_SUPPORT +#include "src/objects/js-relative-time-format.h" +#include "src/objects/js-segment-iterator.h" +#include "src/objects/js-segmenter.h" +#endif // V8_INTL_SUPPORT +#include "src/codegen/source-position-table.h" +#include "src/objects/js-weak-refs-inl.h" +#include "src/objects/literal-objects-inl.h" +#include "src/objects/map-inl.h" +#include "src/objects/map.h" +#include "src/objects/microtask-inl.h" +#include "src/objects/module-inl.h" +#include "src/objects/promise-inl.h" +#include "src/objects/property-descriptor.h" +#include "src/objects/prototype.h" +#include "src/objects/slots-atomic-inl.h" +#include "src/objects/stack-frame-info-inl.h" +#include "src/objects/string-comparator.h" +#include "src/objects/struct-inl.h" +#include "src/objects/template-objects-inl.h" +#include "src/objects/transitions-inl.h" +#include "src/parsing/preparse-data.h" +#include "src/regexp/jsregexp.h" +#include "src/strings/string-builder-inl.h" +#include "src/strings/string-search.h" +#include "src/strings/string-stream.h" +#include "src/strings/unicode-decoder.h" +#include "src/strings/unicode-inl.h" +#include "src/utils/ostreams.h" +#include "src/utils/utils-inl.h" +#include "src/wasm/wasm-engine.h" +#include "src/wasm/wasm-objects.h" +#include "src/zone/zone.h" + +namespace v8 { +namespace internal { + +ShouldThrow GetShouldThrow(Isolate* isolate, Maybe<ShouldThrow> should_throw) { + if (should_throw.IsJust()) return should_throw.FromJust(); + + LanguageMode mode = isolate->context().scope_info().language_mode(); + if (mode == LanguageMode::kStrict) return kThrowOnError; + + for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { + if (!(it.frame()->is_optimized() || it.frame()->is_interpreted())) { + continue; + } + // Get the language mode from closure. + JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(it.frame()); + std::vector<SharedFunctionInfo> functions; + js_frame->GetFunctions(&functions); + LanguageMode closure_language_mode = functions.back().language_mode(); + if (closure_language_mode > mode) { + mode = closure_language_mode; + } + break; + } + + return is_sloppy(mode) ? kDontThrow : kThrowOnError; +} + +bool ComparisonResultToBool(Operation op, ComparisonResult result) { + switch (op) { + case Operation::kLessThan: + return result == ComparisonResult::kLessThan; + case Operation::kLessThanOrEqual: + return result == ComparisonResult::kLessThan || + result == ComparisonResult::kEqual; + case Operation::kGreaterThan: + return result == ComparisonResult::kGreaterThan; + case Operation::kGreaterThanOrEqual: + return result == ComparisonResult::kGreaterThan || + result == ComparisonResult::kEqual; + default: + break; + } + UNREACHABLE(); +} + +std::ostream& operator<<(std::ostream& os, InstanceType instance_type) { + switch (instance_type) { +#define WRITE_TYPE(TYPE) \ + case TYPE: \ + return os << #TYPE; + INSTANCE_TYPE_LIST(WRITE_TYPE) +#undef WRITE_TYPE + } + UNREACHABLE(); +} + +Handle<FieldType> Object::OptimalType(Isolate* isolate, + Representation representation) { + if (representation.IsNone()) return FieldType::None(isolate); + if (FLAG_track_field_types) { + if (representation.IsHeapObject() && IsHeapObject()) { + // We can track only JavaScript objects with stable maps. + Handle<Map> map(HeapObject::cast(*this).map(), isolate); + if (map->is_stable() && map->IsJSReceiverMap()) { + return FieldType::Class(map, isolate); + } + } + } + return FieldType::Any(isolate); +} + +Handle<Object> Object::NewStorageFor(Isolate* isolate, Handle<Object> object, + Representation representation) { + if (!representation.IsDouble()) return object; + auto result = isolate->factory()->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; +} + +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; + + 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 (instance_type == SMALL_ORDERED_HASH_SET_TYPE) { + return SmallOrderedHashSet::SizeFor( + SmallOrderedHashSet::unchecked_cast(*this).Capacity()); + } + if (instance_type == SMALL_ORDERED_HASH_MAP_TYPE) { + return SmallOrderedHashMap::SizeFor( + SmallOrderedHashMap::unchecked_cast(*this).Capacity()); + } + if (instance_type == SMALL_ORDERED_NAME_DICTIONARY_TYPE) { + return SmallOrderedNameDictionary::SizeFor( + SmallOrderedNameDictionary::unchecked_cast(*this).Capacity()); + } + if (instance_type == PROPERTY_ARRAY_TYPE) { + return PropertyArray::SizeFor( + PropertyArray::cast(*this).synchronized_length()); + } + if (instance_type == FEEDBACK_VECTOR_TYPE) { + return FeedbackVector::SizeFor( + FeedbackVector::unchecked_cast(*this).length()); + } + if (instance_type == BIGINT_TYPE) { + return BigInt::SizeFor(BigInt::unchecked_cast(*this).length()); + } + if (instance_type == PREPARSE_DATA_TYPE) { + PreparseData data = PreparseData::unchecked_cast(*this); + return PreparseData::SizeFor(data.data_length(), data.children_length()); + } + if (instance_type == CODE_TYPE) { + return Code::unchecked_cast(*this).CodeSize(); + } + DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE); + return EmbedderDataArray::SizeFor( + EmbedderDataArray::unchecked_cast(*this).length()); +} + +bool HeapObject::NeedsRehashing() const { + switch (map().instance_type()) { + case DESCRIPTOR_ARRAY_TYPE: + return DescriptorArray::cast(*this).number_of_descriptors() > 1; + case TRANSITION_ARRAY_TYPE: + return TransitionArray::cast(*this).number_of_entries() > 1; + case ORDERED_HASH_MAP_TYPE: + return OrderedHashMap::cast(*this).NumberOfElements() > 0; + case ORDERED_HASH_SET_TYPE: + return OrderedHashSet::cast(*this).NumberOfElements() > 0; + case NAME_DICTIONARY_TYPE: + case GLOBAL_DICTIONARY_TYPE: + case NUMBER_DICTIONARY_TYPE: + case SIMPLE_NUMBER_DICTIONARY_TYPE: + case STRING_TABLE_TYPE: + case HASH_TABLE_TYPE: + case SMALL_ORDERED_HASH_MAP_TYPE: + case SMALL_ORDERED_HASH_SET_TYPE: + case SMALL_ORDERED_NAME_DICTIONARY_TYPE: + return true; + default: + return false; + } +} + +bool HeapObject::CanBeRehashed() const { + DCHECK(NeedsRehashing()); + switch (map().instance_type()) { + case ORDERED_HASH_MAP_TYPE: + case ORDERED_HASH_SET_TYPE: + case ORDERED_NAME_DICTIONARY_TYPE: + // TODO(yangguo): actually support rehashing OrderedHash{Map,Set}. + return false; + case NAME_DICTIONARY_TYPE: + case GLOBAL_DICTIONARY_TYPE: + case NUMBER_DICTIONARY_TYPE: + case SIMPLE_NUMBER_DICTIONARY_TYPE: + case STRING_TABLE_TYPE: + return true; + case DESCRIPTOR_ARRAY_TYPE: + return true; + case TRANSITION_ARRAY_TYPE: + return true; + case SMALL_ORDERED_HASH_MAP_TYPE: + return SmallOrderedHashMap::cast(*this).NumberOfElements() == 0; + case SMALL_ORDERED_HASH_SET_TYPE: + return SmallOrderedHashMap::cast(*this).NumberOfElements() == 0; + case SMALL_ORDERED_NAME_DICTIONARY_TYPE: + return SmallOrderedNameDictionary::cast(*this).NumberOfElements() == 0; + default: + return false; + } + return false; +} + +void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) { + switch (map().instance_type()) { + case HASH_TABLE_TYPE: + UNREACHABLE(); + case NAME_DICTIONARY_TYPE: + NameDictionary::cast(*this).Rehash(roots); + break; + case GLOBAL_DICTIONARY_TYPE: + GlobalDictionary::cast(*this).Rehash(roots); + break; + case NUMBER_DICTIONARY_TYPE: + NumberDictionary::cast(*this).Rehash(roots); + break; + case SIMPLE_NUMBER_DICTIONARY_TYPE: + SimpleNumberDictionary::cast(*this).Rehash(roots); + break; + case STRING_TABLE_TYPE: + StringTable::cast(*this).Rehash(roots); + break; + case DESCRIPTOR_ARRAY_TYPE: + DCHECK_LE(1, DescriptorArray::cast(*this).number_of_descriptors()); + DescriptorArray::cast(*this).Sort(); + break; + case TRANSITION_ARRAY_TYPE: + TransitionArray::cast(*this).Sort(); + break; + case SMALL_ORDERED_HASH_MAP_TYPE: + DCHECK_EQ(0, SmallOrderedHashMap::cast(*this).NumberOfElements()); + break; + case SMALL_ORDERED_HASH_SET_TYPE: + DCHECK_EQ(0, SmallOrderedHashSet::cast(*this).NumberOfElements()); + break; + case SMALL_ORDERED_NAME_DICTIONARY_TYPE: + DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements()); + break; + case ONE_BYTE_INTERNALIZED_STRING_TYPE: + case INTERNALIZED_STRING_TYPE: + // Rare case, rehash read-only space strings before they are sealed. + DCHECK(ReadOnlyHeap::Contains(*this)); + String::cast(*this).Hash(); + break; + default: + UNREACHABLE(); + } +} + +bool HeapObject::IsExternal(Isolate* isolate) const { + return map().FindRootMap(isolate) == isolate->heap()->external_map(); +} + +void DescriptorArray::GeneralizeAllFields() { + int length = number_of_descriptors(); + for (int i = 0; i < length; i++) { + PropertyDetails details = GetDetails(i); + details = details.CopyWithRepresentation(Representation::Tagged()); + if (details.location() == kField) { + DCHECK_EQ(kData, details.kind()); + details = details.CopyWithConstness(PropertyConstness::kMutable); + SetValue(i, 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->GetName())); + return Nothing<bool>(); + } + + return AddDataProperty(it, value, NONE, should_throw, store_origin); +} + +Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, + StoreOrigin store_origin, + Maybe<ShouldThrow> should_throw) { + Isolate* isolate = it->isolate(); + + if (it->IsFound()) { + bool found = true; + Maybe<bool> result = + SetPropertyInternal(it, value, should_throw, store_origin, &found); + if (found) return result; + } + + it->UpdateProtector(); + + // The property either doesn't exist on the holder or exists there as a data + // property. + + if (!it->GetReceiver()->IsJSReceiver()) { + return WriteToReadOnlyProperty(it, value, should_throw); + } + Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); + + LookupIterator::Configuration c = LookupIterator::OWN; + LookupIterator own_lookup = + it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c) + : LookupIterator(isolate, receiver, it->name(), c); + + for (; own_lookup.IsFound(); own_lookup.Next()) { + switch (own_lookup.state()) { + case LookupIterator::ACCESS_CHECK: + if (!own_lookup.HasAccess()) { + return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value, + should_throw); + } + break; + + case LookupIterator::ACCESSOR: + if (own_lookup.GetAccessors()->IsAccessorInfo()) { + if (own_lookup.IsReadOnly()) { + return WriteToReadOnlyProperty(&own_lookup, value, should_throw); + } + 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).HasTypedArrayElements()) { + ElementsKind elements_kind = JSObject::cast(*receiver).GetElementsKind(); + if (elements_kind == BIGINT64_ELEMENTS || + elements_kind == BIGUINT64_ELEMENTS) { + ASSIGN_RETURN_ON_EXCEPTION_VALUE(it->isolate(), to_assign, + BigInt::FromObject(it->isolate(), value), + Nothing<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)); + } + } + + 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 { + using Array = FixedArray; + 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()) { + if (!target_desc.configurable()) { + isolate->Throw(*factory->NewTypeError( + MessageTemplate::kProxyDeletePropertyNonConfigurable, name)); + return Nothing<bool>(); + } + // 13. Let extensibleTarget be ? IsExtensible(target). + // 14. If extensibleTarget is false, throw a TypeError exception. + Maybe<bool> extensible = JSReceiver::IsExtensible(target); + MAYBE_RETURN(extensible, Nothing<bool>()); + if (!extensible.FromJust()) { + isolate->Throw(*factory->NewTypeError( + MessageTemplate::kProxyDeletePropertyNonExtensible, 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>(); + } + // 16c. If IsDataDescriptor(targetDesc) is true, + // targetDesc.[[Configurable]] is + // false, and targetDesc.[[Writable]] is true, then + if (PropertyDescriptor::IsDataDescriptor(&target_desc) && + !target_desc.configurable() && target_desc.writable()) { + // 16c i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, + // throw a TypeError exception. + if (desc->has_writable() && !desc->writable()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyDefinePropertyNonConfigurableWritable, + property_name)); + return Nothing<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>(); + } + // 17b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] + // is false, then + if (desc->has_writable() && !desc->writable()) { + // 17b i. If targetDesc.[[Writable]] is true, throw a TypeError exception. + if (target_desc.writable()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate:: + kProxyGetOwnPropertyDescriptorNonConfigurableWritable, + name)); + return Nothing<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; +} + +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; +} + +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( + FunctionLiteral* literal) { + 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; + // We get the start position from the {literal} here, because the + // SharedFunctionInfo itself might not have a way to get to the + // start position early on (currently that's the case when it's + // marked for eager compilation). + if (Script::cast(script()).GetPositionInfo(literal->start_position(), &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; + } else if (data.IsWasmJSFunctionData()) { + return wasm_js_function_data().wrapper_code(); + } else if (data.IsWasmCapiFunctionData()) { + return wasm_capi_function_data().wrapper_code(); + } + UNREACHABLE(); +} + +WasmExportedFunctionData SharedFunctionInfo::wasm_exported_function_data() + const { + DCHECK(HasWasmExportedFunctionData()); + return WasmExportedFunctionData::cast(function_data()); +} + +WasmJSFunctionData SharedFunctionInfo::wasm_js_function_data() const { + DCHECK(HasWasmJSFunctionData()); + return WasmJSFunctionData::cast(function_data()); +} + +WasmCapiFunctionData SharedFunctionInfo::wasm_capi_function_data() const { + DCHECK(HasWasmCapiFunctionData()); + return WasmCapiFunctionData::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_precise_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(); + } +} + +bool SharedFunctionInfo::AreSourcePositionsAvailable() const { + if (FLAG_enable_lazy_source_positions) { + return !HasBytecodeArray() || GetBytecodeArray().HasSourcePositionTable(); + } + return true; +} + +// 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::kSizeOfAllPromiseReactionJobTasks)); + 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_; +}; + +// InternalizedStringKey carries a string/internalized-string object as key. +class InternalizedStringKey final : public StringTableKey { + public: + explicit InternalizedStringKey(Handle<String> string) + : StringTableKey(0, string->length()), string_(string) { + DCHECK(!string->IsInternalizedString()); + DCHECK(string->IsFlat()); + // Make sure hash_field is computed. + string->Hash(); + set_hash_field(string->hash_field()); + } + + bool IsMatch(String string) override { return string_->SlowEquals(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; +} + +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); +} + +// 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()) { + string->MakeThin(isolate, *result); + } + } 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 +template <typename StringTableKey> +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); +} + +template Handle<String> StringTable::LookupKey(Isolate* isolate, + OneByteStringKey* key); +template Handle<String> StringTable::LookupKey(Isolate* isolate, + TwoByteStringKey* key); +template Handle<String> StringTable::LookupKey(Isolate* isolate, + SeqOneByteSubStringKey* key); +template Handle<String> StringTable::LookupKey(Isolate* isolate, + SeqTwoByteSubStringKey* 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 { + +template <typename Char> +Address LookupString(Isolate* isolate, String string, String source, + size_t start) { + DisallowHeapAllocation no_gc; + StringTable table = isolate->heap()->string_table(); + uint64_t seed = HashSeed(isolate); + + int length = string.length(); + + std::unique_ptr<Char[]> buffer; + const Char* chars; + + if (source.IsConsString()) { + DCHECK(!source.IsFlat()); + buffer.reset(new Char[length]); + String::WriteToFlat(source, buffer.get(), 0, length); + chars = buffer.get(); + } else { + chars = source.GetChars<Char>(no_gc) + start; + } + // TODO(verwaest): Internalize to one-byte when possible. + SequentialStringKey<Char> key(Vector<const Char>(chars, length), seed); + + // String could be an array index. + uint32_t hash_field = key.hash_field(); + + if (Name::ContainsCachedArrayIndex(hash_field)) { + return Smi::FromInt(String::ArrayIndexValueBits::decode(hash_field)).ptr(); + } + + if ((hash_field & Name::kIsNotArrayIndexMask) == 0) { + // It is an indexed, but it's not cached. + return Smi::FromInt(ResultSentinel::kUnsupported).ptr(); + } + + int entry = table.FindEntry(ReadOnlyRoots(isolate), &key, key.hash()); + if (entry == kNotFound) { + // 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 internalized = String::cast(table.KeyAt(entry)); + if (FLAG_thin_strings) { + string.MakeThin(isolate, internalized); + } + return internalized.ptr(); +} + +} // namespace + +// static +Address StringTable::LookupStringIfExists_NoAllocate(Isolate* isolate, + Address raw_string) { + String string = String::cast(Object(raw_string)); + DCHECK(!string.IsInternalizedString()); + + // 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)); + + size_t start = 0; + String source = string; + if (source.IsSlicedString()) { + SlicedString sliced = SlicedString::cast(source); + start = sliced.offset(); + source = sliced.parent(); + } else if (source.IsConsString() && source.IsFlat()) { + source = ConsString::cast(source).first(); + } + if (source.IsThinString()) { + source = ThinString::cast(source).actual(); + if (string.length() == source.length()) { + return source.ptr(); + } + } + if (source.IsOneByteRepresentation()) { + return i::LookupString<uint8_t>(isolate, string, source, start); + } + return i::LookupString<uint16_t>(isolate, string, source, start); +} + +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(); + } + } +} + +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()); +} + +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 |