| // 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 <sstream> |
| |
| #include "src/accessors.h" |
| #include "src/allocation-site-scopes.h" |
| #include "src/api.h" |
| #include "src/api-natives.h" |
| #include "src/arguments.h" |
| #include "src/base/bits.h" |
| #include "src/base/utils/random-number-generator.h" |
| #include "src/bootstrapper.h" |
| #include "src/code-stubs.h" |
| #include "src/codegen.h" |
| #include "src/compilation-dependencies.h" |
| #include "src/compiler.h" |
| #include "src/date.h" |
| #include "src/debug/debug.h" |
| #include "src/deoptimizer.h" |
| #include "src/elements.h" |
| #include "src/execution.h" |
| #include "src/field-index.h" |
| #include "src/field-index-inl.h" |
| #include "src/full-codegen/full-codegen.h" |
| #include "src/ic/ic.h" |
| #include "src/identity-map.h" |
| #include "src/interpreter/bytecodes.h" |
| #include "src/interpreter/source-position-table.h" |
| #include "src/isolate-inl.h" |
| #include "src/key-accumulator.h" |
| #include "src/list.h" |
| #include "src/log.h" |
| #include "src/lookup.h" |
| #include "src/macro-assembler.h" |
| #include "src/messages.h" |
| #include "src/objects-inl.h" |
| #include "src/objects-body-descriptors-inl.h" |
| #include "src/profiler/cpu-profiler.h" |
| #include "src/property-descriptor.h" |
| #include "src/prototype.h" |
| #include "src/regexp/jsregexp.h" |
| #include "src/safepoint-table.h" |
| #include "src/string-builder.h" |
| #include "src/string-search.h" |
| #include "src/string-stream.h" |
| #include "src/utils.h" |
| #include "src/zone.h" |
| |
| #ifdef ENABLE_DISASSEMBLER |
| #include "src/disasm.h" |
| #include "src/disassembler.h" |
| #endif |
| |
| namespace v8 { |
| namespace internal { |
| |
| 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(); |
| return os << "UNKNOWN"; // Keep the compiler happy. |
| } |
| |
| 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) { |
| 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) { |
| 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; |
| } |
| |
| |
| // static |
| MaybeHandle<Name> Object::ToName(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); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::ToNumber(Handle<Object> input) { |
| 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)); |
| } |
| Isolate* const isolate = Handle<HeapObject>::cast(input)->GetIsolate(); |
| if (input->IsSymbol()) { |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber), |
| Object); |
| } |
| if (input->IsSimd128Value()) { |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSimdToNumber), |
| Object); |
| } |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input), |
| ToPrimitiveHint::kNumber), |
| Object); |
| } |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::ToInteger(Isolate* isolate, Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); |
| return isolate->factory()->NewNumber(DoubleToInteger(input->Number())); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::ToInt32(Isolate* isolate, Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); |
| return isolate->factory()->NewNumberFromInt(DoubleToInt32(input->Number())); |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::ToUint32(Isolate* isolate, Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); |
| return isolate->factory()->NewNumberFromUint(DoubleToUint32(input->Number())); |
| } |
| |
| |
| // static |
| MaybeHandle<String> Object::ToString(Isolate* isolate, Handle<Object> input) { |
| while (true) { |
| if (input->IsString()) { |
| return Handle<String>::cast(input); |
| } |
| 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->IsSimd128Value()) { |
| return Simd128Value::ToString(Handle<Simd128Value>::cast(input)); |
| } |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, input, JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input), |
| ToPrimitiveHint::kString), |
| String); |
| } |
| } |
| |
| |
| // static |
| MaybeHandle<Object> Object::ToLength(Isolate* isolate, Handle<Object> input) { |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); |
| double len = DoubleToInteger(input->Number()); |
| if (len <= 0.0) { |
| len = 0.0; |
| } else if (len >= kMaxSafeInteger) { |
| len = kMaxSafeInteger; |
| } |
| return isolate->factory()->NewNumber(len); |
| } |
| |
| |
| bool Object::BooleanValue() { |
| if (IsBoolean()) return IsTrue(); |
| if (IsSmi()) return Smi::cast(this)->value() != 0; |
| if (IsUndefined() || IsNull()) return false; |
| if (IsUndetectableObject()) return false; // Undetectable object is false. |
| if (IsString()) return String::cast(this)->length() != 0; |
| if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue(); |
| 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); |
| } |
| |
| } // namespace |
| |
| |
| // static |
| Maybe<ComparisonResult> Object::Compare(Handle<Object> x, Handle<Object> y, |
| Strength strength) { |
| if (!is_strong(strength)) { |
| // 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 (!is_strong(strength)) { |
| if (!Object::ToNumber(x).ToHandle(&x) || |
| !Object::ToNumber(y).ToHandle(&y)) { |
| return Nothing<ComparisonResult>(); |
| } |
| } else { |
| if (!x->IsNumber()) { |
| Isolate* const isolate = Handle<HeapObject>::cast(x)->GetIsolate(); |
| isolate->Throw(*isolate->factory()->NewTypeError( |
| MessageTemplate::kStrongImplicitConversion)); |
| return Nothing<ComparisonResult>(); |
| } else if (!y->IsNumber()) { |
| Isolate* const isolate = Handle<HeapObject>::cast(y)->GetIsolate(); |
| isolate->Throw(*isolate->factory()->NewTypeError( |
| MessageTemplate::kStrongImplicitConversion)); |
| return Nothing<ComparisonResult>(); |
| } |
| } |
| return Just(NumberCompare(x->Number(), y->Number())); |
| } |
| |
| |
| // static |
| Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) { |
| 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->IsJSReceiver() && !y->IsUndetectableObject()) { |
| 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->IsJSReceiver() && !y->IsUndetectableObject()) { |
| 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->IsJSReceiver() && !y->IsUndetectableObject()) { |
| 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() && !y->IsUndetectableObject()) { |
| if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(false); |
| } |
| } else if (x->IsSimd128Value()) { |
| if (y->IsSimd128Value()) { |
| return Just(Simd128Value::Equals(Handle<Simd128Value>::cast(x), |
| Handle<Simd128Value>::cast(y))); |
| } else if (y->IsJSReceiver() && !y->IsUndetectableObject()) { |
| if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) |
| .ToHandle(&y)) { |
| return Nothing<bool>(); |
| } |
| } else { |
| return Just(false); |
| } |
| } else if (x->IsJSReceiver() && !x->IsUndetectableObject()) { |
| if (y->IsJSReceiver()) { |
| return Just(x.is_identical_to(y)); |
| } else if (y->IsNull() || y->IsUndefined()) { |
| return Just(false); |
| } 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->IsNull() || x->IsUndefined() || x->IsUndetectableObject()) && |
| (y->IsNull() || y->IsUndefined() || y->IsUndetectableObject())); |
| } |
| } |
| } |
| |
| |
| 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->IsSimd128Value()) { |
| if (!that->IsSimd128Value()) return false; |
| return Simd128Value::cast(this)->Equals(Simd128Value::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->IsUndefined() || object->IsUndetectableObject()) { |
| return isolate->factory()->undefined_string(); |
| } |
| if (object->IsBoolean()) return isolate->factory()->boolean_string(); |
| if (object->IsString()) return isolate->factory()->string_string(); |
| if (object->IsSymbol()) return isolate->factory()->symbol_string(); |
| if (object->IsString()) return isolate->factory()->string_string(); |
| #define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \ |
| if (object->Is##Type()) return isolate->factory()->type##_string(); |
| SIMD128_TYPES(SIMD128_TYPE) |
| #undef SIMD128_TYPE |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| 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)); |
| } else if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, |
| Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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, Strength strength) { |
| if (!lhs->IsNumber() || !rhs->IsNumber()) { |
| if (is_strong(strength)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kStrongImplicitConversion), |
| Object); |
| } |
| 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)); |
| } |
| |
| |
| Maybe<bool> Object::IsArray(Handle<Object> object) { |
| if (object->IsJSArray()) return Just(true); |
| if (object->IsJSProxy()) { |
| Handle<JSProxy> proxy = Handle<JSProxy>::cast(object); |
| Isolate* isolate = proxy->GetIsolate(); |
| if (proxy->IsRevoked()) { |
| isolate->Throw(*isolate->factory()->NewTypeError( |
| MessageTemplate::kProxyRevoked, |
| isolate->factory()->NewStringFromAsciiChecked("IsArray"))); |
| return Nothing<bool>(); |
| } |
| return Object::IsArray(handle(proxy->target(), isolate)); |
| } |
| return Just(false); |
| } |
| |
| |
| bool Object::IsPromise(Handle<Object> object) { |
| if (!object->IsJSObject()) return false; |
| auto js_object = Handle<JSObject>::cast(object); |
| // Promises can't have access checks. |
| if (js_object->map()->is_access_check_needed()) return false; |
| auto isolate = js_object->GetIsolate(); |
| // TODO(dcarney): this should just be read from the symbol registry so as not |
| // to be context dependent. |
| auto key = isolate->factory()->promise_status_symbol(); |
| // Shouldn't be possible to throw here. |
| return JSObject::HasRealNamedProperty(js_object, key).FromJust(); |
| } |
| |
| |
| // 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->IsNull() || func->IsUndefined()) { |
| return isolate->factory()->undefined_value(); |
| } |
| if (!func->IsCallable()) { |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kPropertyNotFunction, |
| func, name, receiver), |
| Object); |
| } |
| return func; |
| } |
| |
| |
| // static |
| MaybeHandle<FixedArray> Object::CreateListFromArrayLike( |
| Isolate* isolate, Handle<Object> object, ElementTypes element_types) { |
| // 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<Object> raw_length_obj; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, raw_length_obj, |
| JSReceiver::GetProperty(object, isolate->factory()->length_string()), |
| FixedArray); |
| Handle<Object> raw_length_number; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number, |
| Object::ToLength(isolate, raw_length_obj), |
| 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, Object::GetElement(isolate, object, 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 |
| 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: |
| // Call the "has" trap on proxies. |
| return JSProxy::HasProperty(it->isolate(), it->GetHolder<JSProxy>(), |
| it->GetName()); |
| case LookupIterator::INTERCEPTOR: { |
| Maybe<PropertyAttributes> result = |
| JSObject::GetPropertyAttributesWithInterceptor(it); |
| if (!result.IsJust()) 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.IsJust()) 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 |
| MaybeHandle<Object> Object::GetProperty(LookupIterator* it, |
| LanguageMode language_mode) { |
| for (; it->IsFound(); it->Next()) { |
| switch (it->state()) { |
| case LookupIterator::NOT_FOUND: |
| case LookupIterator::TRANSITION: |
| UNREACHABLE(); |
| case LookupIterator::JSPROXY: |
| return JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(), |
| it->GetName(), it->GetReceiver(), |
| language_mode); |
| 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, language_mode); |
| case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| return ReadAbsentProperty(it, language_mode); |
| case LookupIterator::DATA: |
| return it->GetDataValue(); |
| } |
| } |
| return ReadAbsentProperty(it, language_mode); |
| } |
| |
| |
| #define STACK_CHECK(result_value) \ |
| do { \ |
| StackLimitCheck stack_check(isolate); \ |
| if (stack_check.HasOverflowed()) { \ |
| isolate->Throw(*isolate->factory()->NewRangeError( \ |
| MessageTemplate::kStackOverflow)); \ |
| return result_value; \ |
| } \ |
| } while (false) |
| |
| |
| // static |
| MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate, |
| Handle<JSProxy> proxy, |
| Handle<Name> name, |
| Handle<Object> receiver, |
| LanguageMode language_mode) { |
| if (receiver->IsJSGlobalObject()) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kReadGlobalReferenceThroughProxy, name), |
| Object); |
| } |
| |
| DCHECK(!name->IsPrivate()); |
| STACK_CHECK(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(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()) { |
| // 7.a Return target.[[Get]](P, Receiver). |
| LookupIterator it = |
| LookupIterator::PropertyOrElement(isolate, receiver, name, target); |
| return Object::GetProperty(&it, language_mode); |
| } |
| // 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); |
| // 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) { |
| THROW_NEW_ERROR( |
| isolate, NewTypeError(MessageTemplate::kProxyGetNonConfigurableData, |
| name, target_desc.value(), trap_result), |
| 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. |
| inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && |
| !target_desc.configurable() && |
| target_desc.get()->IsUndefined() && |
| !trap_result->IsUndefined(); |
| if (inconsistent) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor, name, |
| trap_result), |
| Object); |
| } |
| } |
| // 11. Return trap_result |
| return trap_result; |
| } |
| |
| |
| Handle<Object> JSReceiver::GetDataProperty(Handle<JSReceiver> object, |
| Handle<Name> name) { |
| LookupIterator it(object, name, |
| LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); |
| return GetDataProperty(&it); |
| } |
| |
| |
| 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::cast(this)->value(); |
| return true; |
| } |
| if (IsHeapNumber()) { |
| double num = HeapNumber::cast(this)->value(); |
| if (FastI2D(FastD2I(num)) == num) { |
| *value = FastD2I(num); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| bool Object::ToUint32(uint32_t* value) { |
| if (IsSmi()) { |
| int num = Smi::cast(this)->value(); |
| if (num < 0) return false; |
| *value = static_cast<uint32_t>(num); |
| return true; |
| } |
| if (IsHeapNumber()) { |
| double num = HeapNumber::cast(this)->value(); |
| if (num < 0) return false; |
| uint32_t uint_value = FastD2UI(num); |
| if (FastUI2D(uint_value) == num) { |
| *value = uint_value; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| bool FunctionTemplateInfo::IsTemplateFor(Object* object) { |
| if (!object->IsHeapObject()) return false; |
| return IsTemplateFor(HeapObject::cast(object)->map()); |
| } |
| |
| |
| 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(); |
| if (!cons_obj->IsJSFunction()) return false; |
| JSFunction* fun = JSFunction::cast(cons_obj); |
| // Iterate through the chain of inheriting function templates to |
| // see if the required one occurs. |
| for (Object* type = fun->shared()->function_data(); |
| type->IsFunctionTemplateInfo(); |
| type = FunctionTemplateInfo::cast(type)->parent_template()) { |
| if (type == this) return true; |
| } |
| // Didn't find the required type in the inheritance chain. |
| return false; |
| } |
| |
| |
| // TODO(dcarney): CallOptimization duplicates this logic, merge. |
| Object* FunctionTemplateInfo::GetCompatibleReceiver(Isolate* isolate, |
| Object* receiver) { |
| // API calls are only supported with JSObject receivers. |
| if (!receiver->IsJSObject()) return isolate->heap()->null_value(); |
| Object* recv_type = this->signature(); |
| // No signature, return holder. |
| if (recv_type->IsUndefined()) return receiver; |
| FunctionTemplateInfo* signature = FunctionTemplateInfo::cast(recv_type); |
| // Check the receiver. |
| for (PrototypeIterator iter(isolate, JSObject::cast(receiver), |
| PrototypeIterator::START_AT_RECEIVER, |
| PrototypeIterator::END_AT_NON_HIDDEN); |
| !iter.IsAtEnd(); iter.Advance()) { |
| if (signature->IsTemplateFor(iter.GetCurrent())) return iter.GetCurrent(); |
| } |
| return isolate->heap()->null_value(); |
| } |
| |
| |
| // 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); |
| isolate->counters()->constructed_objects()->Increment(); |
| isolate->counters()->constructed_objects_runtime()->Increment(); |
| return result; |
| } |
| |
| |
| Handle<FixedArray> JSObject::EnsureWritableFastElements( |
| Handle<JSObject> object) { |
| DCHECK(object->HasFastSmiOrObjectElements() || |
| object->HasFastStringWrapperElements()); |
| Isolate* isolate = object->GetIsolate(); |
| Handle<FixedArray> elems(FixedArray::cast(object->elements()), isolate); |
| if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems; |
| Handle<FixedArray> writable_elems = isolate->factory()->CopyFixedArrayWithMap( |
| elems, isolate->factory()->fixed_array_map()); |
| object->set_elements(*writable_elems); |
| isolate->counters()->cow_arrays_converted()->Increment(); |
| return writable_elems; |
| } |
| |
| |
| // 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(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(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()) { |
| 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())) { |
| 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, LanguageMode language_mode) { |
| Isolate* isolate = it->isolate(); |
| Handle<Object> structure = it->GetAccessors(); |
| Handle<Object> receiver = it->GetReceiver(); |
| |
| // 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. |
| if (structure->IsAccessorInfo()) { |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| 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); |
| } |
| |
| v8::AccessorNameGetterCallback call_fun = |
| v8::ToCData<v8::AccessorNameGetterCallback>(info->getter()); |
| if (call_fun == nullptr) return isolate->factory()->undefined_value(); |
| |
| LOG(isolate, ApiNamedPropertyAccess("load", *holder, *name)); |
| PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder, |
| Object::DONT_THROW); |
| v8::Local<v8::Value> result = args.Call(call_fun, v8::Utils::ToLocal(name)); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| if (result.IsEmpty()) { |
| return ReadAbsentProperty(isolate, receiver, name, language_mode); |
| } |
| Handle<Object> return_value = v8::Utils::OpenHandle(*result); |
| return_value->VerifyApiCallResultType(); |
| // Rebox handle before return. |
| return handle(*return_value, isolate); |
| } |
| |
| // Regular accessor. |
| Handle<Object> getter(AccessorPair::cast(*structure)->getter(), isolate); |
| if (getter->IsFunctionTemplateInfo()) { |
| auto result = Builtins::InvokeApiFunction( |
| Handle<FunctionTemplateInfo>::cast(getter), receiver, 0, nullptr); |
| if (isolate->has_pending_exception()) { |
| return MaybeHandle<Object>(); |
| } |
| Handle<Object> return_value; |
| if (result.ToHandle(&return_value)) { |
| return_value->VerifyApiCallResultType(); |
| return handle(*return_value, isolate); |
| } |
| } 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 ReadAbsentProperty(isolate, receiver, it->GetName(), language_mode); |
| } |
| |
| |
| 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(); |
| |
| // 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. |
| if (structure->IsAccessorInfo()) { |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| 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>(); |
| } |
| |
| v8::AccessorNameSetterCallback call_fun = |
| v8::ToCData<v8::AccessorNameSetterCallback>(info->setter()); |
| // TODO(verwaest): We should not get here anymore once all AccessorInfos are |
| // marked as special_data_property. They cannot both be writable and not |
| // have a setter. |
| if (call_fun == nullptr) return Just(true); |
| |
| LOG(isolate, ApiNamedPropertyAccess("store", *holder, *name)); |
| PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder, |
| should_throw); |
| args.Call(call_fun, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value)); |
| RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); |
| return Just(true); |
| } |
| |
| // Regular accessor. |
| Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); |
| if (setter->IsFunctionTemplateInfo()) { |
| Handle<Object> argv[] = {value}; |
| auto result = |
| Builtins::InvokeApiFunction(Handle<FunctionTemplateInfo>::cast(setter), |
| receiver, arraysize(argv), argv); |
| if (isolate->has_pending_exception()) { |
| return 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, NULL); |
| } |
| |
| |
| 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 Object::IsErrorObject(Isolate* isolate, Handle<Object> object) { |
| if (!object->IsJSObject()) return false; |
| // Use stack_trace_symbol as proxy for [[ErrorData]]. |
| Handle<Name> symbol = isolate->factory()->stack_trace_symbol(); |
| Maybe<bool> has_stack_trace = |
| JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol); |
| DCHECK(!has_stack_trace.IsNothing()); |
| return has_stack_trace.FromJust(); |
| } |
| |
| |
| // 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; |
| } |
| |
| |
| MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck( |
| LookupIterator* it) { |
| Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| while (AllCanRead(it)) { |
| if (it->state() == LookupIterator::ACCESSOR) { |
| return GetPropertyWithAccessor(it, SLOPPY); |
| } |
| DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); |
| bool done; |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION(it->isolate(), result, |
| GetPropertyWithInterceptor(it, &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(); |
| } |
| |
| it->isolate()->ReportFailedAccessCheck(checked); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object); |
| return it->factory()->undefined_value(); |
| } |
| |
| |
| Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck( |
| LookupIterator* it) { |
| Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| while (AllCanRead(it)) { |
| if (it->state() == LookupIterator::ACCESSOR) { |
| return Just(it->property_attributes()); |
| } |
| DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); |
| auto result = GetPropertyAttributesWithInterceptor(it); |
| if (it->isolate()->has_scheduled_exception()) break; |
| if (result.IsJust() && result.FromJust() != ABSENT) return result; |
| } |
| it->isolate()->ReportFailedAccessCheck(checked); |
| RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->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) { |
| Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| if (AllCanWrite(it)) { |
| return SetPropertyWithAccessor(it, value, should_throw); |
| } |
| |
| it->isolate()->ReportFailedAccessCheck(checked); |
| RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); |
| return Just(true); |
| } |
| |
| |
| void JSObject::SetNormalizedProperty(Handle<JSObject> object, |
| Handle<Name> name, |
| Handle<Object> value, |
| PropertyDetails details) { |
| DCHECK(!object->HasFastProperties()); |
| if (!name->IsUniqueName()) { |
| name = object->GetIsolate()->factory()->InternalizeString( |
| Handle<String>::cast(name)); |
| } |
| |
| if (object->IsJSGlobalObject()) { |
| Handle<GlobalDictionary> property_dictionary(object->global_dictionary()); |
| |
| int entry = property_dictionary->FindEntry(name); |
| if (entry == GlobalDictionary::kNotFound) { |
| auto cell = object->GetIsolate()->factory()->NewPropertyCell(); |
| cell->set_value(*value); |
| auto cell_type = value->IsUndefined() ? PropertyCellType::kUndefined |
| : PropertyCellType::kConstant; |
| details = details.set_cell_type(cell_type); |
| value = cell; |
| property_dictionary = |
| GlobalDictionary::Add(property_dictionary, name, value, details); |
| object->set_properties(*property_dictionary); |
| } else { |
| PropertyCell::UpdateCell(property_dictionary, entry, value, details); |
| } |
| } else { |
| Handle<NameDictionary> property_dictionary(object->property_dictionary()); |
| |
| int entry = property_dictionary->FindEntry(name); |
| if (entry == NameDictionary::kNotFound) { |
| property_dictionary = |
| NameDictionary::Add(property_dictionary, name, value, details); |
| object->set_properties(*property_dictionary); |
| } else { |
| PropertyDetails original_details = property_dictionary->DetailsAt(entry); |
| int enumeration_index = original_details.dictionary_index(); |
| DCHECK(enumeration_index > 0); |
| details = details.set_index(enumeration_index); |
| property_dictionary->SetEntry(entry, name, value, details); |
| } |
| } |
| } |
| |
| Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate, |
| Handle<JSReceiver> object, |
| Handle<Object> proto) { |
| PrototypeIterator iter(isolate, object, PrototypeIterator::START_AT_RECEIVER); |
| 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); |
| } |
| } |
| } |
| |
| |
| Map* Object::GetRootMap(Isolate* isolate) { |
| DisallowHeapAllocation no_alloc; |
| if (IsSmi()) { |
| Context* native_context = isolate->context()->native_context(); |
| return native_context->number_function()->initial_map(); |
| } |
| |
| // The object is either a number, a string, a symbol, a boolean, a SIMD value, |
| // a real JS object, or a Harmony proxy. |
| HeapObject* heap_object = HeapObject::cast(this); |
| if (heap_object->IsJSReceiver()) { |
| return heap_object->map(); |
| } |
| int constructor_function_index = |
| heap_object->map()->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(); |
| } |
| |
| |
| Object* Object::GetHash() { |
| Object* hash = GetSimpleHash(); |
| if (hash->IsSmi()) return hash; |
| |
| DCHECK(IsJSReceiver()); |
| return JSReceiver::cast(this)->GetIdentityHash(); |
| } |
| |
| |
| Object* Object::GetSimpleHash() { |
| // The object is either a Smi, a HeapNumber, a name, an odd-ball, |
| // a SIMD value type, a real JS object, or a Harmony proxy. |
| if (IsSmi()) { |
| uint32_t hash = ComputeIntegerHash(Smi::cast(this)->value(), kZeroHashSeed); |
| return Smi::FromInt(hash & Smi::kMaxValue); |
| } |
| if (IsHeapNumber()) { |
| double num = HeapNumber::cast(this)->value(); |
| if (std::isnan(num)) return Smi::FromInt(Smi::kMaxValue); |
| if (i::IsMinusZero(num)) num = 0; |
| if (IsSmiDouble(num)) { |
| return Smi::FromInt(FastD2I(num))->GetHash(); |
| } |
| uint32_t hash = ComputeLongHash(double_to_uint64(num)); |
| return Smi::FromInt(hash & Smi::kMaxValue); |
| } |
| if (IsName()) { |
| uint32_t hash = Name::cast(this)->Hash(); |
| return Smi::FromInt(hash); |
| } |
| if (IsOddball()) { |
| uint32_t hash = Oddball::cast(this)->to_string()->Hash(); |
| return Smi::FromInt(hash); |
| } |
| if (IsSimd128Value()) { |
| uint32_t hash = Simd128Value::cast(this)->Hash(); |
| return Smi::FromInt(hash & Smi::kMaxValue); |
| } |
| DCHECK(IsJSReceiver()); |
| JSReceiver* receiver = JSReceiver::cast(this); |
| return receiver->GetHeap()->undefined_value(); |
| } |
| |
| |
| Handle<Smi> Object::GetOrCreateHash(Isolate* isolate, Handle<Object> object) { |
| Handle<Object> hash(object->GetSimpleHash(), isolate); |
| if (hash->IsSmi()) return Handle<Smi>::cast(hash); |
| |
| DCHECK(object->IsJSReceiver()); |
| return JSReceiver::GetOrCreateIdentityHash(Handle<JSReceiver>::cast(object)); |
| } |
| |
| |
| bool Object::SameValue(Object* other) { |
| if (other == this) return true; |
| |
| // The object is either a number, a name, an odd-ball, |
| // a real JS object, or a Harmony proxy. |
| 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 (IsSimd128Value() && other->IsSimd128Value()) { |
| if (IsFloat32x4() && other->IsFloat32x4()) { |
| Float32x4* a = Float32x4::cast(this); |
| Float32x4* b = Float32x4::cast(other); |
| for (int i = 0; i < 4; i++) { |
| float x = a->get_lane(i); |
| float y = b->get_lane(i); |
| // Implements the ES5 SameValue operation for floating point types. |
| // http://www.ecma-international.org/ecma-262/6.0/#sec-samevalue |
| if (x != y && !(std::isnan(x) && std::isnan(y))) return false; |
| if (std::signbit(x) != std::signbit(y)) return false; |
| } |
| return true; |
| } else { |
| Simd128Value* a = Simd128Value::cast(this); |
| Simd128Value* b = Simd128Value::cast(other); |
| return a->map()->instance_type() == b->map()->instance_type() && |
| a->BitwiseEquals(b); |
| } |
| } |
| return false; |
| } |
| |
| |
| bool Object::SameValueZero(Object* other) { |
| if (other == this) return true; |
| |
| // The object is either a number, a name, an odd-ball, |
| // a real JS object, or a Harmony proxy. |
| 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 (IsSimd128Value() && other->IsSimd128Value()) { |
| if (IsFloat32x4() && other->IsFloat32x4()) { |
| Float32x4* a = Float32x4::cast(this); |
| Float32x4* b = Float32x4::cast(other); |
| for (int i = 0; i < 4; i++) { |
| float x = a->get_lane(i); |
| float y = b->get_lane(i); |
| // Implements the ES6 SameValueZero operation for floating point types. |
| // http://www.ecma-international.org/ecma-262/6.0/#sec-samevaluezero |
| if (x != y && !(std::isnan(x) && std::isnan(y))) return false; |
| // SameValueZero doesn't distinguish between 0 and -0. |
| } |
| return true; |
| } else { |
| Simd128Value* a = Simd128Value::cast(this); |
| Simd128Value* b = Simd128Value::cast(other); |
| return a->map()->instance_type() == b->map()->instance_type() && |
| a->BitwiseEquals(b); |
| } |
| } |
| return false; |
| } |
| |
| |
| MaybeHandle<Object> Object::ArraySpeciesConstructor( |
| Isolate* isolate, Handle<Object> original_array) { |
| Handle<Context> native_context = isolate->native_context(); |
| if (!FLAG_harmony_species) { |
| return Handle<Object>(native_context->array_function(), isolate); |
| } |
| 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 != *native_context && |
| *constructor == constructor_context->array_function()) { |
| constructor = isolate->factory()->undefined_value(); |
| } |
| } |
| if (constructor->IsJSReceiver()) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, constructor, |
| Object::GetProperty(constructor, |
| isolate->factory()->species_symbol()), |
| Object); |
| if (constructor->IsNull()) { |
| constructor = isolate->factory()->undefined_value(); |
| } |
| } |
| } |
| if (constructor->IsUndefined()) { |
| return Handle<Object>(native_context->array_function(), isolate); |
| } else { |
| if (!constructor->IsConstructor()) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kSpeciesNotConstructor), |
| Object); |
| } |
| return constructor; |
| } |
| } |
| |
| |
| void Object::ShortPrint(FILE* out) { |
| OFStream os(out); |
| os << Brief(this); |
| } |
| |
| |
| void Object::ShortPrint(StringStream* accumulator) { |
| std::ostringstream os; |
| os << Brief(this); |
| accumulator->Add(os.str().c_str()); |
| } |
| |
| |
| void Object::ShortPrint(std::ostream& os) { os << Brief(this); } |
| |
| |
| std::ostream& operator<<(std::ostream& os, const Brief& v) { |
| if (v.value->IsSmi()) { |
| Smi::cast(v.value)->SmiPrint(os); |
| } else { |
| // TODO(svenpanne) Const-correct HeapObjectShortPrint! |
| HeapObject* obj = const_cast<HeapObject*>(HeapObject::cast(v.value)); |
| obj->HeapObjectShortPrint(os); |
| } |
| return os; |
| } |
| |
| |
| void Smi::SmiPrint(std::ostream& os) const { // NOLINT |
| os << value(); |
| } |
| |
| |
| // Should a word be prefixed by 'a' or 'an' in order to read naturally in |
| // English? Returns false for non-ASCII or words that don't start with |
| // a capital letter. The a/an rule follows pronunciation in English. |
| // We don't use the BBC's overcorrect "an historic occasion" though if |
| // you speak a dialect you may well say "an 'istoric occasion". |
| static bool AnWord(String* str) { |
| if (str->length() == 0) return false; // A nothing. |
| int c0 = str->Get(0); |
| int c1 = str->length() > 1 ? str->Get(1) : 0; |
| if (c0 == 'U') { |
| if (c1 > 'Z') { |
| return true; // An Umpire, but a UTF8String, a U. |
| } |
| } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') { |
| return true; // An Ape, an ABCBook. |
| } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) && |
| (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' || |
| c0 == 'S' || c0 == 'X')) { |
| return true; // An MP3File, an M. |
| } |
| return false; |
| } |
| |
| |
| Handle<String> String::SlowFlatten(Handle<ConsString> cons, |
| PretenureFlag pretenure) { |
| DCHECK(AllowHeapAllocation::IsAllowed()); |
| DCHECK(cons->second()->length() != 0); |
| Isolate* isolate = cons->GetIsolate(); |
| int length = cons->length(); |
| PretenureFlag tenure = isolate->heap()->InNewSpace(*cons) ? pretenure |
| : TENURED; |
| Handle<SeqString> result; |
| if (cons->IsOneByteRepresentation()) { |
| Handle<SeqOneByteString> flat = isolate->factory()->NewRawOneByteString( |
| length, tenure).ToHandleChecked(); |
| DisallowHeapAllocation no_gc; |
| WriteToFlat(*cons, flat->GetChars(), 0, length); |
| result = flat; |
| } else { |
| Handle<SeqTwoByteString> flat = isolate->factory()->NewRawTwoByteString( |
| length, tenure).ToHandleChecked(); |
| DisallowHeapAllocation no_gc; |
| WriteToFlat(*cons, flat->GetChars(), 0, length); |
| result = flat; |
| } |
| cons->set_first(*result); |
| cons->set_second(isolate->heap()->empty_string()); |
| DCHECK(result->IsFlat()); |
| return result; |
| } |
| |
| |
| |
| bool String::MakeExternal(v8::String::ExternalStringResource* resource) { |
| // Externalizing twice leaks the external resource, so it's |
| // prohibited by the API. |
| DCHECK(!this->IsExternalString()); |
| DCHECK(!resource->IsCompressible()); |
| #ifdef ENABLE_SLOW_DCHECKS |
| if (FLAG_enable_slow_asserts) { |
| // Assert that the resource and the string are equivalent. |
| DCHECK(static_cast<size_t>(this->length()) == resource->length()); |
| ScopedVector<uc16> smart_chars(this->length()); |
| String::WriteToFlat(this, smart_chars.start(), 0, this->length()); |
| DCHECK(memcmp(smart_chars.start(), |
| resource->data(), |
| resource->length() * sizeof(smart_chars[0])) == 0); |
| } |
| #endif // DEBUG |
| int size = this->Size(); // Byte size of the original string. |
| // Abort if size does not allow in-place conversion. |
| if (size < ExternalString::kShortSize) return false; |
| Heap* heap = GetHeap(); |
| bool is_one_byte = this->IsOneByteRepresentation(); |
| bool is_internalized = this->IsInternalizedString(); |
| |
| // Morph the string to an external string by replacing the map and |
| // reinitializing the fields. This won't work if the space the existing |
| // string occupies is too small for a regular external string. |
| // Instead, we resort to a short external string instead, omitting |
| // the field caching the address of the backing store. When we encounter |
| // short external strings in generated code, we need to bailout to runtime. |
| Map* new_map; |
| if (size < ExternalString::kSize) { |
| new_map = is_internalized |
| ? (is_one_byte |
| ? heap->short_external_internalized_string_with_one_byte_data_map() |
| : heap->short_external_internalized_string_map()) |
| : (is_one_byte ? heap->short_external_string_with_one_byte_data_map() |
| : heap->short_external_string_map()); |
| } else { |
| new_map = is_internalized |
| ? (is_one_byte |
| ? heap->external_internalized_string_with_one_byte_data_map() |
| : heap->external_internalized_string_map()) |
| : (is_one_byte ? heap->external_string_with_one_byte_data_map() |
| : heap->external_string_map()); |
| } |
| |
| // Byte size of the external String object. |
| int new_size = this->SizeFromMap(new_map); |
| heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); |
| |
| // We are storing the new map using release store after creating a filler for |
| // the left-over space to avoid races with the sweeper thread. |
| this->synchronized_set_map(new_map); |
| |
| ExternalTwoByteString* self = ExternalTwoByteString::cast(this); |
| self->set_resource(resource); |
| if (is_internalized) self->Hash(); // Force regeneration of the hash value. |
| |
| heap->AdjustLiveBytes(this, new_size - size, Heap::CONCURRENT_TO_SWEEPER); |
| return true; |
| } |
| |
| |
| bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { |
| // Externalizing twice leaks the external resource, so it's |
| // prohibited by the API. |
| DCHECK(!this->IsExternalString()); |
| DCHECK(!resource->IsCompressible()); |
| #ifdef ENABLE_SLOW_DCHECKS |
| if (FLAG_enable_slow_asserts) { |
| // Assert that the resource and the string are equivalent. |
| DCHECK(static_cast<size_t>(this->length()) == resource->length()); |
| if (this->IsTwoByteRepresentation()) { |
| ScopedVector<uint16_t> smart_chars(this->length()); |
| String::WriteToFlat(this, smart_chars.start(), 0, this->length()); |
| DCHECK(String::IsOneByte(smart_chars.start(), this->length())); |
| } |
| ScopedVector<char> smart_chars(this->length()); |
| String::WriteToFlat(this, smart_chars.start(), 0, this->length()); |
| DCHECK(memcmp(smart_chars.start(), |
| resource->data(), |
| resource->length() * sizeof(smart_chars[0])) == 0); |
| } |
| #endif // DEBUG |
| int size = this->Size(); // Byte size of the original string. |
| // Abort if size does not allow in-place conversion. |
| if (size < ExternalString::kShortSize) return false; |
| Heap* heap = GetHeap(); |
| bool is_internalized = this->IsInternalizedString(); |
| |
| // Morph the string to an external string by replacing the map and |
| // reinitializing the fields. This won't work if the space the existing |
| // string occupies is too small for a regular external string. |
| // Instead, we resort to a short external string instead, omitting |
| // the field caching the address of the backing store. When we encounter |
| // short external strings in generated code, we need to bailout to runtime. |
| Map* new_map; |
| if (size < ExternalString::kSize) { |
| new_map = is_internalized |
| ? heap->short_external_one_byte_internalized_string_map() |
| : heap->short_external_one_byte_string_map(); |
| } else { |
| new_map = is_internalized |
| ? heap->external_one_byte_internalized_string_map() |
| : heap->external_one_byte_string_map(); |
| } |
| |
| // Byte size of the external String object. |
| int new_size = this->SizeFromMap(new_map); |
| heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); |
| |
| // We are storing the new map using release store after creating a filler for |
| // the left-over space to avoid races with the sweeper thread. |
| this->synchronized_set_map(new_map); |
| |
| ExternalOneByteString* self = ExternalOneByteString::cast(this); |
| self->set_resource(resource); |
| if (is_internalized) self->Hash(); // Force regeneration of the hash value. |
| |
| heap->AdjustLiveBytes(this, new_size - size, Heap::CONCURRENT_TO_SWEEPER); |
| return true; |
| } |
| |
| |
| void String::StringShortPrint(StringStream* accumulator) { |
| int len = length(); |
| if (len > kMaxShortPrintLength) { |
| accumulator->Add("<Very long string[%u]>", len); |
| return; |
| } |
| |
| if (!LooksValid()) { |
| accumulator->Add("<Invalid String>"); |
| return; |
| } |
| |
| StringCharacterStream stream(this); |
| |
| bool truncated = false; |
| if (len > kMaxShortPrintLength) { |
| len = kMaxShortPrintLength; |
| truncated = true; |
| } |
| bool one_byte = true; |
| for (int i = 0; i < len; i++) { |
| uint16_t c = stream.GetNext(); |
| |
| if (c < 32 || c >= 127) { |
| one_byte = false; |
| } |
| } |
| stream.Reset(this); |
| if (one_byte) { |
| accumulator->Add("<String[%u]: ", length()); |
| for (int i = 0; i < len; i++) { |
| accumulator->Put(static_cast<char>(stream.GetNext())); |
| } |
| accumulator->Put('>'); |
| } else { |
| // Backslash indicates that the string contains control |
| // characters and that backslashes are therefore escaped. |
| accumulator->Add("<String[%u]\\: ", length()); |
| for (int i = 0; i < len; i++) { |
| uint16_t c = stream.GetNext(); |
| if (c == '\n') { |
| accumulator->Add("\\n"); |
| } else if (c == '\r') { |
| accumulator->Add("\\r"); |
| } else if (c == '\\') { |
| accumulator->Add("\\\\"); |
| } else if (c < 32 || c > 126) { |
| accumulator->Add("\\x%02x", c); |
| } else { |
| accumulator->Put(static_cast<char>(c)); |
| } |
| } |
| if (truncated) { |
| accumulator->Put('.'); |
| accumulator->Put('.'); |
| accumulator->Put('.'); |
| } |
| accumulator->Put('>'); |
| } |
| return; |
| } |
| |
| |
| void String::PrintUC16(std::ostream& os, int start, int end) { // NOLINT |
| if (end < 0) end = length(); |
| StringCharacterStream stream(this, start); |
| for (int i = start; i < end && stream.HasMore(); i++) { |
| os << AsUC16(stream.GetNext()); |
| } |
| } |
| |
| |
| void JSObject::JSObjectShortPrint(StringStream* accumulator) { |
| switch (map()->instance_type()) { |
| case JS_ARRAY_TYPE: { |
| double length = JSArray::cast(this)->length()->IsUndefined() |
| ? 0 |
| : JSArray::cast(this)->length()->Number(); |
| accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length)); |
| break; |
| } |
| case JS_BOUND_FUNCTION_TYPE: { |
| JSBoundFunction* bound_function = JSBoundFunction::cast(this); |
| Object* name = bound_function->name(); |
| accumulator->Add("<JS BoundFunction"); |
| if (name->IsString()) { |
| String* str = String::cast(name); |
| if (str->length() > 0) { |
| accumulator->Add(" "); |
| accumulator->Put(str); |
| } |
| } |
| accumulator->Add( |
| " (BoundTargetFunction %p)>", |
| reinterpret_cast<void*>(bound_function->bound_target_function())); |
| break; |
| } |
| case JS_WEAK_MAP_TYPE: { |
| accumulator->Add("<JS WeakMap>"); |
| break; |
| } |
| case JS_WEAK_SET_TYPE: { |
| accumulator->Add("<JS WeakSet>"); |
| break; |
| } |
| case JS_REGEXP_TYPE: { |
| accumulator->Add("<JS RegExp>"); |
| break; |
| } |
| case JS_FUNCTION_TYPE: { |
| JSFunction* function = JSFunction::cast(this); |
| Object* fun_name = function->shared()->DebugName(); |
| bool printed = false; |
| if (fun_name->IsString()) { |
| String* str = String::cast(fun_name); |
| if (str->length() > 0) { |
| accumulator->Add("<JS Function "); |
| accumulator->Put(str); |
| printed = true; |
| } |
| } |
| if (!printed) { |
| accumulator->Add("<JS Function"); |
| } |
| accumulator->Add(" (SharedFunctionInfo %p)", |
| reinterpret_cast<void*>(function->shared())); |
| accumulator->Put('>'); |
| break; |
| } |
| case JS_GENERATOR_OBJECT_TYPE: { |
| accumulator->Add("<JS Generator>"); |
| break; |
| } |
| case JS_MODULE_TYPE: { |
| accumulator->Add("<JS Module>"); |
| break; |
| } |
| // All other JSObjects are rather similar to each other (JSObject, |
| // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). |
| default: { |
| Map* map_of_this = map(); |
| Heap* heap = GetHeap(); |
| Object* constructor = map_of_this->GetConstructor(); |
| bool printed = false; |
| if (constructor->IsHeapObject() && |
| !heap->Contains(HeapObject::cast(constructor))) { |
| accumulator->Add("!!!INVALID CONSTRUCTOR!!!"); |
| } else { |
| bool global_object = IsJSGlobalProxy(); |
| if (constructor->IsJSFunction()) { |
| if (!heap->Contains(JSFunction::cast(constructor)->shared())) { |
| accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!"); |
| } else { |
| Object* constructor_name = |
| JSFunction::cast(constructor)->shared()->name(); |
| if (constructor_name->IsString()) { |
| String* str = String::cast(constructor_name); |
| if (str->length() > 0) { |
| bool vowel = AnWord(str); |
| accumulator->Add("<%sa%s ", |
| global_object ? "Global Object: " : "", |
| vowel ? "n" : ""); |
| accumulator->Put(str); |
| accumulator->Add(" with %smap %p", |
| map_of_this->is_deprecated() ? "deprecated " : "", |
| map_of_this); |
| printed = true; |
| } |
| } |
| } |
| } |
| if (!printed) { |
| accumulator->Add("<JS %sObject", global_object ? "Global " : ""); |
| } |
| } |
| if (IsJSValue()) { |
| accumulator->Add(" value = "); |
| JSValue::cast(this)->value()->ShortPrint(accumulator); |
| } |
| accumulator->Put('>'); |
| break; |
| } |
| } |
| } |
| |
| |
| void JSObject::PrintElementsTransition( |
| FILE* file, Handle<JSObject> object, |
| ElementsKind from_kind, Handle<FixedArrayBase> from_elements, |
| ElementsKind to_kind, Handle<FixedArrayBase> to_elements) { |
| if (from_kind != to_kind) { |
| OFStream os(file); |
| os << "elements transition [" << ElementsKindToString(from_kind) << " -> " |
| << ElementsKindToString(to_kind) << "] in "; |
| JavaScriptFrame::PrintTop(object->GetIsolate(), file, false, true); |
| PrintF(file, " for "); |
| object->ShortPrint(file); |
| PrintF(file, " from "); |
| from_elements->ShortPrint(file); |
| PrintF(file, " to "); |
| to_elements->ShortPrint(file); |
| PrintF(file, "\n"); |
| } |
| } |
| |
| |
| // static |
| MaybeHandle<JSFunction> Map::GetConstructorFunction( |
| Handle<Map> map, Handle<Context> native_context) { |
| if (map->IsPrimitiveMap()) { |
| int const constructor_function_index = map->GetConstructorFunctionIndex(); |
| if (constructor_function_index != kNoConstructorFunctionIndex) { |
| return handle( |
| JSFunction::cast(native_context->get(constructor_function_index))); |
| } |
| } |
| return MaybeHandle<JSFunction>(); |
| } |
| |
| |
| void Map::PrintReconfiguration(FILE* file, int modify_index, PropertyKind kind, |
| PropertyAttributes attributes) { |
| OFStream os(file); |
| os << "[reconfiguring]"; |
| Name* name = instance_descriptors()->GetKey(modify_index); |
| if (name->IsString()) { |
| String::cast(name)->PrintOn(file); |
| } else { |
| os << "{symbol " << static_cast<void*>(name) << "}"; |
| } |
| os << ": " << (kind == kData ? "kData" : "ACCESSORS") << ", attrs: "; |
| os << attributes << " ["; |
| JavaScriptFrame::PrintTop(GetIsolate(), file, false, true); |
| os << "]\n"; |
| } |
| |
| void Map::PrintGeneralization( |
| FILE* file, const char* reason, int modify_index, int split, |
| int descriptors, bool constant_to_field, Representation old_representation, |
| Representation new_representation, MaybeHandle<FieldType> old_field_type, |
| MaybeHandle<Object> old_value, MaybeHandle<FieldType> new_field_type, |
| MaybeHandle<Object> new_value) { |
| OFStream os(file); |
| os << "[generalizing]"; |
| Name* name = instance_descriptors()->GetKey(modify_index); |
| if (name->IsString()) { |
| String::cast(name)->PrintOn(file); |
| } else { |
| os << "{symbol " << static_cast<void*>(name) << "}"; |
| } |
| os << ":"; |
| if (constant_to_field) { |
| os << "c"; |
| } else { |
| os << old_representation.Mnemonic() << "{"; |
| if (old_field_type.is_null()) { |
| os << Brief(*(old_value.ToHandleChecked())); |
| } else { |
| old_field_type.ToHandleChecked()->PrintTo(os); |
| } |
| os << "}"; |
| } |
| os << "->" << new_representation.Mnemonic() << "{"; |
| if (new_field_type.is_null()) { |
| os << Brief(*(new_value.ToHandleChecked())); |
| } else { |
| new_field_type.ToHandleChecked()->PrintTo(os); |
| } |
| os << "} ("; |
| if (strlen(reason) > 0) { |
| os << reason; |
| } else { |
| os << "+" << (descriptors - split) << " maps"; |
| } |
| os << ") ["; |
| JavaScriptFrame::PrintTop(GetIsolate(), file, false, true); |
| os << "]\n"; |
| } |
| |
| |
| void JSObject::PrintInstanceMigration(FILE* file, |
| Map* original_map, |
| Map* new_map) { |
| PrintF(file, "[migrating]"); |
| DescriptorArray* o = original_map->instance_descriptors(); |
| DescriptorArray* n = new_map->instance_descriptors(); |
| for (int i = 0; i < original_map->NumberOfOwnDescriptors(); i++) { |
| Representation o_r = o->GetDetails(i).representation(); |
| Representation n_r = n->GetDetails(i).representation(); |
| if (!o_r.Equals(n_r)) { |
| String::cast(o->GetKey(i))->PrintOn(file); |
| PrintF(file, ":%s->%s ", o_r.Mnemonic(), n_r.Mnemonic()); |
| } else if (o->GetDetails(i).type() == DATA_CONSTANT && |
| n->GetDetails(i).type() == DATA) { |
| Name* name = o->GetKey(i); |
| if (name->IsString()) { |
| String::cast(name)->PrintOn(file); |
| } else { |
| PrintF(file, "{symbol %p}", static_cast<void*>(name)); |
| } |
| PrintF(file, " "); |
| } |
| } |
| PrintF(file, "\n"); |
| } |
| |
| |
| void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT |
| Heap* heap = GetHeap(); |
| if (!heap->Contains(this)) { |
| os << "!!!INVALID POINTER!!!"; |
| return; |
| } |
| if (!heap->Contains(map())) { |
| os << "!!!INVALID MAP!!!"; |
| return; |
| } |
| |
| os << this << " "; |
| |
| if (IsString()) { |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| String::cast(this)->StringShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| return; |
| } |
| if (IsJSObject()) { |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| JSObject::cast(this)->JSObjectShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| return; |
| } |
| switch (map()->instance_type()) { |
| case MAP_TYPE: |
| os << "<Map(" << ElementsKindToString(Map::cast(this)->elements_kind()) |
| << ")>"; |
| break; |
| case FIXED_ARRAY_TYPE: |
| os << "<FixedArray[" << FixedArray::cast(this)->length() << "]>"; |
| break; |
| case FIXED_DOUBLE_ARRAY_TYPE: |
| os << "<FixedDoubleArray[" << FixedDoubleArray::cast(this)->length() |
| << "]>"; |
| break; |
| case BYTE_ARRAY_TYPE: |
| os << "<ByteArray[" << ByteArray::cast(this)->length() << "]>"; |
| break; |
| case BYTECODE_ARRAY_TYPE: |
| os << "<BytecodeArray[" << BytecodeArray::cast(this)->length() << "]>"; |
| break; |
| case TRANSITION_ARRAY_TYPE: |
| os << "<TransitionArray[" << TransitionArray::cast(this)->length() |
| << "]>"; |
| break; |
| case FREE_SPACE_TYPE: |
| os << "<FreeSpace[" << FreeSpace::cast(this)->size() << "]>"; |
| break; |
| #define TYPED_ARRAY_SHORT_PRINT(Type, type, TYPE, ctype, size) \ |
| case FIXED_##TYPE##_ARRAY_TYPE: \ |
| os << "<Fixed" #Type "Array[" << Fixed##Type##Array::cast(this)->length() \ |
| << "]>"; \ |
| break; |
| |
| TYPED_ARRAYS(TYPED_ARRAY_SHORT_PRINT) |
| #undef TYPED_ARRAY_SHORT_PRINT |
| |
| case SHARED_FUNCTION_INFO_TYPE: { |
| SharedFunctionInfo* shared = SharedFunctionInfo::cast(this); |
| base::SmartArrayPointer<char> debug_name = |
| shared->DebugName()->ToCString(); |
| if (debug_name[0] != 0) { |
| os << "<SharedFunctionInfo " << debug_name.get() << ">"; |
| } else { |
| os << "<SharedFunctionInfo>"; |
| } |
| break; |
| } |
| case JS_MESSAGE_OBJECT_TYPE: |
| os << "<JSMessageObject>"; |
| break; |
| #define MAKE_STRUCT_CASE(NAME, Name, name) \ |
| case NAME##_TYPE: \ |
| os << "<" #Name ">"; \ |
| break; |
| STRUCT_LIST(MAKE_STRUCT_CASE) |
| #undef MAKE_STRUCT_CASE |
| case CODE_TYPE: { |
| Code* code = Code::cast(this); |
| os << "<Code: " << Code::Kind2String(code->kind()) << ">"; |
| break; |
| } |
| case ODDBALL_TYPE: { |
| if (IsUndefined()) { |
| os << "<undefined>"; |
| } else if (IsTheHole()) { |
| os << "<the hole>"; |
| } else if (IsNull()) { |
| os << "<null>"; |
| } else if (IsTrue()) { |
| os << "<true>"; |
| } else if (IsFalse()) { |
| os << "<false>"; |
| } else { |
| os << "<Odd Oddball>"; |
| } |
| break; |
| } |
| case SYMBOL_TYPE: { |
| Symbol* symbol = Symbol::cast(this); |
| symbol->SymbolShortPrint(os); |
| break; |
| } |
| case HEAP_NUMBER_TYPE: { |
| os << "<Number: "; |
| HeapNumber::cast(this)->HeapNumberPrint(os); |
| os << ">"; |
| break; |
| } |
| case MUTABLE_HEAP_NUMBER_TYPE: { |
| os << "<MutableNumber: "; |
| HeapNumber::cast(this)->HeapNumberPrint(os); |
| os << '>'; |
| break; |
| } |
| case SIMD128_VALUE_TYPE: { |
| #define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \ |
| if (Is##Type()) { \ |
| os << "<" #Type ">"; \ |
| break; \ |
| } |
| SIMD128_TYPES(SIMD128_TYPE) |
| #undef SIMD128_TYPE |
| UNREACHABLE(); |
| break; |
| } |
| case JS_PROXY_TYPE: |
| os << "<JSProxy>"; |
| break; |
| case FOREIGN_TYPE: |
| os << "<Foreign>"; |
| break; |
| case CELL_TYPE: { |
| os << "Cell for "; |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| Cell::cast(this)->value()->ShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| break; |
| } |
| case PROPERTY_CELL_TYPE: { |
| os << "PropertyCell for "; |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| PropertyCell* cell = PropertyCell::cast(this); |
| cell->value()->ShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| break; |
| } |
| case WEAK_CELL_TYPE: { |
| os << "WeakCell for "; |
| HeapStringAllocator allocator; |
| StringStream accumulator(&allocator); |
| WeakCell::cast(this)->value()->ShortPrint(&accumulator); |
| os << accumulator.ToCString().get(); |
| break; |
| } |
| default: |
| os << "<Other heap object (" << map()->instance_type() << ")>"; |
| break; |
| } |
| } |
| |
| |
| void HeapObject::Iterate(ObjectVisitor* v) { IterateFast<ObjectVisitor>(v); } |
| |
| |
| void HeapObject::IterateBody(ObjectVisitor* v) { |
| Map* m = map(); |
| IterateBodyFast<ObjectVisitor>(m->instance_type(), SizeFromMap(m), v); |
| } |
| |
| |
| void HeapObject::IterateBody(InstanceType type, int object_size, |
| ObjectVisitor* v) { |
| IterateBodyFast<ObjectVisitor>(type, object_size, v); |
| } |
| |
| |
|