| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/objects.h" |
| |
| #include <cmath> |
| #include <iomanip> |
| #include <memory> |
| #include <sstream> |
| #include <vector> |
| |
| #include "src/objects-inl.h" |
| |
| #include "src/accessors.h" |
| #include "src/allocation-site-scopes.h" |
| #include "src/api-arguments-inl.h" |
| #include "src/api-natives.h" |
| #include "src/api.h" |
| #include "src/arguments.h" |
| #include "src/ast/ast.h" |
| #include "src/ast/scopes.h" |
| #include "src/base/bits.h" |
| #include "src/base/utils/random-number-generator.h" |
| #include "src/bootstrapper.h" |
| #include "src/builtins/builtins.h" |
| #include "src/code-stubs.h" |
| #include "src/compilation-dependencies.h" |
| #include "src/compiler.h" |
| #include "src/counters-inl.h" |
| #include "src/counters.h" |
| #include "src/date.h" |
| #include "src/debug/debug-evaluate.h" |
| #include "src/debug/debug.h" |
| #include "src/deoptimizer.h" |
| #include "src/elements.h" |
| #include "src/execution.h" |
| #include "src/field-index-inl.h" |
| #include "src/field-index.h" |
| #include "src/field-type.h" |
| #include "src/frames-inl.h" |
| #include "src/globals.h" |
| #include "src/ic/ic.h" |
| #include "src/identity-map.h" |
| #include "src/interpreter/bytecode-array-iterator.h" |
| #include "src/interpreter/bytecode-decoder.h" |
| #include "src/interpreter/interpreter.h" |
| #include "src/isolate-inl.h" |
| #include "src/keys.h" |
| #include "src/log.h" |
| #include "src/lookup.h" |
| #include "src/macro-assembler.h" |
| #include "src/map-updater.h" |
| #include "src/messages.h" |
| #include "src/objects-body-descriptors-inl.h" |
| #include "src/objects/bigint.h" |
| #include "src/objects/code-inl.h" |
| #include "src/objects/compilation-cache-inl.h" |
| #include "src/objects/debug-objects-inl.h" |
| #include "src/objects/frame-array-inl.h" |
| #include "src/objects/hash-table.h" |
| #include "src/objects/map.h" |
| #include "src/parsing/preparsed-scope-data.h" |
| #include "src/property-descriptor.h" |
| #include "src/prototype.h" |
| #include "src/regexp/jsregexp.h" |
| #include "src/safepoint-table.h" |
| #include "src/snapshot/code-serializer.h" |
| #include "src/source-position-table.h" |
| #include "src/string-builder.h" |
| #include "src/string-search.h" |
| #include "src/string-stream.h" |
| #include "src/trap-handler/trap-handler.h" |
| #include "src/unicode-cache-inl.h" |
| #include "src/utils-inl.h" |
| #include "src/wasm/wasm-engine.h" |
| #include "src/wasm/wasm-objects.h" |
| #include "src/zone/zone.h" |
| |
| #ifdef ENABLE_DISASSEMBLER |
| #include "src/disasm.h" |
| #include "src/disassembler.h" |
| #include "src/eh-frame.h" |
| #endif |
| |
| namespace v8 { |
| namespace internal { |
| |
| 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); |
| } |
| |
| MaybeHandle<JSReceiver> Object::ToObject(Isolate* isolate, |
| Handle<Object> object, |
| Handle<Context> native_context, |
| const char* method_name) { |
| if (object->IsJSReceiver()) return Handle<JSReceiver>::cast(object); |
| 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 == isolate->heap()->null_value() || |
| object->IsUndefined(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(Handle<String>::cast(input)); |
| } |
| if (input->IsOddball()) { |
| return Oddball::ToNumber(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(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->IsNumeric() || input->IsOddball()) { |
| return Object::ToString(isolate, input).ToHandleChecked(); |
| } 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); |
| |
| 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::ToObject(isolate, input, isolate->native_context()) |
| .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(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::Template error_index) { |
| if (input->IsUndefined(isolate)) return handle(Smi::kZero, isolate); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(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() { |
| if (IsSmi()) return Smi::ToInt(this) != 0; |
| DCHECK(IsHeapObject()); |
| Isolate* isolate = HeapObject::cast(this)->GetIsolate(); |
| 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; |
| } |
| |
| |
| namespace { |
| |
| // TODO(bmeurer): Maybe we should introduce a marker interface Number, |
| // where we put all these methods at some point? |
| ComparisonResult NumberCompare(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; |
| } |
| } |
| |
| bool NumberEquals(double x, double y) { |
| // Must check explicitly for NaN's on Windows, but -0 works fine. |
| if (std::isnan(x)) return false; |
| if (std::isnan(y)) return false; |
| return x == y; |
| } |
| |
| bool NumberEquals(const Object* x, const Object* y) { |
| return NumberEquals(x->Number(), y->Number()); |
| } |
| |
| bool NumberEquals(Handle<Object> x, Handle<Object> y) { |
| return NumberEquals(*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(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(Handle<String>::cast(x), Handle<String>::cast(y))); |
| } |
| // ES6 section 7.2.11 Abstract Relational Comparison step 6. |
| if (!Object::ToNumeric(x).ToHandle(&x) || |
| !Object::ToNumeric(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(NumberCompare(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(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(NumberEquals(x, y)); |
| } else if (y->IsBoolean()) { |
| return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); |
| } else if (y->IsString()) { |
| return Just(NumberEquals(x, String::ToNumber(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(Handle<String>::cast(x), Handle<String>::cast(y))); |
| } else if (y->IsNumber()) { |
| x = String::ToNumber(Handle<String>::cast(x)); |
| return Just(NumberEquals(x, y)); |
| } else if (y->IsBoolean()) { |
| x = String::ToNumber(Handle<String>::cast(x)); |
| return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); |
| } else if (y->IsBigInt()) { |
| return Just(BigInt::EqualToString(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(NumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); |
| } else if (y->IsString()) { |
| y = String::ToNumber(Handle<String>::cast(y)); |
| return Just(NumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); |
| } else if (y->IsBigInt()) { |
| x = Oddball::ToNumber(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(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(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(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 NumberEquals(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()); |
| 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::Multiply(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumber(lhs->Number() * rhs->Number()); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::Divide(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumber(lhs->Number() / rhs->Number()); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::Modulus(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumber(Modulo(lhs->Number(), rhs->Number())); |
| } |
| |
| |
| // 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(rhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| return isolate->factory()->NewNumber(lhs->Number() + rhs->Number()); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::Subtract(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumber(lhs->Number() - rhs->Number()); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::ShiftLeft(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) |
| << (NumberToUint32(*rhs) & 0x1F)); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::ShiftRight(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) >> |
| (NumberToUint32(*rhs) & 0x1F)); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::ShiftRightLogical(Isolate* isolate, |
| Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumberFromUint(NumberToUint32(*lhs) >> |
| (NumberToUint32(*rhs) & 0x1F)); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::BitwiseAnd(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) & |
| NumberToInt32(*rhs)); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::BitwiseOr(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) | |
| NumberToInt32(*rhs)); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::BitwiseXor(Isolate* isolate, Handle<Object> lhs, |
| Handle<Object> rhs) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object); |
| } |
| return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) ^ |
| NumberToInt32(*rhs)); |
| } |
| |
| // 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(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, |
| JSReceiver::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()); |
| } |
| |
| // 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, |
| JSReceiver::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(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); |
| uint32_t length = array->length_value(); |
| if (array->WasNeutered() || |
| length > static_cast<uint32_t>(FixedArray::kMaxLength)) { |
| return MaybeHandle<FixedArray>(); |
| } |
| return array->GetElementsAccessor()->CreateListFromArrayLike( |
| isolate, array, 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<Object> object) { |
| Handle<Object> val; |
| Handle<Object> key = isolate->factory()->length_string(); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, val, Runtime::GetObjectProperty(isolate, object, key), Object); |
| return Object::ToLength(isolate, val); |
| } |
| |
| // static |
| Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) { |
| for (; it->IsFound(); it->Next()) { |
| switch (it->state()) { |
| case LookupIterator::NOT_FOUND: |
| case LookupIterator::TRANSITION: |
| UNREACHABLE(); |
| case LookupIterator::JSPROXY: |
| return JSProxy::HasProperty(it->isolate(), it->GetHolder<JSProxy>(), |
| it->GetName()); |
| case LookupIterator::INTERCEPTOR: { |
| Maybe<PropertyAttributes> result = |
| JSObject::GetPropertyAttributesWithInterceptor(it); |
| if (result.IsNothing()) return Nothing<bool>(); |
| if (result.FromJust() != ABSENT) return Just(true); |
| break; |
| } |
| case LookupIterator::ACCESS_CHECK: { |
| if (it->HasAccess()) break; |
| Maybe<PropertyAttributes> result = |
| JSObject::GetPropertyAttributesWithFailedAccessCheck(it); |
| if (result.IsNothing()) return Nothing<bool>(); |
| return Just(result.FromJust() != ABSENT); |
| } |
| case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| // TypedArray out-of-bounds access. |
| return Just(false); |
| case LookupIterator::ACCESSOR: |
| case LookupIterator::DATA: |
| return Just(true); |
| } |
| } |
| return Just(false); |
| } |
| |
| // static |
| Maybe<bool> JSReceiver::HasOwnProperty(Handle<JSReceiver> object, |
| Handle<Name> name) { |
| if (object->IsJSModuleNamespace()) { |
| PropertyDescriptor desc; |
| return JSReceiver::GetOwnPropertyDescriptor(object->GetIsolate(), object, |
| name, &desc); |
| } |
| |
| if (object->IsJSObject()) { // Shortcut. |
| LookupIterator it = LookupIterator::PropertyOrElement( |
| object->GetIsolate(), object, name, object, LookupIterator::OWN); |
| return HasProperty(&it); |
| } |
| |
| Maybe<PropertyAttributes> attributes = |
| JSReceiver::GetOwnPropertyAttributes(object, name); |
| MAYBE_RETURN(attributes, Nothing<bool>()); |
| return Just(attributes.FromJust() != ABSENT); |
| } |
| |
| // static |
| MaybeHandle<Object> Object::GetProperty(LookupIterator* it) { |
| for (; it->IsFound(); it->Next()) { |
| switch (it->state()) { |
| case LookupIterator::NOT_FOUND: |
| case LookupIterator::TRANSITION: |
| UNREACHABLE(); |
| case LookupIterator::JSPROXY: { |
| bool was_found; |
| MaybeHandle<Object> result = |
| JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(), |
| it->GetName(), it->GetReceiver(), &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(); |
| } |
| } |
| 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(); |
| } |
| |
| |
| Handle<Object> JSReceiver::GetDataProperty(LookupIterator* it) { |
| for (; it->IsFound(); it->Next()) { |
| switch (it->state()) { |
| case LookupIterator::INTERCEPTOR: |
| case LookupIterator::NOT_FOUND: |
| case LookupIterator::TRANSITION: |
| UNREACHABLE(); |
| case LookupIterator::ACCESS_CHECK: |
| // Support calling this method without an active context, but refuse |
| // access to access-checked objects in that case. |
| if (it->isolate()->context() != nullptr && it->HasAccess()) continue; |
| // Fall through. |
| case LookupIterator::JSPROXY: |
| it->NotFound(); |
| return it->isolate()->factory()->undefined_value(); |
| case LookupIterator::ACCESSOR: |
| // TODO(verwaest): For now this doesn't call into AccessorInfo, since |
| // clients don't need it. Update once relevant. |
| it->NotFound(); |
| return it->isolate()->factory()->undefined_value(); |
| case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| return it->isolate()->factory()->undefined_value(); |
| case LookupIterator::DATA: |
| return it->GetDataValue(); |
| } |
| } |
| return it->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(); |
| if (FastI2D(FastD2I(num)) == num) { |
| *value = FastD2I(num); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| Handle<SharedFunctionInfo> FunctionTemplateInfo::GetOrCreateSharedFunctionInfo( |
| Isolate* isolate, Handle<FunctionTemplateInfo> info, |
| MaybeHandle<Name> maybe_name) { |
| Object* current_info = info->shared_function_info(); |
| if (current_info->IsSharedFunctionInfo()) { |
| return handle(SharedFunctionInfo::cast(current_info), isolate); |
| } |
| Handle<Object> class_name(info->class_name(), isolate); |
| Handle<Name> name; |
| Handle<String> name_string; |
| if (maybe_name.ToHandle(&name) && name->IsString()) { |
| name_string = Handle<String>::cast(name); |
| } else { |
| name_string = class_name->IsString() ? Handle<String>::cast(class_name) |
| : isolate->factory()->empty_string(); |
| } |
| Handle<Code> code = BUILTIN_CODE(isolate, HandleApiCall); |
| bool is_constructor; |
| FunctionKind function_kind; |
| if (!info->remove_prototype()) { |
| is_constructor = true; |
| function_kind = kNormalFunction; |
| } else { |
| is_constructor = false; |
| function_kind = kConciseMethod; |
| } |
| Handle<SharedFunctionInfo> result = isolate->factory()->NewSharedFunctionInfo( |
| name_string, code, is_constructor, function_kind); |
| if (is_constructor) { |
| result->SetConstructStub(*BUILTIN_CODE(isolate, JSConstructStubApi)); |
| } |
| |
| result->set_length(info->length()); |
| if (class_name->IsString()) { |
| result->set_instance_class_name(String::cast(*class_name)); |
| } |
| result->set_api_func_data(*info); |
| 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)->parent_template(); |
| } |
| // Didn't find the required type in the inheritance chain. |
| return false; |
| } |
| |
| |
| // 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(fixed_array, index, value); |
| fixed_array->set(kLengthIndex, Smi::FromInt(index)); |
| return Handle<TemplateList>::cast(fixed_array); |
| } |
| |
| // static |
| MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor, |
| Handle<JSReceiver> new_target, |
| Handle<AllocationSite> site) { |
| // If called through new, new.target can be: |
| // - a subclass of constructor, |
| // - a proxy wrapper around constructor, or |
| // - the constructor itself. |
| // If called through Reflect.construct, it's guaranteed to be a constructor. |
| Isolate* const isolate = constructor->GetIsolate(); |
| DCHECK(constructor->IsConstructor()); |
| DCHECK(new_target->IsConstructor()); |
| DCHECK(!constructor->has_initial_map() || |
| constructor->initial_map()->instance_type() != JS_FUNCTION_TYPE); |
| |
| Handle<Map> initial_map; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, initial_map, |
| JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject); |
| Handle<JSObject> result = |
| isolate->factory()->NewJSObjectFromMap(initial_map, NOT_TENURED, site); |
| if (initial_map->is_dictionary_map()) { |
| Handle<NameDictionary> dictionary = |
| NameDictionary::New(isolate, NameDictionary::kInitialCapacity); |
| result->SetProperties(*dictionary); |
| } |
| isolate->counters()->constructed_objects()->Increment(); |
| isolate->counters()->constructed_objects_runtime()->Increment(); |
| return result; |
| } |
| |
| void JSObject::EnsureWritableFastElements(Handle<JSObject> object) { |
| DCHECK(object->HasSmiOrObjectElements() || |
| object->HasFastStringWrapperElements()); |
| FixedArray* raw_elems = FixedArray::cast(object->elements()); |
| Heap* heap = object->GetHeap(); |
| if (raw_elems->map() != heap->fixed_cow_array_map()) return; |
| Isolate* isolate = heap->isolate(); |
| Handle<FixedArray> elems(raw_elems, isolate); |
| Handle<FixedArray> writable_elems = isolate->factory()->CopyFixedArrayWithMap( |
| elems, isolate->factory()->fixed_array_map()); |
| object->set_elements(*writable_elems); |
| isolate->counters()->cow_arrays_converted()->Increment(); |
| } |
| |
| int JSObject::GetHeaderSize(InstanceType type, |
| bool function_has_prototype_slot) { |
| switch (type) { |
| case JS_OBJECT_TYPE: |
| case JS_API_OBJECT_TYPE: |
| case JS_SPECIAL_API_OBJECT_TYPE: |
| return JSObject::kHeaderSize; |
| case JS_GENERATOR_OBJECT_TYPE: |
| return JSGeneratorObject::kSize; |
| case JS_ASYNC_GENERATOR_OBJECT_TYPE: |
| return JSAsyncGeneratorObject::kSize; |
| case JS_GLOBAL_PROXY_TYPE: |
| return JSGlobalProxy::kSize; |
| case JS_GLOBAL_OBJECT_TYPE: |
| return JSGlobalObject::kSize; |
| case JS_BOUND_FUNCTION_TYPE: |
| return JSBoundFunction::kSize; |
| case JS_FUNCTION_TYPE: |
| return function_has_prototype_slot ? JSFunction::kSizeWithPrototype |
| : JSFunction::kSizeWithoutPrototype; |
| case JS_VALUE_TYPE: |
| return JSValue::kSize; |
| case JS_DATE_TYPE: |
| return JSDate::kSize; |
| case JS_ARRAY_TYPE: |
| return JSArray::kSize; |
| case JS_ARRAY_BUFFER_TYPE: |
| return JSArrayBuffer::kSize; |
| case JS_TYPED_ARRAY_TYPE: |
| return JSTypedArray::kSize; |
| case JS_DATA_VIEW_TYPE: |
| return JSDataView::kSize; |
| case JS_SET_TYPE: |
| return JSSet::kSize; |
| case JS_MAP_TYPE: |
| return JSMap::kSize; |
| case JS_SET_KEY_VALUE_ITERATOR_TYPE: |
| case JS_SET_VALUE_ITERATOR_TYPE: |
| return JSSetIterator::kSize; |
| case JS_MAP_KEY_ITERATOR_TYPE: |
| case JS_MAP_KEY_VALUE_ITERATOR_TYPE: |
| case JS_MAP_VALUE_ITERATOR_TYPE: |
| return JSMapIterator::kSize; |
| case JS_WEAK_MAP_TYPE: |
| return JSWeakMap::kSize; |
| case JS_WEAK_SET_TYPE: |
| return JSWeakSet::kSize; |
| case JS_PROMISE_TYPE: |
| return JSPromise::kSize; |
| case JS_REGEXP_TYPE: |
| return JSRegExp::kSize; |
| case JS_CONTEXT_EXTENSION_OBJECT_TYPE: |
| return JSObject::kHeaderSize; |
| case JS_MESSAGE_OBJECT_TYPE: |
| return JSMessageObject::kSize; |
| case JS_ARGUMENTS_TYPE: |
| return JSObject::kHeaderSize; |
| case JS_ERROR_TYPE: |
| return JSObject::kHeaderSize; |
| case JS_STRING_ITERATOR_TYPE: |
| return JSStringIterator::kSize; |
| case JS_MODULE_NAMESPACE_TYPE: |
| return JSModuleNamespace::kHeaderSize; |
| case WASM_INSTANCE_TYPE: |
| return WasmInstanceObject::kSize; |
| case WASM_MEMORY_TYPE: |
| return WasmMemoryObject::kSize; |
| case WASM_MODULE_TYPE: |
| return WasmModuleObject::kSize; |
| case WASM_TABLE_TYPE: |
| return WasmTableObject::kSize; |
| default: |
| if (type >= FIRST_ARRAY_ITERATOR_TYPE && |
| type <= LAST_ARRAY_ITERATOR_TYPE) { |
| return JSArrayIterator::kSize; |
| } |
| UNREACHABLE(); |
| } |
| } |
| |
| // ES6 9.5.1 |
| // static |
| MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) { |
| Isolate* isolate = proxy->GetIsolate(); |
| Handle<String> trap_name = isolate->factory()->getPrototypeOf_string(); |
| |
| STACK_CHECK(isolate, MaybeHandle<Object>()); |
| |
| // 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), |
| Object); |
| } |
| 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, GetMethod(handler, trap_name), |
| Object); |
| // 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), Object); |
| // 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), |
| Object); |
| } |
| // 9. Let extensibleTarget be ? IsExtensible(target). |
| Maybe<bool> is_extensible = JSReceiver::IsExtensible(target); |
| MAYBE_RETURN_NULL(is_extensible); |
| // 10. If extensibleTarget is true, return handlerProto. |
| if (is_extensible.FromJust()) return handler_proto; |
| // 11. Let targetProto be ? target.[[GetPrototypeOf]](). |
| Handle<Object> target_proto; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto, |
| JSReceiver::GetPrototype(isolate, target), Object); |
| // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError. |
| if (!handler_proto->SameValue(*target_proto)) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible), |
| Object); |
| } |
| // 13. Return handlerProto. |
| return 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, |
| 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()) { |
| args.CallNamedSetterCallback( |
| reinterpret_cast<GenericNamedPropertySetterCallback>( |
| &Accessors::ReconfigureToDataProperty), |
| name, result); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, 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()) { |
| SaveContext save(isolate); |
| isolate->set_context(*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(Isolate* isolate, Address address, |
| AccessorComponent component) { |
| ApiFunction fun(address); |
| DCHECK_EQ(ACCESSOR_GETTER, component); |
| ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL; |
| return ExternalReference(&fun, type, isolate).address(); |
| } |
| |
| Address AccessorInfo::redirected_getter() const { |
| Address accessor = v8::ToCData<Address>(getter()); |
| if (accessor == nullptr) return nullptr; |
| return redirect(GetIsolate(), 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(&fun, type, GetIsolate()).address(); |
| } |
| |
| bool AccessorInfo::IsCompatibleReceiverMap(Isolate* isolate, |
| 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, |
| ShouldThrow 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>(); |
| } |
| |
| // The actual type of call_fun 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. |
| GenericNamedPropertySetterCallback call_fun = |
| v8::ToCData<GenericNamedPropertySetterCallback>(info->setter()); |
| |
| if (call_fun == nullptr) { |
| // 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>()); |
| } |
| |
| PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder, |
| should_throw); |
| Handle<Object> result = args.CallNamedSetterCallback(call_fun, 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() || should_throw == kDontThrow); |
| return Just(result->BooleanValue()); |
| } |
| |
| // Regular accessor. |
| Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); |
| if (setter->IsFunctionTemplateInfo()) { |
| SaveContext save(isolate); |
| isolate->set_context(*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, should_throw); |
| } |
| |
| RETURN_FAILURE(isolate, 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, |
| ShouldThrow should_throw) { |
| Isolate* isolate = setter->GetIsolate(); |
| |
| Handle<Object> argv[] = { value }; |
| RETURN_ON_EXCEPTION_VALUE(isolate, Execution::Call(isolate, setter, receiver, |
| arraysize(argv), argv), |
| Nothing<bool>()); |
| return Just(true); |
| } |
| |
| |
| // static |
| bool JSObject::AllCanRead(LookupIterator* it) { |
| // Skip current iteration, it's in state ACCESS_CHECK or INTERCEPTOR, both of |
| // which have already been checked. |
| DCHECK(it->state() == LookupIterator::ACCESS_CHECK || |
| it->state() == LookupIterator::INTERCEPTOR); |
| for (it->Next(); it->IsFound(); it->Next()) { |
| if (it->state() == LookupIterator::ACCESSOR) { |
| auto accessors = it->GetAccessors(); |
| if (accessors->IsAccessorInfo()) { |
| if (AccessorInfo::cast(*accessors)->all_can_read()) return true; |
| } |
| } else if (it->state() == LookupIterator::INTERCEPTOR) { |
| if (it->GetInterceptor()->all_can_read()) return true; |
| } else if (it->state() == LookupIterator::JSPROXY) { |
| // Stop lookupiterating. And no, AllCanNotRead. |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| namespace { |
| |
| MaybeHandle<Object> GetPropertyWithInterceptorInternal( |
| LookupIterator* it, Handle<InterceptorInfo> interceptor, bool* done) { |
| *done = false; |
| Isolate* isolate = it->isolate(); |
| // Make sure that the top context does not change when doing callbacks or |
| // interceptor calls. |
| AssertNoContextChange ncc(isolate); |
| |
| if (interceptor->getter()->IsUndefined(isolate)) { |
| return isolate->factory()->undefined_value(); |
| } |
| |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| Handle<Object> result; |
| Handle<Object> receiver = it->GetReceiver(); |
| if (!receiver->IsJSReceiver()) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, receiver, Object::ConvertReceiver(isolate, receiver), Object); |
| } |
| PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, |
| *holder, kDontThrow); |
| |
| if (it->IsElement()) { |
| result = args.CallIndexedGetter(interceptor, it->index()); |
| } else { |
| result = args.CallNamedGetter(interceptor, it->name()); |
| } |
| |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| if (result.is_null()) return isolate->factory()->undefined_value(); |
| *done = true; |
| // Rebox handle before return |
| return handle(*result, isolate); |
| } |
| |
| Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal( |
| LookupIterator* it, Handle<InterceptorInfo> interceptor) { |
| Isolate* isolate = it->isolate(); |
| // Make sure that the top context does not change when doing |
| // callbacks or interceptor calls. |
| AssertNoContextChange ncc(isolate); |
| HandleScope scope(isolate); |
| |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| DCHECK_IMPLIES(!it->IsElement() && it->name()->IsSymbol(), |
| interceptor->can_intercept_symbols()); |
| Handle<Object> receiver = it->GetReceiver(); |
| if (!receiver->IsJSReceiver()) { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, |
| Object::ConvertReceiver(isolate, receiver), |
| Nothing<PropertyAttributes>()); |
| } |
| PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, |
| *holder, kDontThrow); |
| if (!interceptor->query()->IsUndefined(isolate)) { |
| Handle<Object> result; |
| if (it->IsElement()) { |
| result = args.CallIndexedQuery(interceptor, it->index()); |
| } else { |
| result = args.CallNamedQuery(interceptor, it->name()); |
| } |
| if (!result.is_null()) { |
| int32_t value; |
| CHECK(result->ToInt32(&value)); |
| return Just(static_cast<PropertyAttributes>(value)); |
| } |
| } else if (!interceptor->getter()->IsUndefined(isolate)) { |
| // TODO(verwaest): Use GetPropertyWithInterceptor? |
| Handle<Object> result; |
| if (it->IsElement()) { |
| result = args.CallIndexedGetter(interceptor, it->index()); |
| } else { |
| result = args.CallNamedGetter(interceptor, it->name()); |
| } |
| if (!result.is_null()) return Just(DONT_ENUM); |
| } |
| |
| RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); |
| return Just(ABSENT); |
| } |
| |
| Maybe<bool> SetPropertyWithInterceptorInternal( |
| LookupIterator* it, Handle<InterceptorInfo> interceptor, |
| ShouldThrow should_throw, Handle<Object> value) { |
| Isolate* isolate = it->isolate(); |
| // Make sure that the top context does not change when doing callbacks or |
| // interceptor calls. |
| AssertNoContextChange ncc(isolate); |
| |
| if (interceptor->setter()->IsUndefined(isolate)) return Just(false); |
| |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| bool result; |
| Handle<Object> receiver = it->GetReceiver(); |
| if (!receiver->IsJSReceiver()) { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, |
| Object::ConvertReceiver(isolate, receiver), |
| Nothing<bool>()); |
| } |
| PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, |
| *holder, should_throw); |
| |
| if (it->IsElement()) { |
| // TODO(neis): In the future, we may want to actually return the |
| // interceptor's result, which then should be a boolean. |
| result = !args.CallIndexedSetter(interceptor, it->index(), value).is_null(); |
| } else { |
| result = !args.CallNamedSetter(interceptor, it->name(), value).is_null(); |
| } |
| |
| RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); |
| return Just(result); |
| } |
| |
| Maybe<bool> DefinePropertyWithInterceptorInternal( |
| LookupIterator* it, Handle<InterceptorInfo> interceptor, |
| ShouldThrow should_throw, PropertyDescriptor& desc) { |
| Isolate* isolate = it->isolate(); |
| // Make sure that the top context does not change when doing callbacks or |
| // interceptor calls. |
| AssertNoContextChange ncc(isolate); |
| |
| if (interceptor->definer()->IsUndefined(isolate)) return Just(false); |
| |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| bool result; |
| Handle<Object> receiver = it->GetReceiver(); |
| if (!receiver->IsJSReceiver()) { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, |
| Object::ConvertReceiver(isolate, receiver), |
| Nothing<bool>()); |
| } |
| PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, |
| *holder, should_throw); |
| |
| std::unique_ptr<v8::PropertyDescriptor> descriptor( |
| new v8::PropertyDescriptor()); |
| if (PropertyDescriptor::IsAccessorDescriptor(&desc)) { |
| descriptor.reset(new v8::PropertyDescriptor( |
| v8::Utils::ToLocal(desc.get()), v8::Utils::ToLocal(desc.set()))); |
| } else if (PropertyDescriptor::IsDataDescriptor(&desc)) { |
| if (desc.has_writable()) { |
| descriptor.reset(new v8::PropertyDescriptor( |
| v8::Utils::ToLocal(desc.value()), desc.writable())); |
| } else { |
| descriptor.reset( |
| new v8::PropertyDescriptor(v8::Utils::ToLocal(desc.value()))); |
| } |
| } |
| if (desc.has_enumerable()) { |
| descriptor->set_enumerable(desc.enumerable()); |
| } |
| if (desc.has_configurable()) { |
| descriptor->set_configurable(desc.configurable()); |
| } |
| |
| if (it->IsElement()) { |
| result = !args.CallIndexedDefiner(interceptor, it->index(), *descriptor) |
| .is_null(); |
| } else { |
| result = |
| !args.CallNamedDefiner(interceptor, it->name(), *descriptor).is_null(); |
| } |
| |
| RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); |
| return Just(result); |
| } |
| |
| } // namespace |
| |
| MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck( |
| LookupIterator* it) { |
| Isolate* isolate = it->isolate(); |
| Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| Handle<InterceptorInfo> interceptor = |
| it->GetInterceptorForFailedAccessCheck(); |
| if (interceptor.is_null()) { |
| while (AllCanRead(it)) { |
| if (it->state() == LookupIterator::ACCESSOR) { |
| return GetPropertyWithAccessor(it); |
| } |
| DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); |
| bool done; |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| GetPropertyWithInterceptor(it, &done), Object); |
| if (done) return result; |
| } |
| |
| } else { |
| Handle<Object> result; |
| bool done; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, |
| GetPropertyWithInterceptorInternal(it, interceptor, &done), Object); |
| if (done) return result; |
| } |
| |
| // Cross-Origin [[Get]] of Well-Known Symbols does not throw, and returns |
| // undefined. |
| Handle<Name> name = it->GetName(); |
| if (name->IsSymbol() && Symbol::cast(*name)->is_well_known_symbol()) { |
| return it->factory()->undefined_value(); |
| } |
| |
| isolate->ReportFailedAccessCheck(checked); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| return it->factory()->undefined_value(); |
| } |
| |
| |
| Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck( |
| LookupIterator* it) { |
| Isolate* isolate = it->isolate(); |
| Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| Handle<InterceptorInfo> interceptor = |
| it->GetInterceptorForFailedAccessCheck(); |
| if (interceptor.is_null()) { |
| while (AllCanRead(it)) { |
| if (it->state() == LookupIterator::ACCESSOR) { |
| return Just(it->property_attributes()); |
| } |
| DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); |
| auto result = GetPropertyAttributesWithInterceptor(it); |
| if (isolate->has_scheduled_exception()) break; |
| if (result.IsJust() && result.FromJust() != ABSENT) return result; |
| } |
| } else { |
| Maybe<PropertyAttributes> result = |
| GetPropertyAttributesWithInterceptorInternal(it, interceptor); |
| if (isolate->has_pending_exception()) return Nothing<PropertyAttributes>(); |
| if (result.FromMaybe(ABSENT) != ABSENT) return result; |
| } |
| isolate->ReportFailedAccessCheck(checked); |
| RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); |
| return Just(ABSENT); |
| } |
| |
| |
| // static |
| bool JSObject::AllCanWrite(LookupIterator* it) { |
| for (; it->IsFound() && it->state() != LookupIterator::JSPROXY; it->Next()) { |
| if (it->state() == LookupIterator::ACCESSOR) { |
| Handle<Object> accessors = it->GetAccessors(); |
| if (accessors->IsAccessorInfo()) { |
| if (AccessorInfo::cast(*accessors)->all_can_write()) return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| Maybe<bool> JSObject::SetPropertyWithFailedAccessCheck( |
| LookupIterator* it, Handle<Object> value, ShouldThrow should_throw) { |
| Isolate* isolate = it->isolate(); |
| Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| Handle<InterceptorInfo> interceptor = |
| it->GetInterceptorForFailedAccessCheck(); |
| if (interceptor.is_null()) { |
| if (AllCanWrite(it)) { |
| return SetPropertyWithAccessor(it, value, should_throw); |
| } |
| } else { |
| Maybe<bool> result = SetPropertyWithInterceptorInternal( |
| it, interceptor, should_throw, value); |
| if (isolate->has_pending_exception()) return Nothing<bool>(); |
| if (result.IsJust()) return result; |
| } |
| isolate->ReportFailedAccessCheck(checked); |
| RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); |
| return Just(true); |
| } |
| |
| |
| void JSObject::SetNormalizedProperty(Handle<JSObject> object, |
| Handle<Name> name, |
| Handle<Object> value, |
| PropertyDetails details) { |
| DCHECK(!object->HasFastProperties()); |
| DCHECK(name->IsUniqueName()); |
| Isolate* isolate = object->GetIsolate(); |
| |
| uint32_t hash = name->Hash(); |
| |
| if (object->IsJSGlobalObject()) { |
| Handle<JSGlobalObject> global_obj = Handle<JSGlobalObject>::cast(object); |
| Handle<GlobalDictionary> dictionary(global_obj->global_dictionary()); |
| int entry = dictionary->FindEntry(isolate, name, hash); |
| |
| if (entry == GlobalDictionary::kNotFound) { |
| DCHECK_IMPLIES(global_obj->map()->is_prototype_map(), |
| Map::IsPrototypeChainInvalidated(global_obj->map())); |
| auto cell = isolate->factory()->NewPropertyCell(name); |
| cell->set_value(*value); |
| auto cell_type = value->IsUndefined(isolate) |
| ? PropertyCellType::kUndefined |
| : PropertyCellType::kConstant; |
| details = details.set_cell_type(cell_type); |
| value = cell; |
| dictionary = GlobalDictionary::Add(dictionary, name, value, details); |
| global_obj->set_global_dictionary(*dictionary); |
| } else { |
| Handle<PropertyCell> cell = |
| PropertyCell::PrepareForValue(dictionary, entry, value, details); |
| cell->set_value(*value); |
| } |
| } else { |
| Handle<NameDictionary> dictionary(object->property_dictionary()); |
| |
| int entry = dictionary->FindEntry(name); |
| if (entry == NameDictionary::kNotFound) { |
| DCHECK_IMPLIES(object->map()->is_prototype_map(), |
| Map::IsPrototypeChainInvalidated(object->map())); |
| dictionary = NameDictionary::Add(dictionary, name, value, details); |
| object->SetProperties(*dictionary); |
| } else { |
| PropertyDetails original_details = dictionary->DetailsAt(entry); |
| int enumeration_index = original_details.dictionary_index(); |
| DCHECK_GT(enumeration_index, 0); |
| details = details.set_index(enumeration_index); |
| dictionary->SetEntry(entry, *name, *value, details); |
| } |
| } |
| } |
| |
| // static |
| Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate, |
| Handle<JSReceiver> object, |
| Handle<Object> proto) { |
| PrototypeIterator iter(isolate, object, kStartAtReceiver); |
| while (true) { |
| if (!iter.AdvanceFollowingProxies()) return Nothing<bool>(); |
| if (iter.IsAtEnd()) return Just(false); |
| if (PrototypeIterator::GetCurrent(iter).is_identical_to(proto)) { |
| return Just(true); |
| } |
| } |
| } |
| |
| namespace { |
| |
| bool HasExcludedProperty( |
| const ScopedVector<Handle<Object>>* excluded_properties, |
| Handle<Object> search_element) { |
| // TODO(gsathya): Change this to be a hashtable. |
| for (int i = 0; i < excluded_properties->length(); i++) { |
| if (search_element->SameValue(*excluded_properties->at(i))) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| MUST_USE_RESULT Maybe<bool> FastAssign( |
| Handle<JSReceiver> target, Handle<Object> source, |
| const ScopedVector<Handle<Object>>* excluded_properties, bool use_set) { |
| // Non-empty strings are the only non-JSReceivers that need to be handled |
| // explicitly by Object.assign. |
| if (!source->IsJSReceiver()) { |
| return Just(!source->IsString() || String::cast(*source)->length() == 0); |
| } |
| |
| // If the target is deprecated, the object will be updated on first store. If |
| // the source for that store equals the target, this will invalidate the |
| // cached representation of the source. Preventively upgrade the target. |
| // Do this on each iteration since any property load could cause deprecation. |
| if (target->map()->is_deprecated()) { |
| JSObject::MigrateInstance(Handle<JSObject>::cast(target)); |
| } |
| |
| Isolate* isolate = target->GetIsolate(); |
| Handle<Map> map(JSReceiver::cast(*source)->map(), isolate); |
| |
| if (!map->IsJSObjectMap()) return Just(false); |
| if (!map->OnlyHasSimpleProperties()) return Just(false); |
| |
| Handle<JSObject> from = Handle<JSObject>::cast(source); |
| if (from->elements() != isolate->heap()->empty_fixed_array()) { |
| return Just(false); |
| } |
| |
| Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate); |
| int length = map->NumberOfOwnDescriptors(); |
| |
| bool stable = true; |
| |
| for (int i = 0; i < length; i++) { |
| Handle<Name> next_key(descriptors->GetKey(i), isolate); |
| Handle<Object> prop_value; |
| // Directly decode from the descriptor array if |from| did not change shape. |
| if (stable) { |
| PropertyDetails details = descriptors->GetDetails(i); |
| if (!details.IsEnumerable()) continue; |
| if (details.kind() == kData) { |
| if (details.location() == kDescriptor) { |
| prop_value = handle(descriptors->GetValue(i), isolate); |
| } else { |
| Representation representation = details.representation(); |
| FieldIndex index = FieldIndex::ForDescriptor(*map, i); |
| prop_value = JSObject::FastPropertyAt(from, representation, index); |
| } |
| } else { |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, prop_value, JSReceiver::GetProperty(from, next_key), |
| Nothing<bool>()); |
| stable = from->map() == *map; |
| } |
| } else { |
| // If the map did change, do a slower lookup. We are still guaranteed that |
| // the object has a simple shape, and that the key is a name. |
| LookupIterator it(from, next_key, from, |
| LookupIterator::OWN_SKIP_INTERCEPTOR); |
| if (!it.IsFound()) continue; |
| DCHECK(it.state() == LookupIterator::DATA || |
| it.state() == LookupIterator::ACCESSOR); |
| if (!it.IsEnumerable()) continue; |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, prop_value, Object::GetProperty(&it), Nothing<bool>()); |
| } |
| |
| if (use_set) { |
| LookupIterator it(target, next_key, target); |
| Maybe<bool> result = |
| Object::SetProperty(&it, prop_value, LanguageMode::kStrict, |
| Object::CERTAINLY_NOT_STORE_FROM_KEYED); |
| if (result.IsNothing()) return result; |
| if (stable) stable = from->map() == *map; |
| } else { |
| if (excluded_properties != nullptr && |
| HasExcludedProperty(excluded_properties, next_key)) { |
| continue; |
| } |
| |
| // 4a ii 2. Perform ? CreateDataProperty(target, nextKey, propValue). |
| bool success; |
| LookupIterator it = LookupIterator::PropertyOrElement( |
| isolate, target, next_key, &success, LookupIterator::OWN); |
| CHECK(success); |
| CHECK(JSObject::CreateDataProperty(&it, prop_value, kThrowOnError) |
| .FromJust()); |
| } |
| } |
| |
| return Just(true); |
| } |
| } // namespace |
| |
| // static |
| Maybe<bool> JSReceiver::SetOrCopyDataProperties( |
| Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source, |
| const ScopedVector<Handle<Object>>* excluded_properties, bool use_set) { |
| Maybe<bool> fast_assign = |
| FastAssign(target, source, excluded_properties, use_set); |
| if (fast_assign.IsNothing()) return Nothing<bool>(); |
| if (fast_assign.FromJust()) return Just(true); |
| |
| Handle<JSReceiver> from = Object::ToObject(isolate, source).ToHandleChecked(); |
| // 3b. Let keys be ? from.[[OwnPropertyKeys]](). |
| Handle<FixedArray> keys; |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, keys, |
| KeyAccumulator::GetKeys(from, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES, |
| GetKeysConversion::kKeepNumbers), |
| Nothing<bool>()); |
| |
| // 4. Repeat for each element nextKey of keys in List order, |
| for (int j = 0; j < keys->length(); ++j) { |
| Handle<Object> next_key(keys->get(j), isolate); |
| // 4a i. Let desc be ? from.[[GetOwnProperty]](nextKey). |
| PropertyDescriptor desc; |
| Maybe<bool> found = |
| JSReceiver::GetOwnPropertyDescriptor(isolate, from, next_key, &desc); |
| if (found.IsNothing()) return Nothing<bool>(); |
| // 4a ii. If desc is not undefined and desc.[[Enumerable]] is true, then |
| if (found.FromJust() && desc.enumerable()) { |
| // 4a ii 1. Let propValue be ? Get(from, nextKey). |
| Handle<Object> prop_value; |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, prop_value, |
| Runtime::GetObjectProperty(isolate, from, next_key), Nothing<bool>()); |
| |
| if (use_set) { |
| // 4c ii 2. Let status be ? Set(to, nextKey, propValue, true). |
| Handle<Object> status; |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, status, |
| Runtime::SetObjectProperty(isolate, target, next_key, prop_value, |
| LanguageMode::kStrict), |
| Nothing<bool>()); |
| } else { |
| if (excluded_properties != nullptr && |
| HasExcludedProperty(excluded_properties, next_key)) { |
| continue; |
| } |
| |
| // 4a ii 2. Perform ! CreateDataProperty(target, nextKey, propValue). |
| bool success; |
| LookupIterator it = LookupIterator::PropertyOrElement( |
| isolate, target, next_key, &success, LookupIterator::OWN); |
| CHECK(success); |
| CHECK(JSObject::CreateDataProperty(&it, prop_value, kThrowOnError) |
| .FromJust()); |
| } |
| } |
| } |
| |
| 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); |
| } |
| |
| Map* Map::GetPrototypeChainRootMap(Isolate* isolate) const { |
| DisallowHeapAllocation no_alloc; |
| if (IsJSReceiverMap()) { |
| return const_cast<Map*>(this); |
| } |
| int constructor_function_index = GetConstructorFunctionIndex(); |
| if (constructor_function_index != Map::kNoConstructorFunctionIndex) { |
| Context* native_context = isolate->context()->native_context(); |
| JSFunction* constructor_function = |
| JSFunction::cast(native_context->get(constructor_function_index)); |
| return constructor_function->initial_map(); |
| } |
| return isolate->heap()->null_value()->map(); |
| } |
| |
| namespace { |
| |
| // Returns a non-SMI for JSReceivers, but returns the hash code for simple |
| // objects. This avoids a double lookup in the cases where we know we will |
| // add the hash to the JSReceiver if it does not already exist. |
| Object* GetSimpleHash(Object* object) { |
| DisallowHeapAllocation no_gc; |
| if (object->IsSmi()) { |
| uint32_t hash = ComputeIntegerHash(Smi::ToInt(object)); |
| return Smi::FromInt(hash & Smi::kMaxValue); |
| } |
| if (object->IsHeapNumber()) { |
| double num = HeapNumber::cast(object)->value(); |
| if (std::isnan(num)) return Smi::FromInt(Smi::kMaxValue); |
| // Use ComputeIntegerHash for all values in Signed32 range, including -0, |
| // which is considered equal to 0 because collections use SameValueZero. |
| int32_t inum = FastD2I(num); |
| uint32_t hash = (FastI2D(inum) == num) |
| ? ComputeIntegerHash(inum) |
| : ComputeLongHash(double_to_uint64(num)); |
| return Smi::FromInt(hash & Smi::kMaxValue); |
| } |
| if (object->IsName()) { |
| uint32_t hash = Name::cast(object)->Hash(); |
| return Smi::FromInt(hash); |
| } |
| if (object->IsOddball()) { |
| uint32_t hash = Oddball::cast(object)->to_string()->Hash(); |
| return Smi::FromInt(hash); |
| } |
| if (object->IsBigInt()) { |
| uint32_t hash = BigInt::cast(object)->Hash(); |
| return Smi::FromInt(hash & Smi::kMaxValue); |
| } |
| DCHECK(object->IsJSReceiver()); |
| return object; |
| } |
| |
| } // namespace |
| |
| Object* Object::GetHash() { |
| DisallowHeapAllocation no_gc; |
| Object* hash = GetSimpleHash(this); |
| if (hash->IsSmi()) return hash; |
| |
| DCHECK(IsJSReceiver()); |
| JSReceiver* receiver = JSReceiver::cast(this); |
| Isolate* isolate = receiver->GetIsolate(); |
| return receiver->GetIdentityHash(isolate); |
| } |
| |
| // static |
| Smi* Object::GetOrCreateHash(Isolate* isolate, Object* key) { |
| DisallowHeapAllocation no_gc; |
| return key->GetOrCreateHash(isolate); |
| } |
| |
| Smi* Object::GetOrCreateHash(Isolate* isolate) { |
| DisallowHeapAllocation no_gc; |
| Object* hash = 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()) { |
| double this_value = Number(); |
| double other_value = other->Number(); |
| // SameValue(NaN, NaN) is true. |
| if (this_value != other_value) { |
| return std::isnan(this_value) && std::isnan(other_value); |
| } |
| // SameValue(0.0, -0.0) is false. |
| return (std::signbit(this_value) == std::signbit(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; |
| } |
| |
| |
| 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(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(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 ) |
| MUST_USE_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(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(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; |
| |
| // Check that the map of the initial array iterator hasn't changed. |
| Map* iterator_map = isolate->initial_array_iterator_prototype()->map(); |
| if (!isolate->is_initial_array_iterator_prototype_map(iterator_map)) { |
| 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) { |
| OFStream os(out); |
| os << Brief(this); |
| } |
| |
| |
| void Object::ShortPrint(StringStream* accumulator) { |
| std::ostringstream os; |
| os << Brief(this); |
| |