blob: 7a8664cfd6cbdfb4b5f854b4c5f6bbc6dddfd3f4 [file] [log] [blame]
// 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 "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/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/counters-inl.h"
#include "src/counters.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-inl.h"
#include "src/field-index.h"
#include "src/field-type.h"
#include "src/frames-inl.h"
#include "src/full-codegen/full-codegen.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/list.h"
#include "src/log.h"
#include "src/lookup.h"
#include "src/macro-assembler.h"
#include "src/messages.h"
#include "src/objects-body-descriptors-inl.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/utils.h"
#include "src/wasm/wasm-module.h"
#include "src/zone.h"
#ifdef ENABLE_DISASSEMBLER
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/eh-frame.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;
}
// 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::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<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);
}
// 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);
}
}
namespace {
bool IsErrorObject(Isolate* isolate, Handle<Object> object) {
if (!object->IsJSReceiver()) return false;
Handle<Symbol> symbol = isolate->factory()->stack_trace_symbol();
return JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol)
.FromMaybe(false);
}
Handle<String> AsStringOrEmpty(Isolate* isolate, Handle<Object> object) {
return object->IsString() ? Handle<String>::cast(object)
: isolate->factory()->empty_string();
}
Handle<String> NoSideEffectsErrorToString(Isolate* isolate,
Handle<Object> input) {
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input);
Handle<Name> name_key = isolate->factory()->name_string();
Handle<Object> name = JSReceiver::GetDataProperty(receiver, name_key);
Handle<String> name_str = AsStringOrEmpty(isolate, name);
Handle<Name> msg_key = isolate->factory()->message_string();
Handle<Object> msg = JSReceiver::GetDataProperty(receiver, msg_key);
Handle<String> msg_str = AsStringOrEmpty(isolate, msg);
if (name_str->length() == 0) return msg_str;
if (msg_str->length() == 0) return name_str;
IncrementalStringBuilder builder(isolate);
builder.AppendString(name_str);
builder.AppendCString(": ");
builder.AppendString(msg_str);
return builder.Finish().ToHandleChecked();
}
} // namespace
// static
Handle<String> Object::NoSideEffectsToString(Isolate* isolate,
Handle<Object> input) {
DisallowJavascriptExecution no_js(isolate);
if (input->IsString() || input->IsNumber() || input->IsOddball() ||
input->IsSimd128Value()) {
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::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);
}
// static
MaybeHandle<Object> Object::ToIndex(Isolate* isolate, Handle<Object> input,
MessageTemplate::Template error_index) {
if (input->IsUndefined(isolate)) return isolate->factory()->NewNumber(0.0);
ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object);
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::cast(this)->value() != 0;
DCHECK(IsHeapObject());
Isolate* isolate = HeapObject::cast(this)->GetIsolate();
if (IsBoolean()) return IsTrue(isolate);
if (IsUndefined(isolate) || IsNull(isolate)) return false;
if (IsUndetectable()) 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) {
// 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::ToNumber(x).ToHandle(&x) || !Object::ToNumber(y).ToHandle(&y)) {
return Nothing<ComparisonResult>();
}
return Just(NumberCompare(x->Number(), y->Number()));
}
// static
Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) {
// This is the generic version of Abstract Equality Comparison; a version in
// JavaScript land is available in the EqualStub and NotEqualStub. Whenever
// you change something functionality wise in here, remember to update the
// TurboFan code stubs as well.
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()) {
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()) {
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()) {
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->IsSimd128Value()) {
if (y->IsSimd128Value()) {
return Just(Simd128Value::Equals(Handle<Simd128Value>::cast(x),
Handle<Simd128Value>::cast(y)));
} else if (y->IsJSReceiver()) {
if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} 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->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->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->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) {
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;
}
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);
}
// 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(isolate) || func->IsUndefined(isolate)) {
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<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
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 ReadAbsentProperty(it);
case LookupIterator::DATA:
return it->GetDataValue();
}
}
return ReadAbsentProperty(it);
}
// static
MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
Handle<JSProxy> proxy,
Handle<Name> name,
Handle<Object> receiver,
bool* was_found) {
*was_found = true;
if (receiver->IsJSGlobalObject()) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kReadGlobalReferenceThroughProxy, name),
Object);
}
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(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);
// 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(isolate) &&
!trap_result->IsUndefined(isolate);
if (inconsistent) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor, name,
trap_result),
Object);
}
}
// 11. Return trap_result
return trap_result;
}
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;
}
Handle<SharedFunctionInfo> FunctionTemplateInfo::GetOrCreateSharedFunctionInfo(
Isolate* isolate, Handle<FunctionTemplateInfo> info) {
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<String> name = class_name->IsString()
? Handle<String>::cast(class_name)
: isolate->factory()->empty_string();
Handle<Code> code;
if (info->call_code()->IsCallHandlerInfo() &&
CallHandlerInfo::cast(info->call_code())->fast_handler()->IsCode()) {
code = isolate->builtins()->HandleFastApiCall();
} else {
code = isolate->builtins()->HandleApiCall();
}
bool is_constructor = !info->remove_prototype();
Handle<SharedFunctionInfo> result =
isolate->factory()->NewSharedFunctionInfo(name, code, is_constructor);
if (is_constructor) {
result->SetConstructStub(*isolate->builtins()->JSConstructStubApi());
}
result->set_length(info->length());
if (class_name->IsString()) result->set_instance_class_name(*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();
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;
}
// static
Handle<TemplateList> TemplateList::New(Isolate* isolate, int size) {
Handle<FixedArray> list =
isolate->factory()->NewFixedArray(kLengthIndex + size);
list->set(kLengthIndex, Smi::FromInt(0));
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);
isolate->counters()->constructed_objects()->Increment();
isolate->counters()->constructed_objects_runtime()->Increment();
return result;
}
void JSObject::EnsureWritableFastElements(Handle<JSObject> object) {
DCHECK(object->HasFastSmiOrObjectElements() ||
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();
}
// 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(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();
// 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();
if (info->is_sloppy() && !receiver->IsJSReceiver()) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver),
Object);
}
PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder,
Object::DONT_THROW);
Handle<Object> result = args.Call(call_fun, name);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
if (result.is_null()) return ReadAbsentProperty(isolate, receiver, name);
// Rebox handle before return.
return handle(*result, isolate);
}
// Regular accessor.
Handle<Object> getter(AccessorPair::cast(*structure)->getter(), isolate);
if (getter->IsFunctionTemplateInfo()) {
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 ReadAbsentProperty(isolate, receiver, it->GetName());
}
// 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);
}
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);
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);
args.Call(call_fun, name, 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};
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, 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 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, Object::DONT_THROW);
if (it->IsElement()) {
uint32_t index = it->index();
v8::IndexedPropertyGetterCallback getter =
v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter());
result = args.Call(getter, index);
} else {
Handle<Name> name = it->name();
DCHECK(!name->IsPrivate());
if (name->IsSymbol() && !interceptor->can_intercept_symbols()) {
return isolate->factory()->undefined_value();
}
v8::GenericNamedPropertyGetterCallback getter =
v8::ToCData<v8::GenericNamedPropertyGetterCallback>(
interceptor->getter());
result = args.Call(getter, 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>();
if (!it->IsElement() && it->name()->IsSymbol() &&
!interceptor->can_intercept_symbols()) {
return Just(ABSENT);
}
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, Object::DONT_THROW);
if (!interceptor->query()->IsUndefined(isolate)) {
Handle<Object> result;
if (it->IsElement()) {
uint32_t index = it->index();
v8::IndexedPropertyQueryCallback query =
v8::ToCData<v8::IndexedPropertyQueryCallback>(interceptor->query());
result = args.Call(query, index);
} else {
Handle<Name> name = it->name();
DCHECK(!name->IsPrivate());
v8::GenericNamedPropertyQueryCallback query =
v8::ToCData<v8::GenericNamedPropertyQueryCallback>(
interceptor->query());
result = args.Call(query, 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()) {
uint32_t index = it->index();
v8::IndexedPropertyGetterCallback getter =
v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter());
result = args.Call(getter, index);
} else {
Handle<Name> name = it->name();
DCHECK(!name->IsPrivate());
v8::GenericNamedPropertyGetterCallback getter =
v8::ToCData<v8::GenericNamedPropertyGetterCallback>(
interceptor->getter());
result = args.Call(getter, 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,
Object::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()) {
uint32_t index = it->index();
v8::IndexedPropertySetterCallback setter =
v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter());
// TODO(neis): In the future, we may want to actually return the
// interceptor's result, which then should be a boolean.
result = !args.Call(setter, index, value).is_null();
} else {
Handle<Name> name = it->name();
DCHECK(!name->IsPrivate());
if (name->IsSymbol() && !interceptor->can_intercept_symbols()) {
return Just(false);
}
v8::GenericNamedPropertySetterCallback setter =
v8::ToCData<v8::GenericNamedPropertySetterCallback>(
interceptor->setter());
result = !args.Call(setter, name, value).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 {
MaybeHandle<Object> result;
bool done;
result = GetPropertyWithInterceptorInternal(it, interceptor, &done);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, 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);
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, 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);
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, 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());
if (!name->IsUniqueName()) {
name = object->GetIsolate()->factory()->InternalizeString(
Handle<String>::cast(name));
}
if (object->IsJSGlobalObject()) {
Handle<GlobalDictionary> dictionary(object->global_dictionary());
int entry = dictionary->FindEntry(name);
if (entry == GlobalDictionary::kNotFound) {
Isolate* isolate = object->GetIsolate();
auto cell = isolate->factory()->NewPropertyCell();
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);
object->set_properties(*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) {
dictionary = NameDictionary::Add(dictionary, name, value, details);
object->set_properties(*dictionary);
} else {
PropertyDetails original_details = dictionary->DetailsAt(entry);
int enumeration_index = original_details.dictionary_index();
DCHECK(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);
}
}
}
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();
}
namespace {
// Returns a non-SMI for JSObjects, 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 JSObject if it does not already exist.
Object* GetSimpleHash(Object* object) {
// 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 (object->IsSmi()) {
uint32_t hash =
ComputeIntegerHash(Smi::cast(object)->value(), kZeroHashSeed);
return Smi::FromInt(hash & Smi::kMaxValue);
}
if (object->IsHeapNumber()) {
double num = HeapNumber::cast(object)->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 (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->IsSimd128Value()) {
uint32_t hash = Simd128Value::cast(object)->Hash();
return Smi::FromInt(hash & Smi::kMaxValue);
}
DCHECK(object->IsJSReceiver());
// Simply return the receiver as it is guaranteed to not be a SMI.
return object;
}
} // namespace
Object* Object::GetHash() {
Object* hash = GetSimpleHash(this);
if (hash->IsSmi()) return hash;
DisallowHeapAllocation no_gc;
DCHECK(IsJSReceiver());
JSReceiver* receiver = JSReceiver::cast(this);
Isolate* isolate = receiver->GetIsolate();
return JSReceiver::GetIdentityHash(isolate, handle(receiver, isolate));
}
Smi* Object::GetOrCreateHash(Isolate* isolate, Handle<Object> object) {
Object* hash = GetSimpleHash(*object);
if (hash->IsSmi()) return Smi::cast(hash);
DCHECK(object->IsJSReceiver());
return JSReceiver::GetOrCreateIdentityHash(isolate,
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 (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 if (IsSimd128Value() && other->IsSimd128Value()) {
Simd128Value* a = Simd128Value::cast(this);
Simd128Value* b = Simd128Value::cast(other);
return a->map() == b->map() && 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 (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 if (IsSimd128Value() && other->IsSimd128Value()) {
Simd128Value* a = Simd128Value::cast(this);
Simd128Value* b = Simd128Value::cast(other);
return a->map() == b->map() && a->BitwiseEquals(b);
}
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;
}
}
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();
bool has_pointers = this->IsConsString() || this->IsSlicedString();
// 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,
ClearRecordedSlots::kNo);
if (has_pointers) {
heap->ClearRecordedSlotRange(this->address(), this->address() + 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();
bool has_pointers = this->IsConsString() || this->IsSlicedString();
// 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,
ClearRecordedSlots::kNo);
if (has_pointers) {
heap->ClearRecordedSlotRange(this->address(), this->address() + 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, bool show_details) {
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) {
if (show_details) accumulator->Add("<String[%u]: ", length());
for (int i = 0; i < len; i++) {
accumulator->Put(static_cast<char>(stream.GetNext()));
}
if (show_details) accumulator->Put('>');
} else {
// Backslash indicates that the string contains control
// characters and that backslashes are therefore escaped.
if (show_details) 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('.');
}
if (show_details) 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(GetIsolate())
? 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);
accumulator->Add("<JS BoundFunction");
accumulator->Add(
" (BoundTargetFunction %p)>",
reinterpret_cast<void*>(bound_function->bound_target_function()));