diff options
Diffstat (limited to 'deps/v8/src/objects.cc')
-rw-r--r-- | deps/v8/src/objects.cc | 1698 |
1 files changed, 1374 insertions, 324 deletions
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 65d5d5f528..08383030d8 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -2,11 +2,12 @@ // 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/v8.h" - #include "src/accessors.h" #include "src/allocation-site-scopes.h" #include "src/api.h" @@ -18,7 +19,6 @@ #include "src/codegen.h" #include "src/compilation-dependencies.h" #include "src/compiler.h" -#include "src/cpu-profiler.h" #include "src/date.h" #include "src/debug/debug.h" #include "src/deoptimizer.h" @@ -30,13 +30,16 @@ #include "src/hydrogen.h" #include "src/ic/ic.h" #include "src/interpreter/bytecodes.h" +#include "src/isolate-inl.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/profiler/cpu-profiler.h" #include "src/prototype.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" @@ -90,6 +93,106 @@ MaybeHandle<JSReceiver> Object::ToObject(Isolate* isolate, } +// 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; @@ -97,18 +200,169 @@ bool Object::BooleanValue() { if (IsUndetectableObject()) return false; // Undetectable object is false. if (IsString()) return String::cast(this)->length() != 0; if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue(); - if (IsSimd128Value()) return true; // Simd value types evaluate to true. 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()) { + return Just(x.is_identical_to(y)); + } else if (x->IsSimd128Value()) { + if (!y->IsSimd128Value()) return Just(false); + return Just(Simd128Value::Equals(Handle<Simd128Value>::cast(x), + Handle<Simd128Value>::cast(y))); + } else if (x->IsJSReceiver() && !x->IsUndetectableObject()) { + if (y->IsJSReceiver()) { + return Just(x.is_identical_to(y)); + } else if (y->IsNull() || y->IsSimd128Value() || y->IsSymbol() || + y->IsUndefined()) { + return Just(false); + } else if (y->IsBoolean()) { + y = Oddball::ToNumber(Handle<Oddball>::cast(y)); + } + 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; - double const x = this->Number(); - double const y = that->Number(); - // Must check explicitly for NaN:s on Windows, but -0 works fine. - return x == y && !std::isnan(x) && !std::isnan(y); + return NumberEquals(this, that); } else if (this->IsString()) { if (!that->IsString()) return false; return String::cast(this)->Equals(String::cast(that)); @@ -120,14 +374,217 @@ bool Object::StrictEquals(Object* that) { } -bool Object::IsCallable() const { - const Object* fun = this; - while (fun->IsJSFunctionProxy()) { - fun = JSFunctionProxy::cast(fun)->call_trap(); +// 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->IsSymbol()) return isolate->factory()->symbol_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 fun->IsJSFunction() || - (fun->IsHeapObject() && - HeapObject::cast(fun)->map()->has_instance_call_handler()); + 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)); } @@ -139,12 +596,32 @@ bool Object::IsPromise(Handle<Object> object) { 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->promise_status(); + 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()) { + // TODO(bmeurer): Better error message here? + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kCalledNonCallable, func), + Object); + } + return func; +} + + MaybeHandle<Object> Object::GetProperty(LookupIterator* it, LanguageMode language_mode) { for (; it->IsFound(); it->Next()) { @@ -365,7 +842,7 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor( // Regular accessor. Handle<Object> getter(AccessorPair::cast(*structure)->getter(), isolate); - if (getter->IsSpecFunction()) { + if (getter->IsCallable()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... return Object::GetPropertyWithDefinedGetter( receiver, Handle<JSReceiver>::cast(getter)); @@ -421,7 +898,7 @@ MaybeHandle<Object> Object::SetPropertyWithAccessor( // Regular accessor. Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); - if (setter->IsSpecFunction()) { + if (setter->IsCallable()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... return SetPropertyWithDefinedSetter( receiver, Handle<JSReceiver>::cast(setter), value); @@ -460,7 +937,7 @@ MaybeHandle<Object> Object::GetPropertyWithDefinedGetter( // TODO(rossberg): should this apply to getters that are function proxies? if (debug->is_active()) debug->HandleStepIn(getter, false); - return Execution::Call(isolate, getter, receiver, 0, NULL, true); + return Execution::Call(isolate, getter, receiver, 0, NULL); } @@ -477,7 +954,7 @@ MaybeHandle<Object> Object::SetPropertyWithDefinedSetter( Handle<Object> argv[] = { value }; RETURN_ON_EXCEPTION(isolate, Execution::Call(isolate, setter, receiver, - arraysize(argv), argv, true), + arraysize(argv), argv), Object); return value; } @@ -1473,6 +1950,73 @@ void HeapNumber::HeapNumberPrint(std::ostream& os) { // NOLINT (*reinterpret_cast<const byte*>(FIELD_ADDR_CONST(p, offset))) +// static +Handle<String> Simd128Value::ToString(Handle<Simd128Value> input) { +#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \ + if (input->Is##Type()) return Type::ToString(Handle<Type>::cast(input)); + SIMD128_TYPES(SIMD128_TYPE) +#undef SIMD128_TYPE + UNREACHABLE(); + return Handle<String>::null(); +} + + +// static +Handle<String> Float32x4::ToString(Handle<Float32x4> input) { + Isolate* const isolate = input->GetIsolate(); + char arr[100]; + Vector<char> buffer(arr, arraysize(arr)); + std::ostringstream os; + os << "SIMD.Float32x4(" + << std::string(DoubleToCString(input->get_lane(0), buffer)) << ", " + << std::string(DoubleToCString(input->get_lane(1), buffer)) << ", " + << std::string(DoubleToCString(input->get_lane(2), buffer)) << ", " + << std::string(DoubleToCString(input->get_lane(3), buffer)) << ")"; + return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); +} + + +#define SIMD128_BOOL_TO_STRING(Type, lane_count) \ + Handle<String> Type::ToString(Handle<Type> input) { \ + Isolate* const isolate = input->GetIsolate(); \ + std::ostringstream os; \ + os << "SIMD." #Type "("; \ + os << (input->get_lane(0) ? "true" : "false"); \ + for (int i = 1; i < lane_count; i++) { \ + os << ", " << (input->get_lane(i) ? "true" : "false"); \ + } \ + os << ")"; \ + return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); \ + } +SIMD128_BOOL_TO_STRING(Bool32x4, 4) +SIMD128_BOOL_TO_STRING(Bool16x8, 8) +SIMD128_BOOL_TO_STRING(Bool8x16, 16) +#undef SIMD128_BOOL_TO_STRING + + +#define SIMD128_INT_TO_STRING(Type, lane_count) \ + Handle<String> Type::ToString(Handle<Type> input) { \ + Isolate* const isolate = input->GetIsolate(); \ + char arr[100]; \ + Vector<char> buffer(arr, arraysize(arr)); \ + std::ostringstream os; \ + os << "SIMD." #Type "("; \ + os << IntToCString(input->get_lane(0), buffer); \ + for (int i = 1; i < lane_count; i++) { \ + os << ", " << IntToCString(input->get_lane(i), buffer); \ + } \ + os << ")"; \ + return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); \ + } +SIMD128_INT_TO_STRING(Int32x4, 4) +SIMD128_INT_TO_STRING(Uint32x4, 4) +SIMD128_INT_TO_STRING(Int16x8, 8) +SIMD128_INT_TO_STRING(Uint16x8, 8) +SIMD128_INT_TO_STRING(Int8x16, 16) +SIMD128_INT_TO_STRING(Uint8x16, 16) +#undef SIMD128_INT_TO_STRING + + bool Simd128Value::BitwiseEquals(const Simd128Value* other) const { return READ_INT64_FIELD(this, kValueOffset) == READ_INT64_FIELD(other, kValueOffset) && @@ -1776,6 +2320,8 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map, if (!new_map->is_dictionary_map()) { MigrateFastToFast(object, new_map); if (old_map->is_prototype_map()) { + DCHECK(!old_map->is_stable()); + DCHECK(new_map->is_stable()); // Clear out the old descriptor array to avoid problems to sharing // the descriptor array without using an explicit. old_map->InitializeDescriptors( @@ -3280,8 +3826,7 @@ MaybeHandle<Object> Object::SetDataProperty(LookupIterator* it, if (it->IsElement() && receiver->HasFixedTypedArrayElements()) { if (!value->IsNumber() && !value->IsUndefined()) { ASSIGN_RETURN_ON_EXCEPTION(it->isolate(), to_assign, - Execution::ToNumber(it->isolate(), value), - Object); + Object::ToNumber(value), Object); // ToNumber above might modify the receiver, causing the cached // holder_map to mismatch the actual holder->map() after this point. // Reload the map to be in consistent state. Other cached state cannot @@ -3723,6 +4268,7 @@ Handle<Map> Map::TransitionElementsTo(Handle<Map> map, Object* maybe_array_maps = map->is_strong() ? native_context->js_array_strong_maps() : native_context->js_array_maps(); + // Reuse map transitions for JSArrays. if (maybe_array_maps->IsFixedArray()) { DisallowHeapAllocation no_gc; FixedArray* array_maps = FixedArray::cast(maybe_array_maps); @@ -3736,6 +4282,14 @@ Handle<Map> Map::TransitionElementsTo(Handle<Map> map, } DCHECK(!map->IsUndefined()); + // Check if we can go back in the elements kind transition chain. + if (IsHoleyElementsKind(from_kind) && + to_kind == GetPackedElementsKind(from_kind) && + map->GetBackPointer()->IsMap() && + Map::cast(map->GetBackPointer())->elements_kind() == to_kind) { + return handle(Map::cast(map->GetBackPointer())); + } + bool allow_store_transition = IsTransitionElementsKind(from_kind); // Only store fast element maps in ascending generality. if (IsFastElementsKind(to_kind)) { @@ -4064,9 +4618,8 @@ void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) { ElementsKind obj_kind = object->map()->elements_kind(); ElementsKind map_kind = map->elements_kind(); if (map_kind != obj_kind) { - ElementsKind to_kind = map_kind; - if (IsMoreGeneralElementsKindTransition(map_kind, obj_kind) || - IsDictionaryElementsKind(obj_kind)) { + ElementsKind to_kind = GetMoreGeneralElementsKind(map_kind, obj_kind); + if (IsDictionaryElementsKind(obj_kind)) { to_kind = obj_kind; } if (IsDictionaryElementsKind(to_kind)) { @@ -4687,16 +5240,12 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, int index = Smi::cast(iteration_order->get(i))->value(); Object* k = dictionary->KeyAt(index); DCHECK(dictionary->IsKey(k)); + // Dictionary keys are internalized upon insertion. + // TODO(jkummerow): Turn this into a DCHECK if it's not hit in the wild. + CHECK(k->IsUniqueName()); + Handle<Name> key(Name::cast(k), isolate); Object* value = dictionary->ValueAt(index); - Handle<Name> key; - if (k->IsSymbol()) { - key = handle(Symbol::cast(k)); - } else { - // Ensure the key is a unique name before writing into the - // instance descriptor. - key = factory->InternalizeString(handle(String::cast(k))); - } PropertyDetails details = dictionary->DetailsAt(index); int enumeration_index = details.dictionary_index(); @@ -4796,7 +5345,7 @@ void JSObject::RequireSlowElements(SeededNumberDictionary* dictionary) { dictionary->set_requires_slow_elements(); // TODO(verwaest): Remove this hack. if (map()->is_prototype_map()) { - GetHeap()->ClearAllICsByKind(Code::KEYED_STORE_IC); + GetHeap()->ClearAllKeyedStoreICs(); } } @@ -4959,7 +5508,7 @@ Object* JSObject::GetHiddenProperty(Handle<Name> key) { // If the proxy is detached, return undefined. if (iter.IsAtEnd()) return GetHeap()->the_hole_value(); DCHECK(iter.GetCurrent()->IsJSGlobalObject()); - return JSObject::cast(iter.GetCurrent())->GetHiddenProperty(key); + return iter.GetCurrent<JSObject>()->GetHiddenProperty(key); } DCHECK(!IsJSGlobalProxy()); Object* inline_value = GetHiddenPropertiesHashTable(); @@ -4984,9 +5533,8 @@ Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> object, // If the proxy is detached, return undefined. if (iter.IsAtEnd()) return isolate->factory()->undefined_value(); DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); - return SetHiddenProperty( - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), key, - value); + return SetHiddenProperty(PrototypeIterator::GetCurrent<JSObject>(iter), key, + value); } DCHECK(!object->IsJSGlobalProxy()); @@ -5017,8 +5565,8 @@ void JSObject::DeleteHiddenProperty(Handle<JSObject> object, Handle<Name> key) { PrototypeIterator iter(isolate, object); if (iter.IsAtEnd()) return; DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); - return DeleteHiddenProperty( - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), key); + return DeleteHiddenProperty(PrototypeIterator::GetCurrent<JSObject>(iter), + key); } Object* inline_value = object->GetHiddenPropertiesHashTable(); @@ -5397,13 +5945,13 @@ bool JSObject::ReferencesObject(Object* obj) { // Check the context extension (if any) if it can have references. if (context->has_extension() && !context->IsCatchContext()) { - // With harmony scoping, a JSFunction may have a global context. + // With harmony scoping, a JSFunction may have a script context. // TODO(mvstanton): walk into the ScopeInfo. if (context->IsScriptContext()) { return false; } - return JSObject::cast(context->extension())->ReferencesObject(obj); + return context->extension_object()->ReferencesObject(obj); } } @@ -5431,8 +5979,7 @@ MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { PrototypeIterator iter(isolate, object); if (iter.IsAtEnd()) return object; DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); - return PreventExtensions( - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter))); + return PreventExtensions(PrototypeIterator::GetCurrent<JSObject>(iter)); } // It's not possible to seal objects with external array elements @@ -5474,7 +6021,7 @@ bool JSObject::IsExtensible() { PrototypeIterator iter(GetIsolate(), this); if (iter.IsAtEnd()) return false; DCHECK(iter.GetCurrent()->IsJSGlobalObject()); - return JSObject::cast(iter.GetCurrent())->map()->is_extensible(); + return iter.GetCurrent<JSObject>()->map()->is_extensible(); } return map()->is_extensible(); } @@ -5525,7 +6072,7 @@ MaybeHandle<Object> JSObject::PreventExtensionsWithTransition( if (iter.IsAtEnd()) return object; DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); return PreventExtensionsWithTransition<attrs>( - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter))); + PrototypeIterator::GetCurrent<JSObject>(iter)); } // It's not possible to seal or freeze objects with external array elements @@ -5900,6 +6447,76 @@ MaybeHandle<JSObject> JSObject::DeepCopy( } +// static +MaybeHandle<Object> JSReceiver::ToPrimitive(Handle<JSReceiver> receiver, + ToPrimitiveHint hint) { + Isolate* const isolate = receiver->GetIsolate(); + Handle<Object> exotic_to_prim; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, exotic_to_prim, + GetMethod(receiver, isolate->factory()->to_primitive_symbol()), Object); + if (!exotic_to_prim->IsUndefined()) { + Handle<Object> hint_string; + switch (hint) { + case ToPrimitiveHint::kDefault: + hint_string = isolate->factory()->default_string(); + break; + case ToPrimitiveHint::kNumber: + hint_string = isolate->factory()->number_string(); + break; + case ToPrimitiveHint::kString: + hint_string = isolate->factory()->string_string(); + break; + } + Handle<Object> result; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, result, + Execution::Call(isolate, exotic_to_prim, receiver, 1, &hint_string), + Object); + if (result->IsPrimitive()) return result; + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kCannotConvertToPrimitive), + Object); + } + return OrdinaryToPrimitive(receiver, (hint == ToPrimitiveHint::kString) + ? OrdinaryToPrimitiveHint::kString + : OrdinaryToPrimitiveHint::kNumber); +} + + +// static +MaybeHandle<Object> JSReceiver::OrdinaryToPrimitive( + Handle<JSReceiver> receiver, OrdinaryToPrimitiveHint hint) { + Isolate* const isolate = receiver->GetIsolate(); + Handle<String> method_names[2]; + switch (hint) { + case OrdinaryToPrimitiveHint::kNumber: + method_names[0] = isolate->factory()->valueOf_string(); + method_names[1] = isolate->factory()->toString_string(); + break; + case OrdinaryToPrimitiveHint::kString: + method_names[0] = isolate->factory()->toString_string(); + method_names[1] = isolate->factory()->valueOf_string(); + break; + } + for (Handle<String> name : method_names) { + Handle<Object> method; + ASSIGN_RETURN_ON_EXCEPTION(isolate, method, + JSReceiver::GetProperty(receiver, name), Object); + if (method->IsCallable()) { + Handle<Object> result; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, result, Execution::Call(isolate, method, receiver, 0, NULL), + Object); + if (result->IsPrimitive()) return result; + } + } + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kCannotConvertToPrimitive), + Object); +} + + // Tests for the fast common case for property enumeration: // - This object and all prototypes has an enum cache (which means that // it is no proxy, has no interceptors and needs no access checks). @@ -5910,14 +6527,14 @@ bool JSReceiver::IsSimpleEnum() { PrototypeIterator::START_AT_RECEIVER); !iter.IsAtEnd(); iter.Advance()) { if (!iter.GetCurrent()->IsJSObject()) return false; - JSObject* curr = JSObject::cast(iter.GetCurrent()); - int enum_length = curr->map()->EnumLength(); + JSObject* current = iter.GetCurrent<JSObject>(); + int enum_length = current->map()->EnumLength(); if (enum_length == kInvalidEnumCacheSentinel) return false; - if (curr->IsAccessCheckNeeded()) return false; - DCHECK(!curr->HasNamedInterceptor()); - DCHECK(!curr->HasIndexedInterceptor()); - if (curr->NumberOfEnumElements() > 0) return false; - if (curr != this && enum_length != 0) return false; + if (current->IsAccessCheckNeeded()) return false; + DCHECK(!current->HasNamedInterceptor()); + DCHECK(!current->HasIndexedInterceptor()); + if (current->NumberOfEnumElements() > 0) return false; + if (current != this && enum_length != 0) return false; } return true; } @@ -6102,11 +6719,123 @@ Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object, } +Handle<FixedArray> KeyAccumulator::GetKeys() { + if (length_ == 0) { + return isolate_->factory()->empty_fixed_array(); + } + if (set_.is_null()) { + keys_->Shrink(length_); + return keys_; + } + // copy over results from set_ + Handle<FixedArray> result = isolate_->factory()->NewFixedArray(length_); + for (int i = 0; i < length_; i++) { + result->set(i, set_->KeyAt(i)); + } + return result; +} + + +void KeyAccumulator::AddKey(Handle<Object> key, int check_limit) { +#ifdef ENABLE_SLOW_DCHECKS + if (FLAG_enable_slow_asserts) { + DCHECK(key->IsNumber() || key->IsName()); + } +#endif + if (!set_.is_null()) { + set_ = OrderedHashSet::Add(set_, key); + length_ = set_->NumberOfElements(); + return; + } + // check if we already have the key in the case we are still using + // the keys_ FixedArray + check_limit = Min(check_limit, length_); + for (int i = 0; i < check_limit; i++) { + Object* current = keys_->get(i); + if (current->KeyEquals(*key)) return; + } + EnsureCapacity(length_); + keys_->set(length_, *key); + length_++; +} + + +void KeyAccumulator::AddKeys(Handle<FixedArray> array, + FixedArray::KeyFilter filter) { + int add_length = array->length(); + if (add_length == 0) return; + if (keys_.is_null() && filter == FixedArray::ALL_KEYS) { + keys_ = array; + length_ = keys_->length(); + return; + } + PrepareForComparisons(add_length); + int previous_key_count = length_; + for (int i = 0; i < add_length; i++) { + Handle<Object> current(array->get(i), isolate_); + if (filter == FixedArray::NON_SYMBOL_KEYS && current->IsSymbol()) continue; + AddKey(current, previous_key_count); + } +} + + +void KeyAccumulator::AddKeys(Handle<JSObject> array_like, + FixedArray::KeyFilter filter) { + DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements()); + ElementsAccessor* accessor = array_like->GetElementsAccessor(); + accessor->AddElementsToKeyAccumulator(array_like, this, filter); +} + + +void KeyAccumulator::PrepareForComparisons(int count) { + // Depending on how many comparisons we do we should switch to the + // hash-table-based checks which have a one-time overhead for + // initializing but O(1) for HasKey checks. + if (!set_.is_null()) return; + // This limit was obtained through evaluation of a microbench. + if (length_ * count < 50) return; + set_ = OrderedHashSet::Allocate(isolate_, length_); + for (int i = 0; i < length_; i++) { + Handle<Object> value(keys_->get(i), isolate_); + set_ = OrderedHashSet::Add(set_, value); + } +} + + +void KeyAccumulator::EnsureCapacity(int capacity) { + if (keys_.is_null() || keys_->length() <= capacity) { + Grow(); + } +} + + +void KeyAccumulator::Grow() { + // The OrderedHashSet handles growing by itself. + if (!set_.is_null()) return; + // Otherwise, grow the internal keys_ FixedArray + int capacity = keys_.is_null() ? 16 : keys_->length() * 2 + 16; + Handle<FixedArray> new_keys = isolate_->factory()->NewFixedArray(capacity); + if (keys_.is_null()) { + keys_ = new_keys; + return; + } + int buffer_length = keys_->length(); + { + DisallowHeapAllocation no_gc; + WriteBarrierMode mode = new_keys->GetWriteBarrierMode(no_gc); + for (int i = 0; i < buffer_length; i++) { + new_keys->set(i, keys_->get(i), mode); + } + } + keys_ = new_keys; +} + + MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, KeyCollectionType type) { USE(ContainsOnlyValidKeys); Isolate* isolate = object->GetIsolate(); - Handle<FixedArray> content = isolate->factory()->empty_fixed_array(); + KeyAccumulator accumulator(isolate); Handle<JSFunction> arguments_function( JSFunction::cast(isolate->sloppy_arguments_map()->GetConstructor())); @@ -6118,8 +6847,7 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, PrototypeIterator::START_AT_RECEIVER); !iter.IsAtEnd(end); iter.Advance()) { if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { - Handle<JSProxy> proxy(JSProxy::cast(*PrototypeIterator::GetCurrent(iter)), - isolate); + Handle<JSProxy> proxy = PrototypeIterator::GetCurrent<JSProxy>(iter); Handle<Object> args[] = { proxy }; Handle<Object> names; ASSIGN_RETURN_ON_EXCEPTION( @@ -6130,16 +6858,11 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, arraysize(args), args), FixedArray); - ASSIGN_RETURN_ON_EXCEPTION( - isolate, content, - FixedArray::AddKeysFromArrayLike( - content, Handle<JSObject>::cast(names)), - FixedArray); + accumulator.AddKeys(Handle<JSObject>::cast(names), FixedArray::ALL_KEYS); break; } - Handle<JSObject> current = - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); + Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter); // Check access rights if required. if (current->IsAccessCheckNeeded() && !isolate->MayAccess(current)) { @@ -6154,23 +6877,17 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, Handle<FixedArray> element_keys = isolate->factory()->NewFixedArray(current->NumberOfEnumElements()); current->GetEnumElementKeys(*element_keys); - ASSIGN_RETURN_ON_EXCEPTION( - isolate, content, - FixedArray::UnionOfKeys(content, element_keys), - FixedArray); - DCHECK(ContainsOnlyValidKeys(content)); + accumulator.AddKeys(element_keys, FixedArray::ALL_KEYS); + DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys())); // Add the element keys from the interceptor. if (current->HasIndexedInterceptor()) { Handle<JSObject> result; if (JSObject::GetKeysForIndexedInterceptor( current, object).ToHandle(&result)) { - ASSIGN_RETURN_ON_EXCEPTION( - isolate, content, - FixedArray::AddKeysFromArrayLike(content, result), - FixedArray); + accumulator.AddKeys(result, FixedArray::ALL_KEYS); } - DCHECK(ContainsOnlyValidKeys(content)); + DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys())); } // We can cache the computed property keys if access checks are @@ -6188,27 +6905,26 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, !current->IsJSValue() && !current->IsAccessCheckNeeded() && !current->HasNamedInterceptor() && !current->HasIndexedInterceptor()); // Compute the property keys and cache them if possible. - ASSIGN_RETURN_ON_EXCEPTION( - isolate, content, - FixedArray::UnionOfKeys( - content, JSObject::GetEnumPropertyKeys(current, cache_enum_keys)), - FixedArray); - DCHECK(ContainsOnlyValidKeys(content)); + + Handle<FixedArray> enum_keys = + JSObject::GetEnumPropertyKeys(current, cache_enum_keys); + accumulator.AddKeys(enum_keys, FixedArray::ALL_KEYS); + DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys())); // Add the non-symbol property keys from the interceptor. if (current->HasNamedInterceptor()) { Handle<JSObject> result; if (JSObject::GetKeysForNamedInterceptor( current, object).ToHandle(&result)) { - ASSIGN_RETURN_ON_EXCEPTION( - isolate, content, FixedArray::AddKeysFromArrayLike( - content, result, FixedArray::NON_SYMBOL_KEYS), - FixedArray); + accumulator.AddKeys(result, FixedArray::NON_SYMBOL_KEYS); } - DCHECK(ContainsOnlyValidKeys(content)); + DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys())); } } - return content; + + Handle<FixedArray> keys = accumulator.GetKeys(); + DCHECK(ContainsOnlyValidKeys(keys)); + return keys; } @@ -6222,7 +6938,7 @@ bool Map::DictionaryElementsInPrototypeChainOnly() { if (iter.GetCurrent()->IsJSProxy()) return true; // String wrappers have non-configurable, non-writable elements. if (iter.GetCurrent()->IsStringWrapper()) return true; - JSObject* current = JSObject::cast(iter.GetCurrent()); + JSObject* current = iter.GetCurrent<JSObject>(); if (current->HasDictionaryElements() && current->element_dictionary()->requires_slow_elements()) { @@ -6279,8 +6995,8 @@ MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object, } } - DCHECK(getter->IsSpecFunction() || getter->IsUndefined() || getter->IsNull()); - DCHECK(setter->IsSpecFunction() || setter->IsUndefined() || setter->IsNull()); + DCHECK(getter->IsCallable() || getter->IsUndefined() || getter->IsNull()); + DCHECK(setter->IsCallable() || setter->IsUndefined() || setter->IsNull()); // At least one of the accessors needs to be a new value. DCHECK(!getter->IsNull() || !setter->IsNull()); if (!getter->IsNull()) { @@ -6727,7 +7443,7 @@ Handle<Map> Map::CopyInstallDescriptors( #else SLOW_DCHECK(result->layout_descriptor()->IsConsistentWithMap(*result)); #endif - result->set_visitor_id(StaticVisitorBase::GetVisitorId(*result)); + result->set_visitor_id(Heap::GetStaticVisitorIdForMap(*result)); } Handle<Name> name = handle(descriptors->GetKey(new_descriptor)); @@ -6864,7 +7580,7 @@ Handle<Map> Map::Create(Isolate* isolate, int inobject_properties) { copy->SetInObjectProperties(inobject_properties); copy->set_unused_property_fields(inobject_properties); copy->set_instance_size(new_instance_size); - copy->set_visitor_id(StaticVisitorBase::GetVisitorId(*copy)); + copy->set_visitor_id(Heap::GetStaticVisitorIdForMap(*copy)); return copy; } @@ -7709,53 +8425,6 @@ void FixedArray::Shrink(int new_length) { } -MaybeHandle<FixedArray> FixedArray::AddKeysFromArrayLike( - Handle<FixedArray> content, Handle<JSObject> array, KeyFilter filter) { - DCHECK(array->IsJSArray() || array->HasSloppyArgumentsElements()); - ElementsAccessor* accessor = array->GetElementsAccessor(); - Handle<FixedArray> result = - accessor->AddElementsToFixedArray(array, content, filter); - -#ifdef ENABLE_SLOW_DCHECKS - if (FLAG_enable_slow_asserts) { - DisallowHeapAllocation no_allocation; - for (int i = 0; i < result->length(); i++) { - Object* current = result->get(i); - DCHECK(current->IsNumber() || current->IsName()); - } - } -#endif - return result; -} - - -MaybeHandle<FixedArray> FixedArray::UnionOfKeys(Handle<FixedArray> first, - Handle<FixedArray> second) { - if (second->length() == 0) return first; - if (first->length() == 0) return second; - Isolate* isolate = first->GetIsolate(); - Handle<FixedArray> result = - isolate->factory()->NewFixedArray(first->length() + second->length()); - for (int i = 0; i < first->length(); i++) { - result->set(i, first->get(i)); - } - int pos = first->length(); - for (int j = 0; j < second->length(); j++) { - Object* current = second->get(j); - int i; - for (i = 0; i < first->length(); i++) { - if (current->KeyEquals(first->get(i))) break; - } - if (i == first->length()) { - result->set(pos++, current); - } - } - - result->Shrink(pos); - return result; -} - - void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) { DisallowHeapAllocation no_gc; WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc); @@ -7849,6 +8518,17 @@ void WeakFixedArray::Compact() { } +void WeakFixedArray::Iterator::Reset(Object* maybe_array) { + if (maybe_array->IsWeakFixedArray()) { + list_ = WeakFixedArray::cast(maybe_array); + index_ = 0; +#ifdef DEBUG + last_used_index_ = list_->last_used_index(); +#endif // DEBUG + } +} + + void JSObject::PrototypeRegistryCompactionCallback::Callback(Object* value, int old_index, int new_index) { @@ -8102,6 +8782,19 @@ Handle<DeoptimizationOutputData> DeoptimizationOutputData::New( } +// static +Handle<LiteralsArray> LiteralsArray::New(Isolate* isolate, + Handle<TypeFeedbackVector> vector, + int number_of_literals, + PretenureFlag pretenure) { + Handle<FixedArray> literals = isolate->factory()->NewFixedArray( + number_of_literals + kFirstLiteralIndex, pretenure); + Handle<LiteralsArray> casted_literals = Handle<LiteralsArray>::cast(literals); + casted_literals->set_feedback_vector(*vector); + return casted_literals; +} + + int HandlerTable::LookupRange(int pc_offset, int* stack_depth_out, CatchPrediction* prediction_out) { int innermost_handler = -1, innermost_start = -1; @@ -8160,6 +8853,110 @@ bool String::LooksValid() { } +// static +MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) { + if (name->IsString()) return Handle<String>::cast(name); + // ES6 section 9.2.11 SetFunctionName, step 4. + Isolate* const isolate = name->GetIsolate(); + Handle<Object> description(Handle<Symbol>::cast(name)->name(), isolate); + if (description->IsUndefined()) return isolate->factory()->empty_string(); + IncrementalStringBuilder builder(isolate); + builder.AppendCharacter('['); + builder.AppendString(Handle<String>::cast(description)); + builder.AppendCharacter(']'); + return builder.Finish(); +} + + +namespace { + +bool AreDigits(const uint8_t* s, int from, int to) { + for (int i = from; i < to; i++) { + if (s[i] < '0' || s[i] > '9') return false; + } + + return true; +} + + +int ParseDecimalInteger(const uint8_t* s, int from, int to) { + DCHECK(to - from < 10); // Overflow is not possible. + DCHECK(from < to); + int d = s[from] - '0'; + + for (int i = from + 1; i < to; i++) { + d = 10 * d + (s[i] - '0'); + } + + return d; +} + +} // namespace + + +// static +Handle<Object> String::ToNumber(Handle<String> subject) { + Isolate* const isolate = subject->GetIsolate(); + + // Flatten {subject} string first. + subject = String::Flatten(subject); + + // Fast array index case. + uint32_t index; + if (subject->AsArrayIndex(&index)) { + return isolate->factory()->NewNumberFromUint(index); + } + + // Fast case: short integer or some sorts of junk values. + if (subject->IsSeqOneByteString()) { + int len = subject->length(); + if (len == 0) return handle(Smi::FromInt(0), isolate); + + DisallowHeapAllocation no_gc; + uint8_t const* data = Handle<SeqOneByteString>::cast(subject)->GetChars(); + bool minus = (data[0] == '-'); + int start_pos = (minus ? 1 : 0); + + if (start_pos == len) { + return isolate->factory()->nan_value(); + } else if (data[start_pos] > '9') { + // Fast check for a junk value. A valid string may start from a + // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit + // or the 'I' character ('Infinity'). All of that have codes not greater + // than '9' except 'I' and . + if (data[start_pos] != 'I' && data[start_pos] != 0xa0) { + return isolate->factory()->nan_value(); + } + } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) { + // The maximal/minimal smi has 10 digits. If the string has less digits + // we know it will fit into the smi-data type. + int d = ParseDecimalInteger(data, start_pos, len); + if (minus) { + if (d == 0) return isolate->factory()->minus_zero_value(); + d = -d; + } else if (!subject->HasHashCode() && len <= String::kMaxArrayIndexSize && + (len == 1 || data[0] != '0')) { + // String hash is not calculated yet but all the data are present. + // Update the hash field to speed up sequential convertions. + uint32_t hash = StringHasher::MakeArrayIndexHash(d, len); +#ifdef DEBUG + subject->Hash(); // Force hash calculation. + DCHECK_EQ(static_cast<int>(subject->hash_field()), + static_cast<int>(hash)); +#endif + subject->set_hash_field(hash); + } + return handle(Smi::FromInt(d), isolate); + } + } + + // Slower case. + int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY; + return isolate->factory()->NewNumber( + StringToDouble(isolate->unicode_cache(), subject, flags)); +} + + String::FlatContent String::GetFlatContent() { DCHECK(!AllowHeapAllocation::IsAllowed()); int length = this->length(); @@ -8948,6 +9745,69 @@ bool String::SlowEquals(Handle<String> one, Handle<String> two) { } +// static +ComparisonResult String::Compare(Handle<String> x, Handle<String> y) { + // A few fast case tests before we flatten. + if (x.is_identical_to(y)) { + return ComparisonResult::kEqual; + } else if (y->length() == 0) { + return x->length() == 0 ? ComparisonResult::kEqual + : ComparisonResult::kGreaterThan; + } else if (x->length() == 0) { + return ComparisonResult::kLessThan; + } + + int const d = x->Get(0) - y->Get(0); + if (d < 0) { + return ComparisonResult::kLessThan; + } else if (d > 0) { + return ComparisonResult::kGreaterThan; + } + + // Slow case. + x = String::Flatten(x); + y = String::Flatten(y); + + DisallowHeapAllocation no_gc; + ComparisonResult result = ComparisonResult::kEqual; + int prefix_length = x->length(); + if (y->length() < prefix_length) { + prefix_length = y->length(); + result = ComparisonResult::kGreaterThan; + } else if (y->length() > prefix_length) { + result = ComparisonResult::kLessThan; + } + int r; + String::FlatContent x_content = x->GetFlatContent(); + String::FlatContent y_content = y->GetFlatContent(); + if (x_content.IsOneByte()) { + Vector<const uint8_t> x_chars = x_content.ToOneByteVector(); + if (y_content.IsOneByte()) { + Vector<const uint8_t> y_chars = y_content.ToOneByteVector(); + r = CompareChars(x_chars.start(), y_chars.start(), prefix_length); + } else { + Vector<const uc16> y_chars = y_content.ToUC16Vector(); + r = CompareChars(x_chars.start(), y_chars.start(), prefix_length); + } + } else { + Vector<const uc16> x_chars = x_content.ToUC16Vector(); + if (y_content.IsOneByte()) { + Vector<const uint8_t> y_chars = y_content.ToOneByteVector(); + r = CompareChars(x_chars.start(), y_chars.start(), prefix_length); + } else { + Vector<const uc16> y_chars = y_content.ToUC16Vector(); + r = CompareChars(x_chars.start(), y_chars.start(), prefix_length); + } + } + if (r < 0) { + result = ComparisonResult::kLessThan; + } else if (r > 0) { + result = ComparisonResult::kGreaterThan; + } + return result; +} + + bool String::IsUtf8EqualTo(Vector<const char> str, bool allow_prefix_match) { int slen = length(); // Can't check exact length equality, but we can check bounds. @@ -9068,16 +9928,9 @@ Handle<String> SeqString::Truncate(Handle<SeqString> string, int new_length) { DCHECK_OBJECT_ALIGNED(start_of_string + new_size); Heap* heap = string->GetHeap(); - NewSpace* newspace = heap->new_space(); - if (newspace->Contains(start_of_string) && - newspace->top() == start_of_string + old_size) { - // Last allocated object in new space. Simply lower allocation top. - newspace->set_top(start_of_string + new_size); - } else { - // Sizes are pointer size aligned, so that we can use filler objects - // that are a multiple of pointer size. - heap->CreateFillerObjectAt(start_of_string + new_size, delta); - } + // Sizes are pointer size aligned, so that we can use filler objects + // that are a multiple of pointer size. + heap->CreateFillerObjectAt(start_of_string + new_size, delta); heap->AdjustLiveBytes(*string, -delta, Heap::CONCURRENT_TO_SWEEPER); // We are storing the new length using release store after creating a filler @@ -9230,17 +10083,20 @@ int Map::Hash() { } -static bool CheckEquivalent(Map* first, Map* second) { +namespace { + +bool CheckEquivalent(Map* first, Map* second) { return first->GetConstructor() == second->GetConstructor() && first->prototype() == second->prototype() && first->instance_type() == second->instance_type() && first->bit_field() == second->bit_field() && first->is_extensible() == second->is_extensible() && first->is_strong() == second->is_strong() && - first->has_instance_call_handler() == - second->has_instance_call_handler(); + first->is_hidden_prototype() == second->is_hidden_prototype(); } +} // namespace + bool Map::EquivalentToForTransition(Map* other) { return CheckEquivalent(this, other); @@ -9328,30 +10184,6 @@ void JSFunction::AttemptConcurrentOptimization() { } -Handle<JSFunction> JSFunction::CloneClosure(Handle<JSFunction> function) { - Isolate* isolate = function->GetIsolate(); - Handle<Map> map(function->map()); - Handle<SharedFunctionInfo> shared(function->shared()); - Handle<Context> context(function->context()); - Handle<JSFunction> clone = - isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context); - - if (shared->bound()) { - clone->set_function_bindings(function->function_bindings()); - } - - // In typical case, __proto__ of ``function`` is the default Function - // prototype, which means that SetPrototype below is a no-op. - // In rare cases when that is not true, we mutate the clone's __proto__. - Handle<Object> original_prototype(map->prototype(), isolate); - if (*original_prototype != clone->map()->prototype()) { - JSObject::SetPrototype(clone, original_prototype, false).Assert(); - } - - return clone; -} - - void SharedFunctionInfo::AddSharedCodeToOptimizedCodeMap( Handle<SharedFunctionInfo> shared, Handle<Code> code) { Isolate* isolate = shared->GetIsolate(); @@ -9364,47 +10196,60 @@ void SharedFunctionInfo::AddSharedCodeToOptimizedCodeMap( void SharedFunctionInfo::AddToOptimizedCodeMap( - Handle<SharedFunctionInfo> shared, - Handle<Context> native_context, - Handle<Code> code, - Handle<FixedArray> literals, + Handle<SharedFunctionInfo> shared, Handle<Context> native_context, + Handle<HeapObject> code, Handle<LiteralsArray> literals, BailoutId osr_ast_id) { Isolate* isolate = shared->GetIsolate(); - DCHECK(!shared->SearchOptimizedCodeMap(*native_context, osr_ast_id).code); - DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION); + DCHECK(*code == isolate->heap()->undefined_value() || + !shared->SearchOptimizedCodeMap(*native_context, osr_ast_id).code); + DCHECK(*code == isolate->heap()->undefined_value() || + Code::cast(*code)->kind() == Code::OPTIMIZED_FUNCTION); DCHECK(native_context->IsNativeContext()); STATIC_ASSERT(kEntryLength == 4); Handle<FixedArray> new_code_map; Handle<Object> value(shared->optimized_code_map(), isolate); - int old_length; + int entry; if (value->IsSmi()) { // No optimized code map. DCHECK_EQ(0, Smi::cast(*value)->value()); new_code_map = isolate->factory()->NewFixedArray(kInitialLength, TENURED); - old_length = kEntriesStart; + entry = kEntriesStart; } else { - // Copy old optimized code map and append one new entry. Handle<FixedArray> old_code_map = Handle<FixedArray>::cast(value); + entry = shared->SearchOptimizedCodeMapEntry(*native_context, osr_ast_id); + if (entry > kSharedCodeIndex) { + // Found an existing context-specific entry, it must not contain any code. + DCHECK_EQ(isolate->heap()->undefined_value(), + old_code_map->get(entry + kCachedCodeOffset)); + // Just set the code and literals to the entry. + old_code_map->set(entry + kCachedCodeOffset, *code); + old_code_map->set(entry + kLiteralsOffset, *literals); + return; + } + + // Copy old optimized code map and append one new entry. new_code_map = isolate->factory()->CopyFixedArrayAndGrow( old_code_map, kEntryLength, TENURED); - old_length = old_code_map->length(); + int old_length = old_code_map->length(); // Zap the old map to avoid any stale entries. Note that this is required // for correctness because entries are being treated weakly by the GC. MemsetPointer(old_code_map->data_start(), isolate->heap()->the_hole_value(), old_length); + entry = old_length; } - new_code_map->set(old_length + kContextOffset, *native_context); - new_code_map->set(old_length + kCachedCodeOffset, *code); - new_code_map->set(old_length + kLiteralsOffset, *literals); - new_code_map->set(old_length + kOsrAstIdOffset, - Smi::FromInt(osr_ast_id.ToInt())); + new_code_map->set(entry + kContextOffset, *native_context); + new_code_map->set(entry + kCachedCodeOffset, *code); + new_code_map->set(entry + kLiteralsOffset, *literals); + new_code_map->set(entry + kOsrAstIdOffset, Smi::FromInt(osr_ast_id.ToInt())); #ifdef DEBUG for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) { DCHECK(new_code_map->get(i + kContextOffset)->IsNativeContext()); - DCHECK(new_code_map->get(i + kCachedCodeOffset)->IsCode()); - DCHECK(Code::cast(new_code_map->get(i + kCachedCodeOffset))->kind() == - Code::OPTIMIZED_FUNCTION); + Object* code = new_code_map->get(i + kCachedCodeOffset); + if (code != isolate->heap()->undefined_value()) { + DCHECK(code->IsCode()); + DCHECK(Code::cast(code)->kind() == Code::OPTIMIZED_FUNCTION); + } DCHECK(new_code_map->get(i + kLiteralsOffset)->IsFixedArray()); DCHECK(new_code_map->get(i + kOsrAstIdOffset)->IsSmi()); } @@ -9433,37 +10278,43 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, DisallowHeapAllocation no_gc; if (optimized_code_map()->IsSmi()) return; + Heap* heap = GetHeap(); FixedArray* code_map = FixedArray::cast(optimized_code_map()); int dst = kEntriesStart; int length = code_map->length(); for (int src = kEntriesStart; src < length; src += kEntryLength) { DCHECK(code_map->get(src)->IsNativeContext()); - if (Code::cast(code_map->get(src + kCachedCodeOffset)) == optimized_code) { - // Evict the src entry by not copying it to the dst entry. + if (code_map->get(src + kCachedCodeOffset) == optimized_code) { + BailoutId osr(Smi::cast(code_map->get(src + kOsrAstIdOffset))->value()); if (FLAG_trace_opt) { PrintF("[evicting entry from optimizing code map (%s) for ", reason); ShortPrint(); - BailoutId osr(Smi::cast(code_map->get(src + kOsrAstIdOffset))->value()); if (osr.IsNone()) { PrintF("]\n"); } else { PrintF(" (osr ast id %d)]\n", osr.ToInt()); } } - } else { - // Keep the src entry by copying it to the dst entry. - if (dst != src) { - code_map->set(dst + kContextOffset, - code_map->get(src + kContextOffset)); - code_map->set(dst + kCachedCodeOffset, - code_map->get(src + kCachedCodeOffset)); - code_map->set(dst + kLiteralsOffset, - code_map->get(src + kLiteralsOffset)); - code_map->set(dst + kOsrAstIdOffset, - code_map->get(src + kOsrAstIdOffset)); + if (!osr.IsNone()) { + // Evict the src entry by not copying it to the dst entry. + continue; } - dst += kEntryLength; + // In case of non-OSR entry just clear the code in order to proceed + // sharing literals. + code_map->set_undefined(src + kCachedCodeOffset); + } + + // Keep the src entry by copying it to the dst entry. + if (dst != src) { + code_map->set(dst + kContextOffset, code_map->get(src + kContextOffset)); + code_map->set(dst + kCachedCodeOffset, + code_map->get(src + kCachedCodeOffset)); + code_map->set(dst + kLiteralsOffset, + code_map->get(src + kLiteralsOffset)); + code_map->set(dst + kOsrAstIdOffset, + code_map->get(src + kOsrAstIdOffset)); } + dst += kEntryLength; } if (code_map->get(kSharedCodeIndex) == optimized_code) { // Evict context-independent code as well. @@ -9476,8 +10327,8 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, } if (dst != length) { // Always trim even when array is cleared because of heap verifier. - GetHeap()->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(code_map, - length - dst); + heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(code_map, + length - dst); if (code_map->length() == kEntriesStart && code_map->get(kSharedCodeIndex)->IsUndefined()) { ClearOptimizedCodeMap(); @@ -9515,7 +10366,7 @@ static void ShrinkInstanceSize(Map* map, void* data) { map->set_instance_size(map->instance_size() - slack * kPointerSize); // Visitor id might depend on the instance size, recalculate it. - map->set_visitor_id(StaticVisitorBase::GetVisitorId(map)); + map->set_visitor_id(Heap::GetStaticVisitorIdForMap(map)); } @@ -9663,7 +10514,7 @@ bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) { if (slot == PrototypeInfo::UNREGISTERED) return false; if (prototype->IsJSGlobalProxy()) { PrototypeIterator iter(isolate, prototype); - prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); + prototype = PrototypeIterator::GetCurrent<JSObject>(iter); } DCHECK(prototype->map()->is_prototype_map()); Object* maybe_proto_info = prototype->map()->prototype_info(); @@ -9699,16 +10550,10 @@ static void InvalidatePrototypeChainsInternal(Map* map) { cell->set_value(Smi::FromInt(Map::kPrototypeChainInvalid)); } - Object* maybe_array = proto_info->prototype_users(); - if (!maybe_array->IsWeakFixedArray()) return; - - WeakFixedArray* users = WeakFixedArray::cast(maybe_array); - for (int i = 0; i < users->Length(); ++i) { - Object* maybe_user = users->Get(i); - if (maybe_user->IsSmi()) continue; - - // For now, only maps register themselves as users. - Map* user = Map::cast(maybe_user); + WeakFixedArray::Iterator iterator(proto_info->prototype_users()); + // For now, only maps register themselves as users. + Map* user; + while ((user = iterator.Next<Map>())) { // Walk the prototype chain (backwards, towards leaf objects) if necessary. InvalidatePrototypeChainsInternal(user); } @@ -9721,7 +10566,7 @@ void JSObject::InvalidatePrototypeChains(Map* map) { DisallowHeapAllocation no_gc; if (map->IsJSGlobalProxyMap()) { PrototypeIterator iter(map); - map = JSObject::cast(iter.GetCurrent())->map(); + map = iter.GetCurrent<JSObject>()->map(); } InvalidatePrototypeChainsInternal(map); } @@ -9761,7 +10606,7 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map, Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype); if (prototype->IsJSGlobalProxy()) { PrototypeIterator iter(isolate, prototype); - prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); + prototype = PrototypeIterator::GetCurrent<JSObject>(iter); } // Ensure the prototype is registered with its own prototypes so its cell // will be invalidated when necessary. @@ -9860,6 +10705,9 @@ void JSFunction::SetInstancePrototype(Handle<JSFunction> function, function->set_prototype_or_initial_map(*value); } else { Handle<Map> new_map = Map::Copy(initial_map, "SetInstancePrototype"); + if (function->map()->is_strong()) { + new_map->set_is_strong(); + } JSFunction::SetInitialMap(function, new_map, value); // If the function is used as the global Array function, cache the @@ -9897,7 +10745,7 @@ void JSFunction::SetInstancePrototype(Handle<JSFunction> function, void JSFunction::SetPrototype(Handle<JSFunction> function, Handle<Object> value) { - DCHECK(function->should_have_prototype()); + DCHECK(function->IsConstructor()); Handle<Object> construct_prototype = value; // If the value is not a JSReceiver, store the value in the map's @@ -9984,6 +10832,9 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) { in_object_properties = function->shared()->CalculateInObjectProperties(); } Handle<Map> map = isolate->factory()->NewMap(instance_type, instance_size); + if (function->map()->is_strong()) { + map->set_is_strong(); + } // Fetch or allocate prototype. Handle<Object> prototype; @@ -9997,7 +10848,8 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) { DCHECK(map->has_fast_object_elements()); // Finally link initial map and constructor function. - JSFunction::SetInitialMap(function, map, Handle<JSReceiver>::cast(prototype)); + DCHECK(prototype->IsJSReceiver()); + JSFunction::SetInitialMap(function, map, prototype); if (!function->shared()->is_generator()) { function->StartInobjectSlackTracking(); @@ -10108,8 +10960,8 @@ int Script::GetColumnNumber(Handle<Script> script, int code_pos) { DisallowHeapAllocation no_allocation; FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); - line_number = line_number - script->line_offset()->value(); - if (line_number == 0) return code_pos + script->column_offset()->value(); + line_number = line_number - script->line_offset(); + if (line_number == 0) return code_pos + script->column_offset(); int prev_line_end_pos = Smi::cast(line_ends_array->get(line_number - 1))->value(); return code_pos - (prev_line_end_pos + 1); @@ -10124,7 +10976,7 @@ int Script::GetLineNumberWithArray(int code_pos) { if (line_ends_len == 0) return -1; if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) { - return line_offset()->value(); + return line_offset(); } int left = 0; @@ -10136,7 +10988,7 @@ int Script::GetLineNumberWithArray(int code_pos) { left += half; } } - return right + line_offset()->value(); + return right + line_offset(); } @@ -10212,22 +11064,48 @@ Handle<JSObject> Script::GetWrapper(Handle<Script> script) { MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo( FunctionLiteral* fun) { - if (shared_function_infos()->IsWeakFixedArray()) { - WeakFixedArray* array = WeakFixedArray::cast(shared_function_infos()); - for (int i = 0; i < array->Length(); i++) { - Object* obj = array->Get(i); - if (!obj->IsSharedFunctionInfo()) continue; - SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj); - if (fun->function_token_position() == shared->function_token_position() && - fun->start_position() == shared->start_position()) { - return Handle<SharedFunctionInfo>(shared); - } + WeakFixedArray::Iterator iterator(shared_function_infos()); + SharedFunctionInfo* shared; + while ((shared = iterator.Next<SharedFunctionInfo>())) { + if (fun->function_token_position() == shared->function_token_position() && + fun->start_position() == shared->start_position()) { + return Handle<SharedFunctionInfo>(shared); } } return MaybeHandle<SharedFunctionInfo>(); } +Script::Iterator::Iterator(Isolate* isolate) + : iterator_(isolate->heap()->script_list()) {} + + +Script* Script::Iterator::Next() { return iterator_.Next<Script>(); } + + +SharedFunctionInfo::Iterator::Iterator(Isolate* isolate) + : script_iterator_(isolate), sfi_iterator_(NULL) { + NextScript(); +} + + +bool SharedFunctionInfo::Iterator::NextScript() { + Script* script = script_iterator_.Next(); + if (script == NULL) return false; + sfi_iterator_.Reset(script->shared_function_infos()); + return true; +} + + +SharedFunctionInfo* SharedFunctionInfo::Iterator::Next() { + do { + SharedFunctionInfo* next = sfi_iterator_.Next<SharedFunctionInfo>(); + if (next != NULL) return next; + } while (NextScript()); + return NULL; +} + + void SharedFunctionInfo::SetScript(Handle<SharedFunctionInfo> shared, Handle<Object> script_object) { if (shared->script() == *script_object) return; @@ -10245,10 +11123,11 @@ void SharedFunctionInfo::SetScript(Handle<SharedFunctionInfo> shared, Handle<Script> script = Handle<Script>::cast(script_object); Handle<Object> list(script->shared_function_infos(), shared->GetIsolate()); #ifdef DEBUG - if (list->IsWeakFixedArray()) { - Handle<WeakFixedArray> array = Handle<WeakFixedArray>::cast(list); - for (int i = 0; i < array->Length(); ++i) { - DCHECK(array->Get(i) != *shared); + { + WeakFixedArray::Iterator iterator(*list); + SharedFunctionInfo* next; + while ((next = iterator.Next<SharedFunctionInfo>())) { + DCHECK_NE(next, *shared); } } #endif // DEBUG @@ -10471,8 +11350,8 @@ void SharedFunctionInfo::ResetForNewContext(int new_ic_age) { } -CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap( - Context* native_context, BailoutId osr_ast_id) { +int SharedFunctionInfo::SearchOptimizedCodeMapEntry(Context* native_context, + BailoutId osr_ast_id) { DisallowHeapAllocation no_gc; DCHECK(native_context->IsNativeContext()); Object* value = optimized_code_map(); @@ -10483,21 +11362,41 @@ CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap( for (int i = kEntriesStart; i < length; i += kEntryLength) { if (optimized_code_map->get(i + kContextOffset) == native_context && optimized_code_map->get(i + kOsrAstIdOffset) == osr_ast_id_smi) { - return {Code::cast(optimized_code_map->get(i + kCachedCodeOffset)), - FixedArray::cast(optimized_code_map->get(i + kLiteralsOffset))}; + return i; } } Object* shared_code = optimized_code_map->get(kSharedCodeIndex); if (shared_code->IsCode() && osr_ast_id.IsNone()) { - return {Code::cast(shared_code), nullptr}; + return kSharedCodeIndex; } - if (FLAG_trace_opt) { - PrintF("[didn't find optimized code in optimized code map for "); - ShortPrint(); - PrintF("]\n"); + } + return -1; +} + + +CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap( + Context* native_context, BailoutId osr_ast_id) { + CodeAndLiterals result = {nullptr, nullptr}; + int entry = SearchOptimizedCodeMapEntry(native_context, osr_ast_id); + if (entry != kNotFound) { + FixedArray* code_map = FixedArray::cast(optimized_code_map()); + if (entry == kSharedCodeIndex) { + result = {Code::cast(code_map->get(kSharedCodeIndex)), nullptr}; + + } else { + DCHECK_LE(entry + kEntryLength, code_map->length()); + Object* code = code_map->get(entry + kCachedCodeOffset); + result = {code->IsUndefined() ? nullptr : Code::cast(code), + LiteralsArray::cast(code_map->get(entry + kLiteralsOffset))}; } } - return {nullptr, nullptr}; + if (FLAG_trace_opt && !optimized_code_map()->IsSmi() && + result.code == nullptr) { + PrintF("[didn't find optimized code in optimized code map for "); + ShortPrint(); + PrintF("]\n"); + } + return result; } @@ -10605,7 +11504,7 @@ void Code::Relocate(intptr_t delta) { for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) { it.rinfo()->apply(delta); } - CpuFeatures::FlushICache(instruction_start(), instruction_size()); + Assembler::FlushICache(GetIsolate(), instruction_start(), instruction_size()); } @@ -10659,7 +11558,7 @@ void Code::CopyFrom(const CodeDesc& desc) { it.rinfo()->apply(delta); } } - CpuFeatures::FlushICache(instruction_start(), instruction_size()); + Assembler::FlushICache(GetIsolate(), instruction_start(), instruction_size()); } @@ -11439,7 +12338,7 @@ void Code::PrintExtraICState(std::ostream& os, // NOLINT void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT os << "kind = " << Kind2String(kind()) << "\n"; if (IsCodeStubOrIC()) { - const char* n = CodeStub::MajorName(CodeStub::GetMajorKey(this), true); + const char* n = CodeStub::MajorName(CodeStub::GetMajorKey(this)); os << "major_key = " << (n == NULL ? "null" : n) << "\n"; } if (is_inline_cache_stub()) { @@ -11584,6 +12483,7 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT void BytecodeArray::Disassemble(std::ostream& os) { + os << "Parameter count " << parameter_count() << "\n"; os << "Frame size " << frame_size() << "\n"; Vector<char> buf = Vector<char>::New(50); @@ -11597,9 +12497,12 @@ void BytecodeArray::Disassemble(std::ostream& os) { SNPrintF(buf, "%p", bytecode_start); os << buf.start() << " : "; - interpreter::Bytecodes::Decode(os, bytecode_start); + interpreter::Bytecodes::Decode(os, bytecode_start, parameter_count()); os << "\n"; } + + os << "Constant pool (size = " << constant_pool()->length() << ")\n"; + constant_pool()->Print(); } @@ -12061,7 +12964,7 @@ MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> object, for (PrototypeIterator iter(isolate, *value, PrototypeIterator::START_AT_RECEIVER); !iter.IsAtEnd(); iter.Advance()) { - if (JSReceiver::cast(iter.GetCurrent()) == *object) { + if (iter.GetCurrent<JSReceiver>() == *object) { // Cycle detected. THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCyclicProto), Object); @@ -12077,8 +12980,7 @@ MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> object, // hidden and set the new prototype on that object. PrototypeIterator iter(isolate, real_receiver); while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { - real_receiver = - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); + real_receiver = PrototypeIterator::GetCurrent<JSObject>(iter); iter.Advance(); if (!real_receiver->map()->is_extensible()) { THROW_NEW_ERROR( @@ -12107,7 +13009,7 @@ MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> object, // If the prototype chain didn't previously have element callbacks, then // KeyedStoreICs need to be cleared to ensure any that involve this // map go generic. - object->GetHeap()->ClearAllICsByKind(Code::KEYED_STORE_IC); + object->GetHeap()->ClearAllKeyedStoreICs(); } heap->ClearInstanceofCache(); @@ -12277,7 +13179,7 @@ MaybeHandle<Object> JSObject::AddDataElement(Handle<JSObject> object, to = GetHoleyElementsKind(to); kind = GetHoleyElementsKind(kind); } - to = IsMoreGeneralElementsKindTransition(kind, to) ? to : kind; + to = GetMoreGeneralElementsKind(kind, to); ElementsAccessor* accessor = ElementsAccessor::ForKind(to); accessor->Add(object, index, value, attributes, new_capacity); @@ -13834,7 +14736,7 @@ Handle<Object> JSObject::PrepareElementsForSort(Handle<JSObject> object, Handle<FixedArrayBase> elements_base(object->elements()); uint32_t elements_length = static_cast<uint32_t>(elements_base->length()); if (limit > elements_length) { - limit = elements_length ; + limit = elements_length; } if (limit == 0) { return handle(Smi::FromInt(0), isolate); @@ -13950,12 +14852,6 @@ size_t JSTypedArray::element_size() { } -void FixedArray::SetValue(uint32_t index, Object* value) { set(index, value); } - - -void FixedDoubleArray::SetValue(uint32_t index, Object* value) { - set(index, value->Number()); -} void GlobalObject::InvalidatePropertyCell(Handle<GlobalObject> global, Handle<Name> name) { DCHECK(!global->HasFastProperties()); @@ -14106,7 +15002,7 @@ void StringTable::EnsureCapacityForDeserialization(Isolate* isolate, // We need a key instance for the virtual hash function. InternalizedStringKey dummy_key(Handle<String>::null()); table = StringTable::EnsureCapacity(table, expected, &dummy_key); - isolate->factory()->set_string_table(table); + isolate->heap()->SetRootStringTable(*table); } @@ -14140,7 +15036,7 @@ Handle<String> StringTable::LookupKey(Isolate* isolate, HashTableKey* key) { table->set(EntryToIndex(entry), *string); table->ElementAdded(); - isolate->factory()->set_string_table(table); + isolate->heap()->SetRootStringTable(*table); return Handle<String>::cast(string); } @@ -14521,7 +15417,7 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key, if (key > kRequiresSlowElementsLimit) { if (used_as_prototype) { // TODO(verwaest): Remove this hack. - GetHeap()->ClearAllICsByKind(Code::KEYED_STORE_IC); + GetHeap()->ClearAllKeyedStoreICs(); } set_requires_slow_elements(); return; @@ -14870,49 +15766,6 @@ void WeakHashTable::AddEntry(int entry, Handle<WeakCell> key_cell, } -#ifdef DEBUG -Object* WeakValueHashTable::LookupWeak(Handle<Object> key) { - Object* value = Lookup(key); - if (value->IsWeakCell() && !WeakCell::cast(value)->cleared()) { - value = WeakCell::cast(value)->value(); - } - return value; -} -#endif // DEBUG - - -Handle<WeakValueHashTable> WeakValueHashTable::PutWeak( - Handle<WeakValueHashTable> table, Handle<Object> key, - Handle<HeapObject> value) { - Handle<WeakCell> cell = value->GetIsolate()->factory()->NewWeakCell(value); - return Handle<WeakValueHashTable>::cast( - Put(Handle<ObjectHashTable>::cast(table), key, cell)); -} - - -Handle<FixedArray> WeakValueHashTable::GetWeakValues( - Handle<WeakValueHashTable> table) { - Isolate* isolate = table->GetIsolate(); - uint32_t capacity = table->Capacity(); - Handle<FixedArray> results = isolate->factory()->NewFixedArray(capacity); - int length = 0; - for (uint32_t i = 0; i < capacity; i++) { - uint32_t key_index = table->EntryToIndex(i); - Object* key = table->get(key_index); - if (!table->IsKey(key)) continue; - uint32_t value_index = table->EntryToValueIndex(i); - WeakCell* value_cell = WeakCell::cast(table->get(value_index)); - if (value_cell->cleared()) { - table->RemoveEntry(i); - } else { - results->set(length++, value_cell->value()); - } - } - results->Shrink(length); - return results; -} - - template<class Derived, class Iterator, int entrysize> Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Allocate( Isolate* isolate, int capacity, PretenureFlag pretenure) { @@ -14985,6 +15838,48 @@ Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Clear( return new_table; } +template <class Derived, class Iterator, int entrysize> +bool OrderedHashTable<Derived, Iterator, entrysize>::HasKey( + Handle<Derived> table, Handle<Object> key) { + int entry = table->KeyToFirstEntry(*key); + // Walk the chain in the bucket to find the key. + while (entry != kNotFound) { + Object* candidate_key = table->KeyAt(entry); + if (candidate_key->SameValueZero(*key)) return true; + entry = table->NextChainEntry(entry); + } + return false; +} + + +Handle<OrderedHashSet> OrderedHashSet::Add(Handle<OrderedHashSet> table, + Handle<Object> key) { + int hash = Object::GetOrCreateHash(table->GetIsolate(), key)->value(); + int entry = table->HashToEntry(hash); + // Walk the chain of the bucket and try finding the key. + while (entry != kNotFound) { + Object* candidate_key = table->KeyAt(entry); + // Do not add if we have the key already + if (candidate_key->SameValueZero(*key)) return table; + entry = table->NextChainEntry(entry); + } + + table = OrderedHashSet::EnsureGrowable(table); + // Read the existing bucket values. + int bucket = table->HashToBucket(hash); + int previous_entry = table->HashToEntry(hash); + int nof = table->NumberOfElements(); + // Insert a new entry at the end, + int new_entry = nof + table->NumberOfDeletedElements(); + int new_index = table->EntryToIndex(new_entry); + table->set(new_index, *key); + table->set(new_index + kChainOffset, Smi::FromInt(previous_entry)); + // and point the bucket to the new entry. + table->set(kHashTableStartIndex + bucket, Smi::FromInt(new_entry)); + table->SetNumberOfElements(nof + 1); + return table; +} + template<class Derived, class Iterator, int entrysize> Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Rehash( @@ -15047,6 +15942,9 @@ template Handle<OrderedHashSet> OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Clear( Handle<OrderedHashSet> table); +template bool OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::HasKey( + Handle<OrderedHashSet> table, Handle<Object> key); + template Handle<OrderedHashMap> OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Allocate( @@ -15064,6 +15962,9 @@ template Handle<OrderedHashMap> OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Clear( Handle<OrderedHashMap> table); +template bool OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::HasKey( + Handle<OrderedHashMap> table, Handle<Object> key); + template<class Derived, class TableType> void OrderedHashTableIterator<Derived, TableType>::Transition() { @@ -15169,6 +16070,75 @@ template void OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Transition(); +void JSSet::Initialize(Handle<JSSet> set, Isolate* isolate) { + Handle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet(); + set->set_table(*table); +} + + +void JSSet::Clear(Handle<JSSet> set) { + Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table())); + table = OrderedHashSet::Clear(table); + set->set_table(*table); +} + + +void JSMap::Initialize(Handle<JSMap> map, Isolate* isolate) { + Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap(); + map->set_table(*table); +} + + +void JSMap::Clear(Handle<JSMap> map) { + Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table())); + table = OrderedHashMap::Clear(table); + map->set_table(*table); +} + + +void JSWeakCollection::Initialize(Handle<JSWeakCollection> weak_collection, + Isolate* isolate) { + DCHECK_EQ(0, weak_collection->map()->GetInObjectProperties()); + Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 0); + weak_collection->set_table(*table); +} + + +void JSWeakCollection::Set(Handle<JSWeakCollection> weak_collection, + Handle<Object> key, Handle<Object> value, + int32_t hash) { + DCHECK(key->IsJSReceiver() || key->IsSymbol()); + Handle<ObjectHashTable> table( + ObjectHashTable::cast(weak_collection->table())); + DCHECK(table->IsKey(*key)); + Handle<ObjectHashTable> new_table = + ObjectHashTable::Put(table, key, value, hash); + weak_collection->set_table(*new_table); + if (*table != *new_table) { + // Zap the old table since we didn't record slots for its elements. + table->FillWithHoles(0, table->length()); + } +} + + +bool JSWeakCollection::Delete(Handle<JSWeakCollection> weak_collection, + Handle<Object> key, int32_t hash) { + DCHECK(key->IsJSReceiver() || key->IsSymbol()); + Handle<ObjectHashTable> table( + ObjectHashTable::cast(weak_collection->table())); + DCHECK(table->IsKey(*key)); + bool was_present = false; + Handle<ObjectHashTable> new_table = + ObjectHashTable::Remove(table, key, &was_present, hash); + weak_collection->set_table(*new_table); + if (*table != *new_table) { + // Zap the old table since we didn't record slots for its elements. + table->FillWithHoles(0, table->length()); + } + return was_present; +} + + // Check if there is a break point at this code position. bool DebugInfo::HasBreakPoint(int code_position) { // Get the break point info object for this code position. @@ -15249,10 +16219,9 @@ void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, // Allocate new BreakPointInfo object and set the break point. Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast( isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE)); - new_break_point_info->set_code_position(Smi::FromInt(code_position)); - new_break_point_info->set_source_position(Smi::FromInt(source_position)); - new_break_point_info-> - set_statement_position(Smi::FromInt(statement_position)); + new_break_point_info->set_code_position(code_position); + new_break_point_info->set_source_position(source_position); + new_break_point_info->set_statement_position(statement_position); new_break_point_info->set_break_point_objects( isolate->heap()->undefined_value()); BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object); @@ -15314,7 +16283,7 @@ int DebugInfo::GetBreakPointInfoIndex(int code_position) { if (!break_points()->get(i)->IsUndefined()) { BreakPointInfo* break_point_info = BreakPointInfo::cast(break_points()->get(i)); - if (break_point_info->code_position()->value() == code_position) { + if (break_point_info->code_position() == code_position) { return i; } } @@ -15536,6 +16505,27 @@ void JSDate::SetValue(Object* value, bool is_value_nan) { } +// static +MaybeHandle<Object> JSDate::ToPrimitive(Handle<JSReceiver> receiver, + Handle<Object> hint) { + Isolate* const isolate = receiver->GetIsolate(); + if (hint->IsString()) { + Handle<String> hint_string = Handle<String>::cast(hint); + if (hint_string->Equals(isolate->heap()->number_string())) { + return JSReceiver::OrdinaryToPrimitive(receiver, + OrdinaryToPrimitiveHint::kNumber); + } + if (hint_string->Equals(isolate->heap()->default_string()) || + hint_string->Equals(isolate->heap()->string_string())) { + return JSReceiver::OrdinaryToPrimitive(receiver, + OrdinaryToPrimitiveHint::kString); + } + } + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kInvalidHint, hint), + Object); +} + + void JSDate::SetCachedFields(int64_t local_time_ms, DateCache* date_cache) { int days = DateCache::DaysFromTime(local_time_ms); int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days); @@ -15565,6 +16555,61 @@ void JSArrayBuffer::Neuter() { } +void JSArrayBuffer::Setup(Handle<JSArrayBuffer> array_buffer, Isolate* isolate, + bool is_external, void* data, size_t allocated_length, + SharedFlag shared) { + DCHECK(array_buffer->GetInternalFieldCount() == + v8::ArrayBuffer::kInternalFieldCount); + for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) { + array_buffer->SetInternalField(i, Smi::FromInt(0)); + } + array_buffer->set_bit_field(0); + array_buffer->set_is_external(is_external); + array_buffer->set_is_neuterable(shared == SharedFlag::kNotShared); + array_buffer->set_is_shared(shared == SharedFlag::kShared); + + Handle<Object> byte_length = + isolate->factory()->NewNumberFromSize(allocated_length); + CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber()); + array_buffer->set_byte_length(*byte_length); + // Initialize backing store at last to avoid handling of |JSArrayBuffers| that + // are currently being constructed in the |ArrayBufferTracker|. The + // registration method below handles the case of registering a buffer that has + // already been promoted. + array_buffer->set_backing_store(data); + + if (data && !is_external) { + isolate->heap()->RegisterNewArrayBuffer(*array_buffer); + } +} + + +bool JSArrayBuffer::SetupAllocatingData(Handle<JSArrayBuffer> array_buffer, + Isolate* isolate, + size_t allocated_length, + bool initialize, SharedFlag shared) { + void* data; + CHECK(isolate->array_buffer_allocator() != NULL); + // Prevent creating array buffers when serializing. + DCHECK(!isolate->serializer_enabled()); + if (allocated_length != 0) { + if (initialize) { + data = isolate->array_buffer_allocator()->Allocate(allocated_length); + } else { + data = isolate->array_buffer_allocator()->AllocateUninitialized( + allocated_length); + } + if (data == NULL) return false; + } else { + data = NULL; + } + + JSArrayBuffer::Setup(array_buffer, isolate, false, data, allocated_length, + shared); + return true; +} + + Handle<JSArrayBuffer> JSTypedArray::MaterializeArrayBuffer( Handle<JSTypedArray> typed_array) { @@ -15581,11 +16626,16 @@ Handle<JSArrayBuffer> JSTypedArray::MaterializeArrayBuffer( void* backing_store = isolate->array_buffer_allocator()->AllocateUninitialized( fixed_typed_array->DataSize()); - buffer->set_backing_store(backing_store); buffer->set_is_external(false); - isolate->heap()->RegisterNewArrayBuffer(isolate->heap()->InNewSpace(*buffer), - backing_store, - fixed_typed_array->DataSize()); + DCHECK(buffer->byte_length()->IsSmi() || + buffer->byte_length()->IsHeapNumber()); + DCHECK(NumberToInt32(buffer->byte_length()) == fixed_typed_array->DataSize()); + // Initialize backing store at last to avoid handling of |JSArrayBuffers| that + // are currently being constructed in the |ArrayBufferTracker|. The + // registration method below handles the case of registering a buffer that has + // already been promoted. + buffer->set_backing_store(backing_store); + isolate->heap()->RegisterNewArrayBuffer(*buffer); memcpy(buffer->backing_store(), fixed_typed_array->DataPtr(), fixed_typed_array->DataSize()); |