Upgrade V8 to version 4.9.385.28
https://chromium.googlesource.com/v8/v8/+/4.9.385.28
FPIIM-449
Change-Id: I4b2e74289d4bf3667f2f3dc8aa2e541f63e26eb4
diff --git a/src/objects.cc b/src/objects.cc
index 6b64c3f..d9d8213 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -1,42 +1,54 @@
-// Copyright 2013 the V8 project authors. All rights reserved.
+// 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 <sstream>
+#include "src/objects.h"
-#include "src/v8.h"
+#include <cmath>
+#include <iomanip>
+#include <sstream>
#include "src/accessors.h"
#include "src/allocation-site-scopes.h"
#include "src/api.h"
#include "src/arguments.h"
#include "src/base/bits.h"
+#include "src/base/utils/random-number-generator.h"
#include "src/bootstrapper.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
-#include "src/cpu-profiler.h"
+#include "src/compilation-dependencies.h"
+#include "src/compiler.h"
#include "src/date.h"
-#include "src/debug.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/full-codegen.h"
-#include "src/heap/mark-compact.h"
-#include "src/heap/objects-visiting-inl.h"
-#include "src/hydrogen.h"
+#include "src/field-index-inl.h"
+#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
+#include "src/identity-map.h"
+#include "src/interpreter/bytecodes.h"
#include "src/isolate-inl.h"
+#include "src/key-accumulator.h"
+#include "src/list.h"
#include "src/log.h"
#include "src/lookup.h"
#include "src/macro-assembler.h"
+#include "src/messages.h"
#include "src/objects-inl.h"
+#include "src/objects-body-descriptors-inl.h"
+#include "src/profiler/cpu-profiler.h"
+#include "src/property-descriptor.h"
#include "src/prototype.h"
+#include "src/regexp/jsregexp.h"
#include "src/safepoint-table.h"
+#include "src/string-builder.h"
#include "src/string-search.h"
#include "src/string-stream.h"
#include "src/utils.h"
+#include "src/zone.h"
#ifdef ENABLE_DISASSEMBLER
#include "src/disasm.h"
@@ -46,6 +58,19 @@
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<HeapType> Object::OptimalType(Isolate* isolate,
Representation representation) {
if (representation.IsNone()) return HeapType::None(isolate);
@@ -53,9 +78,7 @@
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->instance_type() >= FIRST_NONCALLABLE_SPEC_OBJECT_TYPE &&
- map->instance_type() <= LAST_NONCALLABLE_SPEC_OBJECT_TYPE) {
+ if (map->is_stable() && map->IsJSReceiverMap()) {
return HeapType::Class(map, isolate);
}
}
@@ -69,16 +92,17 @@
Handle<Context> native_context) {
if (object->IsJSReceiver()) return Handle<JSReceiver>::cast(object);
Handle<JSFunction> constructor;
- if (object->IsNumber()) {
+ if (object->IsSmi()) {
constructor = handle(native_context->number_function(), isolate);
- } else if (object->IsBoolean()) {
- constructor = handle(native_context->boolean_function(), isolate);
- } else if (object->IsString()) {
- constructor = handle(native_context->string_function(), isolate);
- } else if (object->IsSymbol()) {
- constructor = handle(native_context->symbol_function(), isolate);
} else {
- return MaybeHandle<JSReceiver>();
+ int constructor_function_index =
+ Handle<HeapObject>::cast(object)->map()->GetConstructorFunctionIndex();
+ if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
+ return MaybeHandle<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);
@@ -86,6 +110,106 @@
}
+// 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,57 +221,704 @@
}
-bool Object::IsCallable() const {
- const Object* fun = this;
- while (fun->IsJSFunctionProxy()) {
- fun = JSFunctionProxy::cast(fun)->call_trap();
+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;
}
- return fun->IsJSFunction() ||
- (fun->IsHeapObject() &&
- HeapObject::cast(fun)->map()->has_instance_call_handler());
}
-MaybeHandle<Object> Object::GetProperty(LookupIterator* it) {
+bool NumberEquals(double x, double y) {
+ // Must check explicitly for NaN's on Windows, but -0 works fine.
+ if (std::isnan(x)) return false;
+ if (std::isnan(y)) return false;
+ return x == y;
+}
+
+
+bool NumberEquals(const Object* x, const Object* y) {
+ return NumberEquals(x->Number(), y->Number());
+}
+
+
+bool NumberEquals(Handle<Object> x, Handle<Object> y) {
+ return NumberEquals(*x, *y);
+}
+
+} // namespace
+
+
+// static
+Maybe<ComparisonResult> Object::Compare(Handle<Object> x, Handle<Object> y,
+ Strength strength) {
+ if (!is_strong(strength)) {
+ // ES6 section 7.2.11 Abstract Relational Comparison step 3 and 4.
+ if (!Object::ToPrimitive(x, ToPrimitiveHint::kNumber).ToHandle(&x) ||
+ !Object::ToPrimitive(y, ToPrimitiveHint::kNumber).ToHandle(&y)) {
+ return Nothing<ComparisonResult>();
+ }
+ }
+ if (x->IsString() && y->IsString()) {
+ // ES6 section 7.2.11 Abstract Relational Comparison step 5.
+ return Just(
+ String::Compare(Handle<String>::cast(x), Handle<String>::cast(y)));
+ }
+ // ES6 section 7.2.11 Abstract Relational Comparison step 6.
+ if (!is_strong(strength)) {
+ if (!Object::ToNumber(x).ToHandle(&x) ||
+ !Object::ToNumber(y).ToHandle(&y)) {
+ return Nothing<ComparisonResult>();
+ }
+ } else {
+ if (!x->IsNumber()) {
+ Isolate* const isolate = Handle<HeapObject>::cast(x)->GetIsolate();
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kStrongImplicitConversion));
+ return Nothing<ComparisonResult>();
+ } else if (!y->IsNumber()) {
+ Isolate* const isolate = Handle<HeapObject>::cast(y)->GetIsolate();
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kStrongImplicitConversion));
+ return Nothing<ComparisonResult>();
+ }
+ }
+ return Just(NumberCompare(x->Number(), y->Number()));
+}
+
+
+// static
+Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) {
+ while (true) {
+ if (x->IsNumber()) {
+ if (y->IsNumber()) {
+ return Just(NumberEquals(x, y));
+ } else if (y->IsBoolean()) {
+ return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
+ } else if (y->IsString()) {
+ return Just(NumberEquals(x, String::ToNumber(Handle<String>::cast(y))));
+ } else if (y->IsJSReceiver() && !y->IsUndetectableObject()) {
+ if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
+ .ToHandle(&y)) {
+ return Nothing<bool>();
+ }
+ } else {
+ return Just(false);
+ }
+ } else if (x->IsString()) {
+ if (y->IsString()) {
+ return Just(
+ String::Equals(Handle<String>::cast(x), Handle<String>::cast(y)));
+ } else if (y->IsNumber()) {
+ x = String::ToNumber(Handle<String>::cast(x));
+ return Just(NumberEquals(x, y));
+ } else if (y->IsBoolean()) {
+ x = String::ToNumber(Handle<String>::cast(x));
+ return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
+ } else if (y->IsJSReceiver() && !y->IsUndetectableObject()) {
+ if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
+ .ToHandle(&y)) {
+ return Nothing<bool>();
+ }
+ } else {
+ return Just(false);
+ }
+ } else if (x->IsBoolean()) {
+ if (y->IsOddball()) {
+ return Just(x.is_identical_to(y));
+ } else if (y->IsNumber()) {
+ return Just(NumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
+ } else if (y->IsString()) {
+ y = String::ToNumber(Handle<String>::cast(y));
+ return Just(NumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
+ } else if (y->IsJSReceiver() && !y->IsUndetectableObject()) {
+ if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
+ .ToHandle(&y)) {
+ return Nothing<bool>();
+ }
+ x = Oddball::ToNumber(Handle<Oddball>::cast(x));
+ } else {
+ return Just(false);
+ }
+ } else if (x->IsSymbol()) {
+ if (y->IsSymbol()) {
+ return Just(x.is_identical_to(y));
+ } else if (y->IsJSReceiver() && !y->IsUndetectableObject()) {
+ if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
+ .ToHandle(&y)) {
+ return Nothing<bool>();
+ }
+ } else {
+ return Just(false);
+ }
+ } else if (x->IsSimd128Value()) {
+ if (y->IsSimd128Value()) {
+ return Just(Simd128Value::Equals(Handle<Simd128Value>::cast(x),
+ Handle<Simd128Value>::cast(y)));
+ } else if (y->IsJSReceiver() && !y->IsUndetectableObject()) {
+ if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
+ .ToHandle(&y)) {
+ return Nothing<bool>();
+ }
+ } else {
+ return Just(false);
+ }
+ } else if (x->IsJSReceiver() && !x->IsUndetectableObject()) {
+ if (y->IsJSReceiver()) {
+ return Just(x.is_identical_to(y));
+ } else if (y->IsNull() || y->IsUndefined()) {
+ return Just(false);
+ } else if (y->IsBoolean()) {
+ y = Oddball::ToNumber(Handle<Oddball>::cast(y));
+ } else if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(x))
+ .ToHandle(&x)) {
+ return Nothing<bool>();
+ }
+ } else {
+ return Just(
+ (x->IsNull() || x->IsUndefined() || x->IsUndetectableObject()) &&
+ (y->IsNull() || y->IsUndefined() || y->IsUndetectableObject()));
+ }
+ }
+}
+
+
+bool Object::StrictEquals(Object* that) {
+ if (this->IsNumber()) {
+ if (!that->IsNumber()) return false;
+ return NumberEquals(this, that);
+ } else if (this->IsString()) {
+ if (!that->IsString()) return false;
+ return String::cast(this)->Equals(String::cast(that));
+ } else if (this->IsSimd128Value()) {
+ if (!that->IsSimd128Value()) return false;
+ return Simd128Value::cast(this)->Equals(Simd128Value::cast(that));
+ }
+ return this == that;
+}
+
+
+// static
+Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) {
+ if (object->IsNumber()) return isolate->factory()->number_string();
+ if (object->IsUndefined() || object->IsUndetectableObject()) {
+ return isolate->factory()->undefined_string();
+ }
+ if (object->IsBoolean()) return isolate->factory()->boolean_string();
+ if (object->IsString()) return isolate->factory()->string_string();
+ if (object->IsSymbol()) return isolate->factory()->symbol_string();
+ if (object->IsString()) return isolate->factory()->string_string();
+#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \
+ if (object->Is##Type()) return isolate->factory()->type##_string();
+ SIMD128_TYPES(SIMD128_TYPE)
+#undef SIMD128_TYPE
+ if (object->IsCallable()) return isolate->factory()->function_string();
+ return isolate->factory()->object_string();
+}
+
+
+// static
+MaybeHandle<Object> Object::Multiply(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumber(lhs->Number() * rhs->Number());
+}
+
+
+// static
+MaybeHandle<Object> Object::Divide(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumber(lhs->Number() / rhs->Number());
+}
+
+
+// static
+MaybeHandle<Object> Object::Modulus(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumber(modulo(lhs->Number(), rhs->Number()));
+}
+
+
+// static
+MaybeHandle<Object> Object::Add(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (lhs->IsNumber() && rhs->IsNumber()) {
+ return isolate->factory()->NewNumber(lhs->Number() + rhs->Number());
+ } else if (lhs->IsString() && rhs->IsString()) {
+ return isolate->factory()->NewConsString(Handle<String>::cast(lhs),
+ Handle<String>::cast(rhs));
+ } else if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToPrimitive(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToPrimitive(rhs), Object);
+ if (lhs->IsString() || rhs->IsString()) {
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToString(isolate, rhs),
+ Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToString(isolate, lhs),
+ Object);
+ return isolate->factory()->NewConsString(Handle<String>::cast(lhs),
+ Handle<String>::cast(rhs));
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ return isolate->factory()->NewNumber(lhs->Number() + rhs->Number());
+}
+
+
+// static
+MaybeHandle<Object> Object::Subtract(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumber(lhs->Number() - rhs->Number());
+}
+
+
+// static
+MaybeHandle<Object> Object::ShiftLeft(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs)
+ << (NumberToUint32(*rhs) & 0x1F));
+}
+
+
+// static
+MaybeHandle<Object> Object::ShiftRight(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) >>
+ (NumberToUint32(*rhs) & 0x1F));
+}
+
+
+// static
+MaybeHandle<Object> Object::ShiftRightLogical(Isolate* isolate,
+ Handle<Object> lhs,
+ Handle<Object> rhs,
+ Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumberFromUint(NumberToUint32(*lhs) >>
+ (NumberToUint32(*rhs) & 0x1F));
+}
+
+
+// static
+MaybeHandle<Object> Object::BitwiseAnd(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) &
+ NumberToInt32(*rhs));
+}
+
+
+// static
+MaybeHandle<Object> Object::BitwiseOr(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) |
+ NumberToInt32(*rhs));
+}
+
+
+// static
+MaybeHandle<Object> Object::BitwiseXor(Isolate* isolate, Handle<Object> lhs,
+ Handle<Object> rhs, Strength strength) {
+ if (!lhs->IsNumber() || !rhs->IsNumber()) {
+ if (is_strong(strength)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kStrongImplicitConversion),
+ Object);
+ }
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, lhs, Object::ToNumber(lhs), Object);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, rhs, Object::ToNumber(rhs), Object);
+ }
+ return isolate->factory()->NewNumberFromInt(NumberToInt32(*lhs) ^
+ NumberToInt32(*rhs));
+}
+
+
+Maybe<bool> Object::IsArray(Handle<Object> object) {
+ if (object->IsJSArray()) return Just(true);
+ if (object->IsJSProxy()) {
+ Handle<JSProxy> proxy = Handle<JSProxy>::cast(object);
+ Isolate* isolate = proxy->GetIsolate();
+ if (proxy->IsRevoked()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyRevoked,
+ isolate->factory()->NewStringFromAsciiChecked("IsArray")));
+ return Nothing<bool>();
+ }
+ return Object::IsArray(handle(proxy->target(), isolate));
+ }
+ return Just(false);
+}
+
+
+bool Object::IsPromise(Handle<Object> object) {
+ if (!object->IsJSObject()) return false;
+ auto js_object = Handle<JSObject>::cast(object);
+ // Promises can't have access checks.
+ if (js_object->map()->is_access_check_needed()) return false;
+ auto isolate = js_object->GetIsolate();
+ // TODO(dcarney): this should just be read from the symbol registry so as not
+ // to be context dependent.
+ auto key = isolate->factory()->promise_status_symbol();
+ // Shouldn't be possible to throw here.
+ return JSObject::HasRealNamedProperty(js_object, key).FromJust();
+}
+
+
+// static
+MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver,
+ Handle<Name> name) {
+ Handle<Object> func;
+ Isolate* isolate = receiver->GetIsolate();
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, func,
+ JSReceiver::GetProperty(receiver, name), Object);
+ if (func->IsNull() || func->IsUndefined()) {
+ return isolate->factory()->undefined_value();
+ }
+ if (!func->IsCallable()) {
+ THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kPropertyNotFunction,
+ func, name, receiver),
+ Object);
+ }
+ return func;
+}
+
+
+// static
+MaybeHandle<FixedArray> Object::CreateListFromArrayLike(
+ Isolate* isolate, Handle<Object> object, ElementTypes element_types) {
+ // 1. ReturnIfAbrupt(object).
+ // 2. (default elementTypes -- not applicable.)
+ // 3. If Type(obj) is not Object, throw a TypeError exception.
+ if (!object->IsJSReceiver()) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kCalledOnNonObject,
+ isolate->factory()->NewStringFromAsciiChecked(
+ "CreateListFromArrayLike")),
+ FixedArray);
+ }
+ // 4. Let len be ? ToLength(? Get(obj, "length")).
+ Handle<Object> raw_length_obj;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, raw_length_obj,
+ JSReceiver::GetProperty(object, isolate->factory()->length_string()),
+ FixedArray);
+ Handle<Object> raw_length_number;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number,
+ Object::ToLength(isolate, raw_length_obj),
+ FixedArray);
+ uint32_t len;
+ if (!raw_length_number->ToUint32(&len) ||
+ len > static_cast<uint32_t>(FixedArray::kMaxLength)) {
+ THROW_NEW_ERROR(isolate,
+ NewRangeError(MessageTemplate::kInvalidArrayLength),
+ FixedArray);
+ }
+ // 5. Let list be an empty List.
+ Handle<FixedArray> list = isolate->factory()->NewFixedArray(len);
+ // 6. Let index be 0.
+ // 7. Repeat while index < len:
+ for (uint32_t index = 0; index < len; ++index) {
+ // 7a. Let indexName be ToString(index).
+ // 7b. Let next be ? Get(obj, indexName).
+ Handle<Object> next;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, next, Object::GetElement(isolate, object, index), FixedArray);
+ switch (element_types) {
+ case ElementTypes::kAll:
+ // Nothing to do.
+ break;
+ case ElementTypes::kStringAndSymbol: {
+ // 7c. If Type(next) is not an element of elementTypes, throw a
+ // TypeError exception.
+ if (!next->IsName()) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kNotPropertyName, next),
+ FixedArray);
+ }
+ // 7d. Append next as the last element of list.
+ // Internalize on the fly so we can use pointer identity later.
+ next = isolate->factory()->InternalizeName(Handle<Name>::cast(next));
+ break;
+ }
+ }
+ list->set(index, *next);
+ // 7e. Set index to index + 1. (See loop header.)
+ }
+ // 8. Return list.
+ return list;
+}
+
+
+// static
+Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) {
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::JSPROXY:
- return JSProxy::GetPropertyWithHandler(it->GetHolder<JSProxy>(),
- it->GetReceiver(), it->name());
+ // Call the "has" trap on proxies.
+ return JSProxy::HasProperty(it->isolate(), it->GetHolder<JSProxy>(),
+ it->GetName());
case LookupIterator::INTERCEPTOR: {
- MaybeHandle<Object> maybe_result = JSObject::GetPropertyWithInterceptor(
- it->GetHolder<JSObject>(), it->GetReceiver(), it->name());
- if (!maybe_result.is_null()) return maybe_result;
- if (it->isolate()->has_pending_exception()) return maybe_result;
+ Maybe<PropertyAttributes> result =
+ JSObject::GetPropertyAttributesWithInterceptor(it);
+ if (!result.IsJust()) return Nothing<bool>();
+ if (result.FromJust() != ABSENT) return Just(true);
+ break;
+ }
+ case LookupIterator::ACCESS_CHECK: {
+ if (it->HasAccess()) break;
+ Maybe<PropertyAttributes> result =
+ JSObject::GetPropertyAttributesWithFailedAccessCheck(it);
+ if (!result.IsJust()) return Nothing<bool>();
+ return Just(result.FromJust() != ABSENT);
+ }
+ case LookupIterator::INTEGER_INDEXED_EXOTIC:
+ // TypedArray out-of-bounds access.
+ return Just(false);
+ case LookupIterator::ACCESSOR:
+ case LookupIterator::DATA:
+ return Just(true);
+ }
+ }
+ return Just(false);
+}
+
+
+// static
+MaybeHandle<Object> Object::GetProperty(LookupIterator* it,
+ LanguageMode language_mode) {
+ for (; it->IsFound(); it->Next()) {
+ switch (it->state()) {
+ case LookupIterator::NOT_FOUND:
+ case LookupIterator::TRANSITION:
+ UNREACHABLE();
+ case LookupIterator::JSPROXY:
+ return JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(),
+ it->GetName(), it->GetReceiver(),
+ language_mode);
+ case LookupIterator::INTERCEPTOR: {
+ bool done;
+ Handle<Object> result;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ it->isolate(), result,
+ JSObject::GetPropertyWithInterceptor(it, &done), Object);
+ if (done) return result;
break;
}
case LookupIterator::ACCESS_CHECK:
- if (it->HasAccess(v8::ACCESS_GET)) break;
+ if (it->HasAccess()) break;
return JSObject::GetPropertyWithFailedAccessCheck(it);
case LookupIterator::ACCESSOR:
- return GetPropertyWithAccessor(it->GetReceiver(), it->name(),
- it->GetHolder<JSObject>(),
- it->GetAccessors());
+ return GetPropertyWithAccessor(it, language_mode);
+ case LookupIterator::INTEGER_INDEXED_EXOTIC:
+ return ReadAbsentProperty(it, language_mode);
case LookupIterator::DATA:
return it->GetDataValue();
}
}
- return it->factory()->undefined_value();
+ return ReadAbsentProperty(it, language_mode);
}
-Handle<Object> JSObject::GetDataProperty(Handle<JSObject> object,
- Handle<Name> key) {
- LookupIterator it(object, key,
+#define STACK_CHECK(result_value) \
+ do { \
+ StackLimitCheck stack_check(isolate); \
+ if (stack_check.HasOverflowed()) { \
+ isolate->Throw(*isolate->factory()->NewRangeError( \
+ MessageTemplate::kStackOverflow)); \
+ return result_value; \
+ } \
+ } while (false)
+
+
+// static
+MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
+ Handle<JSProxy> proxy,
+ Handle<Name> name,
+ Handle<Object> receiver,
+ LanguageMode language_mode) {
+ if (receiver->IsJSGlobalObject()) {
+ THROW_NEW_ERROR(
+ isolate,
+ NewTypeError(MessageTemplate::kReadGlobalReferenceThroughProxy, name),
+ Object);
+ }
+
+ DCHECK(!name->IsPrivate());
+ STACK_CHECK(MaybeHandle<Object>());
+ Handle<Name> trap_name = isolate->factory()->get_string();
+ // 1. Assert: IsPropertyKey(P) is true.
+ // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+ Handle<Object> handler(proxy->handler(), isolate);
+ // 3. If handler is null, throw a TypeError exception.
+ // 4. Assert: Type(handler) is Object.
+ if (proxy->IsRevoked()) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kProxyRevoked, trap_name),
+ Object);
+ }
+ // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ // 6. Let trap be ? GetMethod(handler, "get").
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, trap,
+ Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Object);
+ // 7. If trap is undefined, then
+ if (trap->IsUndefined()) {
+ // 7.a Return target.[[Get]](P, Receiver).
+ LookupIterator it =
+ LookupIterator::PropertyOrElement(isolate, receiver, name, target);
+ return Object::GetProperty(&it, language_mode);
+ }
+ // 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»).
+ Handle<Object> trap_result;
+ Handle<Object> args[] = {target, name, receiver};
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, trap_result,
+ Execution::Call(isolate, trap, handler, arraysize(args), args), Object);
+ // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
+ PropertyDescriptor target_desc;
+ Maybe<bool> target_found =
+ JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
+ MAYBE_RETURN_NULL(target_found);
+ // 10. If targetDesc is not undefined, then
+ if (target_found.FromJust()) {
+ // 10.a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is
+ // false and targetDesc.[[Writable]] is false, then
+ // 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false,
+ // throw a TypeError exception.
+ bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) &&
+ !target_desc.configurable() &&
+ !target_desc.writable() &&
+ !trap_result->SameValue(*target_desc.value());
+ if (inconsistent) {
+ THROW_NEW_ERROR(
+ isolate, NewTypeError(MessageTemplate::kProxyGetNonConfigurableData,
+ name, target_desc.value(), trap_result),
+ Object);
+ }
+ // 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]]
+ // is false and targetDesc.[[Get]] is undefined, then
+ // 10.b.i. If trapResult is not undefined, throw a TypeError exception.
+ inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
+ !target_desc.configurable() &&
+ target_desc.get()->IsUndefined() &&
+ !trap_result->IsUndefined();
+ if (inconsistent) {
+ THROW_NEW_ERROR(
+ isolate,
+ NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor, name,
+ trap_result),
+ Object);
+ }
+ }
+ // 11. Return trap_result
+ return trap_result;
+}
+
+
+Handle<Object> JSReceiver::GetDataProperty(Handle<JSReceiver> object,
+ Handle<Name> name) {
+ LookupIterator it(object, name,
LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
return GetDataProperty(&it);
}
-Handle<Object> JSObject::GetDataProperty(LookupIterator* it) {
+Handle<Object> JSReceiver::GetDataProperty(LookupIterator* it) {
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
case LookupIterator::INTERCEPTOR:
@@ -155,7 +926,9 @@
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
- if (it->HasAccess(v8::ACCESS_GET)) continue;
+ // 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();
@@ -166,6 +939,8 @@
// 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();
}
@@ -193,15 +968,16 @@
bool Object::ToUint32(uint32_t* value) {
if (IsSmi()) {
int num = Smi::cast(this)->value();
- if (num >= 0) {
- *value = static_cast<uint32_t>(num);
- return true;
- }
+ if (num < 0) return false;
+ *value = static_cast<uint32_t>(num);
+ return true;
}
if (IsHeapNumber()) {
double num = HeapNumber::cast(this)->value();
- if (num >= 0 && FastUI2D(FastD2UI(num)) == num) {
- *value = FastD2UI(num);
+ if (num < 0) return false;
+ uint32_t uint_value = FastD2UI(num);
+ if (FastUI2D(uint_value) == num) {
+ *value = uint_value;
return true;
}
}
@@ -219,7 +995,7 @@
// There is a constraint on the object; check.
if (!map->IsJSObjectMap()) return false;
// Fetch the constructor function of the object.
- Object* cons_obj = map->constructor();
+ 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
@@ -234,151 +1010,49 @@
}
-template<typename To>
-static inline To* CheckedCast(void *from) {
- uintptr_t temp = reinterpret_cast<uintptr_t>(from);
- DCHECK(temp % sizeof(To) == 0);
- return reinterpret_cast<To*>(temp);
-}
-
-
-static Handle<Object> PerformCompare(const BitmaskCompareDescriptor& descriptor,
- char* ptr,
- Isolate* isolate) {
- uint32_t bitmask = descriptor.bitmask;
- uint32_t compare_value = descriptor.compare_value;
- uint32_t value;
- switch (descriptor.size) {
- case 1:
- value = static_cast<uint32_t>(*CheckedCast<uint8_t>(ptr));
- compare_value &= 0xff;
- bitmask &= 0xff;
- break;
- case 2:
- value = static_cast<uint32_t>(*CheckedCast<uint16_t>(ptr));
- compare_value &= 0xffff;
- bitmask &= 0xffff;
- break;
- case 4:
- value = *CheckedCast<uint32_t>(ptr);
- break;
- default:
- UNREACHABLE();
- return isolate->factory()->undefined_value();
+// TODO(dcarney): CallOptimization duplicates this logic, merge.
+Object* FunctionTemplateInfo::GetCompatibleReceiver(Isolate* isolate,
+ Object* receiver) {
+ // API calls are only supported with JSObject receivers.
+ if (!receiver->IsJSObject()) return isolate->heap()->null_value();
+ Object* recv_type = this->signature();
+ // No signature, return holder.
+ if (recv_type->IsUndefined()) return receiver;
+ FunctionTemplateInfo* signature = FunctionTemplateInfo::cast(recv_type);
+ // Check the receiver.
+ for (PrototypeIterator iter(isolate, receiver,
+ PrototypeIterator::START_AT_RECEIVER);
+ !iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN); iter.Advance()) {
+ if (signature->IsTemplateFor(iter.GetCurrent())) return iter.GetCurrent();
}
- return isolate->factory()->ToBoolean(
- (bitmask & value) == (bitmask & compare_value));
+ return isolate->heap()->null_value();
}
-static Handle<Object> PerformCompare(const PointerCompareDescriptor& descriptor,
- char* ptr,
- Isolate* isolate) {
- uintptr_t compare_value =
- reinterpret_cast<uintptr_t>(descriptor.compare_value);
- uintptr_t value = *CheckedCast<uintptr_t>(ptr);
- return isolate->factory()->ToBoolean(compare_value == value);
-}
+// static
+MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
+ Handle<JSReceiver> new_target,
+ Handle<AllocationSite> site) {
+ // If called through new, new.target can be:
+ // - a subclass of constructor,
+ // - a proxy wrapper around constructor, or
+ // - the constructor itself.
+ // If called through Reflect.construct, it's guaranteed to be a constructor.
+ Isolate* const isolate = constructor->GetIsolate();
+ DCHECK(constructor->IsConstructor());
+ DCHECK(new_target->IsConstructor());
+ DCHECK(!constructor->has_initial_map() ||
+ constructor->initial_map()->instance_type() != JS_FUNCTION_TYPE);
-
-static Handle<Object> GetPrimitiveValue(
- const PrimitiveValueDescriptor& descriptor,
- char* ptr,
- Isolate* isolate) {
- int32_t int32_value = 0;
- switch (descriptor.data_type) {
- case kDescriptorInt8Type:
- int32_value = *CheckedCast<int8_t>(ptr);
- break;
- case kDescriptorUint8Type:
- int32_value = *CheckedCast<uint8_t>(ptr);
- break;
- case kDescriptorInt16Type:
- int32_value = *CheckedCast<int16_t>(ptr);
- break;
- case kDescriptorUint16Type:
- int32_value = *CheckedCast<uint16_t>(ptr);
- break;
- case kDescriptorInt32Type:
- int32_value = *CheckedCast<int32_t>(ptr);
- break;
- case kDescriptorUint32Type: {
- uint32_t value = *CheckedCast<uint32_t>(ptr);
- AllowHeapAllocation allow_gc;
- return isolate->factory()->NewNumberFromUint(value);
- }
- case kDescriptorBoolType: {
- uint8_t byte = *CheckedCast<uint8_t>(ptr);
- return isolate->factory()->ToBoolean(
- byte & (0x1 << descriptor.bool_offset));
- }
- case kDescriptorFloatType: {
- float value = *CheckedCast<float>(ptr);
- AllowHeapAllocation allow_gc;
- return isolate->factory()->NewNumber(value);
- }
- case kDescriptorDoubleType: {
- double value = *CheckedCast<double>(ptr);
- AllowHeapAllocation allow_gc;
- return isolate->factory()->NewNumber(value);
- }
- }
- AllowHeapAllocation allow_gc;
- return isolate->factory()->NewNumberFromInt(int32_value);
-}
-
-
-static Handle<Object> GetDeclaredAccessorProperty(
- Handle<Object> receiver,
- Handle<DeclaredAccessorInfo> info,
- Isolate* isolate) {
- DisallowHeapAllocation no_gc;
- char* current = reinterpret_cast<char*>(*receiver);
- DeclaredAccessorDescriptorIterator iterator(info->descriptor());
- while (true) {
- const DeclaredAccessorDescriptorData* data = iterator.Next();
- switch (data->type) {
- case kDescriptorReturnObject: {
- DCHECK(iterator.Complete());
- current = *CheckedCast<char*>(current);
- return handle(*CheckedCast<Object*>(current), isolate);
- }
- case kDescriptorPointerDereference:
- DCHECK(!iterator.Complete());
- current = *reinterpret_cast<char**>(current);
- break;
- case kDescriptorPointerShift:
- DCHECK(!iterator.Complete());
- current += data->pointer_shift_descriptor.byte_offset;
- break;
- case kDescriptorObjectDereference: {
- DCHECK(!iterator.Complete());
- Object* object = CheckedCast<Object>(current);
- int field = data->object_dereference_descriptor.internal_field;
- Object* smi = JSObject::cast(object)->GetInternalField(field);
- DCHECK(smi->IsSmi());
- current = reinterpret_cast<char*>(smi);
- break;
- }
- case kDescriptorBitmaskCompare:
- DCHECK(iterator.Complete());
- return PerformCompare(data->bitmask_compare_descriptor,
- current,
- isolate);
- case kDescriptorPointerCompare:
- DCHECK(iterator.Complete());
- return PerformCompare(data->pointer_compare_descriptor,
- current,
- isolate);
- case kDescriptorPrimitiveValue:
- DCHECK(iterator.Complete());
- return GetPrimitiveValue(data->primitive_value_descriptor,
- current,
- isolate);
- }
- }
- UNREACHABLE();
- return isolate->factory()->undefined_value();
+ 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;
}
@@ -396,56 +1070,100 @@
}
-MaybeHandle<Object> JSProxy::GetPropertyWithHandler(Handle<JSProxy> proxy,
- Handle<Object> receiver,
- Handle<Name> name) {
+// ES6 9.5.1
+// static
+MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) {
Isolate* isolate = proxy->GetIsolate();
+ Handle<String> trap_name = isolate->factory()->getPrototypeOf_string();
- // TODO(rossberg): adjust once there is a story for symbols vs proxies.
- if (name->IsSymbol()) return isolate->factory()->undefined_value();
+ STACK_CHECK(MaybeHandle<Object>());
- Handle<Object> args[] = { receiver, name };
- return CallTrap(
- proxy, "get", isolate->derived_get_trap(), arraysize(args), args);
+ // 1. Let handler be the value of the [[ProxyHandler]] internal slot.
+ // 2. If handler is null, throw a TypeError exception.
+ // 3. Assert: Type(handler) is Object.
+ // 4. Let target be the value of the [[ProxyTarget]] internal slot.
+ if (proxy->IsRevoked()) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kProxyRevoked, trap_name),
+ Object);
+ }
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
+
+ // 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, trap, GetMethod(handler, trap_name),
+ Object);
+ // 6. If trap is undefined, then return target.[[GetPrototypeOf]]().
+ if (trap->IsUndefined()) {
+ return Object::GetPrototype(isolate, target);
+ }
+ // 7. Let handlerProto be ? Call(trap, handler, «target»).
+ Handle<Object> argv[] = {target};
+ Handle<Object> handler_proto;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, handler_proto,
+ Execution::Call(isolate, trap, handler, arraysize(argv), argv), Object);
+ // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError.
+ if (!(handler_proto->IsJSReceiver() || handler_proto->IsNull())) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid),
+ Object);
+ }
+ // 9. Let extensibleTarget be ? IsExtensible(target).
+ Maybe<bool> is_extensible = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN_NULL(is_extensible);
+ // 10. If extensibleTarget is true, return handlerProto.
+ if (is_extensible.FromJust()) return handler_proto;
+ // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
+ Handle<Object> target_proto;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto,
+ Object::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(Handle<Object> receiver,
- Handle<Name> name,
- Handle<JSObject> holder,
- Handle<Object> structure) {
- Isolate* isolate = name->GetIsolate();
+MaybeHandle<Object> Object::GetPropertyWithAccessor(
+ LookupIterator* it, LanguageMode language_mode) {
+ Isolate* isolate = it->isolate();
+ Handle<Object> structure = it->GetAccessors();
+ Handle<Object> receiver = it->GetReceiver();
+
+ // We should never get here to initialize a const with the hole value since a
+ // const declaration would conflict with the getter.
DCHECK(!structure->IsForeign());
- // api style callbacks.
+
+ // API style callbacks.
if (structure->IsAccessorInfo()) {
- Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure);
+ Handle<JSObject> holder = it->GetHolder<JSObject>();
+ Handle<Name> name = it->GetName();
+ Handle<ExecutableAccessorInfo> info =
+ Handle<ExecutableAccessorInfo>::cast(structure);
if (!info->IsCompatibleReceiver(*receiver)) {
- Handle<Object> args[2] = { name, receiver };
THROW_NEW_ERROR(isolate,
- NewTypeError("incompatible_method_receiver",
- HandleVector(args, arraysize(args))),
+ NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
+ name, receiver),
Object);
}
- if (structure->IsDeclaredAccessorInfo()) {
- return GetDeclaredAccessorProperty(
- receiver,
- Handle<DeclaredAccessorInfo>::cast(structure),
- isolate);
- }
- Handle<ExecutableAccessorInfo> data =
- Handle<ExecutableAccessorInfo>::cast(structure);
v8::AccessorNameGetterCallback call_fun =
- v8::ToCData<v8::AccessorNameGetterCallback>(data->getter());
- if (call_fun == NULL) return isolate->factory()->undefined_value();
+ v8::ToCData<v8::AccessorNameGetterCallback>(info->getter());
+ if (call_fun == nullptr) return isolate->factory()->undefined_value();
LOG(isolate, ApiNamedPropertyAccess("load", *holder, *name));
- PropertyCallbackArguments args(isolate, data->data(), *receiver, *holder);
- v8::Handle<v8::Value> result =
- args.Call(call_fun, v8::Utils::ToLocal(name));
+ PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder);
+ v8::Local<v8::Value> result = args.Call(call_fun, v8::Utils::ToLocal(name));
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
if (result.IsEmpty()) {
- return isolate->factory()->undefined_value();
+ return ReadAbsentProperty(isolate, receiver, name, language_mode);
}
Handle<Object> return_value = v8::Utils::OpenHandle(*result);
return_value->VerifyApiCallResultType();
@@ -453,85 +1171,77 @@
return handle(*return_value, isolate);
}
- // __defineGetter__ callback
- Handle<Object> getter(Handle<AccessorPair>::cast(structure)->getter(),
- isolate);
- if (getter->IsSpecFunction()) {
+ // Regular accessor.
+ Handle<Object> getter(AccessorPair::cast(*structure)->getter(), isolate);
+ if (getter->IsCallable()) {
// TODO(rossberg): nicer would be to cast to some JSCallable here...
return Object::GetPropertyWithDefinedGetter(
receiver, Handle<JSReceiver>::cast(getter));
}
// Getter is not a function.
- return isolate->factory()->undefined_value();
+ return ReadAbsentProperty(isolate, receiver, it->GetName(), language_mode);
}
-bool AccessorInfo::IsCompatibleReceiverType(Isolate* isolate,
- Handle<AccessorInfo> info,
- Handle<HeapType> type) {
+bool AccessorInfo::IsCompatibleReceiverMap(Isolate* isolate,
+ Handle<AccessorInfo> info,
+ Handle<Map> map) {
if (!info->HasExpectedReceiverType()) return true;
- Handle<Map> map = IC::TypeToMap(*type, isolate);
if (!map->IsJSObjectMap()) return false;
return FunctionTemplateInfo::cast(info->expected_receiver_type())
->IsTemplateFor(*map);
}
-MaybeHandle<Object> Object::SetPropertyWithAccessor(
- Handle<Object> receiver, Handle<Name> name, Handle<Object> value,
- Handle<JSObject> holder, Handle<Object> structure, StrictMode strict_mode) {
- Isolate* isolate = name->GetIsolate();
+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.
+ // 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->IsExecutableAccessorInfo()) {
- // Don't call executable accessor setters with non-JSObject receivers.
- if (!receiver->IsJSObject()) return value;
- // api style callbacks
- ExecutableAccessorInfo* info = ExecutableAccessorInfo::cast(*structure);
+ Handle<JSObject> holder = it->GetHolder<JSObject>();
+ Handle<Name> name = it->GetName();
+ Handle<ExecutableAccessorInfo> info =
+ Handle<ExecutableAccessorInfo>::cast(structure);
if (!info->IsCompatibleReceiver(*receiver)) {
- Handle<Object> args[2] = { name, receiver };
- THROW_NEW_ERROR(isolate,
- NewTypeError("incompatible_method_receiver",
- HandleVector(args, arraysize(args))),
- Object);
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kIncompatibleMethodReceiver, name, receiver));
+ return Nothing<bool>();
}
- Object* call_obj = info->setter();
+
v8::AccessorNameSetterCallback call_fun =
- v8::ToCData<v8::AccessorNameSetterCallback>(call_obj);
- if (call_fun == NULL) return value;
+ v8::ToCData<v8::AccessorNameSetterCallback>(info->setter());
+ if (call_fun == nullptr) return Just(true);
+ // TODO(verwaest): Shouldn't this case be unreachable (at least in the
+ // long run?) Should we have ExecutableAccessorPairs with missing setter
+ // that are "writable"? If they aren't writable, shouldn't we have bailed
+ // out already earlier?
+
LOG(isolate, ApiNamedPropertyAccess("store", *holder, *name));
PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder);
- args.Call(call_fun,
- v8::Utils::ToLocal(name),
- v8::Utils::ToLocal(value));
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return value;
+ args.Call(call_fun, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value));
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ return Just(true);
}
- if (structure->IsAccessorPair()) {
- Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate);
- if (setter->IsSpecFunction()) {
- // TODO(rossberg): nicer would be to cast to some JSCallable here...
- return SetPropertyWithDefinedSetter(
- receiver, Handle<JSReceiver>::cast(setter), value);
- } else {
- if (strict_mode == SLOPPY) return value;
- Handle<Object> args[2] = { name, holder };
- THROW_NEW_ERROR(
- isolate, NewTypeError("no_setter_in_callback", HandleVector(args, 2)),
- Object);
- }
+ // Regular accessor.
+ Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate);
+ if (setter->IsCallable()) {
+ // TODO(rossberg): nicer would be to cast to some JSCallable here...
+ return SetPropertyWithDefinedSetter(
+ receiver, Handle<JSReceiver>::cast(setter), value, should_throw);
}
- // TODO(dcarney): Handle correctly.
- if (structure->IsDeclaredAccessorInfo()) {
- return value;
- }
-
- UNREACHABLE();
- return MaybeHandle<Object>();
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kNoSetterInCallback,
+ it->GetName(), it->GetHolder<JSObject>()));
}
@@ -539,39 +1249,53 @@
Handle<Object> receiver,
Handle<JSReceiver> getter) {
Isolate* isolate = getter->GetIsolate();
- Debug* debug = isolate->debug();
- // Handle stepping into a getter if step into is active.
- // TODO(rossberg): should this apply to getters that are function proxies?
- if (debug->is_active()) {
- debug->HandleStepIn(getter, Handle<Object>::null(), 0, false);
+
+ // 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, true);
+ return Execution::Call(isolate, getter, receiver, 0, NULL);
}
-MaybeHandle<Object> Object::SetPropertyWithDefinedSetter(
- Handle<Object> receiver,
- Handle<JSReceiver> setter,
- Handle<Object> value) {
+Maybe<bool> Object::SetPropertyWithDefinedSetter(Handle<Object> receiver,
+ Handle<JSReceiver> setter,
+ Handle<Object> value,
+ ShouldThrow should_throw) {
Isolate* isolate = setter->GetIsolate();
- Debug* debug = isolate->debug();
- // Handle stepping into a setter if step into is active.
- // TODO(rossberg): should this apply to getters that are function proxies?
- if (debug->is_active()) {
- debug->HandleStepIn(setter, Handle<Object>::null(), 0, false);
- }
-
Handle<Object> argv[] = { value };
- RETURN_ON_EXCEPTION(isolate, Execution::Call(isolate, setter, receiver,
- arraysize(argv), argv, true),
- Object);
- return value;
+ RETURN_ON_EXCEPTION_VALUE(isolate, Execution::Call(isolate, setter, receiver,
+ arraysize(argv), argv),
+ Nothing<bool>());
+ return Just(true);
}
-static bool FindAllCanReadHolder(LookupIterator* it) {
+// static
+bool Object::IsErrorObject(Isolate* isolate, Handle<Object> object) {
+ if (!object->IsJSObject()) return false;
+ // Use stack_trace_symbol as proxy for [[ErrorData]].
+ Handle<Name> symbol = isolate->factory()->stack_trace_symbol();
+ Maybe<bool> has_stack_trace =
+ JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol);
+ DCHECK(!has_stack_trace.IsNothing());
+ return has_stack_trace.FromJust();
+}
+
+
+// static
+bool JSObject::AllCanRead(LookupIterator* it) {
// Skip current iteration, it's in state ACCESS_CHECK or INTERCEPTOR, both of
// which have already been checked.
DCHECK(it->state() == LookupIterator::ACCESS_CHECK ||
@@ -583,8 +1307,10 @@
if (AccessorInfo::cast(*accessors)->all_can_read()) return true;
}
} else if (it->state() == LookupIterator::INTERCEPTOR) {
- auto holder = it->GetHolder<JSObject>();
- if (holder->GetNamedInterceptor()->all_can_read()) return true;
+ if (it->GetInterceptor()->all_can_read()) return true;
+ } else if (it->state() == LookupIterator::JSPROXY) {
+ // Stop lookupiterating. And no, AllCanNotRead.
+ return false;
}
}
return false;
@@ -594,20 +1320,26 @@
MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck(
LookupIterator* it) {
Handle<JSObject> checked = it->GetHolder<JSObject>();
- while (FindAllCanReadHolder(it)) {
+ while (AllCanRead(it)) {
if (it->state() == LookupIterator::ACCESSOR) {
- return GetPropertyWithAccessor(it->GetReceiver(), it->name(),
- it->GetHolder<JSObject>(),
- it->GetAccessors());
+ return GetPropertyWithAccessor(it, SLOPPY);
}
DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
- auto receiver = Handle<JSObject>::cast(it->GetReceiver());
- auto result = GetPropertyWithInterceptor(it->GetHolder<JSObject>(),
- receiver, it->name());
- if (it->isolate()->has_scheduled_exception()) break;
- if (!result.is_null()) return result;
+ bool done;
+ Handle<Object> result;
+ ASSIGN_RETURN_ON_EXCEPTION(it->isolate(), result,
+ GetPropertyWithInterceptor(it, &done), Object);
+ if (done) return result;
}
- it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_GET);
+
+ // Cross-Origin [[Get]] of Well-Known Symbols does not throw, and returns
+ // undefined.
+ Handle<Name> name = it->GetName();
+ if (name->IsSymbol() && Symbol::cast(*name)->is_well_known_symbol()) {
+ return it->factory()->undefined_value();
+ }
+
+ it->isolate()->ReportFailedAccessCheck(checked);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
return it->factory()->undefined_value();
}
@@ -616,25 +1348,25 @@
Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck(
LookupIterator* it) {
Handle<JSObject> checked = it->GetHolder<JSObject>();
- while (FindAllCanReadHolder(it)) {
+ while (AllCanRead(it)) {
if (it->state() == LookupIterator::ACCESSOR) {
- return maybe(it->property_details().attributes());
+ return Just(it->property_details().attributes());
}
DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
- auto result = GetPropertyAttributesWithInterceptor(
- it->GetHolder<JSObject>(), it->GetReceiver(), it->name());
+ auto result = GetPropertyAttributesWithInterceptor(it);
if (it->isolate()->has_scheduled_exception()) break;
- if (result.has_value && result.value != ABSENT) return result;
+ if (result.IsJust() && result.FromJust() != ABSENT) return result;
}
- it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_HAS);
+ it->isolate()->ReportFailedAccessCheck(checked);
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(),
- Maybe<PropertyAttributes>());
- return maybe(ABSENT);
+ Nothing<PropertyAttributes>());
+ return Just(ABSENT);
}
-static bool FindAllCanWriteHolder(LookupIterator* it) {
- for (; it->IsFound(); it->Next()) {
+// 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()) {
@@ -646,18 +1378,16 @@
}
-MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck(
- LookupIterator* it, Handle<Object> value, StrictMode strict_mode) {
+Maybe<bool> JSObject::SetPropertyWithFailedAccessCheck(
+ LookupIterator* it, Handle<Object> value, ShouldThrow should_throw) {
Handle<JSObject> checked = it->GetHolder<JSObject>();
- if (FindAllCanWriteHolder(it)) {
- return SetPropertyWithAccessor(it->GetReceiver(), it->name(), value,
- it->GetHolder<JSObject>(),
- it->GetAccessors(), strict_mode);
+ if (AllCanWrite(it)) {
+ return SetPropertyWithAccessor(it, value, should_throw);
}
- it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_SET);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
- return value;
+ it->isolate()->ReportFailedAccessCheck(checked);
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>());
+ return Just(true);
}
@@ -666,327 +1396,107 @@
Handle<Object> value,
PropertyDetails details) {
DCHECK(!object->HasFastProperties());
- Handle<NameDictionary> property_dictionary(object->property_dictionary());
-
if (!name->IsUniqueName()) {
name = object->GetIsolate()->factory()->InternalizeString(
Handle<String>::cast(name));
}
- int entry = property_dictionary->FindEntry(name);
- if (entry == NameDictionary::kNotFound) {
- Handle<Object> store_value = value;
- if (object->IsGlobalObject()) {
- store_value = object->GetIsolate()->factory()->NewPropertyCell(value);
- }
+ if (object->IsJSGlobalObject()) {
+ Handle<GlobalDictionary> property_dictionary(object->global_dictionary());
- property_dictionary = NameDictionary::Add(
- property_dictionary, name, store_value, details);
- object->set_properties(*property_dictionary);
- return;
- }
-
- PropertyDetails original_details = property_dictionary->DetailsAt(entry);
- int enumeration_index;
- // Preserve the enumeration index unless the property was deleted.
- if (original_details.IsDeleted()) {
- enumeration_index = property_dictionary->NextEnumerationIndex();
- property_dictionary->SetNextEnumerationIndex(enumeration_index + 1);
- } else {
- enumeration_index = original_details.dictionary_index();
- DCHECK(enumeration_index > 0);
- }
-
- details = PropertyDetails(
- details.attributes(), details.type(), enumeration_index);
-
- if (object->IsGlobalObject()) {
- Handle<PropertyCell> cell(
- PropertyCell::cast(property_dictionary->ValueAt(entry)));
- PropertyCell::SetValueInferType(cell, value);
- // Please note we have to update the property details.
- property_dictionary->DetailsAtPut(entry, details);
- } else {
- property_dictionary->SetEntry(entry, name, value, details);
- }
-}
-
-
-Handle<Object> JSObject::DeleteNormalizedProperty(Handle<JSObject> object,
- Handle<Name> name,
- DeleteMode mode) {
- DCHECK(!object->HasFastProperties());
- Isolate* isolate = object->GetIsolate();
- Handle<NameDictionary> dictionary(object->property_dictionary());
- int entry = dictionary->FindEntry(name);
- if (entry != NameDictionary::kNotFound) {
- // If we have a global object set the cell to the hole.
- if (object->IsGlobalObject()) {
- PropertyDetails details = dictionary->DetailsAt(entry);
- if (!details.IsConfigurable()) {
- if (mode != FORCE_DELETION) return isolate->factory()->false_value();
- // When forced to delete global properties, we have to make a
- // map change to invalidate any ICs that think they can load
- // from the non-configurable cell without checking if it contains
- // the hole value.
- Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
- DCHECK(new_map->is_dictionary_map());
-#if TRACE_MAPS
- if (FLAG_trace_maps) {
- PrintF("[TraceMaps: GlobalDeleteNormalized from= %p to= %p ]\n",
- reinterpret_cast<void*>(object->map()),
- reinterpret_cast<void*>(*new_map));
- }
-#endif
- JSObject::MigrateToMap(object, new_map);
- }
- Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry)));
- Handle<Object> value = isolate->factory()->the_hole_value();
- PropertyCell::SetValueInferType(cell, value);
- dictionary->DetailsAtPut(entry, details.AsDeleted());
+ int entry = property_dictionary->FindEntry(name);
+ if (entry == GlobalDictionary::kNotFound) {
+ auto cell = object->GetIsolate()->factory()->NewPropertyCell();
+ cell->set_value(*value);
+ auto cell_type = value->IsUndefined() ? PropertyCellType::kUndefined
+ : PropertyCellType::kConstant;
+ details = details.set_cell_type(cell_type);
+ value = cell;
+ property_dictionary =
+ GlobalDictionary::Add(property_dictionary, name, value, details);
+ object->set_properties(*property_dictionary);
} else {
- Handle<Object> deleted(
- NameDictionary::DeleteProperty(dictionary, entry, mode));
- if (*deleted == isolate->heap()->true_value()) {
- Handle<NameDictionary> new_properties =
- NameDictionary::Shrink(dictionary, name);
- object->set_properties(*new_properties);
- }
- return deleted;
+ PropertyCell::UpdateCell(property_dictionary, entry, value, details);
+ }
+ } else {
+ Handle<NameDictionary> property_dictionary(object->property_dictionary());
+
+ int entry = property_dictionary->FindEntry(name);
+ if (entry == NameDictionary::kNotFound) {
+ property_dictionary =
+ NameDictionary::Add(property_dictionary, name, value, details);
+ object->set_properties(*property_dictionary);
+ } else {
+ PropertyDetails original_details = property_dictionary->DetailsAt(entry);
+ int enumeration_index = original_details.dictionary_index();
+ DCHECK(enumeration_index > 0);
+ details = details.set_index(enumeration_index);
+ property_dictionary->SetEntry(entry, name, value, details);
}
}
- return isolate->factory()->true_value();
}
-static MaybeHandle<JSObject> FindIndexedAllCanReadHolder(
- Isolate* isolate, Handle<JSObject> js_object,
- PrototypeIterator::WhereToStart where_to_start) {
- for (PrototypeIterator iter(isolate, js_object, where_to_start);
- !iter.IsAtEnd(); iter.Advance()) {
- auto curr = PrototypeIterator::GetCurrent(iter);
- if (!curr->IsJSObject()) break;
- auto obj = Handle<JSObject>::cast(curr);
- if (!obj->HasIndexedInterceptor()) continue;
- if (obj->GetIndexedInterceptor()->all_can_read()) return obj;
- }
- return MaybeHandle<JSObject>();
-}
-
-
-MaybeHandle<Object> JSObject::GetElementWithFailedAccessCheck(
- Isolate* isolate, Handle<JSObject> object, Handle<Object> receiver,
- uint32_t index) {
- Handle<JSObject> holder = object;
- PrototypeIterator::WhereToStart where_to_start =
- PrototypeIterator::START_AT_RECEIVER;
+Maybe<bool> Object::HasInPrototypeChain(Isolate* isolate, Handle<Object> object,
+ Handle<Object> proto) {
+ PrototypeIterator iter(isolate, object, PrototypeIterator::START_AT_RECEIVER);
while (true) {
- auto all_can_read_holder =
- FindIndexedAllCanReadHolder(isolate, holder, where_to_start);
- if (!all_can_read_holder.ToHandle(&holder)) break;
- auto result =
- JSObject::GetElementWithInterceptor(holder, receiver, index, false);
- if (isolate->has_scheduled_exception()) break;
- if (!result.is_null()) return result;
- where_to_start = PrototypeIterator::START_AT_PROTOTYPE;
+ if (!iter.AdvanceFollowingProxies()) return Nothing<bool>();
+ if (iter.IsAtEnd()) return Just(false);
+ if (iter.IsAtEnd(proto)) return Just(true);
}
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_GET);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return isolate->factory()->undefined_value();
-}
-
-
-Maybe<PropertyAttributes> JSObject::GetElementAttributesWithFailedAccessCheck(
- Isolate* isolate, Handle<JSObject> object, Handle<Object> receiver,
- uint32_t index) {
- Handle<JSObject> holder = object;
- PrototypeIterator::WhereToStart where_to_start =
- PrototypeIterator::START_AT_RECEIVER;
- while (true) {
- auto all_can_read_holder =
- FindIndexedAllCanReadHolder(isolate, holder, where_to_start);
- if (!all_can_read_holder.ToHandle(&holder)) break;
- auto result =
- JSObject::GetElementAttributeFromInterceptor(object, receiver, index);
- if (isolate->has_scheduled_exception()) break;
- if (result.has_value && result.value != ABSENT) return result;
- where_to_start = PrototypeIterator::START_AT_PROTOTYPE;
- }
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_HAS);
- RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe<PropertyAttributes>());
- return maybe(ABSENT);
-}
-
-
-MaybeHandle<Object> Object::GetElementWithReceiver(Isolate* isolate,
- Handle<Object> object,
- Handle<Object> receiver,
- uint32_t index) {
- if (object->IsUndefined()) {
- // TODO(verwaest): Why is this check here?
- UNREACHABLE();
- return isolate->factory()->undefined_value();
- }
-
- // Iterate up the prototype chain until an element is found or the null
- // prototype is encountered.
- for (PrototypeIterator iter(isolate, object,
- object->IsJSProxy() || object->IsJSObject()
- ? PrototypeIterator::START_AT_RECEIVER
- : PrototypeIterator::START_AT_PROTOTYPE);
- !iter.IsAtEnd(); iter.Advance()) {
- if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
- return JSProxy::GetElementWithHandler(
- Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), receiver,
- index);
- }
-
- // Inline the case for JSObjects. Doing so significantly improves the
- // performance of fetching elements where checking the prototype chain is
- // necessary.
- Handle<JSObject> js_object =
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
-
- // Check access rights if needed.
- if (js_object->IsAccessCheckNeeded()) {
- if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) {
- return JSObject::GetElementWithFailedAccessCheck(isolate, js_object,
- receiver, index);
- }
- }
-
- if (js_object->HasIndexedInterceptor()) {
- return JSObject::GetElementWithInterceptor(js_object, receiver, index,
- true);
- }
-
- if (js_object->elements() != isolate->heap()->empty_fixed_array()) {
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, result,
- js_object->GetElementsAccessor()->Get(receiver, js_object, index),
- Object);
- if (!result->IsTheHole()) return result;
- }
- }
-
- return isolate->factory()->undefined_value();
-}
-
-
-MaybeHandle<Object> Object::SetElementWithReceiver(
- Isolate* isolate, Handle<Object> object, Handle<Object> receiver,
- uint32_t index, Handle<Object> value, StrictMode strict_mode) {
- // Iterate up the prototype chain until an element is found or the null
- // prototype is encountered.
- bool done = false;
- for (PrototypeIterator iter(isolate, object,
- object->IsJSProxy() || object->IsJSObject()
- ? PrototypeIterator::START_AT_RECEIVER
- : PrototypeIterator::START_AT_PROTOTYPE);
- !iter.IsAtEnd() && !done; iter.Advance()) {
- if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
- // TODO(dslomov): implement.
- isolate->ThrowIllegalOperation();
- return MaybeHandle<Object>();
- }
-
- Handle<JSObject> js_object =
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
-
- // Check access rights if needed.
- if (js_object->IsAccessCheckNeeded()) {
- if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_SET)) {
- isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_SET);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return isolate->factory()->undefined_value();
- }
- }
-
- if (js_object->HasIndexedInterceptor()) {
- Maybe<PropertyAttributes> from_interceptor =
- JSObject::GetElementAttributeFromInterceptor(js_object, receiver,
- index);
- if (!from_interceptor.has_value) return MaybeHandle<Object>();
- if ((from_interceptor.value & READ_ONLY) != 0) {
- return WriteToReadOnlyElement(isolate, receiver, index, value,
- strict_mode);
- }
- done = from_interceptor.value != ABSENT;
- }
-
- if (!done &&
- js_object->elements() != isolate->heap()->empty_fixed_array()) {
- ElementsAccessor* accessor = js_object->GetElementsAccessor();
- PropertyAttributes attrs =
- accessor->GetAttributes(receiver, js_object, index);
- if ((attrs & READ_ONLY) != 0) {
- return WriteToReadOnlyElement(isolate, receiver, index, value,
- strict_mode);
- }
- Handle<AccessorPair> accessor_pair;
- if (accessor->GetAccessorPair(receiver, js_object, index)
- .ToHandle(&accessor_pair)) {
- return JSObject::SetElementWithCallback(receiver, accessor_pair, index,
- value, js_object, strict_mode);
- } else {
- done = attrs != ABSENT;
- }
- }
- }
-
- if (!receiver->IsJSObject()) {
- return WriteToReadOnlyElement(isolate, receiver, index, value, strict_mode);
- }
- Handle<JSObject> target = Handle<JSObject>::cast(receiver);
- ElementsAccessor* accessor = target->GetElementsAccessor();
- PropertyAttributes attrs = accessor->GetAttributes(receiver, target, index);
- if ((attrs & READ_ONLY) != 0) {
- return WriteToReadOnlyElement(isolate, receiver, index, value, strict_mode);
- }
- PropertyAttributes new_attrs = attrs != ABSENT ? attrs : NONE;
- return JSObject::SetElement(target, index, value, new_attrs, strict_mode,
- false);
}
Map* Object::GetRootMap(Isolate* isolate) {
DisallowHeapAllocation no_alloc;
if (IsSmi()) {
- Context* context = isolate->context()->native_context();
- return context->number_function()->initial_map();
+ Context* native_context = isolate->context()->native_context();
+ return native_context->number_function()->initial_map();
}
- HeapObject* heap_object = HeapObject::cast(this);
-
- // The object is either a number, a string, a boolean,
+ // 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();
}
- Context* context = isolate->context()->native_context();
-
- if (heap_object->IsHeapNumber()) {
- return context->number_function()->initial_map();
- }
- if (heap_object->IsString()) {
- return context->string_function()->initial_map();
- }
- if (heap_object->IsSymbol()) {
- return context->symbol_function()->initial_map();
- }
- if (heap_object->IsBoolean()) {
- return context->boolean_function()->initial_map();
+ int constructor_function_index =
+ heap_object->map()->GetConstructorFunctionIndex();
+ if (constructor_function_index != Map::kNoConstructorFunctionIndex) {
+ Context* native_context = isolate->context()->native_context();
+ JSFunction* constructor_function =
+ JSFunction::cast(native_context->get(constructor_function_index));
+ return constructor_function->initial_map();
}
return isolate->heap()->null_value()->map();
}
Object* Object::GetHash() {
- // The object is either a number, a name, an odd-ball,
- // a real JS object, or a Harmony proxy.
- if (IsNumber()) {
- uint32_t hash = ComputeLongHash(double_to_uint64(Number()));
+ Object* hash = GetSimpleHash();
+ if (hash->IsSmi()) return hash;
+
+ DCHECK(IsJSReceiver());
+ return JSReceiver::cast(this)->GetIdentityHash();
+}
+
+
+Object* Object::GetSimpleHash() {
+ // The object is either a Smi, a HeapNumber, a name, an odd-ball,
+ // a SIMD value type, a real JS object, or a Harmony proxy.
+ if (IsSmi()) {
+ uint32_t hash = ComputeIntegerHash(Smi::cast(this)->value(), kZeroHashSeed);
+ return Smi::FromInt(hash & Smi::kMaxValue);
+ }
+ if (IsHeapNumber()) {
+ double num = HeapNumber::cast(this)->value();
+ if (std::isnan(num)) return Smi::FromInt(Smi::kMaxValue);
+ if (i::IsMinusZero(num)) num = 0;
+ if (IsSmiDouble(num)) {
+ return Smi::FromInt(FastD2I(num))->GetHash();
+ }
+ uint32_t hash = ComputeLongHash(double_to_uint64(num));
return Smi::FromInt(hash & Smi::kMaxValue);
}
if (IsName()) {
@@ -997,14 +1507,18 @@
uint32_t hash = Oddball::cast(this)->to_string()->Hash();
return Smi::FromInt(hash);
}
-
+ if (IsSimd128Value()) {
+ uint32_t hash = Simd128Value::cast(this)->Hash();
+ return Smi::FromInt(hash & Smi::kMaxValue);
+ }
DCHECK(IsJSReceiver());
- return JSReceiver::cast(this)->GetIdentityHash();
+ JSReceiver* receiver = JSReceiver::cast(this);
+ return receiver->GetHeap()->undefined_value();
}
Handle<Smi> Object::GetOrCreateHash(Isolate* isolate, Handle<Object> object) {
- Handle<Object> hash(object->GetHash(), isolate);
+ Handle<Object> hash(object->GetSimpleHash(), isolate);
if (hash->IsSmi()) return Handle<Smi>::cast(hash);
DCHECK(object->IsJSReceiver());
@@ -1020,15 +1534,36 @@
if (IsNumber() && other->IsNumber()) {
double this_value = Number();
double other_value = other->Number();
- bool equal = this_value == other_value;
// SameValue(NaN, NaN) is true.
- if (!equal) return std::isnan(this_value) && std::isnan(other_value);
+ if (this_value != other_value) {
+ return std::isnan(this_value) && std::isnan(other_value);
+ }
// SameValue(0.0, -0.0) is false.
- return (this_value != 0) || ((1 / this_value) == (1 / other_value));
+ return (std::signbit(this_value) == std::signbit(other_value));
}
if (IsString() && other->IsString()) {
return String::cast(this)->Equals(String::cast(other));
}
+ if (IsSimd128Value() && other->IsSimd128Value()) {
+ if (IsFloat32x4() && other->IsFloat32x4()) {
+ Float32x4* a = Float32x4::cast(this);
+ Float32x4* b = Float32x4::cast(other);
+ for (int i = 0; i < 4; i++) {
+ float x = a->get_lane(i);
+ float y = b->get_lane(i);
+ // Implements the ES5 SameValue operation for floating point types.
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-samevalue
+ if (x != y && !(std::isnan(x) && std::isnan(y))) return false;
+ if (std::signbit(x) != std::signbit(y)) return false;
+ }
+ return true;
+ } else {
+ Simd128Value* a = Simd128Value::cast(this);
+ Simd128Value* b = Simd128Value::cast(other);
+ return a->map()->instance_type() == b->map()->instance_type() &&
+ a->BitwiseEquals(b);
+ }
+ }
return false;
}
@@ -1042,16 +1577,86 @@
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));
+ return this_value == other_value ||
+ (std::isnan(this_value) && std::isnan(other_value));
}
if (IsString() && other->IsString()) {
return String::cast(this)->Equals(String::cast(other));
}
+ if (IsSimd128Value() && other->IsSimd128Value()) {
+ if (IsFloat32x4() && other->IsFloat32x4()) {
+ Float32x4* a = Float32x4::cast(this);
+ Float32x4* b = Float32x4::cast(other);
+ for (int i = 0; i < 4; i++) {
+ float x = a->get_lane(i);
+ float y = b->get_lane(i);
+ // Implements the ES6 SameValueZero operation for floating point types.
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-samevaluezero
+ if (x != y && !(std::isnan(x) && std::isnan(y))) return false;
+ // SameValueZero doesn't distinguish between 0 and -0.
+ }
+ return true;
+ } else {
+ Simd128Value* a = Simd128Value::cast(this);
+ Simd128Value* b = Simd128Value::cast(other);
+ return a->map()->instance_type() == b->map()->instance_type() &&
+ a->BitwiseEquals(b);
+ }
+ }
return false;
}
+MaybeHandle<Object> Object::ArraySpeciesConstructor(
+ Isolate* isolate, Handle<Object> original_array) {
+ Handle<Context> native_context = isolate->native_context();
+ if (!FLAG_harmony_species) {
+ return Handle<Object>(native_context->array_function(), isolate);
+ }
+ Handle<Object> constructor = isolate->factory()->undefined_value();
+ Maybe<bool> is_array = Object::IsArray(original_array);
+ MAYBE_RETURN_NULL(is_array);
+ if (is_array.FromJust()) {
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, constructor,
+ Object::GetProperty(original_array,
+ isolate->factory()->constructor_string()),
+ Object);
+ if (constructor->IsConstructor()) {
+ Handle<Context> constructor_context;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, constructor_context,
+ JSReceiver::GetFunctionRealm(Handle<JSReceiver>::cast(constructor)),
+ Object);
+ if (*constructor_context != *native_context &&
+ *constructor == constructor_context->array_function()) {
+ constructor = isolate->factory()->undefined_value();
+ }
+ }
+ if (constructor->IsJSReceiver()) {
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, constructor,
+ Object::GetProperty(constructor,
+ isolate->factory()->species_symbol()),
+ Object);
+ if (constructor->IsNull()) {
+ constructor = isolate->factory()->undefined_value();
+ }
+ }
+ }
+ if (constructor->IsUndefined()) {
+ return Handle<Object>(native_context->array_function(), isolate);
+ } else {
+ if (!constructor->IsConstructor()) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kSpeciesNotConstructor),
+ Object);
+ }
+ return constructor;
+ }
+}
+
+
void Object::ShortPrint(FILE* out) {
OFStream os(out);
os << Brief(this);
@@ -1143,6 +1748,7 @@
// 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.
@@ -1196,7 +1802,7 @@
self->set_resource(resource);
if (is_internalized) self->Hash(); // Force regeneration of the hash value.
- heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR);
+ heap->AdjustLiveBytes(this, new_size - size, Heap::CONCURRENT_TO_SWEEPER);
return true;
}
@@ -1205,6 +1811,7 @@
// 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.
@@ -1256,7 +1863,7 @@
self->set_resource(resource);
if (is_internalized) self->Hash(); // Force regeneration of the hash value.
- heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR);
+ heap->AdjustLiveBytes(this, new_size - size, Heap::CONCURRENT_TO_SWEEPER);
return true;
}
@@ -1342,6 +1949,22 @@
accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length));
break;
}
+ case JS_BOUND_FUNCTION_TYPE: {
+ JSBoundFunction* bound_function = JSBoundFunction::cast(this);
+ Object* name = bound_function->name();
+ accumulator->Add("<JS BoundFunction");
+ if (name->IsString()) {
+ String* str = String::cast(name);
+ if (str->length() > 0) {
+ accumulator->Add(" ");
+ accumulator->Put(str);
+ }
+ }
+ accumulator->Add(
+ " (BoundTargetFunction %p)>",
+ reinterpret_cast<void*>(bound_function->bound_target_function()));
+ break;
+ }
case JS_WEAK_MAP_TYPE: {
accumulator->Add("<JS WeakMap>");
break;
@@ -1387,7 +2010,7 @@
default: {
Map* map_of_this = map();
Heap* heap = GetHeap();
- Object* constructor = map_of_this->constructor();
+ Object* constructor = map_of_this->GetConstructor();
bool printed = false;
if (constructor->IsHeapObject() &&
!heap->Contains(HeapObject::cast(constructor))) {
@@ -1451,6 +2074,37 @@
}
+// static
+MaybeHandle<JSFunction> Map::GetConstructorFunction(
+ Handle<Map> map, Handle<Context> native_context) {
+ if (map->IsPrimitiveMap()) {
+ int const constructor_function_index = map->GetConstructorFunctionIndex();
+ if (constructor_function_index != kNoConstructorFunctionIndex) {
+ return handle(
+ JSFunction::cast(native_context->get(constructor_function_index)));
+ }
+ }
+ return MaybeHandle<JSFunction>();
+}
+
+
+void Map::PrintReconfiguration(FILE* file, int modify_index, PropertyKind kind,
+ PropertyAttributes attributes) {
+ OFStream os(file);
+ os << "[reconfiguring]";
+ Name* name = instance_descriptors()->GetKey(modify_index);
+ if (name->IsString()) {
+ String::cast(name)->PrintOn(file);
+ } else {
+ os << "{symbol " << static_cast<void*>(name) << "}";
+ }
+ os << ": " << (kind == kData ? "kData" : "ACCESSORS") << ", attrs: ";
+ os << attributes << " [";
+ JavaScriptFrame::PrintTop(GetIsolate(), file, false, true);
+ os << "]\n";
+}
+
+
void Map::PrintGeneralization(FILE* file,
const char* reason,
int modify_index,
@@ -1462,9 +2116,7 @@
HeapType* old_field_type,
HeapType* new_field_type) {
OFStream os(file);
- os << "[generalizing ";
- constructor_name()->PrintOn(file);
- os << "] ";
+ os << "[generalizing]";
Name* name = instance_descriptors()->GetKey(modify_index);
if (name->IsString()) {
String::cast(name)->PrintOn(file);
@@ -1496,9 +2148,7 @@
void JSObject::PrintInstanceMigration(FILE* file,
Map* original_map,
Map* new_map) {
- PrintF(file, "[migrating ");
- map()->constructor_name()->PrintOn(file);
- PrintF(file, "] ");
+ PrintF(file, "[migrating]");
DescriptorArray* o = original_map->instance_descriptors();
DescriptorArray* n = new_map->instance_descriptors();
for (int i = 0; i < original_map->NumberOfOwnDescriptors(); i++) {
@@ -1507,8 +2157,8 @@
if (!o_r.Equals(n_r)) {
String::cast(o->GetKey(i))->PrintOn(file);
PrintF(file, ":%s->%s ", o_r.Mnemonic(), n_r.Mnemonic());
- } else if (o->GetDetails(i).type() == CONSTANT &&
- n->GetDetails(i).type() == FIELD) {
+ } else if (o->GetDetails(i).type() == DATA_CONSTANT &&
+ n->GetDetails(i).type() == DATA) {
Name* name = o->GetKey(i);
if (name->IsString()) {
String::cast(name)->PrintOn(file);
@@ -1551,7 +2201,8 @@
}
switch (map()->instance_type()) {
case MAP_TYPE:
- os << "<Map(elements=" << Map::cast(this)->elements_kind() << ")>";
+ os << "<Map(" << ElementsKindToString(Map::cast(this)->elements_kind())
+ << ")>";
break;
case FIXED_ARRAY_TYPE:
os << "<FixedArray[" << FixedArray::cast(this)->length() << "]>";
@@ -1563,14 +2214,17 @@
case BYTE_ARRAY_TYPE:
os << "<ByteArray[" << ByteArray::cast(this)->length() << "]>";
break;
+ case BYTECODE_ARRAY_TYPE:
+ os << "<BytecodeArray[" << BytecodeArray::cast(this)->length() << "]>";
+ break;
+ case TRANSITION_ARRAY_TYPE:
+ os << "<TransitionArray[" << TransitionArray::cast(this)->length()
+ << "]>";
+ break;
case FREE_SPACE_TYPE:
- os << "<FreeSpace[" << FreeSpace::cast(this)->Size() << "]>";
+ os << "<FreeSpace[" << FreeSpace::cast(this)->size() << "]>";
break;
#define TYPED_ARRAY_SHORT_PRINT(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ARRAY_TYPE: \
- os << "<External" #Type "Array[" \
- << External##Type##Array::cast(this)->length() << "]>"; \
- break; \
case FIXED_##TYPE##_ARRAY_TYPE: \
os << "<Fixed" #Type "Array[" << Fixed##Type##Array::cast(this)->length() \
<< "]>"; \
@@ -1581,7 +2235,7 @@
case SHARED_FUNCTION_INFO_TYPE: {
SharedFunctionInfo* shared = SharedFunctionInfo::cast(this);
- SmartArrayPointer<char> debug_name =
+ base::SmartArrayPointer<char> debug_name =
shared->DebugName()->ToCString();
if (debug_name[0] != 0) {
os << "<SharedFunctionInfo " << debug_name.get() << ">";
@@ -1637,12 +2291,20 @@
os << '>';
break;
}
+ case SIMD128_VALUE_TYPE: {
+#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \
+ if (Is##Type()) { \
+ os << "<" #Type ">"; \
+ break; \
+ }
+ SIMD128_TYPES(SIMD128_TYPE)
+#undef SIMD128_TYPE
+ UNREACHABLE();
+ break;
+ }
case JS_PROXY_TYPE:
os << "<JSProxy>";
break;
- case JS_FUNCTION_PROXY_TYPE:
- os << "<JSFunctionProxy>";
- break;
case FOREIGN_TYPE:
os << "<Foreign>";
break;
@@ -1658,7 +2320,8 @@
os << "PropertyCell for ";
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
- PropertyCell::cast(this)->value()->ShortPrint(&accumulator);
+ PropertyCell* cell = PropertyCell::cast(this);
+ cell->value()->ShortPrint(&accumulator);
os << accumulator.ToCString().get();
break;
}
@@ -1677,143 +2340,33 @@
}
-void HeapObject::Iterate(ObjectVisitor* v) {
- // Handle header
- IteratePointer(v, kMapOffset);
- // Handle object body
+void HeapObject::Iterate(ObjectVisitor* v) { IterateFast<ObjectVisitor>(v); }
+
+
+void HeapObject::IterateBody(ObjectVisitor* v) {
Map* m = map();
- IterateBody(m->instance_type(), SizeFromMap(m), v);
+ IterateBodyFast<ObjectVisitor>(m->instance_type(), SizeFromMap(m), v);
}
void HeapObject::IterateBody(InstanceType type, int object_size,
ObjectVisitor* v) {
- // Avoiding <Type>::cast(this) because it accesses the map pointer field.
- // During GC, the map pointer field is encoded.
- if (type < FIRST_NONSTRING_TYPE) {
- switch (type & kStringRepresentationMask) {
- case kSeqStringTag:
- break;
- case kConsStringTag:
- ConsString::BodyDescriptor::IterateBody(this, v);
- break;
- case kSlicedStringTag:
- SlicedString::BodyDescriptor::IterateBody(this, v);
- break;
- case kExternalStringTag:
- if ((type & kStringEncodingMask) == kOneByteStringTag) {
- reinterpret_cast<ExternalOneByteString*>(this)
- ->ExternalOneByteStringIterateBody(v);
- } else {
- reinterpret_cast<ExternalTwoByteString*>(this)->
- ExternalTwoByteStringIterateBody(v);
- }
- break;
- }
- return;
+ IterateBodyFast<ObjectVisitor>(type, object_size, v);
+}
+
+
+struct CallIsValidSlot {
+ template <typename BodyDescriptor>
+ static bool apply(HeapObject* obj, int offset, int) {
+ return BodyDescriptor::IsValidSlot(obj, offset);
}
+};
- switch (type) {
- case FIXED_ARRAY_TYPE:
- FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
- break;
- case CONSTANT_POOL_ARRAY_TYPE:
- reinterpret_cast<ConstantPoolArray*>(this)->ConstantPoolIterateBody(v);
- break;
- case FIXED_DOUBLE_ARRAY_TYPE:
- break;
- case JS_OBJECT_TYPE:
- case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
- case JS_GENERATOR_OBJECT_TYPE:
- case JS_MODULE_TYPE:
- case JS_VALUE_TYPE:
- case JS_DATE_TYPE:
- case JS_ARRAY_TYPE:
- case JS_ARRAY_BUFFER_TYPE:
- case JS_TYPED_ARRAY_TYPE:
- case JS_DATA_VIEW_TYPE:
- case JS_SET_TYPE:
- case JS_MAP_TYPE:
- case JS_SET_ITERATOR_TYPE:
- case JS_MAP_ITERATOR_TYPE:
- case JS_WEAK_MAP_TYPE:
- case JS_WEAK_SET_TYPE:
- case JS_REGEXP_TYPE:
- case JS_GLOBAL_PROXY_TYPE:
- case JS_GLOBAL_OBJECT_TYPE:
- case JS_BUILTINS_OBJECT_TYPE:
- case JS_MESSAGE_OBJECT_TYPE:
- JSObject::BodyDescriptor::IterateBody(this, object_size, v);
- break;
- case JS_FUNCTION_TYPE:
- reinterpret_cast<JSFunction*>(this)
- ->JSFunctionIterateBody(object_size, v);
- break;
- case ODDBALL_TYPE:
- Oddball::BodyDescriptor::IterateBody(this, v);
- break;
- case JS_PROXY_TYPE:
- JSProxy::BodyDescriptor::IterateBody(this, v);
- break;
- case JS_FUNCTION_PROXY_TYPE:
- JSFunctionProxy::BodyDescriptor::IterateBody(this, v);
- break;
- case FOREIGN_TYPE:
- reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
- break;
- case MAP_TYPE:
- Map::BodyDescriptor::IterateBody(this, v);
- break;
- case CODE_TYPE:
- reinterpret_cast<Code*>(this)->CodeIterateBody(v);
- break;
- case CELL_TYPE:
- Cell::BodyDescriptor::IterateBody(this, v);
- break;
- case PROPERTY_CELL_TYPE:
- PropertyCell::BodyDescriptor::IterateBody(this, v);
- break;
- case WEAK_CELL_TYPE:
- WeakCell::BodyDescriptor::IterateBody(this, v);
- break;
- case SYMBOL_TYPE:
- Symbol::BodyDescriptor::IterateBody(this, v);
- break;
- case HEAP_NUMBER_TYPE:
- case MUTABLE_HEAP_NUMBER_TYPE:
- case FILLER_TYPE:
- case BYTE_ARRAY_TYPE:
- case FREE_SPACE_TYPE:
- break;
-
-#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ARRAY_TYPE: \
- case FIXED_##TYPE##_ARRAY_TYPE: \
- break;
-
- TYPED_ARRAYS(TYPED_ARRAY_CASE)
-#undef TYPED_ARRAY_CASE
-
- case SHARED_FUNCTION_INFO_TYPE: {
- SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
- break;
- }
-
-#define MAKE_STRUCT_CASE(NAME, Name, name) \
- case NAME##_TYPE:
- STRUCT_LIST(MAKE_STRUCT_CASE)
-#undef MAKE_STRUCT_CASE
- if (type == ALLOCATION_SITE_TYPE) {
- AllocationSite::BodyDescriptor::IterateBody(this, v);
- } else {
- StructBodyDescriptor::IterateBody(this, object_size, v);
- }
- break;
- default:
- PrintF("Unknown type: %d\n", type);
- UNREACHABLE();
- }
+bool HeapObject::IsValidSlot(int offset) {
+ DCHECK_NE(0, offset);
+ return BodyDescriptorApply<CallIsValidSlot, bool>(map()->instance_type(),
+ this, offset, 0);
}
@@ -1827,12 +2380,120 @@
}
+#define FIELD_ADDR_CONST(p, offset) \
+ (reinterpret_cast<const byte*>(p) + offset - kHeapObjectTag)
+
+#define READ_INT32_FIELD(p, offset) \
+ (*reinterpret_cast<const int32_t*>(FIELD_ADDR_CONST(p, offset)))
+
+#define READ_INT64_FIELD(p, offset) \
+ (*reinterpret_cast<const int64_t*>(FIELD_ADDR_CONST(p, offset)))
+
+#define READ_BYTE_FIELD(p, offset) \
+ (*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) &&
+ READ_INT64_FIELD(this, kValueOffset + kInt64Size) ==
+ READ_INT64_FIELD(other, kValueOffset + kInt64Size);
+}
+
+
+uint32_t Simd128Value::Hash() const {
+ uint32_t seed = v8::internal::kZeroHashSeed;
+ uint32_t hash;
+ hash = ComputeIntegerHash(READ_INT32_FIELD(this, kValueOffset), seed);
+ hash = ComputeIntegerHash(
+ READ_INT32_FIELD(this, kValueOffset + 1 * kInt32Size), hash * 31);
+ hash = ComputeIntegerHash(
+ READ_INT32_FIELD(this, kValueOffset + 2 * kInt32Size), hash * 31);
+ hash = ComputeIntegerHash(
+ READ_INT32_FIELD(this, kValueOffset + 3 * kInt32Size), hash * 31);
+ return hash;
+}
+
+
+void Simd128Value::CopyBits(void* destination) const {
+ memcpy(destination, &READ_BYTE_FIELD(this, kValueOffset), kSimd128Size);
+}
+
+
String* JSReceiver::class_name() {
- if (IsJSFunction() || IsJSFunctionProxy()) {
+ if (IsFunction()) {
return GetHeap()->Function_string();
}
- if (map()->constructor()->IsJSFunction()) {
- JSFunction* constructor = JSFunction::cast(map()->constructor());
+ Object* maybe_constructor = map()->GetConstructor();
+ if (maybe_constructor->IsJSFunction()) {
+ JSFunction* constructor = JSFunction::cast(maybe_constructor);
return String::cast(constructor->shared()->instance_class_name());
}
// If the constructor is not present, return "Object".
@@ -1840,24 +2501,95 @@
}
-String* Map::constructor_name() {
- if (constructor()->IsJSFunction()) {
- JSFunction* constructor = JSFunction::cast(this->constructor());
- String* name = String::cast(constructor->shared()->name());
- if (name->length() > 0) return name;
- String* inferred_name = constructor->shared()->inferred_name();
- if (inferred_name->length() > 0) return inferred_name;
- Object* proto = prototype();
- if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
+MaybeHandle<String> JSReceiver::BuiltinStringTag(Handle<JSReceiver> object) {
+ Maybe<bool> is_array = Object::IsArray(object);
+ MAYBE_RETURN(is_array, MaybeHandle<String>());
+ Isolate* const isolate = object->GetIsolate();
+ if (is_array.FromJust()) {
+ return isolate->factory()->Array_string();
}
- // TODO(rossberg): what about proxies?
- // If the constructor is not present, return "Object".
- return GetHeap()->Object_string();
+ // TODO(adamk): According to ES2015, we should return "Function" when
+ // object has a [[Call]] internal method (corresponds to IsCallable).
+ // But this is well cemented in layout tests and might cause webbreakage.
+ // if (object->IsCallable()) {
+ // return isolate->factory()->Function_string();
+ // }
+ // TODO(adamk): class_name() is expensive, replace with instance type
+ // checks where possible.
+ return handle(object->class_name(), isolate);
}
-String* JSReceiver::constructor_name() {
- return map()->constructor_name();
+// static
+Handle<String> JSReceiver::GetConstructorName(Handle<JSReceiver> receiver) {
+ Isolate* isolate = receiver->GetIsolate();
+
+ // If the object was instantiated simply with base == new.target, the
+ // constructor on the map provides the most accurate name.
+ // Don't provide the info for prototypes, since their constructors are
+ // reclaimed and replaced by Object in OptimizeAsPrototype.
+ if (!receiver->IsJSProxy() && receiver->map()->new_target_is_base() &&
+ !receiver->map()->is_prototype_map()) {
+ Object* maybe_constructor = receiver->map()->GetConstructor();
+ if (maybe_constructor->IsJSFunction()) {
+ JSFunction* constructor = JSFunction::cast(maybe_constructor);
+ String* name = String::cast(constructor->shared()->name());
+ if (name->length() == 0) name = constructor->shared()->inferred_name();
+ if (name->length() != 0 &&
+ !name->Equals(isolate->heap()->Object_string())) {
+ return handle(name, isolate);
+ }
+ }
+ }
+
+ if (FLAG_harmony_tostring) {
+ Handle<Object> maybe_tag = JSReceiver::GetDataProperty(
+ receiver, isolate->factory()->to_string_tag_symbol());
+ if (maybe_tag->IsString()) return Handle<String>::cast(maybe_tag);
+ }
+
+ PrototypeIterator iter(isolate, receiver);
+ if (iter.IsAtEnd()) return handle(receiver->class_name());
+ Handle<JSReceiver> start = PrototypeIterator::GetCurrent<JSReceiver>(iter);
+ LookupIterator it(receiver, isolate->factory()->constructor_string(), start,
+ LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
+ Handle<Object> maybe_constructor = JSReceiver::GetDataProperty(&it);
+ Handle<String> result = isolate->factory()->Object_string();
+ if (maybe_constructor->IsJSFunction()) {
+ JSFunction* constructor = JSFunction::cast(*maybe_constructor);
+ String* name = String::cast(constructor->shared()->name());
+ if (name->length() == 0) name = constructor->shared()->inferred_name();
+ if (name->length() > 0) result = handle(name, isolate);
+ }
+
+ return result.is_identical_to(isolate->factory()->Object_string())
+ ? handle(receiver->class_name())
+ : result;
+}
+
+
+Context* JSReceiver::GetCreationContext() {
+ if (IsJSBoundFunction()) {
+ return JSBoundFunction::cast(this)->creation_context();
+ }
+ Object* constructor = map()->GetConstructor();
+ JSFunction* function;
+ if (constructor->IsJSFunction()) {
+ function = JSFunction::cast(constructor);
+ } else {
+ // Functions have null as a constructor,
+ // but any JSFunction knows its context immediately.
+ CHECK(IsJSFunction());
+ function = JSFunction::cast(this);
+ }
+
+ return function->context()->native_context();
+}
+
+
+static Handle<Object> WrapType(Handle<HeapType> type) {
+ if (type->IsClass()) return Map::WeakCellForMap(type->AsClass()->Map());
+ return type;
}
@@ -1886,7 +2618,10 @@
type = HeapType::Any(isolate);
}
- FieldDescriptor new_field_desc(name, index, type, attributes, representation);
+ Handle<Object> wrapped_type(WrapType(type));
+
+ DataDescriptor new_field_desc(name, index, wrapped_type, attributes,
+ representation);
Handle<Map> new_map = Map::CopyAddDescriptor(map, &new_field_desc, flag);
int unused_property_fields = new_map->unused_property_fields() - 1;
if (unused_property_fields < 0) {
@@ -1908,7 +2643,7 @@
}
// Allocate new instance descriptors with (name, constant) added.
- ConstantDescriptor new_constant_desc(name, constant, attributes);
+ DataConstantDescriptor new_constant_desc(name, constant, attributes);
return Map::CopyAddDescriptor(map, &new_constant_desc, flag);
}
@@ -1919,44 +2654,40 @@
PropertyAttributes attributes) {
DCHECK(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
- Handle<NameDictionary> dict(object->property_dictionary());
- if (object->IsGlobalObject()) {
- // In case name is an orphaned property reuse the cell.
+ if (object->IsJSGlobalObject()) {
+ Handle<GlobalDictionary> dict(object->global_dictionary());
+ PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
int entry = dict->FindEntry(name);
- if (entry != NameDictionary::kNotFound) {
- Handle<PropertyCell> cell(PropertyCell::cast(dict->ValueAt(entry)));
- PropertyCell::SetValueInferType(cell, value);
- // Assign an enumeration index to the property and update
- // SetNextEnumerationIndex.
+ // If there's a cell there, just invalidate and set the property.
+ if (entry != GlobalDictionary::kNotFound) {
+ PropertyCell::UpdateCell(dict, entry, value, details);
+ // TODO(ishell): move this to UpdateCell.
+ // Need to adjust the details.
int index = dict->NextEnumerationIndex();
- PropertyDetails details(attributes, FIELD, index);
dict->SetNextEnumerationIndex(index + 1);
- dict->SetEntry(entry, name, cell, details);
- return;
+ PropertyCell* cell = PropertyCell::cast(dict->ValueAt(entry));
+ details = cell->property_details().set_index(index);
+ cell->set_property_details(details);
+
+ } else {
+ auto cell = isolate->factory()->NewPropertyCell();
+ cell->set_value(*value);
+ auto cell_type = value->IsUndefined() ? PropertyCellType::kUndefined
+ : PropertyCellType::kConstant;
+ details = details.set_cell_type(cell_type);
+ value = cell;
+
+ Handle<GlobalDictionary> result =
+ GlobalDictionary::Add(dict, name, value, details);
+ if (*dict != *result) object->set_properties(*result);
}
- Handle<PropertyCell> cell = isolate->factory()->NewPropertyCell(value);
- PropertyCell::SetValueInferType(cell, value);
- value = cell;
- }
- PropertyDetails details(attributes, FIELD, 0);
- Handle<NameDictionary> result =
- NameDictionary::Add(dict, name, value, details);
- if (*dict != *result) object->set_properties(*result);
-}
-
-
-Context* JSObject::GetCreationContext() {
- Object* constructor = this->map()->constructor();
- JSFunction* function;
- if (!constructor->IsJSFunction()) {
- // Functions have null as a constructor,
- // but any JSFunction knows its context immediately.
- function = JSFunction::cast(this);
} else {
- function = JSFunction::cast(constructor);
+ Handle<NameDictionary> dict(object->property_dictionary());
+ PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
+ Handle<NameDictionary> result =
+ NameDictionary::Add(dict, name, value, details);
+ if (*dict != *result) object->set_properties(*result);
}
-
- return function->context()->native_context();
}
@@ -2015,11 +2746,11 @@
// If no fields were added, and no inobject properties were removed, setting
// the map is sufficient.
- if (target_inobject == inobject_properties()) return false;
+ if (target_inobject == GetInObjectProperties()) return false;
// In-object slack tracking may have reduced the object size of the new map.
// In that case, succeed if all existing fields were inobject, and they still
// fit within the new inobject size.
- DCHECK(target_inobject < inobject_properties());
+ DCHECK(target_inobject < GetInObjectProperties());
if (target_number_of_fields <= target_inobject) {
DCHECK(target_number_of_fields + target_unused == target_inobject);
return false;
@@ -2029,40 +2760,83 @@
}
-void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) {
- Isolate* isolate = parent->GetIsolate();
- Handle<Name> name = isolate->factory()->elements_transition_symbol();
- ConnectTransition(parent, child, name, SPECIAL_TRANSITION);
+// static
+void JSObject::UpdatePrototypeUserRegistration(Handle<Map> old_map,
+ Handle<Map> new_map,
+ Isolate* isolate) {
+ if (!FLAG_track_prototype_users) return;
+ if (!old_map->is_prototype_map()) return;
+ DCHECK(new_map->is_prototype_map());
+ bool was_registered = JSObject::UnregisterPrototypeUser(old_map, isolate);
+ new_map->set_prototype_info(old_map->prototype_info());
+ old_map->set_prototype_info(Smi::FromInt(0));
+ if (FLAG_trace_prototype_users) {
+ PrintF("Moving prototype_info %p from map %p to map %p.\n",
+ reinterpret_cast<void*>(new_map->prototype_info()),
+ reinterpret_cast<void*>(*old_map),
+ reinterpret_cast<void*>(*new_map));
+ }
+ if (was_registered) {
+ if (new_map->prototype_info()->IsPrototypeInfo()) {
+ // The new map isn't registered with its prototype yet; reflect this fact
+ // in the PrototypeInfo it just inherited from the old map.
+ PrototypeInfo::cast(new_map->prototype_info())
+ ->set_registry_slot(PrototypeInfo::UNREGISTERED);
+ }
+ JSObject::LazyRegisterPrototypeUser(new_map, isolate);
+ }
}
-void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
+void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
+ int expected_additional_properties) {
if (object->map() == *new_map) return;
+ // If this object is a prototype (the callee will check), invalidate any
+ // prototype chains involving it.
+ InvalidatePrototypeChains(object->map());
+ Handle<Map> old_map(object->map());
+
+ // If the map was registered with its prototype before, ensure that it
+ // registers with its new prototype now. This preserves the invariant that
+ // when a map on a prototype chain is registered with its prototype, then
+ // all prototypes further up the chain are also registered with their
+ // respective prototypes.
+ UpdatePrototypeUserRegistration(old_map, new_map, new_map->GetIsolate());
+
if (object->HasFastProperties()) {
if (!new_map->is_dictionary_map()) {
- Handle<Map> old_map(object->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(
old_map->GetHeap()->empty_descriptor_array(),
LayoutDescriptor::FastPointerLayout());
// Ensure that no transition was inserted for prototype migrations.
- DCHECK(!old_map->HasTransitionArray());
+ DCHECK_EQ(0, TransitionArray::NumberOfTransitions(
+ old_map->raw_transitions()));
DCHECK(new_map->GetBackPointer()->IsUndefined());
}
} else {
- MigrateFastToSlow(object, new_map, 0);
+ MigrateFastToSlow(object, new_map, expected_additional_properties);
}
} else {
- // For slow-to-fast migrations JSObject::TransformToFastProperties()
+ // For slow-to-fast migrations JSObject::MigrateSlowToFast()
// must be used instead.
CHECK(new_map->is_dictionary_map());
// Slow-to-slow migration is trivial.
object->set_map(*new_map);
}
+
+ // Careful: Don't allocate here!
+ // For some callers of this method, |object| might be in an inconsistent
+ // state now: the new map might have a new elements_kind, but the object's
+ // elements pointer hasn't been updated yet. Callers will fix this, but in
+ // the meantime, (indirectly) calling JSObjectVerify() must be avoided.
+ // When adding code here, add a DisallowHeapAllocation too.
}
@@ -2086,7 +2860,7 @@
Handle<Map> old_map(object->map());
int old_number_of_fields;
int number_of_fields = new_map->NumberOfFields();
- int inobject = new_map->inobject_properties();
+ int inobject = new_map->GetInObjectProperties();
int unused = new_map->unused_property_fields();
// Nothing to do if no functions were converted to fields and no smis were
@@ -2122,9 +2896,10 @@
DCHECK(number_of_fields == old_number_of_fields + 1);
// This migration is a transition from a map that has run out of property
// space. Therefore it could be done by extending the backing store.
+ int grow_by = external - object->properties()->length();
Handle<FixedArray> old_storage = handle(object->properties(), isolate);
Handle<FixedArray> new_storage =
- FixedArray::CopySize(old_storage, external);
+ isolate->factory()->CopyFixedArrayAndGrow(old_storage, grow_by);
// Properly initialize newly added property.
Handle<Object> value;
@@ -2133,7 +2908,7 @@
} else {
value = isolate->factory()->uninitialized_value();
}
- DCHECK(details.type() == FIELD);
+ DCHECK(details.type() == DATA);
int target_index = details.field_index() - inobject;
DCHECK(target_index >= 0); // Must be a backing store index.
new_storage->set(target_index, *value);
@@ -2159,18 +2934,21 @@
for (int i = 0; i < old_nof; i++) {
PropertyDetails details = new_descriptors->GetDetails(i);
- if (details.type() != FIELD) continue;
+ if (details.type() != DATA) continue;
PropertyDetails old_details = old_descriptors->GetDetails(i);
- if (old_details.type() == CALLBACKS) {
- DCHECK(details.representation().IsTagged());
- continue;
- }
Representation old_representation = old_details.representation();
Representation representation = details.representation();
- DCHECK(old_details.type() == CONSTANT ||
- old_details.type() == FIELD);
Handle<Object> value;
- if (old_details.type() == CONSTANT) {
+ if (old_details.type() == ACCESSOR_CONSTANT) {
+ // In case of kAccessor -> kData property reconfiguration, the property
+ // must already be prepared for data or certain type.
+ DCHECK(!details.representation().IsNone());
+ if (details.representation().IsDouble()) {
+ value = isolate->factory()->NewHeapNumber(0, MUTABLE);
+ } else {
+ value = isolate->factory()->uninitialized_value();
+ }
+ } else if (old_details.type() == DATA_CONSTANT) {
value = handle(old_descriptors->GetValue(i), isolate);
DCHECK(!old_representation.IsDouble() && !representation.IsDouble());
} else {
@@ -2201,7 +2979,7 @@
for (int i = old_nof; i < new_nof; i++) {
PropertyDetails details = new_descriptors->GetDetails(i);
- if (details.type() != FIELD) continue;
+ if (details.type() != DATA) continue;
Handle<Object> value;
if (details.representation().IsDouble()) {
value = isolate->factory()->NewHeapNumber(0, MUTABLE);
@@ -2238,7 +3016,7 @@
// If there are properties in the new backing store, trim it to the correct
// size and install the backing store into the object.
if (external > 0) {
- heap->RightTrimFixedArray<Heap::FROM_MUTATOR>(*array, inobject);
+ heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(*array, inobject);
object->set_properties(*array);
}
@@ -2251,7 +3029,8 @@
Address address = object->address();
heap->CreateFillerObjectAt(
address + new_instance_size, instance_size_delta);
- heap->AdjustLiveBytes(address, -instance_size_delta, Heap::FROM_MUTATOR);
+ heap->AdjustLiveBytes(*object, -instance_size_delta,
+ Heap::CONCURRENT_TO_SWEEPER);
}
// We are storing the new map using release store after creating a filler for
@@ -2264,17 +3043,15 @@
DescriptorArray* descriptors = instance_descriptors();
int result = 0;
for (int i = 0; i < NumberOfOwnDescriptors(); i++) {
- if (descriptors->GetDetails(i).type() == FIELD) result++;
+ if (descriptors->GetDetails(i).location() == kField) result++;
}
return result;
}
-Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map,
- int modify_index,
- StoreMode store_mode,
- PropertyAttributes attributes,
- const char* reason) {
+Handle<Map> Map::CopyGeneralizeAllRepresentations(
+ Handle<Map> map, int modify_index, StoreMode store_mode, PropertyKind kind,
+ PropertyAttributes attributes, const char* reason) {
Isolate* isolate = map->GetIsolate();
Handle<DescriptorArray> old_descriptors(map->instance_descriptors(), isolate);
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
@@ -2283,7 +3060,7 @@
for (int i = 0; i < number_of_own_descriptors; i++) {
descriptors->SetRepresentation(i, Representation::Tagged());
- if (descriptors->GetDetails(i).type() == FIELD) {
+ if (descriptors->GetDetails(i).type() == DATA) {
descriptors->SetValue(i, HeapType::Any());
}
}
@@ -2295,59 +3072,49 @@
MaybeHandle<Name>(), reason, SPECIAL_TRANSITION);
// Unless the instance is being migrated, ensure that modify_index is a field.
- PropertyDetails details = descriptors->GetDetails(modify_index);
- if (store_mode == FORCE_IN_OBJECT &&
- (details.type() != FIELD || details.attributes() != attributes)) {
- int field_index = details.type() == FIELD ? details.field_index()
- : new_map->NumberOfFields();
- FieldDescriptor d(handle(descriptors->GetKey(modify_index), isolate),
- field_index, attributes, Representation::Tagged());
- descriptors->Replace(modify_index, &d);
- if (details.type() != FIELD) {
- int unused_property_fields = new_map->unused_property_fields() - 1;
- if (unused_property_fields < 0) {
- unused_property_fields += JSObject::kFieldsAdded;
+ if (modify_index >= 0) {
+ PropertyDetails details = descriptors->GetDetails(modify_index);
+ if (store_mode == FORCE_FIELD &&
+ (details.type() != DATA || details.attributes() != attributes)) {
+ int field_index = details.type() == DATA ? details.field_index()
+ : new_map->NumberOfFields();
+ DataDescriptor d(handle(descriptors->GetKey(modify_index), isolate),
+ field_index, attributes, Representation::Tagged());
+ descriptors->Replace(modify_index, &d);
+ if (details.type() != DATA) {
+ int unused_property_fields = new_map->unused_property_fields() - 1;
+ if (unused_property_fields < 0) {
+ unused_property_fields += JSObject::kFieldsAdded;
+ }
+ new_map->set_unused_property_fields(unused_property_fields);
}
- new_map->set_unused_property_fields(unused_property_fields);
+ } else {
+ DCHECK(details.attributes() == attributes);
}
- } else {
- DCHECK(details.attributes() == attributes);
- }
- if (FLAG_trace_generalization) {
- HeapType* field_type = (details.type() == FIELD)
- ? map->instance_descriptors()->GetFieldType(modify_index)
- : NULL;
- map->PrintGeneralization(
- stdout, reason, modify_index, new_map->NumberOfOwnDescriptors(),
- new_map->NumberOfOwnDescriptors(),
- details.type() == CONSTANT && store_mode == FORCE_IN_OBJECT,
- details.representation(), Representation::Tagged(), field_type,
- HeapType::Any());
+ if (FLAG_trace_generalization) {
+ HeapType* field_type =
+ (details.type() == DATA)
+ ? map->instance_descriptors()->GetFieldType(modify_index)
+ : NULL;
+ map->PrintGeneralization(
+ stdout, reason, modify_index, new_map->NumberOfOwnDescriptors(),
+ new_map->NumberOfOwnDescriptors(),
+ details.type() == DATA_CONSTANT && store_mode == FORCE_FIELD,
+ details.representation(), Representation::Tagged(), field_type,
+ HeapType::Any());
+ }
}
return new_map;
}
-// static
-Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map,
- int modify_index,
- StoreMode store_mode,
- const char* reason) {
- PropertyDetails details =
- map->instance_descriptors()->GetDetails(modify_index);
- return CopyGeneralizeAllRepresentations(map, modify_index, store_mode,
- details.attributes(), reason);
-}
-
-
void Map::DeprecateTransitionTree() {
if (is_deprecated()) return;
- if (HasTransitionArray()) {
- TransitionArray* transitions = this->transitions();
- for (int i = 0; i < transitions->number_of_transitions(); i++) {
- transitions->GetTarget(i)->DeprecateTransitionTree();
- }
+ Object* transitions = raw_transitions();
+ int num_transitions = TransitionArray::NumberOfTransitions(transitions);
+ for (int i = 0; i < num_transitions; ++i) {
+ TransitionArray::GetTarget(transitions, i)->DeprecateTransitionTree();
}
deprecate();
dependent_code()->DeoptimizeDependentCodeGroup(
@@ -2356,40 +3123,33 @@
}
-// Invalidates a transition target at |key|, and installs |new_descriptors| over
-// the current instance_descriptors to ensure proper sharing of descriptor
-// arrays.
-// Returns true if the transition target at given key was deprecated.
-bool Map::DeprecateTarget(PropertyKind kind, Name* key,
- PropertyAttributes attributes,
- DescriptorArray* new_descriptors,
- LayoutDescriptor* new_layout_descriptor) {
- bool transition_target_deprecated = false;
- if (HasTransitionArray()) {
- TransitionArray* transitions = this->transitions();
- int transition = transitions->Search(kind, key, attributes);
- if (transition != TransitionArray::kNotFound) {
- transitions->GetTarget(transition)->DeprecateTransitionTree();
- transition_target_deprecated = true;
- }
- }
+static inline bool EqualImmutableValues(Object* obj1, Object* obj2) {
+ if (obj1 == obj2) return true; // Valid for both kData and kAccessor kinds.
+ // TODO(ishell): compare AccessorPairs.
+ return false;
+}
- // Don't overwrite the empty descriptor array.
- if (NumberOfOwnDescriptors() == 0) return transition_target_deprecated;
+
+// Installs |new_descriptors| over the current instance_descriptors to ensure
+// proper sharing of descriptor arrays.
+void Map::ReplaceDescriptors(DescriptorArray* new_descriptors,
+ LayoutDescriptor* new_layout_descriptor) {
+ // Don't overwrite the empty descriptor array or initial map's descriptors.
+ if (NumberOfOwnDescriptors() == 0 || GetBackPointer()->IsUndefined()) {
+ return;
+ }
DescriptorArray* to_replace = instance_descriptors();
- Map* current = this;
GetHeap()->incremental_marking()->RecordWrites(to_replace);
+ Map* current = this;
while (current->instance_descriptors() == to_replace) {
+ Object* next = current->GetBackPointer();
+ if (next->IsUndefined()) break; // Stop overwriting at initial map.
current->SetEnumLength(kInvalidEnumCacheSentinel);
current->UpdateDescriptors(new_descriptors, new_layout_descriptor);
- Object* next = current->GetBackPointer();
- if (next->IsUndefined()) break;
current = Map::cast(next);
}
-
set_owns_descriptors(false);
- return transition_target_deprecated;
}
@@ -2397,7 +3157,14 @@
Map* result = this;
while (true) {
Object* back = result->GetBackPointer();
- if (back->IsUndefined()) return result;
+ if (back->IsUndefined()) {
+ // Initial map always owns descriptors and doesn't have unused entries
+ // in the descriptor array.
+ DCHECK(result->owns_descriptors());
+ DCHECK_EQ(result->NumberOfOwnDescriptors(),
+ result->instance_descriptors()->number_of_descriptors());
+ return result;
+ }
result = Map::cast(back);
}
}
@@ -2409,33 +3176,35 @@
DisallowHeapAllocation no_allocation;
// This can only be called on roots of transition trees.
- DCHECK(GetBackPointer()->IsUndefined());
+ DCHECK_EQ(verbatim, NumberOfOwnDescriptors());
Map* current = this;
for (int i = verbatim; i < length; i++) {
- if (!current->HasTransitionArray()) break;
Name* name = descriptors->GetKey(i);
PropertyDetails details = descriptors->GetDetails(i);
- TransitionArray* transitions = current->transitions();
- int transition =
- transitions->Search(details.kind(), name, details.attributes());
- if (transition == TransitionArray::kNotFound) break;
-
- Map* next = transitions->GetTarget(transition);
+ Map* next = TransitionArray::SearchTransition(current, details.kind(), name,
+ details.attributes());
+ if (next == NULL) break;
DescriptorArray* next_descriptors = next->instance_descriptors();
PropertyDetails next_details = next_descriptors->GetDetails(i);
- if (details.type() != next_details.type()) break;
- if (details.attributes() != next_details.attributes()) break;
+ DCHECK_EQ(details.kind(), next_details.kind());
+ DCHECK_EQ(details.attributes(), next_details.attributes());
+ if (details.location() != next_details.location()) break;
if (!details.representation().Equals(next_details.representation())) break;
- if (next_details.type() == FIELD) {
- if (!descriptors->GetFieldType(i)->NowIs(
- next_descriptors->GetFieldType(i))) break;
- } else {
- if (descriptors->GetValue(i) != next_descriptors->GetValue(i)) break;
- }
+ if (next_details.location() == kField) {
+ HeapType* next_field_type = next_descriptors->GetFieldType(i);
+ if (!descriptors->GetFieldType(i)->NowIs(next_field_type)) {
+ break;
+ }
+ } else {
+ if (!EqualImmutableValues(descriptors->GetValue(i),
+ next_descriptors->GetValue(i))) {
+ break;
+ }
+ }
current = next;
}
return current;
@@ -2444,7 +3213,7 @@
Map* Map::FindFieldOwner(int descriptor) {
DisallowHeapAllocation no_allocation;
- DCHECK_EQ(FIELD, instance_descriptors()->GetDetails(descriptor).type());
+ DCHECK_EQ(DATA, instance_descriptors()->GetDetails(descriptor).type());
Map* result = this;
while (true) {
Object* back = result->GetBackPointer();
@@ -2459,45 +3228,49 @@
void Map::UpdateFieldType(int descriptor, Handle<Name> name,
Representation new_representation,
- Handle<HeapType> new_type) {
+ Handle<Object> new_wrapped_type) {
+ DCHECK(new_wrapped_type->IsSmi() || new_wrapped_type->IsWeakCell());
DisallowHeapAllocation no_allocation;
PropertyDetails details = instance_descriptors()->GetDetails(descriptor);
- if (details.type() != FIELD) return;
- if (HasTransitionArray()) {
- TransitionArray* transitions = this->transitions();
- for (int i = 0; i < transitions->number_of_transitions(); ++i) {
- transitions->GetTarget(i)
- ->UpdateFieldType(descriptor, name, new_representation, new_type);
- }
+ if (details.type() != DATA) return;
+ Object* transitions = raw_transitions();
+ int num_transitions = TransitionArray::NumberOfTransitions(transitions);
+ for (int i = 0; i < num_transitions; ++i) {
+ Map* target = TransitionArray::GetTarget(transitions, i);
+ target->UpdateFieldType(descriptor, name, new_representation,
+ new_wrapped_type);
}
// It is allowed to change representation here only from None to something.
DCHECK(details.representation().Equals(new_representation) ||
details.representation().IsNone());
// Skip if already updated the shared descriptor.
- if (instance_descriptors()->GetFieldType(descriptor) == *new_type) return;
- FieldDescriptor d(name, instance_descriptors()->GetFieldIndex(descriptor),
- new_type, details.attributes(), new_representation);
+ if (instance_descriptors()->GetValue(descriptor) == *new_wrapped_type) return;
+ DataDescriptor d(name, instance_descriptors()->GetFieldIndex(descriptor),
+ new_wrapped_type, details.attributes(), new_representation);
instance_descriptors()->Replace(descriptor, &d);
}
+bool FieldTypeIsCleared(Representation rep, HeapType* type) {
+ return type->Is(HeapType::None()) && rep.IsHeapObject();
+}
+
+
// static
-Handle<HeapType> Map::GeneralizeFieldType(Handle<HeapType> type1,
+Handle<HeapType> Map::GeneralizeFieldType(Representation rep1,
+ Handle<HeapType> type1,
+ Representation rep2,
Handle<HeapType> type2,
Isolate* isolate) {
- static const int kMaxClassesPerFieldType = 5;
+ // Cleared field types need special treatment. They represent lost knowledge,
+ // so we must be conservative, so their generalization with any other type
+ // is "Any".
+ if (FieldTypeIsCleared(rep1, *type1) || FieldTypeIsCleared(rep2, *type2)) {
+ return HeapType::Any(isolate);
+ }
if (type1->NowIs(type2)) return type2;
if (type2->NowIs(type1)) return type1;
- if (type1->NowStable() && type2->NowStable()) {
- Handle<HeapType> type = HeapType::Union(type1, type2, isolate);
- if (type->NumClasses() <= kMaxClassesPerFieldType) {
- DCHECK(type->NowStable());
- DCHECK(type1->NowIs(type));
- DCHECK(type2->NowIs(type));
- return type;
- }
- }
return HeapType::Any(isolate);
}
@@ -2516,10 +3289,13 @@
isolate);
if (old_representation.Equals(new_representation) &&
+ !FieldTypeIsCleared(new_representation, *new_field_type) &&
+ // Checking old_field_type for being cleared is not necessary because
+ // the NowIs check below would fail anyway in that case.
new_field_type->NowIs(old_field_type)) {
- DCHECK(Map::GeneralizeFieldType(old_field_type,
- new_field_type,
- isolate)->NowIs(old_field_type));
+ DCHECK(Map::GeneralizeFieldType(old_representation, old_field_type,
+ new_representation, new_field_type, isolate)
+ ->NowIs(old_field_type));
return;
}
@@ -2529,14 +3305,16 @@
field_owner->instance_descriptors(), isolate);
DCHECK_EQ(*old_field_type, descriptors->GetFieldType(modify_index));
- // Determine the generalized new field type.
- new_field_type = Map::GeneralizeFieldType(
- old_field_type, new_field_type, isolate);
+ new_field_type =
+ Map::GeneralizeFieldType(old_representation, old_field_type,
+ new_representation, new_field_type, isolate);
PropertyDetails details = descriptors->GetDetails(modify_index);
Handle<Name> name(descriptors->GetKey(modify_index));
+
+ Handle<Object> wrapped_type(WrapType(new_field_type));
field_owner->UpdateFieldType(modify_index, name, new_representation,
- new_field_type);
+ wrapped_type);
field_owner->dependent_code()->DeoptimizeDependentCodeGroup(
isolate, DependentCode::kFieldTypeGroup);
@@ -2551,16 +3329,42 @@
}
-// Generalize the representation of the descriptor at |modify_index|.
-// This method rewrites the transition tree to reflect the new change. To avoid
-// high degrees over polymorphism, and to stabilize quickly, on every rewrite
-// the new type is deduced by merging the current type with any potential new
-// (partial) version of the type in the transition tree.
+static inline Handle<HeapType> GetFieldType(Isolate* isolate,
+ Handle<DescriptorArray> descriptors,
+ int descriptor,
+ PropertyLocation location,
+ Representation representation) {
+#ifdef DEBUG
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+ DCHECK_EQ(kData, details.kind());
+ DCHECK_EQ(details.location(), location);
+#endif
+ if (location == kField) {
+ return handle(descriptors->GetFieldType(descriptor), isolate);
+ } else {
+ return descriptors->GetValue(descriptor)
+ ->OptimalType(isolate, representation);
+ }
+}
+
+
+// Reconfigures property at |modify_index| with |new_kind|, |new_attributes|,
+// |store_mode| and/or |new_representation|/|new_field_type|.
+// If |modify_index| is negative then no properties are reconfigured but the
+// map is migrated to the up-to-date non-deprecated state.
+//
+// This method rewrites or completes the transition tree to reflect the new
+// change. To avoid high degrees over polymorphism, and to stabilize quickly,
+// on every rewrite the new type is deduced by merging the current type with
+// any potential new (partial) version of the type in the transition tree.
// To do this, on each rewrite:
// - Search the root of the transition tree using FindRootMap.
-// - Find |target_map|, the newest matching version of this map using the keys
-// in the |old_map|'s descriptor array to walk the transition tree.
-// - Merge/generalize the descriptor array of the |old_map| and |target_map|.
+// - Find |target_map|, the newest matching version of this map using the
+// virtually "enhanced" |old_map|'s descriptor array (i.e. whose entry at
+// |modify_index| is considered to be of |new_kind| and having
+// |new_attributes|) to walk the transition tree.
+// - Merge/generalize the "enhanced" descriptor array of the |old_map| and
+// descriptor array of the |target_map|.
// - Generalize the |modify_index| descriptor using |new_representation| and
// |new_field_type|.
// - Walk the tree again starting from the root towards |target_map|. Stop at
@@ -2570,111 +3374,182 @@
// Return it.
// - Otherwise, invalidate the outdated transition target from |target_map|, and
// replace its transition tree with a new branch for the updated descriptors.
-Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
- int modify_index,
- Representation new_representation,
- Handle<HeapType> new_field_type,
- StoreMode store_mode) {
+Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index,
+ PropertyKind new_kind,
+ PropertyAttributes new_attributes,
+ Representation new_representation,
+ Handle<HeapType> new_field_type,
+ StoreMode store_mode) {
+ DCHECK_NE(kAccessor, new_kind); // TODO(ishell): not supported yet.
+ DCHECK(store_mode != FORCE_FIELD || modify_index >= 0);
Isolate* isolate = old_map->GetIsolate();
Handle<DescriptorArray> old_descriptors(
old_map->instance_descriptors(), isolate);
int old_nof = old_map->NumberOfOwnDescriptors();
- PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
- Representation old_representation = old_details.representation();
- // It's fine to transition from None to anything but double without any
- // modification to the object, because the default uninitialized value for
- // representation None can be overwritten by both smi and tagged values.
- // Doubles, however, would require a box allocation.
- if (old_representation.IsNone() && !new_representation.IsNone() &&
+ // If it's just a representation generalization case (i.e. property kind and
+ // attributes stays unchanged) it's fine to transition from None to anything
+ // but double without any modification to the object, because the default
+ // uninitialized value for representation None can be overwritten by both
+ // smi and tagged values. Doubles, however, would require a box allocation.
+ if (modify_index >= 0 && !new_representation.IsNone() &&
!new_representation.IsDouble()) {
- DCHECK(old_details.type() == FIELD);
- if (FLAG_trace_generalization) {
- old_map->PrintGeneralization(
- stdout, "uninitialized field",
- modify_index, old_map->NumberOfOwnDescriptors(),
- old_map->NumberOfOwnDescriptors(), false,
- old_representation, new_representation,
- old_descriptors->GetFieldType(modify_index), *new_field_type);
- }
- Handle<Map> field_owner(old_map->FindFieldOwner(modify_index), isolate);
+ PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
+ Representation old_representation = old_details.representation();
- GeneralizeFieldType(field_owner, modify_index, new_representation,
- new_field_type);
- DCHECK(old_descriptors->GetDetails(modify_index).representation().Equals(
- new_representation));
- DCHECK(old_descriptors->GetFieldType(modify_index)->NowIs(new_field_type));
- return old_map;
+ if (old_representation.IsNone()) {
+ DCHECK_EQ(new_kind, old_details.kind());
+ DCHECK_EQ(new_attributes, old_details.attributes());
+ DCHECK_EQ(DATA, old_details.type());
+ if (FLAG_trace_generalization) {
+ old_map->PrintGeneralization(
+ stdout, "uninitialized field", modify_index,
+ old_map->NumberOfOwnDescriptors(),
+ old_map->NumberOfOwnDescriptors(), false, old_representation,
+ new_representation, old_descriptors->GetFieldType(modify_index),
+ *new_field_type);
+ }
+ Handle<Map> field_owner(old_map->FindFieldOwner(modify_index), isolate);
+
+ GeneralizeFieldType(field_owner, modify_index, new_representation,
+ new_field_type);
+ DCHECK(old_descriptors->GetDetails(modify_index)
+ .representation()
+ .Equals(new_representation));
+ DCHECK(
+ old_descriptors->GetFieldType(modify_index)->NowIs(new_field_type));
+ return old_map;
+ }
}
// Check the state of the root map.
Handle<Map> root_map(old_map->FindRootMap(), isolate);
if (!old_map->EquivalentToForTransition(*root_map)) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
+ new_kind, new_attributes,
"GenAll_NotEquivalent");
}
+
+ ElementsKind from_kind = root_map->elements_kind();
+ ElementsKind to_kind = old_map->elements_kind();
+ // TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
+ if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
+ to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
+ !(IsTransitionableFastElementsKind(from_kind) &&
+ IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
+ return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
+ new_kind, new_attributes,
+ "GenAll_InvalidElementsTransition");
+ }
int root_nof = root_map->NumberOfOwnDescriptors();
- if (modify_index < root_nof) {
+ if (modify_index >= 0 && modify_index < root_nof) {
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
- if ((old_details.type() != FIELD && store_mode == FORCE_IN_OBJECT) ||
- (old_details.type() == FIELD &&
+ if (old_details.kind() != new_kind ||
+ old_details.attributes() != new_attributes) {
+ return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
+ new_kind, new_attributes,
+ "GenAll_RootModification1");
+ }
+ if ((old_details.type() != DATA && store_mode == FORCE_FIELD) ||
+ (old_details.type() == DATA &&
(!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) ||
!new_representation.fits_into(old_details.representation())))) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
- "GenAll_RootModification");
+ new_kind, new_attributes,
+ "GenAll_RootModification2");
}
}
+ // From here on, use the map with correct elements kind as root map.
+ if (from_kind != to_kind) {
+ root_map = Map::AsElementsKind(root_map, to_kind);
+ }
+
Handle<Map> target_map = root_map;
for (int i = root_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
- int j = target_map->SearchTransition(old_details.kind(),
- old_descriptors->GetKey(i),
- old_details.attributes());
- if (j == TransitionArray::kNotFound) break;
- Handle<Map> tmp_map(target_map->GetTransition(j), isolate);
+ PropertyKind next_kind;
+ PropertyLocation next_location;
+ PropertyAttributes next_attributes;
+ Representation next_representation;
+ bool property_kind_reconfiguration = false;
+
+ if (modify_index == i) {
+ DCHECK_EQ(FORCE_FIELD, store_mode);
+ property_kind_reconfiguration = old_details.kind() != new_kind;
+
+ next_kind = new_kind;
+ next_location = kField;
+ next_attributes = new_attributes;
+ // If property kind is not reconfigured merge the result with
+ // representation/field type from the old descriptor.
+ next_representation = new_representation;
+ if (!property_kind_reconfiguration) {
+ next_representation =
+ next_representation.generalize(old_details.representation());
+ }
+
+ } else {
+ next_kind = old_details.kind();
+ next_location = old_details.location();
+ next_attributes = old_details.attributes();
+ next_representation = old_details.representation();
+ }
+ Map* transition = TransitionArray::SearchTransition(
+ *target_map, next_kind, old_descriptors->GetKey(i), next_attributes);
+ if (transition == NULL) break;
+ Handle<Map> tmp_map(transition, isolate);
+
Handle<DescriptorArray> tmp_descriptors = handle(
tmp_map->instance_descriptors(), isolate);
// Check if target map is incompatible.
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
- PropertyType old_type = old_details.type();
- PropertyType tmp_type = tmp_details.type();
- DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
- if ((tmp_type == CALLBACKS || old_type == CALLBACKS) &&
- (tmp_type != old_type ||
- tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) {
+ DCHECK_EQ(next_kind, tmp_details.kind());
+ DCHECK_EQ(next_attributes, tmp_details.attributes());
+ if (next_kind == kAccessor &&
+ !EqualImmutableValues(old_descriptors->GetValue(i),
+ tmp_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
+ new_kind, new_attributes,
"GenAll_Incompatible");
}
- Representation old_representation = old_details.representation();
+ if (next_location == kField && tmp_details.location() == kDescriptor) break;
+
Representation tmp_representation = tmp_details.representation();
- if (!old_representation.fits_into(tmp_representation) ||
- (!new_representation.fits_into(tmp_representation) &&
- modify_index == i)) {
+ if (!next_representation.fits_into(tmp_representation)) break;
+
+ PropertyLocation old_location = old_details.location();
+ PropertyLocation tmp_location = tmp_details.location();
+ if (tmp_location == kField) {
+ if (next_kind == kData) {
+ Handle<HeapType> next_field_type;
+ if (modify_index == i) {
+ next_field_type = new_field_type;
+ if (!property_kind_reconfiguration) {
+ Handle<HeapType> old_field_type =
+ GetFieldType(isolate, old_descriptors, i,
+ old_details.location(), tmp_representation);
+ Representation old_representation = old_details.representation();
+ next_field_type = GeneralizeFieldType(
+ old_representation, old_field_type, new_representation,
+ next_field_type, isolate);
+ }
+ } else {
+ Handle<HeapType> old_field_type =
+ GetFieldType(isolate, old_descriptors, i, old_details.location(),
+ tmp_representation);
+ next_field_type = old_field_type;
+ }
+ GeneralizeFieldType(tmp_map, i, tmp_representation, next_field_type);
+ }
+ } else if (old_location == kField ||
+ !EqualImmutableValues(old_descriptors->GetValue(i),
+ tmp_descriptors->GetValue(i))) {
break;
}
- if (tmp_type == FIELD) {
- // Generalize the field type as necessary.
- Handle<HeapType> old_field_type = (old_type == FIELD)
- ? handle(old_descriptors->GetFieldType(i), isolate)
- : old_descriptors->GetValue(i)->OptimalType(
- isolate, tmp_representation);
- if (modify_index == i) {
- old_field_type = GeneralizeFieldType(
- new_field_type, old_field_type, isolate);
- }
- GeneralizeFieldType(tmp_map, i, tmp_representation, old_field_type);
- } else if (tmp_type == CONSTANT) {
- if (old_type != CONSTANT ||
- old_descriptors->GetConstant(i) != tmp_descriptors->GetConstant(i)) {
- break;
- }
- } else {
- DCHECK_EQ(tmp_type, old_type);
- DCHECK_EQ(tmp_descriptors->GetValue(i), old_descriptors->GetValue(i));
- }
+ DCHECK(!tmp_map->is_deprecated());
target_map = tmp_map;
}
@@ -2683,37 +3558,59 @@
target_map->instance_descriptors(), isolate);
int target_nof = target_map->NumberOfOwnDescriptors();
if (target_nof == old_nof &&
- (store_mode != FORCE_IN_OBJECT ||
- target_descriptors->GetDetails(modify_index).type() == FIELD)) {
- DCHECK(modify_index < target_nof);
- DCHECK(new_representation.fits_into(
- target_descriptors->GetDetails(modify_index).representation()));
- DCHECK(target_descriptors->GetDetails(modify_index).type() != FIELD ||
- new_field_type->NowIs(
- target_descriptors->GetFieldType(modify_index)));
+ (store_mode != FORCE_FIELD ||
+ (modify_index >= 0 &&
+ target_descriptors->GetDetails(modify_index).location() == kField))) {
+#ifdef DEBUG
+ if (modify_index >= 0) {
+ PropertyDetails details = target_descriptors->GetDetails(modify_index);
+ DCHECK_EQ(new_kind, details.kind());
+ DCHECK_EQ(new_attributes, details.attributes());
+ DCHECK(new_representation.fits_into(details.representation()));
+ DCHECK(details.location() != kField ||
+ new_field_type->NowIs(
+ target_descriptors->GetFieldType(modify_index)));
+ }
+#endif
+ if (*target_map != *old_map) {
+ old_map->NotifyLeafMapLayoutChange();
+ }
return target_map;
}
// Find the last compatible target map in the transition tree.
for (int i = target_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
- int j = target_map->SearchTransition(old_details.kind(),
- old_descriptors->GetKey(i),
- old_details.attributes());
- if (j == TransitionArray::kNotFound) break;
- Handle<Map> tmp_map(target_map->GetTransition(j), isolate);
+ PropertyKind next_kind;
+ PropertyAttributes next_attributes;
+ if (modify_index == i) {
+ next_kind = new_kind;
+ next_attributes = new_attributes;
+ } else {
+ next_kind = old_details.kind();
+ next_attributes = old_details.attributes();
+ }
+ Map* transition = TransitionArray::SearchTransition(
+ *target_map, next_kind, old_descriptors->GetKey(i), next_attributes);
+ if (transition == NULL) break;
+ Handle<Map> tmp_map(transition, isolate);
Handle<DescriptorArray> tmp_descriptors(
tmp_map->instance_descriptors(), isolate);
// Check if target map is compatible.
+#ifdef DEBUG
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
- DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
- if ((tmp_details.type() == CALLBACKS || old_details.type() == CALLBACKS) &&
- (tmp_details.type() != old_details.type() ||
- tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) {
+ DCHECK_EQ(next_kind, tmp_details.kind());
+ DCHECK_EQ(next_attributes, tmp_details.attributes());
+#endif
+ if (next_kind == kAccessor &&
+ !EqualImmutableValues(old_descriptors->GetValue(i),
+ tmp_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
+ new_kind, new_attributes,
"GenAll_Incompatible");
}
+ DCHECK(!tmp_map->is_deprecated());
target_map = tmp_map;
}
target_nof = target_map->NumberOfOwnDescriptors();
@@ -2736,7 +3633,7 @@
int current_offset = 0;
for (int i = 0; i < root_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
- if (old_details.type() == FIELD) {
+ if (old_details.location() == kField) {
current_offset += old_details.field_width_in_words();
}
Descriptor d(handle(old_descriptors->GetKey(i), isolate),
@@ -2750,41 +3647,89 @@
Handle<Name> target_key(target_descriptors->GetKey(i), isolate);
PropertyDetails old_details = old_descriptors->GetDetails(i);
PropertyDetails target_details = target_descriptors->GetDetails(i);
- target_details = target_details.CopyWithRepresentation(
- old_details.representation().generalize(
- target_details.representation()));
+
+ PropertyKind next_kind;
+ PropertyAttributes next_attributes;
+ PropertyLocation next_location;
+ Representation next_representation;
+ bool property_kind_reconfiguration = false;
+
if (modify_index == i) {
- target_details = target_details.CopyWithRepresentation(
- new_representation.generalize(target_details.representation()));
- }
- DCHECK_EQ(old_details.attributes(), target_details.attributes());
- if (old_details.type() == FIELD || target_details.type() == FIELD ||
- (modify_index == i && store_mode == FORCE_IN_OBJECT) ||
- (target_descriptors->GetValue(i) != old_descriptors->GetValue(i))) {
- Handle<HeapType> old_field_type = (old_details.type() == FIELD)
- ? handle(old_descriptors->GetFieldType(i), isolate)
- : old_descriptors->GetValue(i)->OptimalType(
- isolate, target_details.representation());
- Handle<HeapType> target_field_type = (target_details.type() == FIELD)
- ? handle(target_descriptors->GetFieldType(i), isolate)
- : target_descriptors->GetValue(i)->OptimalType(
- isolate, target_details.representation());
- target_field_type = GeneralizeFieldType(
- target_field_type, old_field_type, isolate);
- if (modify_index == i) {
- target_field_type = GeneralizeFieldType(
- target_field_type, new_field_type, isolate);
+ DCHECK_EQ(FORCE_FIELD, store_mode);
+ property_kind_reconfiguration = old_details.kind() != new_kind;
+
+ next_kind = new_kind;
+ next_attributes = new_attributes;
+ next_location = kField;
+
+ // Merge new representation/field type with ones from the target
+ // descriptor. If property kind is not reconfigured merge the result with
+ // representation/field type from the old descriptor.
+ next_representation =
+ new_representation.generalize(target_details.representation());
+ if (!property_kind_reconfiguration) {
+ next_representation =
+ next_representation.generalize(old_details.representation());
}
- FieldDescriptor d(target_key, current_offset, target_field_type,
- target_details.attributes(),
- target_details.representation());
- current_offset += d.GetDetails().field_width_in_words();
- new_descriptors->Set(i, &d);
} else {
- DCHECK_NE(FIELD, target_details.type());
- Descriptor d(target_key,
- handle(target_descriptors->GetValue(i), isolate),
- target_details);
+ // Merge old_descriptor and target_descriptor entries.
+ DCHECK_EQ(target_details.kind(), old_details.kind());
+ next_kind = target_details.kind();
+ next_attributes = target_details.attributes();
+ next_location =
+ old_details.location() == kField ||
+ target_details.location() == kField ||
+ !EqualImmutableValues(target_descriptors->GetValue(i),
+ old_descriptors->GetValue(i))
+ ? kField
+ : kDescriptor;
+
+ next_representation = old_details.representation().generalize(
+ target_details.representation());
+ }
+ DCHECK_EQ(next_kind, target_details.kind());
+ DCHECK_EQ(next_attributes, target_details.attributes());
+
+ if (next_location == kField) {
+ if (next_kind == kData) {
+ Handle<HeapType> target_field_type =
+ GetFieldType(isolate, target_descriptors, i,
+ target_details.location(), next_representation);
+
+ Handle<HeapType> next_field_type;
+ if (modify_index == i) {
+ next_field_type = GeneralizeFieldType(
+ target_details.representation(), target_field_type,
+ new_representation, new_field_type, isolate);
+ if (!property_kind_reconfiguration) {
+ Handle<HeapType> old_field_type =
+ GetFieldType(isolate, old_descriptors, i,
+ old_details.location(), next_representation);
+ next_field_type = GeneralizeFieldType(
+ old_details.representation(), old_field_type,
+ next_representation, next_field_type, isolate);
+ }
+ } else {
+ Handle<HeapType> old_field_type =
+ GetFieldType(isolate, old_descriptors, i, old_details.location(),
+ next_representation);
+ next_field_type = GeneralizeFieldType(
+ old_details.representation(), old_field_type, next_representation,
+ target_field_type, isolate);
+ }
+ Handle<Object> wrapped_type(WrapType(next_field_type));
+ DataDescriptor d(target_key, current_offset, wrapped_type,
+ next_attributes, next_representation);
+ current_offset += d.GetDetails().field_width_in_words();
+ new_descriptors->Set(i, &d);
+ } else {
+ UNIMPLEMENTED(); // TODO(ishell): implement.
+ }
+ } else {
+ PropertyDetails details(next_attributes, next_kind, next_location,
+ next_representation);
+ Descriptor d(target_key, handle(target_descriptors->GetValue(i), isolate),
+ details);
new_descriptors->Set(i, &d);
}
}
@@ -2793,106 +3738,160 @@
for (int i = target_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
Handle<Name> old_key(old_descriptors->GetKey(i), isolate);
+
+ // Merge old_descriptor entry and modified details together.
+ PropertyKind next_kind;
+ PropertyAttributes next_attributes;
+ PropertyLocation next_location;
+ Representation next_representation;
+ bool property_kind_reconfiguration = false;
+
if (modify_index == i) {
- old_details = old_details.CopyWithRepresentation(
- new_representation.generalize(old_details.representation()));
- }
- if (old_details.type() == FIELD) {
- Handle<HeapType> old_field_type(
- old_descriptors->GetFieldType(i), isolate);
- if (modify_index == i) {
- old_field_type = GeneralizeFieldType(
- old_field_type, new_field_type, isolate);
+ DCHECK_EQ(FORCE_FIELD, store_mode);
+ // In case of property kind reconfiguration it is not necessary to
+ // take into account representation/field type of the old descriptor.
+ property_kind_reconfiguration = old_details.kind() != new_kind;
+
+ next_kind = new_kind;
+ next_attributes = new_attributes;
+ next_location = kField;
+ next_representation = new_representation;
+ if (!property_kind_reconfiguration) {
+ next_representation =
+ next_representation.generalize(old_details.representation());
}
- FieldDescriptor d(old_key, current_offset, old_field_type,
- old_details.attributes(), old_details.representation());
- current_offset += d.GetDetails().field_width_in_words();
- new_descriptors->Set(i, &d);
} else {
- DCHECK(old_details.type() == CONSTANT || old_details.type() == CALLBACKS);
- if (modify_index == i && store_mode == FORCE_IN_OBJECT) {
- FieldDescriptor d(
- old_key, current_offset,
- GeneralizeFieldType(old_descriptors->GetValue(i)->OptimalType(
- isolate, old_details.representation()),
- new_field_type, isolate),
- old_details.attributes(), old_details.representation());
+ next_kind = old_details.kind();
+ next_attributes = old_details.attributes();
+ next_location = old_details.location();
+ next_representation = old_details.representation();
+ }
+
+ if (next_location == kField) {
+ if (next_kind == kData) {
+ Handle<HeapType> next_field_type;
+ if (modify_index == i) {
+ next_field_type = new_field_type;
+ if (!property_kind_reconfiguration) {
+ Handle<HeapType> old_field_type =
+ GetFieldType(isolate, old_descriptors, i,
+ old_details.location(), next_representation);
+ next_field_type = GeneralizeFieldType(
+ old_details.representation(), old_field_type,
+ next_representation, next_field_type, isolate);
+ }
+ } else {
+ Handle<HeapType> old_field_type =
+ GetFieldType(isolate, old_descriptors, i, old_details.location(),
+ next_representation);
+ next_field_type = old_field_type;
+ }
+
+ Handle<Object> wrapped_type(WrapType(next_field_type));
+
+ DataDescriptor d(old_key, current_offset, wrapped_type, next_attributes,
+ next_representation);
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
- DCHECK_NE(FIELD, old_details.type());
- Descriptor d(old_key,
- handle(old_descriptors->GetValue(i), isolate),
- old_details);
- new_descriptors->Set(i, &d);
+ UNIMPLEMENTED(); // TODO(ishell): implement.
}
+ } else {
+ PropertyDetails details(next_attributes, next_kind, next_location,
+ next_representation);
+ Descriptor d(old_key, handle(old_descriptors->GetValue(i), isolate),
+ details);
+ new_descriptors->Set(i, &d);
}
}
new_descriptors->Sort();
- DCHECK(store_mode != FORCE_IN_OBJECT ||
- new_descriptors->GetDetails(modify_index).type() == FIELD);
+ DCHECK(store_mode != FORCE_FIELD ||
+ new_descriptors->GetDetails(modify_index).location() == kField);
Handle<Map> split_map(root_map->FindLastMatchMap(
root_nof, old_nof, *new_descriptors), isolate);
int split_nof = split_map->NumberOfOwnDescriptors();
DCHECK_NE(old_nof, split_nof);
- Handle<LayoutDescriptor> new_layout_descriptor =
- LayoutDescriptor::New(split_map, new_descriptors, old_nof);
- PropertyDetails split_prop_details = old_descriptors->GetDetails(split_nof);
- bool transition_target_deprecated = split_map->DeprecateTarget(
- split_prop_details.kind(), old_descriptors->GetKey(split_nof),
- split_prop_details.attributes(), *new_descriptors,
- *new_layout_descriptor);
+ PropertyKind split_kind;
+ PropertyAttributes split_attributes;
+ if (modify_index == split_nof) {
+ split_kind = new_kind;
+ split_attributes = new_attributes;
+ } else {
+ PropertyDetails split_prop_details = old_descriptors->GetDetails(split_nof);
+ split_kind = split_prop_details.kind();
+ split_attributes = split_prop_details.attributes();
+ }
- // If |transition_target_deprecated| is true then the transition array
- // already contains entry for given descriptor. This means that the transition
+ // Invalidate a transition target at |key|.
+ Map* maybe_transition = TransitionArray::SearchTransition(
+ *split_map, split_kind, old_descriptors->GetKey(split_nof),
+ split_attributes);
+ if (maybe_transition != NULL) {
+ maybe_transition->DeprecateTransitionTree();
+ }
+
+ // If |maybe_transition| is not NULL then the transition array already
+ // contains entry for given descriptor. This means that the transition
// could be inserted regardless of whether transitions array is full or not.
- if (!transition_target_deprecated && !split_map->CanHaveMoreTransitions()) {
+ if (maybe_transition == NULL &&
+ !TransitionArray::CanHaveMoreTransitions(split_map)) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
+ new_kind, new_attributes,
"GenAll_CantHaveMoreTransitions");
}
- if (FLAG_trace_generalization) {
+ old_map->NotifyLeafMapLayoutChange();
+
+ if (FLAG_trace_generalization && modify_index >= 0) {
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
PropertyDetails new_details = new_descriptors->GetDetails(modify_index);
- Handle<HeapType> old_field_type = (old_details.type() == FIELD)
- ? handle(old_descriptors->GetFieldType(modify_index), isolate)
- : HeapType::Constant(handle(old_descriptors->GetValue(modify_index),
- isolate), isolate);
- Handle<HeapType> new_field_type = (new_details.type() == FIELD)
- ? handle(new_descriptors->GetFieldType(modify_index), isolate)
- : HeapType::Constant(handle(new_descriptors->GetValue(modify_index),
- isolate), isolate);
+ Handle<HeapType> old_field_type =
+ (old_details.type() == DATA)
+ ? handle(old_descriptors->GetFieldType(modify_index), isolate)
+ : HeapType::Constant(
+ handle(old_descriptors->GetValue(modify_index), isolate),
+ isolate);
+ Handle<HeapType> new_field_type =
+ (new_details.type() == DATA)
+ ? handle(new_descriptors->GetFieldType(modify_index), isolate)
+ : HeapType::Constant(
+ handle(new_descriptors->GetValue(modify_index), isolate),
+ isolate);
old_map->PrintGeneralization(
stdout, "", modify_index, split_nof, old_nof,
- old_details.type() == CONSTANT && store_mode == FORCE_IN_OBJECT,
+ old_details.location() == kDescriptor && store_mode == FORCE_FIELD,
old_details.representation(), new_details.representation(),
*old_field_type, *new_field_type);
}
- // Add missing transitions.
- Handle<Map> new_map = split_map;
- for (int i = split_nof; i < old_nof; ++i) {
- new_map = CopyInstallDescriptors(new_map, i, new_descriptors,
- new_layout_descriptor);
- }
- new_map->set_owns_descriptors(true);
+ Handle<LayoutDescriptor> new_layout_descriptor =
+ LayoutDescriptor::New(split_map, new_descriptors, old_nof);
+
+ Handle<Map> new_map =
+ AddMissingTransitions(split_map, new_descriptors, new_layout_descriptor);
+
+ // Deprecated part of the transition tree is no longer reachable, so replace
+ // current instance descriptors in the "survived" part of the tree with
+ // the new descriptors to maintain descriptors sharing invariant.
+ split_map->ReplaceDescriptors(*new_descriptors, *new_layout_descriptor);
return new_map;
}
-// Generalize the representation of all FIELD descriptors.
+// Generalize the representation of all DATA descriptors.
Handle<Map> Map::GeneralizeAllFieldRepresentations(
Handle<Map> map) {
Handle<DescriptorArray> descriptors(map->instance_descriptors());
for (int i = 0; i < map->NumberOfOwnDescriptors(); ++i) {
- if (descriptors->GetDetails(i).type() == FIELD) {
- map = GeneralizeRepresentation(map, i, Representation::Tagged(),
- HeapType::Any(map->GetIsolate()),
- FORCE_IN_OBJECT);
+ PropertyDetails details = descriptors->GetDetails(i);
+ if (details.type() == DATA) {
+ map = ReconfigureProperty(map, i, kData, details.attributes(),
+ Representation::Tagged(),
+ HeapType::Any(map->GetIsolate()), FORCE_FIELD);
}
}
return map;
@@ -2900,30 +3899,7 @@
// static
-MaybeHandle<Map> Map::TryUpdate(Handle<Map> map) {
- Handle<Map> proto_map(map);
- while (proto_map->prototype()->IsJSObject()) {
- Handle<JSObject> holder(JSObject::cast(proto_map->prototype()));
- proto_map = Handle<Map>(holder->map());
- if (proto_map->is_deprecated() && JSObject::TryMigrateInstance(holder)) {
- proto_map = Handle<Map>(holder->map());
- }
- }
- return TryUpdateInternal(map);
-}
-
-
-// static
-Handle<Map> Map::Update(Handle<Map> map) {
- if (!map->is_deprecated()) return map;
- return GeneralizeRepresentation(map, 0, Representation::None(),
- HeapType::None(map->GetIsolate()),
- ALLOW_IN_DESCRIPTOR);
-}
-
-
-// static
-MaybeHandle<Map> Map::TryUpdateInternal(Handle<Map> old_map) {
+MaybeHandle<Map> Map::TryUpdate(Handle<Map> old_map) {
DisallowHeapAllocation no_allocation;
DisallowDeoptimization no_deoptimization(old_map->GetIsolate());
@@ -2932,6 +3908,15 @@
// Check the state of the root map.
Map* root_map = old_map->FindRootMap();
if (!old_map->EquivalentToForTransition(root_map)) return MaybeHandle<Map>();
+
+ ElementsKind from_kind = root_map->elements_kind();
+ ElementsKind to_kind = old_map->elements_kind();
+ if (from_kind != to_kind) {
+ // Try to follow existing elements kind transitions.
+ root_map = root_map->LookupElementsTransitionMap(to_kind);
+ if (root_map == NULL) return MaybeHandle<Map>();
+ // From here on, use the map with correct elements kind as root map.
+ }
int root_nof = root_map->NumberOfOwnDescriptors();
int old_nof = old_map->NumberOfOwnDescriptors();
@@ -2940,11 +3925,11 @@
Map* new_map = root_map;
for (int i = root_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
- int j = new_map->SearchTransition(old_details.kind(),
- old_descriptors->GetKey(i),
- old_details.attributes());
- if (j == TransitionArray::kNotFound) return MaybeHandle<Map>();
- new_map = new_map->GetTransition(j);
+ Map* transition = TransitionArray::SearchTransition(
+ new_map, old_details.kind(), old_descriptors->GetKey(i),
+ old_details.attributes());
+ if (transition == NULL) return MaybeHandle<Map>();
+ new_map = transition;
DescriptorArray* new_descriptors = new_map->instance_descriptors();
PropertyDetails new_details = new_descriptors->GetDetails(i);
@@ -2953,33 +3938,47 @@
if (!old_details.representation().fits_into(new_details.representation())) {
return MaybeHandle<Map>();
}
- Object* new_value = new_descriptors->GetValue(i);
- Object* old_value = old_descriptors->GetValue(i);
switch (new_details.type()) {
- case FIELD: {
- PropertyType old_type = old_details.type();
- if (old_type == FIELD) {
- if (!HeapType::cast(old_value)->NowIs(HeapType::cast(new_value))) {
+ case DATA: {
+ HeapType* new_type = new_descriptors->GetFieldType(i);
+ // Cleared field types need special treatment. They represent lost
+ // knowledge, so we must first generalize the new_type to "Any".
+ if (FieldTypeIsCleared(new_details.representation(), new_type)) {
+ return MaybeHandle<Map>();
+ }
+ PropertyType old_property_type = old_details.type();
+ if (old_property_type == DATA) {
+ HeapType* old_type = old_descriptors->GetFieldType(i);
+ if (FieldTypeIsCleared(old_details.representation(), old_type) ||
+ !old_type->NowIs(new_type)) {
return MaybeHandle<Map>();
}
} else {
- DCHECK(old_type == CONSTANT);
- if (!HeapType::cast(new_value)->NowContains(old_value)) {
+ DCHECK(old_property_type == DATA_CONSTANT);
+ Object* old_value = old_descriptors->GetValue(i);
+ if (!new_type->NowContains(old_value)) {
return MaybeHandle<Map>();
}
}
break;
}
- case ACCESSOR_FIELD:
- DCHECK(HeapType::Any()->Is(HeapType::cast(new_value)));
+ case ACCESSOR: {
+#ifdef DEBUG
+ HeapType* new_type = new_descriptors->GetFieldType(i);
+ DCHECK(HeapType::Any()->Is(new_type));
+#endif
break;
+ }
- case CONSTANT:
- case CALLBACKS:
- if (old_details.location() == IN_OBJECT || old_value != new_value) {
+ case DATA_CONSTANT:
+ case ACCESSOR_CONSTANT: {
+ Object* old_value = old_descriptors->GetValue(i);
+ Object* new_value = new_descriptors->GetValue(i);
+ if (old_details.location() == kField || old_value != new_value) {
return MaybeHandle<Map>();
}
break;
+ }
}
}
if (new_map->NumberOfOwnDescriptors() != old_nof) return MaybeHandle<Map>();
@@ -2987,50 +3986,91 @@
}
-MaybeHandle<Object> JSObject::SetPropertyWithInterceptor(LookupIterator* it,
- Handle<Object> value) {
- Handle<Name> name = it->name();
+// static
+Handle<Map> Map::Update(Handle<Map> map) {
+ if (!map->is_deprecated()) return map;
+ return ReconfigureProperty(map, -1, kData, NONE, Representation::None(),
+ HeapType::None(map->GetIsolate()),
+ ALLOW_IN_DESCRIPTOR);
+}
+
+
+Maybe<bool> JSObject::SetPropertyWithInterceptor(LookupIterator* it,
+ 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);
+
+ DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
+ Handle<InterceptorInfo> interceptor(it->GetInterceptor());
+ if (interceptor->setter()->IsUndefined()) return Just(false);
+
Handle<JSObject> holder = it->GetHolder<JSObject>();
- Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor());
- if (interceptor->setter()->IsUndefined() ||
- (name->IsSymbol() && !interceptor->can_intercept_symbols())) {
- return MaybeHandle<Object>();
+ v8::Local<v8::Value> result;
+ PropertyCallbackArguments args(isolate, interceptor->data(),
+ *it->GetReceiver(), *holder);
+
+ if (it->IsElement()) {
+ uint32_t index = it->index();
+ v8::IndexedPropertySetterCallback setter =
+ v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-set", *holder, index));
+ result = args.Call(setter, index, v8::Utils::ToLocal(value));
+ } 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());
+ LOG(it->isolate(),
+ ApiNamedPropertyAccess("interceptor-named-set", *holder, *name));
+ result =
+ args.Call(setter, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value));
}
- LOG(it->isolate(),
- ApiNamedPropertyAccess("interceptor-named-set", *holder, *name));
- PropertyCallbackArguments args(it->isolate(), interceptor->data(), *holder,
- *holder);
- v8::GenericNamedPropertySetterCallback setter =
- v8::ToCData<v8::GenericNamedPropertySetterCallback>(
- interceptor->setter());
- v8::Handle<v8::Value> result =
- args.Call(setter, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value));
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
- if (!result.IsEmpty()) return value;
-
- return MaybeHandle<Object>();
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>());
+ if (result.IsEmpty()) return Just(false);
+#ifdef DEBUG
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+#endif
+ return Just(true);
+ // TODO(neis): In the future, we may want to actually return the interceptor's
+ // result, which then should be a boolean.
}
MaybeHandle<Object> Object::SetProperty(Handle<Object> object,
Handle<Name> name, Handle<Object> value,
- StrictMode strict_mode,
+ LanguageMode language_mode,
StoreFromKeyed store_mode) {
LookupIterator it(object, name);
- return SetProperty(&it, value, strict_mode, store_mode);
+ MAYBE_RETURN_NULL(SetProperty(&it, value, language_mode, store_mode));
+ return value;
}
-MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
+Maybe<bool> Object::SetPropertyInternal(LookupIterator* it,
Handle<Object> value,
- StrictMode strict_mode,
+ LanguageMode language_mode,
StoreFromKeyed store_mode,
- StorePropertyMode data_store_mode) {
+ bool* found) {
+ ShouldThrow should_throw =
+ is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR;
+
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc(it->isolate());
+ *found = true;
+
bool done = false;
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
@@ -3038,64 +4078,51 @@
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
- // TODO(verwaest): Remove the distinction. This is mostly bogus since we
- // don't know whether we'll want to fetch attributes or call a setter
- // until we find the property.
- if (it->HasAccess(v8::ACCESS_SET)) break;
+ if (it->HasAccess()) break;
+ // Check whether it makes sense to reuse the lookup iterator. Here it
+ // might still call into setters up the prototype chain.
return JSObject::SetPropertyWithFailedAccessCheck(it, value,
- strict_mode);
+ should_throw);
case LookupIterator::JSPROXY:
- if (it->HolderIsReceiverOrHiddenPrototype()) {
- return JSProxy::SetPropertyWithHandler(it->GetHolder<JSProxy>(),
- it->GetReceiver(), it->name(),
- value, strict_mode);
- } else {
- // TODO(verwaest): Use the MaybeHandle to indicate result.
- bool has_result = false;
- MaybeHandle<Object> maybe_result =
- JSProxy::SetPropertyViaPrototypesWithHandler(
- it->GetHolder<JSProxy>(), it->GetReceiver(), it->name(),
- value, strict_mode, &has_result);
- if (has_result) return maybe_result;
- done = true;
- }
- break;
+ return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(),
+ value, it->GetReceiver(), language_mode);
case LookupIterator::INTERCEPTOR:
if (it->HolderIsReceiverOrHiddenPrototype()) {
- MaybeHandle<Object> maybe_result =
- JSObject::SetPropertyWithInterceptor(it, value);
- if (!maybe_result.is_null()) return maybe_result;
- if (it->isolate()->has_pending_exception()) return maybe_result;
+ Maybe<bool> result = JSObject::SetPropertyWithInterceptor(it, value);
+ if (result.IsNothing() || result.FromJust()) return result;
} else {
Maybe<PropertyAttributes> maybe_attributes =
- JSObject::GetPropertyAttributesWithInterceptor(
- it->GetHolder<JSObject>(), it->GetReceiver(), it->name());
- if (!maybe_attributes.has_value) return MaybeHandle<Object>();
- done = maybe_attributes.value != ABSENT;
- if (done && (maybe_attributes.value & READ_ONLY) != 0) {
- return WriteToReadOnlyProperty(it, value, strict_mode);
+ JSObject::GetPropertyAttributesWithInterceptor(it);
+ if (!maybe_attributes.IsJust()) return Nothing<bool>();
+ done = maybe_attributes.FromJust() != ABSENT;
+ if (done && (maybe_attributes.FromJust() & READ_ONLY) != 0) {
+ return WriteToReadOnlyProperty(it, value, should_throw);
}
}
break;
- case LookupIterator::ACCESSOR:
- if (it->property_details().IsReadOnly()) {
- return WriteToReadOnlyProperty(it, value, strict_mode);
+ case LookupIterator::ACCESSOR: {
+ if (it->IsReadOnly()) {
+ return WriteToReadOnlyProperty(it, value, should_throw);
}
- if (it->HolderIsReceiverOrHiddenPrototype() ||
- !it->GetAccessors()->IsDeclaredAccessorInfo()) {
- return SetPropertyWithAccessor(it->GetReceiver(), it->name(), value,
- it->GetHolder<JSObject>(),
- it->GetAccessors(), strict_mode);
+ Handle<Object> accessors = it->GetAccessors();
+ if (accessors->IsAccessorInfo() &&
+ !it->HolderIsReceiverOrHiddenPrototype() &&
+ AccessorInfo::cast(*accessors)->is_special_data_property()) {
+ done = true;
+ break;
}
- done = true;
- break;
+ return SetPropertyWithAccessor(it, value, should_throw);
+ }
+ case LookupIterator::INTEGER_INDEXED_EXOTIC:
+ // TODO(verwaest): We should throw an exception.
+ return Just(true);
case LookupIterator::DATA:
- if (it->property_details().IsReadOnly()) {
- return WriteToReadOnlyProperty(it, value, strict_mode);
+ if (it->IsReadOnly()) {
+ return WriteToReadOnlyProperty(it, value, should_throw);
}
if (it->HolderIsReceiverOrHiddenPrototype()) {
return SetDataProperty(it, value);
@@ -3113,58 +4140,187 @@
// If the receiver is the JSGlobalObject, the store was contextual. In case
// the property did not exist yet on the global object itself, we have to
- // throw a reference error in strict mode.
- if (it->GetReceiver()->IsJSGlobalObject() && strict_mode == STRICT) {
- Handle<Object> args[1] = {it->name()};
+ // throw a reference error in strict mode. In sloppy mode, we continue.
+ if (it->GetReceiver()->IsJSGlobalObject() && is_strict(language_mode)) {
+ it->isolate()->Throw(*it->isolate()->factory()->NewReferenceError(
+ MessageTemplate::kNotDefined, it->name()));
+ return Nothing<bool>();
+ }
+
+ *found = false;
+ return Nothing<bool>();
+}
+
+
+Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value,
+ LanguageMode language_mode,
+ StoreFromKeyed store_mode) {
+ ShouldThrow should_throw =
+ is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR;
+ if (it->GetReceiver()->IsJSProxy() && it->GetName()->IsPrivate()) {
+ RETURN_FAILURE(it->isolate(), should_throw,
+ NewTypeError(MessageTemplate::kProxyPrivate));
+ }
+ bool found = false;
+ Maybe<bool> result =
+ SetPropertyInternal(it, value, language_mode, store_mode, &found);
+ if (found) return result;
+ return AddDataProperty(it, value, NONE, should_throw, store_mode);
+}
+
+
+Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value,
+ LanguageMode language_mode,
+ StoreFromKeyed store_mode) {
+ ShouldThrow should_throw =
+ is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR;
+ Isolate* isolate = it->isolate();
+ if (it->GetReceiver()->IsJSProxy() && it->GetName()->IsPrivate()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kProxyPrivate));
+ }
+
+ bool found = false;
+ Maybe<bool> result =
+ SetPropertyInternal(it, value, language_mode, store_mode, &found);
+ if (found) return result;
+
+ // The property either doesn't exist on the holder or exists there as a data
+ // property.
+
+ if (!it->GetReceiver()->IsJSReceiver()) {
+ return WriteToReadOnlyProperty(it, value, should_throw);
+ }
+ Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
+
+ LookupIterator::Configuration c = LookupIterator::OWN;
+ LookupIterator own_lookup =
+ it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c)
+ : LookupIterator(receiver, it->name(), c);
+
+ for (; own_lookup.IsFound(); own_lookup.Next()) {
+ switch (own_lookup.state()) {
+ case LookupIterator::ACCESS_CHECK:
+ if (!own_lookup.HasAccess()) {
+ return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value,
+ should_throw);
+ }
+ break;
+
+ case LookupIterator::INTEGER_INDEXED_EXOTIC:
+ case LookupIterator::ACCESSOR:
+ return RedefineIncompatibleProperty(isolate, it->GetName(), value,
+ should_throw);
+
+ case LookupIterator::DATA: {
+ PropertyDetails details = own_lookup.property_details();
+ if (details.IsReadOnly()) {
+ return WriteToReadOnlyProperty(&own_lookup, value, should_throw);
+ }
+ return SetDataProperty(&own_lookup, value);
+ }
+
+ case LookupIterator::INTERCEPTOR:
+ case LookupIterator::JSPROXY: {
+ PropertyDescriptor desc;
+ Maybe<bool> owned =
+ JSReceiver::GetOwnPropertyDescriptor(&own_lookup, &desc);
+ MAYBE_RETURN(owned, Nothing<bool>());
+ if (!owned.FromJust()) {
+ return JSReceiver::CreateDataProperty(&own_lookup, value,
+ should_throw);
+ }
+ if (PropertyDescriptor::IsAccessorDescriptor(&desc) ||
+ !desc.writable()) {
+ return RedefineIncompatibleProperty(isolate, it->GetName(), value,
+ should_throw);
+ }
+
+ PropertyDescriptor value_desc;
+ value_desc.set_value(value);
+ return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(),
+ &value_desc, should_throw);
+ }
+
+ case LookupIterator::NOT_FOUND:
+ case LookupIterator::TRANSITION:
+ UNREACHABLE();
+ }
+ }
+
+ return JSObject::AddDataProperty(&own_lookup, value, NONE, should_throw,
+ store_mode);
+}
+
+
+MaybeHandle<Object> Object::ReadAbsentProperty(LookupIterator* it,
+ LanguageMode language_mode) {
+ if (is_strong(language_mode)) {
THROW_NEW_ERROR(it->isolate(),
- NewReferenceError("not_defined", HandleVector(args, 1)),
+ NewTypeError(MessageTemplate::kStrongPropertyAccess,
+ it->GetName(), it->GetReceiver()),
Object);
}
+ return it->isolate()->factory()->undefined_value();
+}
- if (data_store_mode == SUPER_PROPERTY) {
- LookupIterator own_lookup(it->GetReceiver(), it->name(),
- LookupIterator::OWN);
-
- return JSObject::SetProperty(&own_lookup, value, strict_mode, store_mode,
- NORMAL_PROPERTY);
+MaybeHandle<Object> Object::ReadAbsentProperty(Isolate* isolate,
+ Handle<Object> receiver,
+ Handle<Object> name,
+ LanguageMode language_mode) {
+ if (is_strong(language_mode)) {
+ THROW_NEW_ERROR(
+ isolate,
+ NewTypeError(MessageTemplate::kStrongPropertyAccess, name, receiver),
+ Object);
}
-
- return AddDataProperty(it, value, NONE, strict_mode, store_mode);
+ return isolate->factory()->undefined_value();
}
-MaybeHandle<Object> Object::WriteToReadOnlyProperty(LookupIterator* it,
- Handle<Object> value,
- StrictMode strict_mode) {
- if (strict_mode != STRICT) return value;
-
- Handle<Object> args[] = {it->name(), it->GetReceiver()};
- THROW_NEW_ERROR(it->isolate(),
- NewTypeError("strict_read_only_property",
- HandleVector(args, arraysize(args))),
- Object);
+Maybe<bool> Object::CannotCreateProperty(Isolate* isolate,
+ Handle<Object> receiver,
+ Handle<Object> name,
+ Handle<Object> value,
+ ShouldThrow should_throw) {
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kStrictCannotCreateProperty, name,
+ Object::TypeOf(isolate, receiver), receiver));
}
-MaybeHandle<Object> Object::WriteToReadOnlyElement(Isolate* isolate,
- Handle<Object> receiver,
- uint32_t index,
- Handle<Object> value,
- StrictMode strict_mode) {
- if (strict_mode != STRICT) return value;
-
- Handle<Object> args[] = {isolate->factory()->NewNumberFromUint(index),
- receiver};
- THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
- HandleVector(args, arraysize(args))),
- Object);
+Maybe<bool> Object::WriteToReadOnlyProperty(LookupIterator* it,
+ Handle<Object> value,
+ ShouldThrow should_throw) {
+ return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(),
+ it->GetName(), value, should_throw);
}
-MaybeHandle<Object> Object::SetDataProperty(LookupIterator* it,
- Handle<Object> value) {
- // Proxies are handled on the WithHandler path. Other non-JSObjects cannot
- // have own properties.
+Maybe<bool> Object::WriteToReadOnlyProperty(Isolate* isolate,
+ Handle<Object> receiver,
+ Handle<Object> name,
+ Handle<Object> value,
+ ShouldThrow should_throw) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kStrictReadOnlyProperty, name,
+ Object::TypeOf(isolate, receiver), receiver));
+}
+
+
+Maybe<bool> Object::RedefineIncompatibleProperty(Isolate* isolate,
+ Handle<Object> name,
+ Handle<Object> value,
+ ShouldThrow should_throw) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed, name));
+}
+
+
+Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) {
+ // Proxies are handled elsewhere. Other non-JSObjects cannot have own
+ // properties.
Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver());
// Store on the holder which may be hidden behind the receiver.
@@ -3174,121 +4330,184 @@
// Fetch before transforming the object since the encoding may become
// incompatible with what's cached in |it|.
bool is_observed = receiver->map()->is_observed() &&
- !it->isolate()->IsInternallyUsedPropertyName(it->name());
+ (it->IsElement() ||
+ !it->isolate()->IsInternallyUsedPropertyName(it->name()));
MaybeHandle<Object> maybe_old;
if (is_observed) maybe_old = it->GetDataValue();
+ Handle<Object> to_assign = value;
+ // Convert the incoming value to a number for storing into typed arrays.
+ if (it->IsElement() && receiver->HasFixedTypedArrayElements()) {
+ if (!value->IsNumber() && !value->IsUndefined()) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ it->isolate(), to_assign, Object::ToNumber(value), Nothing<bool>());
+ // 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
+ // have been invalidated since typed array elements cannot be reconfigured
+ // in any way.
+ it->ReloadHolderMap();
+
+ // We have to recheck the length. However, it can only change if the
+ // underlying buffer was neutered, so just check that.
+ if (Handle<JSArrayBufferView>::cast(receiver)->WasNeutered()) {
+ return Just(true);
+ // TODO(neis): According to the spec, this should throw a TypeError.
+ }
+ }
+ }
+
// Possibly migrate to the most up-to-date map that will be able to store
// |value| under it->name().
- it->PrepareForDataProperty(value);
+ it->PrepareForDataProperty(to_assign);
// Write the property value.
- value = it->WriteDataValue(value);
+ it->WriteDataValue(to_assign);
// Send the change record if there are observers.
if (is_observed && !value->SameValue(*maybe_old.ToHandleChecked())) {
- RETURN_ON_EXCEPTION(it->isolate(), JSObject::EnqueueChangeRecord(
- receiver, "update", it->name(),
- maybe_old.ToHandleChecked()),
- Object);
+ RETURN_ON_EXCEPTION_VALUE(
+ it->isolate(),
+ JSObject::EnqueueChangeRecord(receiver, "update", it->GetName(),
+ maybe_old.ToHandleChecked()),
+ Nothing<bool>());
}
- return value;
+#if VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ receiver->JSObjectVerify();
+ }
+#endif
+ return Just(true);
}
-MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it,
- Handle<Object> value,
- PropertyAttributes attributes,
- StrictMode strict_mode,
- StoreFromKeyed store_mode) {
+MUST_USE_RESULT static MaybeHandle<Object> BeginPerformSplice(
+ Handle<JSArray> object) {
+ Isolate* isolate = object->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> args[] = {object};
+
+ return Execution::Call(
+ isolate, Handle<JSFunction>(isolate->observers_begin_perform_splice()),
+ isolate->factory()->undefined_value(), arraysize(args), args);
+}
+
+
+MUST_USE_RESULT static MaybeHandle<Object> EndPerformSplice(
+ Handle<JSArray> object) {
+ Isolate* isolate = object->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> args[] = {object};
+
+ return Execution::Call(
+ isolate, Handle<JSFunction>(isolate->observers_end_perform_splice()),
+ isolate->factory()->undefined_value(), arraysize(args), args);
+}
+
+
+MUST_USE_RESULT static MaybeHandle<Object> EnqueueSpliceRecord(
+ Handle<JSArray> object, uint32_t index, Handle<JSArray> deleted,
+ uint32_t add_count) {
+ Isolate* isolate = object->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> index_object = isolate->factory()->NewNumberFromUint(index);
+ Handle<Object> add_count_object =
+ isolate->factory()->NewNumberFromUint(add_count);
+
+ Handle<Object> args[] = {object, index_object, deleted, add_count_object};
+
+ return Execution::Call(
+ isolate, Handle<JSFunction>(isolate->observers_enqueue_splice()),
+ isolate->factory()->undefined_value(), arraysize(args), args);
+}
+
+
+Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value,
+ PropertyAttributes attributes,
+ ShouldThrow should_throw,
+ StoreFromKeyed store_mode) {
DCHECK(!it->GetReceiver()->IsJSProxy());
if (!it->GetReceiver()->IsJSObject()) {
- // TODO(verwaest): Throw a TypeError with a more specific message.
- return WriteToReadOnlyProperty(it, value, strict_mode);
+ return CannotCreateProperty(it->isolate(), it->GetReceiver(), it->GetName(),
+ value, should_throw);
}
+ DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, it->state());
+
Handle<JSObject> receiver = it->GetStoreTarget();
// If the receiver is a JSGlobalProxy, store on the prototype (JSGlobalObject)
// instead. If the prototype is Null, the proxy is detached.
- if (receiver->IsJSGlobalProxy()) return value;
+ if (receiver->IsJSGlobalProxy()) return Just(true);
- // If the receiver is Indexed Exotic object (currently only typed arrays),
- // disallow adding properties with numeric names.
- if (it->IsSpecialNumericIndex()) return value;
+ Isolate* isolate = it->isolate();
- // Possibly migrate to the most up-to-date map that will be able to store
- // |value| under it->name() with |attributes|.
- it->PrepareTransitionToDataProperty(value, attributes, store_mode);
- if (it->state() != LookupIterator::TRANSITION) {
- if (strict_mode == SLOPPY) return value;
-
- Handle<Object> args[1] = {it->name()};
- THROW_NEW_ERROR(it->isolate(),
- NewTypeError("object_not_extensible",
- HandleVector(args, arraysize(args))),
- Object);
- }
- it->ApplyTransitionToDataProperty();
-
- // TODO(verwaest): Encapsulate dictionary handling better.
- if (receiver->map()->is_dictionary_map()) {
- // TODO(verwaest): Probably should ensure this is done beforehand.
- it->InternalizeName();
- JSObject::AddSlowProperty(receiver, it->name(), value, attributes);
- } else {
- // Write the property value.
- value = it->WriteDataValue(value);
+ if (!receiver->map()->is_extensible() &&
+ (it->IsElement() || !isolate->IsInternallyUsedPropertyName(it->name()))) {
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kObjectNotExtensible, it->GetName()));
}
- // Send the change record if there are observers.
- if (receiver->map()->is_observed() &&
- !it->isolate()->IsInternallyUsedPropertyName(it->name())) {
- RETURN_ON_EXCEPTION(it->isolate(), JSObject::EnqueueChangeRecord(
- receiver, "add", it->name(),
- it->factory()->the_hole_value()),
- Object);
- }
+ if (it->IsElement()) {
+ if (receiver->IsJSArray()) {
+ Handle<JSArray> array = Handle<JSArray>::cast(receiver);
+ if (JSArray::WouldChangeReadOnlyLength(array, it->index())) {
+ RETURN_FAILURE(array->GetIsolate(), should_throw,
+ NewTypeError(MessageTemplate::kStrictReadOnlyProperty,
+ isolate->factory()->length_string(),
+ Object::TypeOf(isolate, array), array));
+ }
- return value;
-}
+ if (FLAG_trace_external_array_abuse &&
+ array->HasFixedTypedArrayElements()) {
+ CheckArrayAbuse(array, "typed elements write", it->index(), true);
+ }
-
-MaybeHandle<Object> JSObject::SetElementWithCallbackSetterInPrototypes(
- Handle<JSObject> object,
- uint32_t index,
- Handle<Object> value,
- bool* found,
- StrictMode strict_mode) {
- Isolate *isolate = object->GetIsolate();
- for (PrototypeIterator iter(isolate, object); !iter.IsAtEnd();
- iter.Advance()) {
- if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
- return JSProxy::SetPropertyViaPrototypesWithHandler(
- Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), object,
- isolate->factory()->Uint32ToString(index), // name
- value, strict_mode, found);
- }
- Handle<JSObject> js_proto =
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
- if (!js_proto->HasDictionaryElements()) {
- continue;
- }
- Handle<SeededNumberDictionary> dictionary(js_proto->element_dictionary());
- int entry = dictionary->FindEntry(index);
- if (entry != SeededNumberDictionary::kNotFound) {
- PropertyDetails details = dictionary->DetailsAt(entry);
- if (details.type() == CALLBACKS) {
- *found = true;
- Handle<Object> structure(dictionary->ValueAt(entry), isolate);
- return SetElementWithCallback(object, structure, index, value, js_proto,
- strict_mode);
+ if (FLAG_trace_js_array_abuse && !array->HasFixedTypedArrayElements()) {
+ CheckArrayAbuse(array, "elements write", it->index(), false);
}
}
+
+ Maybe<bool> result = JSObject::AddDataElement(receiver, it->index(), value,
+ attributes, should_throw);
+ JSObject::ValidateElements(receiver);
+ return result;
+ } else {
+ // Migrate to the most up-to-date map that will be able to store |value|
+ // under it->name() with |attributes|.
+ it->PrepareTransitionToDataProperty(value, attributes, store_mode);
+ DCHECK_EQ(LookupIterator::TRANSITION, it->state());
+ it->ApplyTransitionToDataProperty();
+
+ // TODO(verwaest): Encapsulate dictionary handling better.
+ if (receiver->map()->is_dictionary_map()) {
+ // TODO(verwaest): Probably should ensure this is done beforehand.
+ it->InternalizeName();
+ // TODO(dcarney): just populate TransitionPropertyCell here?
+ JSObject::AddSlowProperty(receiver, it->name(), value, attributes);
+ } else {
+ // Write the property value.
+ it->WriteDataValue(value);
+ }
+
+ // Send the change record if there are observers.
+ if (receiver->map()->is_observed() &&
+ !isolate->IsInternallyUsedPropertyName(it->name())) {
+ RETURN_ON_EXCEPTION_VALUE(isolate, JSObject::EnqueueChangeRecord(
+ receiver, "add", it->name(),
+ it->factory()->the_hole_value()),
+ Nothing<bool>());
+ }
+#if VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ receiver->JSObjectVerify();
+ }
+#endif
}
- *found = false;
- return isolate->factory()->the_hole_value();
+
+ return Just(true);
}
@@ -3324,15 +4543,13 @@
// Replace descriptors by new_descriptors in all maps that share it.
map->GetHeap()->incremental_marking()->RecordWrites(*descriptors);
- Map* walk_map;
- for (Object* current = map->GetBackPointer();
- !current->IsUndefined();
- current = walk_map->GetBackPointer()) {
- walk_map = Map::cast(current);
- if (walk_map->instance_descriptors() != *descriptors) break;
- walk_map->UpdateDescriptors(*new_descriptors, layout_descriptor);
+ Map* current = *map;
+ while (current->instance_descriptors() == *descriptors) {
+ Object* next = current->GetBackPointer();
+ if (next->IsUndefined()) break; // Stop overwriting at initial map.
+ current->UpdateDescriptors(*new_descriptors, layout_descriptor);
+ current = Map::cast(next);
}
-
map->UpdateDescriptors(*new_descriptors, layout_descriptor);
}
@@ -3386,7 +4603,7 @@
int valid_descriptors,
Handle<DescriptorArray> array) {
DisallowHeapAllocation no_gc;
- CallbacksDescriptor desc(key, entry, entry->property_attributes());
+ AccessorConstantDescriptor desc(key, entry, entry->property_attributes());
array->Append(&desc);
}
};
@@ -3435,79 +4652,48 @@
}
-static bool ContainsMap(MapHandleList* maps, Handle<Map> map) {
- DCHECK(!map.is_null());
+static bool ContainsMap(MapHandleList* maps, Map* map) {
+ DCHECK_NOT_NULL(map);
for (int i = 0; i < maps->length(); ++i) {
- if (!maps->at(i).is_null() && maps->at(i).is_identical_to(map)) return true;
+ if (!maps->at(i).is_null() && *maps->at(i) == map) return true;
}
return false;
}
-template <class T>
-static Handle<T> MaybeNull(T* p) {
- if (p == NULL) return Handle<T>::null();
- return Handle<T>(p);
-}
-
-
-Handle<Map> Map::FindTransitionedMap(MapHandleList* candidates) {
- ElementsKind kind = elements_kind();
- Handle<Map> transitioned_map = Handle<Map>::null();
- Handle<Map> current_map(this);
+Handle<Map> Map::FindTransitionedMap(Handle<Map> map,
+ MapHandleList* candidates) {
+ ElementsKind kind = map->elements_kind();
bool packed = IsFastPackedElementsKind(kind);
+
+ Map* transition = nullptr;
if (IsTransitionableFastElementsKind(kind)) {
- while (CanTransitionToMoreGeneralFastElementsKind(kind, false)) {
- kind = GetNextMoreGeneralFastElementsKind(kind, false);
- Handle<Map> maybe_transitioned_map =
- MaybeNull(current_map->LookupElementsTransitionMap(kind));
- if (maybe_transitioned_map.is_null()) break;
- if (ContainsMap(candidates, maybe_transitioned_map) &&
- (packed || !IsFastPackedElementsKind(kind))) {
- transitioned_map = maybe_transitioned_map;
- if (!IsFastPackedElementsKind(kind)) packed = false;
+ for (Map* current = map->ElementsTransitionMap();
+ current != nullptr && current->has_fast_elements();
+ current = current->ElementsTransitionMap()) {
+ if (ContainsMap(candidates, current) &&
+ (packed || !IsFastPackedElementsKind(current->elements_kind()))) {
+ transition = current;
+ packed = packed && IsFastPackedElementsKind(current->elements_kind());
}
- current_map = maybe_transitioned_map;
}
}
- return transitioned_map;
+ return transition == nullptr ? Handle<Map>() : handle(transition);
}
static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) {
Map* current_map = map;
- int target_kind =
- IsFastElementsKind(to_kind) || IsExternalArrayElementsKind(to_kind)
- ? to_kind
- : TERMINAL_FAST_ELEMENTS_KIND;
-
- // Support for legacy API: SetIndexedPropertiesTo{External,Pixel}Data
- // allows to change elements from arbitrary kind to any ExternalArray
- // elements kind. Satisfy its requirements, checking whether we already
- // have the cached transition.
- if (IsExternalArrayElementsKind(to_kind) &&
- !IsFixedTypedArrayElementsKind(map->elements_kind())) {
- if (map->HasElementsTransition()) {
- Map* next_map = map->elements_transition_map();
- if (next_map->elements_kind() == to_kind) return next_map;
- }
- return map;
- }
ElementsKind kind = map->elements_kind();
- while (kind != target_kind) {
- kind = GetNextTransitionElementsKind(kind);
- if (!current_map->HasElementsTransition()) return current_map;
- current_map = current_map->elements_transition_map();
+ while (kind != to_kind) {
+ Map* next_map = current_map->ElementsTransitionMap();
+ if (next_map == nullptr) return current_map;
+ kind = next_map->elements_kind();
+ current_map = next_map;
}
- if (to_kind != kind && current_map->HasElementsTransition()) {
- DCHECK(to_kind == DICTIONARY_ELEMENTS);
- Map* next_map = current_map->elements_transition_map();
- if (next_map->elements_kind() == to_kind) return next_map;
- }
-
- DCHECK(current_map->elements_kind() == target_kind);
+ DCHECK_EQ(to_kind, current_map->elements_kind());
return current_map;
}
@@ -3515,7 +4701,7 @@
Map* Map::LookupElementsTransitionMap(ElementsKind to_kind) {
Map* to_map = FindClosestElementsTransition(this, to_kind);
if (to_map->elements_kind() == to_kind) return to_map;
- return NULL;
+ return nullptr;
}
@@ -3535,15 +4721,11 @@
Handle<WeakCell> Map::WeakCellForMap(Handle<Map> map) {
Isolate* isolate = map->GetIsolate();
- if (map->code_cache()->IsFixedArray()) {
- return isolate->factory()->NewWeakCell(map);
- }
- Handle<CodeCache> code_cache(CodeCache::cast(map->code_cache()), isolate);
- if (code_cache->weak_cell_cache()->IsWeakCell()) {
- return Handle<WeakCell>(WeakCell::cast(code_cache->weak_cell_cache()));
+ if (map->weak_cell_cache()->IsWeakCell()) {
+ return Handle<WeakCell>(WeakCell::cast(map->weak_cell_cache()));
}
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(map);
- code_cache->set_weak_cell_cache(*weak_cell);
+ map->set_weak_cell_cache(*weak_cell);
return weak_cell;
}
@@ -3555,19 +4737,23 @@
Handle<Map> current_map = map;
ElementsKind kind = map->elements_kind();
- if (!map->is_prototype_map()) {
- while (kind != to_kind && !IsTerminalElementsKind(kind)) {
- kind = GetNextTransitionElementsKind(kind);
- current_map =
- Map::CopyAsElementsKind(current_map, kind, INSERT_TRANSITION);
+ TransitionFlag flag;
+ if (map->is_prototype_map()) {
+ flag = OMIT_TRANSITION;
+ } else {
+ flag = INSERT_TRANSITION;
+ if (IsFastElementsKind(kind)) {
+ while (kind != to_kind && !IsTerminalElementsKind(kind)) {
+ kind = GetNextTransitionElementsKind(kind);
+ current_map = Map::CopyAsElementsKind(current_map, kind, flag);
+ }
}
}
// In case we are exiting the fast elements kind system, just add the map in
// the end.
if (kind != to_kind) {
- current_map = Map::CopyAsElementsKind(
- current_map, to_kind, INSERT_TRANSITION);
+ current_map = Map::CopyAsElementsKind(current_map, to_kind, flag);
}
DCHECK(current_map->elements_kind() == to_kind);
@@ -3582,40 +4768,44 @@
Isolate* isolate = map->GetIsolate();
Context* native_context = isolate->context()->native_context();
- Object* maybe_array_maps = native_context->js_array_maps();
- if (maybe_array_maps->IsFixedArray()) {
+ if (from_kind == FAST_SLOPPY_ARGUMENTS_ELEMENTS) {
+ if (*map == native_context->fast_aliased_arguments_map()) {
+ DCHECK_EQ(SLOW_SLOPPY_ARGUMENTS_ELEMENTS, to_kind);
+ return handle(native_context->slow_aliased_arguments_map());
+ }
+ } else if (from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS) {
+ if (*map == native_context->slow_aliased_arguments_map()) {
+ DCHECK_EQ(FAST_SLOPPY_ARGUMENTS_ELEMENTS, to_kind);
+ return handle(native_context->fast_aliased_arguments_map());
+ }
+ } else if (IsFastElementsKind(from_kind) && IsFastElementsKind(to_kind)) {
+ // Reuse map transitions for JSArrays.
DisallowHeapAllocation no_gc;
- FixedArray* array_maps = FixedArray::cast(maybe_array_maps);
- if (array_maps->get(from_kind) == *map) {
- Object* maybe_transitioned_map = array_maps->get(to_kind);
+ Strength strength = map->is_strong() ? Strength::STRONG : Strength::WEAK;
+ if (native_context->get(Context::ArrayMapIndex(from_kind, strength)) ==
+ *map) {
+ Object* maybe_transitioned_map =
+ native_context->get(Context::ArrayMapIndex(to_kind, strength));
if (maybe_transitioned_map->IsMap()) {
- return handle(Map::cast(maybe_transitioned_map));
+ return handle(Map::cast(maybe_transitioned_map), isolate);
}
}
}
- return TransitionElementsToSlow(map, to_kind);
-}
-
-
-Handle<Map> Map::TransitionElementsToSlow(Handle<Map> map,
- ElementsKind to_kind) {
- ElementsKind from_kind = map->elements_kind();
-
- if (from_kind == to_kind) {
- return 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 =
- // Only remember the map transition if there is not an already existing
- // non-matching element transition.
- !map->IsUndefined() && !map->is_dictionary_map() &&
- IsTransitionElementsKind(from_kind);
-
+ bool allow_store_transition = IsTransitionElementsKind(from_kind);
// Only store fast element maps in ascending generality.
if (IsFastElementsKind(to_kind)) {
- allow_store_transition &=
- IsTransitionableFastElementsKind(from_kind) &&
+ allow_store_transition =
+ allow_store_transition && IsTransitionableFastElementsKind(from_kind) &&
IsMoreGeneralElementsKindTransition(from_kind, to_kind);
}
@@ -3646,329 +4836,302 @@
}
-Maybe<bool> JSProxy::HasPropertyWithHandler(Handle<JSProxy> proxy,
- Handle<Name> name) {
+void JSProxy::Revoke(Handle<JSProxy> proxy) {
Isolate* isolate = proxy->GetIsolate();
-
- // TODO(rossberg): adjust once there is a story for symbols vs proxies.
- if (name->IsSymbol()) return maybe(false);
-
- Handle<Object> args[] = { name };
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, result, CallTrap(proxy, "has", isolate->derived_has_trap(),
- arraysize(args), args),
- Maybe<bool>());
-
- return maybe(result->BooleanValue());
+ if (!proxy->IsRevoked()) proxy->set_handler(isolate->heap()->null_value());
+ DCHECK(proxy->IsRevoked());
}
-MaybeHandle<Object> JSProxy::SetPropertyWithHandler(Handle<JSProxy> proxy,
- Handle<Object> receiver,
- Handle<Name> name,
- Handle<Object> value,
- StrictMode strict_mode) {
- Isolate* isolate = proxy->GetIsolate();
-
- // TODO(rossberg): adjust once there is a story for symbols vs proxies.
- if (name->IsSymbol()) return value;
-
- Handle<Object> args[] = { receiver, name, value };
- RETURN_ON_EXCEPTION(
- isolate,
- CallTrap(proxy,
- "set",
- isolate->derived_set_trap(),
- arraysize(args),
- args),
- Object);
-
- return value;
-}
-
-
-MaybeHandle<Object> JSProxy::SetPropertyViaPrototypesWithHandler(
- Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name,
- Handle<Object> value, StrictMode strict_mode, bool* done) {
- Isolate* isolate = proxy->GetIsolate();
- Handle<Object> handler(proxy->handler(), isolate); // Trap might morph proxy.
-
- // TODO(rossberg): adjust once there is a story for symbols vs proxies.
- if (name->IsSymbol()) {
- *done = false;
- return isolate->factory()->the_hole_value();
- }
-
- *done = true; // except where redefined...
- Handle<Object> args[] = { name };
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, result,
- CallTrap(proxy,
- "getPropertyDescriptor",
- Handle<Object>(),
- arraysize(args),
- args),
- Object);
-
- if (result->IsUndefined()) {
- *done = false;
- return isolate->factory()->the_hole_value();
- }
-
- // Emulate [[GetProperty]] semantics for proxies.
- Handle<Object> argv[] = { result };
- Handle<Object> desc;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, desc,
- Execution::Call(isolate,
- isolate->to_complete_property_descriptor(),
- result,
- arraysize(argv),
- argv),
- Object);
-
- // [[GetProperty]] requires to check that all properties are configurable.
- Handle<String> configurable_name =
- isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("configurable_"));
- Handle<Object> configurable =
- Object::GetProperty(desc, configurable_name).ToHandleChecked();
- DCHECK(configurable->IsBoolean());
- if (configurable->IsFalse()) {
- Handle<String> trap = isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("getPropertyDescriptor"));
- Handle<Object> args[] = { handler, trap, name };
- THROW_NEW_ERROR(isolate, NewTypeError("proxy_prop_not_configurable",
- HandleVector(args, arraysize(args))),
- Object);
- }
- DCHECK(configurable->IsTrue());
-
- // Check for DataDescriptor.
- Handle<String> hasWritable_name =
- isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("hasWritable_"));
- Handle<Object> hasWritable =
- Object::GetProperty(desc, hasWritable_name).ToHandleChecked();
- DCHECK(hasWritable->IsBoolean());
- if (hasWritable->IsTrue()) {
- Handle<String> writable_name = isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("writable_"));
- Handle<Object> writable =
- Object::GetProperty(desc, writable_name).ToHandleChecked();
- DCHECK(writable->IsBoolean());
- *done = writable->IsFalse();
- if (!*done) return isolate->factory()->the_hole_value();
- if (strict_mode == SLOPPY) return value;
- Handle<Object> args[] = { name, receiver };
- THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
- HandleVector(args, arraysize(args))),
- Object);
- }
-
- // We have an AccessorDescriptor.
- Handle<String> set_name =
- isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("set_"));
- Handle<Object> setter = Object::GetProperty(desc, set_name).ToHandleChecked();
- if (!setter->IsUndefined()) {
- // TODO(rossberg): nicer would be to cast to some JSCallable here...
- return SetPropertyWithDefinedSetter(
- receiver, Handle<JSReceiver>::cast(setter), value);
- }
-
- if (strict_mode == SLOPPY) return value;
- Handle<Object> args2[] = { name, proxy };
- THROW_NEW_ERROR(isolate, NewTypeError("no_setter_in_callback",
- HandleVector(args2, arraysize(args2))),
- Object);
-}
-
-
-MaybeHandle<Object> JSProxy::DeletePropertyWithHandler(
- Handle<JSProxy> proxy, Handle<Name> name, DeleteMode mode) {
- Isolate* isolate = proxy->GetIsolate();
-
- // TODO(rossberg): adjust once there is a story for symbols vs proxies.
- if (name->IsSymbol()) return isolate->factory()->false_value();
-
- Handle<Object> args[] = { name };
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, result,
- CallTrap(proxy,
- "delete",
- Handle<Object>(),
- arraysize(args),
- args),
- Object);
-
- bool result_bool = result->BooleanValue();
- if (mode == STRICT_DELETION && !result_bool) {
- Handle<Object> handler(proxy->handler(), isolate);
- Handle<String> trap_name = isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("delete"));
- Handle<Object> args[] = { handler, trap_name };
- THROW_NEW_ERROR(isolate, NewTypeError("handler_failed",
- HandleVector(args, arraysize(args))),
- Object);
- }
- return isolate->factory()->ToBoolean(result_bool);
-}
-
-
-MaybeHandle<Object> JSProxy::DeleteElementWithHandler(
- Handle<JSProxy> proxy, uint32_t index, DeleteMode mode) {
- Isolate* isolate = proxy->GetIsolate();
- Handle<String> name = isolate->factory()->Uint32ToString(index);
- return JSProxy::DeletePropertyWithHandler(proxy, name, mode);
-}
-
-
-Maybe<PropertyAttributes> JSProxy::GetPropertyAttributesWithHandler(
- Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name) {
- Isolate* isolate = proxy->GetIsolate();
- HandleScope scope(isolate);
-
- // TODO(rossberg): adjust once there is a story for symbols vs proxies.
- if (name->IsSymbol()) return maybe(ABSENT);
-
- Handle<Object> args[] = { name };
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, result,
- proxy->CallTrap(proxy, "getPropertyDescriptor", Handle<Object>(),
- arraysize(args), args),
- Maybe<PropertyAttributes>());
-
- if (result->IsUndefined()) return maybe(ABSENT);
-
- Handle<Object> argv[] = { result };
- Handle<Object> desc;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, desc,
- Execution::Call(isolate, isolate->to_complete_property_descriptor(),
- result, arraysize(argv), argv),
- Maybe<PropertyAttributes>());
-
- // Convert result to PropertyAttributes.
- Handle<String> enum_n = isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("enumerable_"));
- Handle<Object> enumerable;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, enumerable,
- Object::GetProperty(desc, enum_n),
- Maybe<PropertyAttributes>());
- Handle<String> conf_n = isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("configurable_"));
- Handle<Object> configurable;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, configurable,
- Object::GetProperty(desc, conf_n),
- Maybe<PropertyAttributes>());
- Handle<String> writ_n = isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("writable_"));
- Handle<Object> writable;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, writable,
- Object::GetProperty(desc, writ_n),
- Maybe<PropertyAttributes>());
- if (!writable->BooleanValue()) {
- Handle<String> set_n = isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("set_"));
- Handle<Object> setter;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, setter,
- Object::GetProperty(desc, set_n),
- Maybe<PropertyAttributes>());
- writable = isolate->factory()->ToBoolean(!setter->IsUndefined());
- }
-
- if (configurable->IsFalse()) {
- Handle<Object> handler(proxy->handler(), isolate);
- Handle<String> trap = isolate->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("getPropertyDescriptor"));
- Handle<Object> args[] = { handler, trap, name };
- Handle<Object> error;
- MaybeHandle<Object> maybe_error = isolate->factory()->NewTypeError(
- "proxy_prop_not_configurable", HandleVector(args, arraysize(args)));
- if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
- return maybe(NONE);
- }
-
- int attributes = NONE;
- if (!enumerable->BooleanValue()) attributes |= DONT_ENUM;
- if (!configurable->BooleanValue()) attributes |= DONT_DELETE;
- if (!writable->BooleanValue()) attributes |= READ_ONLY;
- return maybe(static_cast<PropertyAttributes>(attributes));
-}
-
-
-Maybe<PropertyAttributes> JSProxy::GetElementAttributeWithHandler(
- Handle<JSProxy> proxy, Handle<JSReceiver> receiver, uint32_t index) {
- Isolate* isolate = proxy->GetIsolate();
- Handle<String> name = isolate->factory()->Uint32ToString(index);
- return GetPropertyAttributesWithHandler(proxy, receiver, name);
-}
-
-
-void JSProxy::Fix(Handle<JSProxy> proxy) {
- Isolate* isolate = proxy->GetIsolate();
-
- // Save identity hash.
- Handle<Object> hash(proxy->GetIdentityHash(), isolate);
-
- if (proxy->IsJSFunctionProxy()) {
- isolate->factory()->BecomeJSFunction(proxy);
- // Code will be set on the JavaScript side.
- } else {
- isolate->factory()->BecomeJSObject(proxy);
- }
- DCHECK(proxy->IsJSObject());
-
- // Inherit identity, if it was present.
- if (hash->IsSmi()) {
- JSObject::SetIdentityHash(Handle<JSObject>::cast(proxy),
- Handle<Smi>::cast(hash));
- }
-}
-
-
-MaybeHandle<Object> JSProxy::CallTrap(Handle<JSProxy> proxy,
- const char* name,
- Handle<Object> derived,
- int argc,
- Handle<Object> argv[]) {
- Isolate* isolate = proxy->GetIsolate();
+Maybe<bool> JSProxy::HasProperty(Isolate* isolate, Handle<JSProxy> proxy,
+ Handle<Name> name) {
+ DCHECK(!name->IsPrivate());
+ STACK_CHECK(Nothing<bool>());
+ // 1. (Assert)
+ // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
Handle<Object> handler(proxy->handler(), isolate);
-
- Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name);
+ // 3. If handler is null, throw a TypeError exception.
+ // 4. Assert: Type(handler) is Object.
+ if (proxy->IsRevoked()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyRevoked, isolate->factory()->has_string()));
+ return Nothing<bool>();
+ }
+ // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ // 6. Let trap be ? GetMethod(handler, "has").
Handle<Object> trap;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, trap,
- Object::GetPropertyOrElement(handler, trap_name),
- Object);
-
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
+ isolate->factory()->has_string()),
+ Nothing<bool>());
+ // 7. If trap is undefined, then
if (trap->IsUndefined()) {
- if (derived.is_null()) {
- Handle<Object> args[] = { handler, trap_name };
- THROW_NEW_ERROR(isolate,
- NewTypeError("handler_trap_missing",
- HandleVector(args, arraysize(args))),
- Object);
+ // 7a. Return target.[[HasProperty]](P).
+ return JSReceiver::HasProperty(target, name);
+ }
+ // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, P»)).
+ Handle<Object> trap_result_obj;
+ Handle<Object> args[] = {target, name};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result_obj,
+ Execution::Call(isolate, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+ bool boolean_trap_result = trap_result_obj->BooleanValue();
+ // 9. If booleanTrapResult is false, then:
+ if (!boolean_trap_result) {
+ // 9a. Let targetDesc be ? target.[[GetOwnProperty]](P).
+ PropertyDescriptor target_desc;
+ Maybe<bool> target_found = JSReceiver::GetOwnPropertyDescriptor(
+ isolate, target, name, &target_desc);
+ MAYBE_RETURN(target_found, Nothing<bool>());
+ // 9b. If targetDesc is not undefined, then:
+ if (target_found.FromJust()) {
+ // 9b i. If targetDesc.[[Configurable]] is false, throw a TypeError
+ // exception.
+ if (!target_desc.configurable()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyHasNonConfigurable, name));
+ return Nothing<bool>();
+ }
+ // 9b ii. Let extensibleTarget be ? IsExtensible(target).
+ Maybe<bool> extensible_target = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN(extensible_target, Nothing<bool>());
+ // 9b iii. If extensibleTarget is false, throw a TypeError exception.
+ if (!extensible_target.FromJust()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyHasNonExtensible, name));
+ return Nothing<bool>();
+ }
}
- trap = Handle<Object>(derived);
+ }
+ // 10. Return booleanTrapResult.
+ return Just(boolean_trap_result);
+}
+
+
+Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name,
+ Handle<Object> value, Handle<Object> receiver,
+ LanguageMode language_mode) {
+ DCHECK(!name->IsPrivate());
+ Isolate* isolate = proxy->GetIsolate();
+ STACK_CHECK(Nothing<bool>());
+ Factory* factory = isolate->factory();
+ Handle<String> trap_name = factory->set_string();
+ ShouldThrow should_throw =
+ is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR;
+
+ if (proxy->IsRevoked()) {
+ isolate->Throw(
+ *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
+ return Nothing<bool>();
+ }
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
+
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
+ if (trap->IsUndefined()) {
+ LookupIterator it =
+ LookupIterator::PropertyOrElement(isolate, receiver, name, target);
+ return Object::SetSuperProperty(&it, value, language_mode,
+ Object::MAY_BE_STORE_FROM_KEYED);
}
- return Execution::Call(isolate, trap, handler, argc, argv);
+ Handle<Object> trap_result;
+ Handle<Object> args[] = {target, name, value, receiver};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result,
+ Execution::Call(isolate, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+ if (!trap_result->BooleanValue()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
+ trap_name, name));
+ }
+
+ // Enforce the invariant.
+ PropertyDescriptor target_desc;
+ Maybe<bool> owned =
+ JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
+ MAYBE_RETURN(owned, Nothing<bool>());
+ if (owned.FromJust()) {
+ bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) &&
+ !target_desc.configurable() &&
+ !target_desc.writable() &&
+ !value->SameValue(*target_desc.value());
+ if (inconsistent) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxySetFrozenData, name));
+ return Nothing<bool>();
+ }
+ inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
+ !target_desc.configurable() &&
+ target_desc.set()->IsUndefined();
+ if (inconsistent) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxySetFrozenAccessor, name));
+ return Nothing<bool>();
+ }
+ }
+ return Just(true);
+}
+
+
+Maybe<bool> JSProxy::DeletePropertyOrElement(Handle<JSProxy> proxy,
+ Handle<Name> name,
+ LanguageMode language_mode) {
+ DCHECK(!name->IsPrivate());
+ ShouldThrow should_throw =
+ is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR;
+ Isolate* isolate = proxy->GetIsolate();
+ STACK_CHECK(Nothing<bool>());
+ Factory* factory = isolate->factory();
+ Handle<String> trap_name = factory->deleteProperty_string();
+
+ if (proxy->IsRevoked()) {
+ isolate->Throw(
+ *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
+ return Nothing<bool>();
+ }
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
+
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
+ if (trap->IsUndefined()) {
+ return JSReceiver::DeletePropertyOrElement(target, name, language_mode);
+ }
+
+ Handle<Object> trap_result;
+ Handle<Object> args[] = {target, name};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result,
+ Execution::Call(isolate, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+ if (!trap_result->BooleanValue()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
+ trap_name, name));
+ }
+
+ // Enforce the invariant.
+ PropertyDescriptor target_desc;
+ Maybe<bool> owned =
+ JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
+ MAYBE_RETURN(owned, Nothing<bool>());
+ if (owned.FromJust() && !target_desc.configurable()) {
+ isolate->Throw(*factory->NewTypeError(
+ MessageTemplate::kProxyDeletePropertyNonConfigurable, name));
+ return Nothing<bool>();
+ }
+ return Just(true);
+}
+
+
+// static
+MaybeHandle<JSProxy> JSProxy::New(Isolate* isolate, Handle<Object> target,
+ Handle<Object> handler) {
+ if (!target->IsJSReceiver()) {
+ THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject),
+ JSProxy);
+ }
+ if (target->IsJSProxy() && JSProxy::cast(*target)->IsRevoked()) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kProxyHandlerOrTargetRevoked),
+ JSProxy);
+ }
+ if (!handler->IsJSReceiver()) {
+ THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject),
+ JSProxy);
+ }
+ if (handler->IsJSProxy() && JSProxy::cast(*handler)->IsRevoked()) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kProxyHandlerOrTargetRevoked),
+ JSProxy);
+ }
+ return isolate->factory()->NewJSProxy(Handle<JSReceiver>::cast(target),
+ Handle<JSReceiver>::cast(handler));
+}
+
+
+// static
+MaybeHandle<Context> JSProxy::GetFunctionRealm(Handle<JSProxy> proxy) {
+ DCHECK(proxy->map()->is_constructor());
+ if (proxy->IsRevoked()) {
+ THROW_NEW_ERROR(proxy->GetIsolate(),
+ NewTypeError(MessageTemplate::kProxyRevoked), Context);
+ }
+ Handle<JSReceiver> target(JSReceiver::cast(proxy->target()));
+ return JSReceiver::GetFunctionRealm(target);
+}
+
+
+// static
+MaybeHandle<Context> JSBoundFunction::GetFunctionRealm(
+ Handle<JSBoundFunction> function) {
+ DCHECK(function->map()->is_constructor());
+ return JSReceiver::GetFunctionRealm(
+ handle(function->bound_target_function()));
+}
+
+
+// static
+Handle<Context> JSFunction::GetFunctionRealm(Handle<JSFunction> function) {
+ DCHECK(function->map()->is_constructor());
+ return handle(function->context()->native_context());
+}
+
+
+// static
+MaybeHandle<Context> JSObject::GetFunctionRealm(Handle<JSObject> object) {
+ DCHECK(object->map()->is_constructor());
+ DCHECK(!object->IsJSFunction());
+ return handle(object->GetCreationContext());
+}
+
+
+// static
+MaybeHandle<Context> JSReceiver::GetFunctionRealm(Handle<JSReceiver> receiver) {
+ if (receiver->IsJSProxy()) {
+ return JSProxy::GetFunctionRealm(Handle<JSProxy>::cast(receiver));
+ }
+
+ if (receiver->IsJSFunction()) {
+ return JSFunction::GetFunctionRealm(Handle<JSFunction>::cast(receiver));
+ }
+
+ if (receiver->IsJSBoundFunction()) {
+ return JSBoundFunction::GetFunctionRealm(
+ Handle<JSBoundFunction>::cast(receiver));
+ }
+
+ return JSObject::GetFunctionRealm(Handle<JSObject>::cast(receiver));
+}
+
+
+Maybe<PropertyAttributes> JSProxy::GetPropertyAttributes(LookupIterator* it) {
+ Isolate* isolate = it->isolate();
+ HandleScope scope(isolate);
+ PropertyDescriptor desc;
+ Maybe<bool> found = JSProxy::GetOwnPropertyDescriptor(
+ isolate, it->GetHolder<JSProxy>(), it->GetName(), &desc);
+ MAYBE_RETURN(found, Nothing<PropertyAttributes>());
+ if (!found.FromJust()) return Just(ABSENT);
+ return Just(desc.ToAttributes());
}
void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) {
- DCHECK(object->map()->inobject_properties() == map->inobject_properties());
+ DCHECK(object->map()->GetInObjectProperties() ==
+ map->GetInObjectProperties());
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)) {
@@ -3990,6 +5153,11 @@
if (FLAG_trace_migration) {
object->PrintInstanceMigration(stdout, *original_map, *map);
}
+#if VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ object->JSObjectVerify();
+ }
+#endif
}
@@ -4006,32 +5174,12 @@
if (FLAG_trace_migration) {
object->PrintInstanceMigration(stdout, *original_map, object->map());
}
- return true;
-}
-
-
-void JSObject::WriteToField(int descriptor, Object* value) {
- DisallowHeapAllocation no_gc;
-
- DescriptorArray* desc = map()->instance_descriptors();
- PropertyDetails details = desc->GetDetails(descriptor);
-
- DCHECK(details.type() == FIELD);
-
- FieldIndex index = FieldIndex::ForDescriptor(map(), descriptor);
- if (details.representation().IsDouble()) {
- // Nothing more to be done.
- if (value->IsUninitialized()) return;
- if (IsUnboxedDoubleField(index)) {
- RawFastDoublePropertyAtPut(index, value->Number());
- } else {
- HeapNumber* box = HeapNumber::cast(RawFastPropertyAt(index));
- DCHECK(box->IsMutableHeapNumber());
- box->set_value(value->Number());
- }
- } else {
- RawFastPropertyAtPut(index, value);
+#if VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ object->JSObjectVerify();
}
+#endif
+ return true;
}
@@ -4045,186 +5193,255 @@
DCHECK(!object->IsJSProxy());
DCHECK(!name->AsArrayIndex(&index));
Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it);
- DCHECK(maybe.has_value);
+ DCHECK(maybe.IsJust());
DCHECK(!it.IsFound());
DCHECK(object->map()->is_extensible() ||
it.isolate()->IsInternallyUsedPropertyName(name));
#endif
- AddDataProperty(&it, value, attributes, STRICT,
- CERTAINLY_NOT_STORE_FROM_KEYED).Check();
+ CHECK(AddDataProperty(&it, value, attributes, THROW_ON_ERROR,
+ CERTAINLY_NOT_STORE_FROM_KEYED)
+ .IsJust());
+}
+
+
+// static
+void ExecutableAccessorInfo::ClearSetter(Handle<ExecutableAccessorInfo> info) {
+ Handle<Object> object = v8::FromCData(info->GetIsolate(), nullptr);
+ info->set_setter(*object);
}
// Reconfigures a property to a data property with attributes, even if it is not
// reconfigurable.
-MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
- Handle<JSObject> object,
- Handle<Name> name,
- Handle<Object> value,
- PropertyAttributes attributes,
+// Requires a LookupIterator that does not look at the prototype chain beyond
+// hidden prototypes.
+MaybeHandle<Object> JSObject::DefineOwnPropertyIgnoreAttributes(
+ LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
ExecutableAccessorInfoHandling handling) {
- DCHECK(!value->IsTheHole());
- LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
+ MAYBE_RETURN_NULL(DefineOwnPropertyIgnoreAttributes(
+ it, value, attributes, THROW_ON_ERROR, handling));
+ return value;
+}
+
+
+Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes(
+ LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
+ ShouldThrow should_throw, ExecutableAccessorInfoHandling handling) {
+ Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver());
bool is_observed = object->map()->is_observed() &&
- !it.isolate()->IsInternallyUsedPropertyName(name);
- for (; it.IsFound(); it.Next()) {
- switch (it.state()) {
- case LookupIterator::INTERCEPTOR:
+ (it->IsElement() ||
+ !it->isolate()->IsInternallyUsedPropertyName(it->name()));
+
+ for (; it->IsFound(); it->Next()) {
+ switch (it->state()) {
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
- if (!it.isolate()->MayNamedAccess(object, name, v8::ACCESS_SET)) {
- return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY);
+ if (!it->HasAccess()) {
+ it->isolate()->ReportFailedAccessCheck(it->GetHolder<JSObject>());
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>());
+ return Just(true);
+ }
+ break;
+
+ // If there's an interceptor, try to store the property with the
+ // interceptor.
+ // In case of success, the attributes will have been reset to the default
+ // attributes of the interceptor, rather than the incoming attributes.
+ //
+ // TODO(verwaest): JSProxy afterwards verify the attributes that the
+ // JSProxy claims it has, and verifies that they are compatible. If not,
+ // they throw. Here we should do the same.
+ case LookupIterator::INTERCEPTOR:
+ if (handling == DONT_FORCE_FIELD) {
+ Maybe<bool> result = JSObject::SetPropertyWithInterceptor(it, value);
+ if (result.IsNothing() || result.FromJust()) return result;
}
break;
case LookupIterator::ACCESSOR: {
- PropertyDetails details = it.property_details();
- // Ensure the context isn't changed after calling into accessors.
- AssertNoContextChange ncc(it.isolate());
-
- Handle<Object> accessors = it.GetAccessors();
+ Handle<Object> accessors = it->GetAccessors();
// Special handling for ExecutableAccessorInfo, which behaves like a
// data property.
- if (handling == DONT_FORCE_FIELD &&
- accessors->IsExecutableAccessorInfo()) {
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION(
- it.isolate(), result,
- JSObject::SetPropertyWithAccessor(it.GetReceiver(), it.name(),
- value, it.GetHolder<JSObject>(),
- accessors, STRICT),
- Object);
- DCHECK(result->SameValue(*value));
+ if (accessors->IsExecutableAccessorInfo() &&
+ handling == DONT_FORCE_FIELD) {
+ PropertyDetails details = it->property_details();
+ // Ensure the context isn't changed after calling into accessors.
+ AssertNoContextChange ncc(it->isolate());
- if (details.attributes() == attributes) {
- return value;
- }
+ Maybe<bool> result =
+ JSObject::SetPropertyWithAccessor(it, value, should_throw);
+ if (result.IsNothing() || !result.FromJust()) return result;
+
+ if (details.attributes() == attributes) return Just(true);
// Reconfigure the accessor if attributes mismatch.
Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor(
- it.isolate(), Handle<ExecutableAccessorInfo>::cast(accessors));
+ it->isolate(), Handle<ExecutableAccessorInfo>::cast(accessors));
new_data->set_property_attributes(attributes);
// By clearing the setter we don't have to introduce a lookup to
// the setter, simply make it unavailable to reflect the
// attributes.
- if (attributes & READ_ONLY) new_data->clear_setter();
- SetPropertyCallback(object, name, new_data, attributes);
- if (is_observed) {
- RETURN_ON_EXCEPTION(
- it.isolate(),
- EnqueueChangeRecord(object, "reconfigure", name,
- it.isolate()->factory()->the_hole_value()),
- Object);
+ if (attributes & READ_ONLY) {
+ ExecutableAccessorInfo::ClearSetter(new_data);
}
- return value;
- }
- it.ReconfigureDataProperty(value, attributes);
- it.PrepareForDataProperty(value);
- value = it.WriteDataValue(value);
+ it->TransitionToAccessorPair(new_data, attributes);
+ } else {
+ it->ReconfigureDataProperty(value, attributes);
+ }
if (is_observed) {
- RETURN_ON_EXCEPTION(
- it.isolate(),
- EnqueueChangeRecord(object, "reconfigure", name,
- it.isolate()->factory()->the_hole_value()),
- Object);
+ RETURN_ON_EXCEPTION_VALUE(
+ it->isolate(),
+ EnqueueChangeRecord(object, "reconfigure", it->GetName(),
+ it->factory()->the_hole_value()),
+ Nothing<bool>());
}
- return value;
+ return Just(true);
}
+ case LookupIterator::INTEGER_INDEXED_EXOTIC:
+ return RedefineIncompatibleProperty(it->isolate(), it->GetName(), value,
+ should_throw);
case LookupIterator::DATA: {
- PropertyDetails details = it.property_details();
- Handle<Object> old_value = it.isolate()->factory()->the_hole_value();
+ PropertyDetails details = it->property_details();
+ Handle<Object> old_value = it->factory()->the_hole_value();
// Regular property update if the attributes match.
if (details.attributes() == attributes) {
- return SetDataProperty(&it, value);
+ return SetDataProperty(it, value);
}
- // Reconfigure the data property if the attributes mismatch.
- if (is_observed) old_value = it.GetDataValue();
- it.ReconfigureDataProperty(value, attributes);
- it.PrepareForDataProperty(value);
- value = it.WriteDataValue(value);
+ // Special case: properties of typed arrays cannot be reconfigured to
+ // non-writable nor to non-enumerable.
+ if (it->IsElement() && object->HasFixedTypedArrayElements()) {
+ return RedefineIncompatibleProperty(it->isolate(), it->GetName(),
+ value, should_throw);
+ }
+
+ // Reconfigure the data property if the attributes mismatch.
+ if (is_observed) old_value = it->GetDataValue();
+
+ it->ReconfigureDataProperty(value, attributes);
if (is_observed) {
if (old_value->SameValue(*value)) {
- old_value = it.isolate()->factory()->the_hole_value();
+ old_value = it->factory()->the_hole_value();
}
- RETURN_ON_EXCEPTION(
- it.isolate(),
- EnqueueChangeRecord(object, "reconfigure", name, old_value),
- Object);
+ RETURN_ON_EXCEPTION_VALUE(
+ it->isolate(), EnqueueChangeRecord(object, "reconfigure",
+ it->GetName(), old_value),
+ Nothing<bool>());
}
-
- return value;
+ return Just(true);
}
}
}
- return AddDataProperty(&it, value, attributes, STRICT,
+ return AddDataProperty(it, value, attributes, should_throw,
CERTAINLY_NOT_STORE_FROM_KEYED);
}
-Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor(
- Handle<JSObject> holder,
- Handle<Object> receiver,
- Handle<Name> name) {
- Isolate* isolate = holder->GetIsolate();
- HandleScope scope(isolate);
-
- // Make sure that the top context does not change when doing
- // callbacks or interceptor calls.
- AssertNoContextChange ncc(isolate);
-
- Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor());
- if (name->IsSymbol() && !interceptor->can_intercept_symbols()) {
- return maybe(ABSENT);
- }
- PropertyCallbackArguments args(
- isolate, interceptor->data(), *receiver, *holder);
- if (!interceptor->query()->IsUndefined()) {
- v8::GenericNamedPropertyQueryCallback query =
- v8::ToCData<v8::GenericNamedPropertyQueryCallback>(
- interceptor->query());
- LOG(isolate,
- ApiNamedPropertyAccess("interceptor-named-has", *holder, *name));
- v8::Handle<v8::Integer> result = args.Call(query, v8::Utils::ToLocal(name));
- if (!result.IsEmpty()) {
- DCHECK(result->IsInt32());
- return maybe(static_cast<PropertyAttributes>(result->Int32Value()));
- }
- } else if (!interceptor->getter()->IsUndefined()) {
- v8::GenericNamedPropertyGetterCallback getter =
- v8::ToCData<v8::GenericNamedPropertyGetterCallback>(
- interceptor->getter());
- LOG(isolate,
- ApiNamedPropertyAccess("interceptor-named-get-has", *holder, *name));
- v8::Handle<v8::Value> result = args.Call(getter, v8::Utils::ToLocal(name));
- if (!result.IsEmpty()) return maybe(DONT_ENUM);
- }
-
- RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe<PropertyAttributes>());
- return maybe(ABSENT);
+MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
+ Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
+ PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) {
+ DCHECK(!value->IsTheHole());
+ LookupIterator it(object, name, LookupIterator::OWN);
+ return DefineOwnPropertyIgnoreAttributes(&it, value, attributes, handling);
}
-Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes(
- Handle<JSReceiver> object, Handle<Name> name) {
- // Check whether the name is an array index.
- uint32_t index = 0;
- if (object->IsJSObject() && name->AsArrayIndex(&index)) {
- return GetOwnElementAttribute(object, index);
+MaybeHandle<Object> JSObject::SetOwnElementIgnoreAttributes(
+ Handle<JSObject> object, uint32_t index, Handle<Object> value,
+ PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) {
+ Isolate* isolate = object->GetIsolate();
+ LookupIterator it(isolate, object, index, LookupIterator::OWN);
+ return DefineOwnPropertyIgnoreAttributes(&it, value, attributes, handling);
+}
+
+
+MaybeHandle<Object> JSObject::DefinePropertyOrElementIgnoreAttributes(
+ Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
+ PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) {
+ Isolate* isolate = object->GetIsolate();
+ LookupIterator it = LookupIterator::PropertyOrElement(isolate, object, name,
+ LookupIterator::OWN);
+ return DefineOwnPropertyIgnoreAttributes(&it, value, attributes, handling);
+}
+
+
+Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor(
+ LookupIterator* it) {
+ 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>();
+ Handle<InterceptorInfo> interceptor(it->GetInterceptor());
+ if (!it->IsElement() && it->name()->IsSymbol() &&
+ !interceptor->can_intercept_symbols()) {
+ return Just(ABSENT);
}
- LookupIterator it(object, name, LookupIterator::HIDDEN);
- return GetPropertyAttributes(&it);
+ PropertyCallbackArguments args(isolate, interceptor->data(),
+ *it->GetReceiver(), *holder);
+ if (!interceptor->query()->IsUndefined()) {
+ v8::Local<v8::Integer> result;
+ if (it->IsElement()) {
+ uint32_t index = it->index();
+ v8::IndexedPropertyQueryCallback query =
+ v8::ToCData<v8::IndexedPropertyQueryCallback>(interceptor->query());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-has", *holder, index));
+ result = args.Call(query, index);
+ } else {
+ Handle<Name> name = it->name();
+ DCHECK(!name->IsPrivate());
+ v8::GenericNamedPropertyQueryCallback query =
+ v8::ToCData<v8::GenericNamedPropertyQueryCallback>(
+ interceptor->query());
+ LOG(isolate,
+ ApiNamedPropertyAccess("interceptor-named-has", *holder, *name));
+ result = args.Call(query, v8::Utils::ToLocal(name));
+ }
+ if (!result.IsEmpty()) {
+ DCHECK(result->IsInt32());
+ return Just(static_cast<PropertyAttributes>(
+ result->Int32Value(reinterpret_cast<v8::Isolate*>(isolate)
+ ->GetCurrentContext()).FromJust()));
+ }
+ } else if (!interceptor->getter()->IsUndefined()) {
+ // TODO(verwaest): Use GetPropertyWithInterceptor?
+ v8::Local<v8::Value> result;
+ if (it->IsElement()) {
+ uint32_t index = it->index();
+ v8::IndexedPropertyGetterCallback getter =
+ v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter());
+ LOG(isolate, ApiIndexedPropertyAccess("interceptor-indexed-get-has",
+ *holder, index));
+ result = args.Call(getter, index);
+ } else {
+ Handle<Name> name = it->name();
+ DCHECK(!name->IsPrivate());
+ v8::GenericNamedPropertyGetterCallback getter =
+ v8::ToCData<v8::GenericNamedPropertyGetterCallback>(
+ interceptor->getter());
+ LOG(isolate,
+ ApiNamedPropertyAccess("interceptor-named-get-has", *holder, *name));
+ result = args.Call(getter, v8::Utils::ToLocal(name));
+ }
+ if (!result.IsEmpty()) return Just(DONT_ENUM);
+ }
+
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>());
+ return Just(ABSENT);
}
@@ -4236,136 +5453,25 @@
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::JSPROXY:
- return JSProxy::GetPropertyAttributesWithHandler(
- it->GetHolder<JSProxy>(), it->GetReceiver(), it->name());
+ return JSProxy::GetPropertyAttributes(it);
case LookupIterator::INTERCEPTOR: {
Maybe<PropertyAttributes> result =
- JSObject::GetPropertyAttributesWithInterceptor(
- it->GetHolder<JSObject>(), it->GetReceiver(), it->name());
- if (!result.has_value) return result;
- if (result.value != ABSENT) return result;
+ JSObject::GetPropertyAttributesWithInterceptor(it);
+ if (!result.IsJust()) return result;
+ if (result.FromJust() != ABSENT) return result;
break;
}
case LookupIterator::ACCESS_CHECK:
- if (it->HasAccess(v8::ACCESS_HAS)) break;
+ if (it->HasAccess()) break;
return JSObject::GetPropertyAttributesWithFailedAccessCheck(it);
+ case LookupIterator::INTEGER_INDEXED_EXOTIC:
+ return Just(ABSENT);
case LookupIterator::ACCESSOR:
case LookupIterator::DATA:
- return maybe(it->property_details().attributes());
+ return Just(it->property_details().attributes());
}
}
- return maybe(ABSENT);
-}
-
-
-Maybe<PropertyAttributes> JSObject::GetElementAttributeWithReceiver(
- Handle<JSObject> object, Handle<JSReceiver> receiver, uint32_t index,
- bool check_prototype) {
- Isolate* isolate = object->GetIsolate();
-
- // Check access rights if needed.
- if (object->IsAccessCheckNeeded()) {
- if (!isolate->MayIndexedAccess(object, index, v8::ACCESS_HAS)) {
- return GetElementAttributesWithFailedAccessCheck(isolate, object,
- receiver, index);
- }
- }
-
- if (object->IsJSGlobalProxy()) {
- PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return maybe(ABSENT);
- DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
- return JSObject::GetElementAttributeWithReceiver(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), receiver,
- index, check_prototype);
- }
-
- // Check for lookup interceptor except when bootstrapping.
- if (object->HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) {
- return JSObject::GetElementAttributeWithInterceptor(
- object, receiver, index, check_prototype);
- }
-
- return GetElementAttributeWithoutInterceptor(
- object, receiver, index, check_prototype);
-}
-
-
-Maybe<PropertyAttributes> JSObject::GetElementAttributeWithInterceptor(
- Handle<JSObject> object, Handle<JSReceiver> receiver, uint32_t index,
- bool check_prototype) {
- Isolate* isolate = object->GetIsolate();
- HandleScope scope(isolate);
-
- // Make sure that the top context does not change when doing
- // callbacks or interceptor calls.
- AssertNoContextChange ncc(isolate);
-
- Maybe<PropertyAttributes> from_interceptor =
- GetElementAttributeFromInterceptor(object, receiver, index);
- if (!from_interceptor.has_value) return Maybe<PropertyAttributes>();
- if (from_interceptor.value != ABSENT) return maybe(from_interceptor.value);
-
- return GetElementAttributeWithoutInterceptor(object, receiver, index,
- check_prototype);
-}
-
-
-Maybe<PropertyAttributes> JSObject::GetElementAttributeFromInterceptor(
- Handle<JSObject> object, Handle<Object> receiver, uint32_t index) {
- Isolate* isolate = object->GetIsolate();
- AssertNoContextChange ncc(isolate);
-
- Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor());
- PropertyCallbackArguments args(
- isolate, interceptor->data(), *receiver, *object);
- if (!interceptor->query()->IsUndefined()) {
- v8::IndexedPropertyQueryCallback query =
- v8::ToCData<v8::IndexedPropertyQueryCallback>(interceptor->query());
- LOG(isolate,
- ApiIndexedPropertyAccess("interceptor-indexed-has", *object, index));
- v8::Handle<v8::Integer> result = args.Call(query, index);
- if (!result.IsEmpty())
- return maybe(static_cast<PropertyAttributes>(result->Int32Value()));
- } else if (!interceptor->getter()->IsUndefined()) {
- v8::IndexedPropertyGetterCallback getter =
- v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter());
- LOG(isolate,
- ApiIndexedPropertyAccess(
- "interceptor-indexed-get-has", *object, index));
- v8::Handle<v8::Value> result = args.Call(getter, index);
- if (!result.IsEmpty()) return maybe(NONE);
- }
- RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe<PropertyAttributes>());
- return maybe(ABSENT);
-}
-
-
-Maybe<PropertyAttributes> JSObject::GetElementAttributeWithoutInterceptor(
- Handle<JSObject> object, Handle<JSReceiver> receiver, uint32_t index,
- bool check_prototype) {
- PropertyAttributes attr = object->GetElementsAccessor()->GetAttributes(
- receiver, object, index);
- if (attr != ABSENT) return maybe(attr);
-
- // Handle [] on String objects.
- if (object->IsStringObjectWithCharacterAt(index)) {
- return maybe(static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE));
- }
-
- if (!check_prototype) return maybe(ABSENT);
-
- PrototypeIterator iter(object->GetIsolate(), object);
- if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
- // We need to follow the spec and simulate a call to [[GetOwnProperty]].
- return JSProxy::GetElementAttributeWithHandler(
- Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), receiver,
- index);
- }
- if (iter.IsAtEnd()) return maybe(ABSENT);
- return GetElementAttributeWithReceiver(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), receiver,
- index, true);
+ return Just(ABSENT);
}
@@ -4421,7 +5527,7 @@
Handle<Map> map(object->map());
Handle<Map> new_map = Map::Normalize(map, mode, reason);
- MigrateFastToSlow(object, new_map, expected_additional_properties);
+ MigrateToMap(object, new_map, expected_additional_properties);
}
@@ -4429,7 +5535,7 @@
Handle<Map> new_map,
int expected_additional_properties) {
// The global object is always normalized.
- DCHECK(!object->IsGlobalObject());
+ DCHECK(!object->IsJSGlobalObject());
// JSGlobalProxy must never be normalized
DCHECK(!object->IsJSGlobalProxy());
@@ -4453,13 +5559,14 @@
PropertyDetails details = descs->GetDetails(i);
Handle<Name> key(descs->GetKey(i));
switch (details.type()) {
- case CONSTANT: {
+ case DATA_CONSTANT: {
Handle<Object> value(descs->GetConstant(i), isolate);
- PropertyDetails d(details.attributes(), FIELD, i + 1);
+ PropertyDetails d(details.attributes(), DATA, i + 1,
+ PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
- case FIELD: {
+ case DATA: {
FieldIndex index = FieldIndex::ForDescriptor(*map, i);
Handle<Object> value;
if (object->IsUnboxedDoubleField(index)) {
@@ -4473,20 +5580,23 @@
value = isolate->factory()->NewHeapNumber(old->value());
}
}
- PropertyDetails d(details.attributes(), FIELD, i + 1);
+ PropertyDetails d(details.attributes(), DATA, i + 1,
+ PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
- case ACCESSOR_FIELD: {
+ case ACCESSOR: {
FieldIndex index = FieldIndex::ForDescriptor(*map, i);
Handle<Object> value(object->RawFastPropertyAt(index), isolate);
- PropertyDetails d(details.attributes(), CALLBACKS, i + 1);
+ PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1,
+ PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
- case CALLBACKS: {
+ case ACCESSOR_CONSTANT: {
Handle<Object> value(descs->GetCallbacksObject(i), isolate);
- PropertyDetails d(details.attributes(), CALLBACKS, i + 1);
+ PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1,
+ PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
@@ -4508,8 +5618,8 @@
Heap* heap = isolate->heap();
heap->CreateFillerObjectAt(object->address() + new_instance_size,
instance_size_delta);
- heap->AdjustLiveBytes(object->address(), -instance_size_delta,
- Heap::FROM_MUTATOR);
+ heap->AdjustLiveBytes(*object, -instance_size_delta,
+ Heap::CONCURRENT_TO_SWEEPER);
}
// We are storing the new map using release store after creating a filler for
@@ -4520,7 +5630,7 @@
// Ensure that in-object space of slow-mode object does not contain random
// garbage.
- int inobject_properties = new_map->inobject_properties();
+ int inobject_properties = new_map->GetInObjectProperties();
for (int i = 0; i < inobject_properties; i++) {
FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
object->RawFastPropertyAtPut(index, Smi::FromInt(0));
@@ -4542,7 +5652,7 @@
int unused_property_fields,
const char* reason) {
if (object->HasFastProperties()) return;
- DCHECK(!object->IsGlobalObject());
+ DCHECK(!object->IsJSGlobalObject());
Isolate* isolate = object->GetIsolate();
Factory* factory = isolate->factory();
Handle<NameDictionary> dictionary(object->property_dictionary());
@@ -4570,22 +5680,26 @@
Object* value = dictionary->ValueAt(index);
PropertyType type = dictionary->DetailsAt(index).type();
- if (type == FIELD && !value->IsJSFunction()) {
+ if (type == DATA && !value->IsJSFunction()) {
number_of_fields += 1;
}
}
- int inobject_props = object->map()->inobject_properties();
+ Handle<Map> old_map(object->map(), isolate);
+
+ int inobject_props = old_map->GetInObjectProperties();
// Allocate new map.
- Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
+ Handle<Map> new_map = Map::CopyDropDescriptors(old_map);
new_map->set_dictionary_map(false);
+ UpdatePrototypeUserRegistration(old_map, new_map, isolate);
+
#if TRACE_MAPS
if (FLAG_trace_maps) {
PrintF("[TraceMaps: SlowToFast from= %p to= %p reason= %s ]\n",
- reinterpret_cast<void*>(object->map()),
- reinterpret_cast<void*>(*new_map), reason);
+ reinterpret_cast<void*>(*old_map), reinterpret_cast<void*>(*new_map),
+ reason);
}
#endif
@@ -4623,25 +5737,22 @@
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();
PropertyType type = details.type();
if (value->IsJSFunction()) {
- ConstantDescriptor d(key, handle(value, isolate), details.attributes());
+ DataConstantDescriptor d(key, handle(value, isolate),
+ details.attributes());
descriptors->Set(enumeration_index - 1, &d);
- } else if (type == FIELD) {
+ } else if (type == DATA) {
if (current_offset < inobject_props) {
object->InObjectPropertyAtPut(current_offset, value,
UPDATE_WRITE_BARRIER);
@@ -4649,13 +5760,14 @@
int offset = current_offset - inobject_props;
fields->set(offset, value);
}
- FieldDescriptor d(key, current_offset, details.attributes(),
- // TODO(verwaest): value->OptimalRepresentation();
- Representation::Tagged());
+ DataDescriptor d(key, current_offset, details.attributes(),
+ // TODO(verwaest): value->OptimalRepresentation();
+ Representation::Tagged());
current_offset += d.GetDetails().field_width_in_words();
descriptors->Set(enumeration_index - 1, &d);
- } else if (type == CALLBACKS) {
- CallbacksDescriptor d(key, handle(value, isolate), details.attributes());
+ } else if (type == ACCESSOR_CONSTANT) {
+ AccessorConstantDescriptor d(key, handle(value, isolate),
+ details.attributes());
descriptors->Set(enumeration_index - 1, &d);
} else {
UNREACHABLE();
@@ -4697,9 +5809,8 @@
static Handle<SeededNumberDictionary> CopyFastElementsToDictionary(
- Handle<FixedArrayBase> array,
- int length,
- Handle<SeededNumberDictionary> dictionary) {
+ Handle<FixedArrayBase> array, int length,
+ Handle<SeededNumberDictionary> dictionary, bool used_as_prototype) {
Isolate* isolate = array->GetIsolate();
Factory* factory = isolate->factory();
bool has_double_elements = array->IsFixedDoubleArray();
@@ -4717,56 +5828,78 @@
value = handle(Handle<FixedArray>::cast(array)->get(i), isolate);
}
if (!value->IsTheHole()) {
- PropertyDetails details(NONE, FIELD, 0);
- dictionary =
- SeededNumberDictionary::AddNumberEntry(dictionary, i, value, details);
+ PropertyDetails details = PropertyDetails::Empty();
+ dictionary = SeededNumberDictionary::AddNumberEntry(
+ dictionary, i, value, details, used_as_prototype);
}
}
return dictionary;
}
+void JSObject::RequireSlowElements(SeededNumberDictionary* dictionary) {
+ if (dictionary->requires_slow_elements()) return;
+ dictionary->set_requires_slow_elements();
+ // TODO(verwaest): Remove this hack.
+ if (map()->is_prototype_map()) {
+ TypeFeedbackVector::ClearAllKeyedStoreICs(GetIsolate());
+ }
+}
+
+
+Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
+ Handle<JSObject> object, Handle<FixedArrayBase> elements) {
+ DCHECK(!object->HasDictionaryElements());
+ DCHECK(!object->HasSlowArgumentsElements());
+ Isolate* isolate = object->GetIsolate();
+ // Ensure that notifications fire if the array or object prototypes are
+ // normalizing.
+ isolate->UpdateArrayProtectorOnNormalizeElements(object);
+ int length = object->IsJSArray()
+ ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
+ : elements->length();
+ int used = object->GetFastElementsUsage();
+ Handle<SeededNumberDictionary> dictionary =
+ SeededNumberDictionary::New(isolate, used);
+ return CopyFastElementsToDictionary(elements, length, dictionary,
+ object->map()->is_prototype_map());
+}
+
+
Handle<SeededNumberDictionary> JSObject::NormalizeElements(
Handle<JSObject> object) {
- DCHECK(!object->HasExternalArrayElements() &&
- !object->HasFixedTypedArrayElements());
+ DCHECK(!object->HasFixedTypedArrayElements());
Isolate* isolate = object->GetIsolate();
// Find the backing store.
- Handle<FixedArrayBase> array(FixedArrayBase::cast(object->elements()));
- bool is_arguments =
- (array->map() == isolate->heap()->sloppy_arguments_elements_map());
+ Handle<FixedArrayBase> elements(object->elements(), isolate);
+ bool is_arguments = object->HasSloppyArgumentsElements();
if (is_arguments) {
- array = handle(FixedArrayBase::cast(
- Handle<FixedArray>::cast(array)->get(1)));
+ FixedArray* parameter_map = FixedArray::cast(*elements);
+ elements = handle(FixedArrayBase::cast(parameter_map->get(1)), isolate);
}
- if (array->IsDictionary()) return Handle<SeededNumberDictionary>::cast(array);
+
+ if (elements->IsDictionary()) {
+ return Handle<SeededNumberDictionary>::cast(elements);
+ }
DCHECK(object->HasFastSmiOrObjectElements() ||
object->HasFastDoubleElements() ||
object->HasFastArgumentsElements());
- // Compute the effective length and allocate a new backing store.
- int length = object->IsJSArray()
- ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
- : array->length();
- int old_capacity = 0;
- int used_elements = 0;
- object->GetElementsCapacityAndUsage(&old_capacity, &used_elements);
- Handle<SeededNumberDictionary> dictionary =
- SeededNumberDictionary::New(isolate, used_elements);
- dictionary = CopyFastElementsToDictionary(array, length, dictionary);
+ Handle<SeededNumberDictionary> dictionary =
+ GetNormalizedElementDictionary(object, elements);
// Switch to using the dictionary as the backing storage for elements.
+ ElementsKind target_kind =
+ is_arguments ? SLOW_SLOPPY_ARGUMENTS_ELEMENTS : DICTIONARY_ELEMENTS;
+ Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, target_kind);
+ // Set the new map first to satify the elements type assert in set_elements().
+ JSObject::MigrateToMap(object, new_map);
+
if (is_arguments) {
FixedArray::cast(object->elements())->set(1, *dictionary);
} else {
- // Set the new map first to satify the elements type assert in
- // set_elements().
- Handle<Map> new_map =
- JSObject::GetElementsTransitionMap(object, DICTIONARY_ELEMENTS);
-
- JSObject::MigrateToMap(object, new_map);
object->set_elements(*dictionary);
}
@@ -4780,8 +5913,7 @@
}
#endif
- DCHECK(object->HasDictionaryElements() ||
- object->HasDictionaryArgumentsElements());
+ DCHECK(object->HasDictionaryElements() || object->HasSlowArgumentsElements());
return dictionary;
}
@@ -4804,7 +5936,8 @@
void JSObject::SetIdentityHash(Handle<JSObject> object, Handle<Smi> hash) {
DCHECK(!object->IsJSGlobalProxy());
Isolate* isolate = object->GetIsolate();
- SetHiddenProperty(object, isolate->factory()->identity_hash_string(), hash);
+ Handle<Name> hash_code_symbol(isolate->heap()->hash_code_symbol());
+ JSObject::AddProperty(object, hash_code_symbol, hash, NONE);
}
@@ -4827,11 +5960,12 @@
if (IsJSGlobalProxy()) {
return JSGlobalProxy::cast(this)->hash();
}
- Object* stored_value =
- GetHiddenProperty(isolate->factory()->identity_hash_string());
- return stored_value->IsSmi()
- ? stored_value
- : isolate->heap()->undefined_value();
+ Handle<Name> hash_code_symbol(isolate->heap()->hash_code_symbol());
+ Handle<Object> stored_value =
+ Object::GetPropertyOrElement(Handle<Object>(this, isolate),
+ hash_code_symbol).ToHandleChecked();
+ return stored_value->IsSmi() ? *stored_value
+ : isolate->heap()->undefined_value();
}
@@ -4839,14 +5973,14 @@
if (object->IsJSGlobalProxy()) {
return GetOrCreateIdentityHashHelper(Handle<JSGlobalProxy>::cast(object));
}
-
Isolate* isolate = object->GetIsolate();
Handle<Object> maybe_hash(object->GetIdentityHash(), isolate);
if (maybe_hash->IsSmi()) return Handle<Smi>::cast(maybe_hash);
Handle<Smi> hash(GenerateIdentityHash(isolate), isolate);
- SetHiddenProperty(object, isolate->factory()->identity_hash_string(), hash);
+ Handle<Name> hash_code_symbol(isolate->heap()->hash_code_symbol());
+ JSObject::AddProperty(object, hash_code_symbol, hash, NONE);
return hash;
}
@@ -4865,27 +5999,16 @@
DisallowHeapAllocation no_gc;
DCHECK(key->IsUniqueName());
if (IsJSGlobalProxy()) {
- // JSGlobalProxies store their hash internally.
- DCHECK(*key != GetHeap()->identity_hash_string());
// For a proxy, use the prototype as target object.
PrototypeIterator iter(GetIsolate(), this);
// 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();
- if (inline_value->IsSmi()) {
- // Handle inline-stored identity hash.
- if (*key == GetHeap()->identity_hash_string()) {
- return inline_value;
- } else {
- return GetHeap()->the_hole_value();
- }
- }
-
if (inline_value->IsUndefined()) return GetHeap()->the_hole_value();
ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value);
@@ -4901,28 +6024,18 @@
DCHECK(key->IsUniqueName());
if (object->IsJSGlobalProxy()) {
- // JSGlobalProxies store their hash internally.
- DCHECK(*key != *isolate->factory()->identity_hash_string());
// For a proxy, use the prototype as target object.
PrototypeIterator iter(isolate, 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());
Handle<Object> inline_value(object->GetHiddenPropertiesHashTable(), isolate);
- // If there is no backing store yet, store the identity hash inline.
- if (value->IsSmi() &&
- *key == *isolate->factory()->identity_hash_string() &&
- (inline_value->IsUndefined() || inline_value->IsSmi())) {
- return JSObject::SetHiddenPropertiesHashTable(object, value);
- }
-
Handle<ObjectHashTable> hashtable =
GetOrCreateHiddenPropertiesHashtable(object);
@@ -4948,15 +6061,13 @@
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();
- // We never delete (inline-stored) identity hashes.
- DCHECK(*key != *isolate->factory()->identity_hash_string());
- if (inline_value->IsUndefined() || inline_value->IsSmi()) return;
+ if (inline_value->IsUndefined()) return;
Handle<ObjectHashTable> hashtable(ObjectHashTable::cast(inline_value));
bool was_present = false;
@@ -4969,8 +6080,8 @@
LookupIterator it(object, hidden, LookupIterator::OWN_SKIP_INTERCEPTOR);
Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it);
// Cannot get an exception since the hidden_string isn't accessible to JS.
- DCHECK(maybe.has_value);
- return maybe.value != ABSENT;
+ DCHECK(maybe.IsJust());
+ return maybe.FromJust() != ABSENT;
}
@@ -4986,7 +6097,7 @@
int sorted_index = descriptors->GetSortedKeyIndex(0);
if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_string() &&
sorted_index < map()->NumberOfOwnDescriptors()) {
- DCHECK(descriptors->GetType(sorted_index) == FIELD);
+ DCHECK(descriptors->GetType(sorted_index) == DATA);
DCHECK(descriptors->GetDetails(sorted_index).representation().
IsCompatibleForLoad(Representation::Tagged()));
FieldIndex index = FieldIndex::ForDescriptor(this->map(),
@@ -5020,14 +6131,7 @@
Handle<ObjectHashTable> hashtable = ObjectHashTable::New(
isolate, kInitialCapacity, USE_CUSTOM_MINIMUM_CAPACITY);
- if (inline_value->IsSmi()) {
- // We were storing the identity hash inline and now allocated an actual
- // dictionary. Put the identity hash into the new dictionary.
- hashtable = ObjectHashTable::Put(hashtable,
- isolate->factory()->identity_hash_string(),
- inline_value);
- }
-
+ DCHECK(inline_value->IsUndefined());
SetHiddenPropertiesHashTable(object, hashtable);
return hashtable;
}
@@ -5043,255 +6147,1265 @@
}
-MaybeHandle<Object> JSObject::DeletePropertyWithInterceptor(
- Handle<JSObject> holder, Handle<JSObject> receiver, Handle<Name> name) {
- Isolate* isolate = holder->GetIsolate();
+Maybe<bool> JSObject::DeletePropertyWithInterceptor(LookupIterator* it) {
+ Isolate* isolate = it->isolate();
+ // Make sure that the top context does not change when doing callbacks or
+ // interceptor calls.
+ AssertNoContextChange ncc(isolate);
- Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor());
- if (interceptor->deleter()->IsUndefined() ||
- (name->IsSymbol() && !interceptor->can_intercept_symbols())) {
- return MaybeHandle<Object>();
+ DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
+ Handle<InterceptorInfo> interceptor(it->GetInterceptor());
+ if (interceptor->deleter()->IsUndefined()) return Nothing<bool>();
+
+ Handle<JSObject> holder = it->GetHolder<JSObject>();
+
+ PropertyCallbackArguments args(isolate, interceptor->data(),
+ *it->GetReceiver(), *holder);
+ v8::Local<v8::Boolean> result;
+ if (it->IsElement()) {
+ uint32_t index = it->index();
+ v8::IndexedPropertyDeleterCallback deleter =
+ v8::ToCData<v8::IndexedPropertyDeleterCallback>(interceptor->deleter());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-delete", *holder, index));
+ result = args.Call(deleter, index);
+ } else if (it->name()->IsSymbol() && !interceptor->can_intercept_symbols()) {
+ return Nothing<bool>();
+ } else {
+ Handle<Name> name = it->name();
+ DCHECK(!name->IsPrivate());
+ v8::GenericNamedPropertyDeleterCallback deleter =
+ v8::ToCData<v8::GenericNamedPropertyDeleterCallback>(
+ interceptor->deleter());
+ LOG(isolate,
+ ApiNamedPropertyAccess("interceptor-named-delete", *holder, *name));
+ result = args.Call(deleter, v8::Utils::ToLocal(name));
}
- v8::GenericNamedPropertyDeleterCallback deleter =
- v8::ToCData<v8::GenericNamedPropertyDeleterCallback>(
- interceptor->deleter());
- LOG(isolate,
- ApiNamedPropertyAccess("interceptor-named-delete", *holder, *name));
- PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
- *holder);
- v8::Handle<v8::Boolean> result = args.Call(deleter, v8::Utils::ToLocal(name));
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- if (result.IsEmpty()) return MaybeHandle<Object>();
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ if (result.IsEmpty()) return Nothing<bool>();
DCHECK(result->IsBoolean());
Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
result_internal->VerifyApiCallResultType();
// Rebox CustomArguments::kReturnValueOffset before returning.
- return handle(*result_internal, isolate);
+ return Just(result_internal->BooleanValue());
}
-MaybeHandle<Object> JSObject::DeleteElementWithInterceptor(
- Handle<JSObject> object,
- uint32_t index) {
+void JSReceiver::DeleteNormalizedProperty(Handle<JSReceiver> object,
+ Handle<Name> name, int entry) {
+ DCHECK(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
- Factory* factory = isolate->factory();
- // Make sure that the top context does not change when doing
- // callbacks or interceptor calls.
- AssertNoContextChange ncc(isolate);
+ if (object->IsJSGlobalObject()) {
+ // If we have a global object, invalidate the cell and swap in a new one.
+ Handle<GlobalDictionary> dictionary(
+ JSObject::cast(*object)->global_dictionary());
+ DCHECK_NE(GlobalDictionary::kNotFound, entry);
- Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor());
- if (interceptor->deleter()->IsUndefined()) return factory->false_value();
- v8::IndexedPropertyDeleterCallback deleter =
- v8::ToCData<v8::IndexedPropertyDeleterCallback>(interceptor->deleter());
- LOG(isolate,
- ApiIndexedPropertyAccess("interceptor-indexed-delete", *object, index));
- PropertyCallbackArguments args(
- isolate, interceptor->data(), *object, *object);
- v8::Handle<v8::Boolean> result = args.Call(deleter, index);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- if (!result.IsEmpty()) {
- DCHECK(result->IsBoolean());
- Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
- result_internal->VerifyApiCallResultType();
- // Rebox CustomArguments::kReturnValueOffset before returning.
- return handle(*result_internal, isolate);
- }
- MaybeHandle<Object> delete_result = object->GetElementsAccessor()->Delete(
- object, index, NORMAL_DELETION);
- return delete_result;
-}
-
-
-MaybeHandle<Object> JSObject::DeleteElement(Handle<JSObject> object,
- uint32_t index,
- DeleteMode mode) {
- Isolate* isolate = object->GetIsolate();
- Factory* factory = isolate->factory();
-
- // Check access rights if needed.
- if (object->IsAccessCheckNeeded() &&
- !isolate->MayIndexedAccess(object, index, v8::ACCESS_DELETE)) {
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_DELETE);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return factory->false_value();
- }
-
- if (object->IsStringObjectWithCharacterAt(index)) {
- if (mode == STRICT_DELETION) {
- // Deleting a non-configurable property in strict mode.
- Handle<Object> name = factory->NewNumberFromUint(index);
- Handle<Object> args[2] = { name, object };
- THROW_NEW_ERROR(isolate, NewTypeError("strict_delete_property",
- HandleVector(args, 2)),
- Object);
- }
- return factory->false_value();
- }
-
- if (object->IsJSGlobalProxy()) {
- PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return factory->false_value();
- DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
- return DeleteElement(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index,
- mode);
- }
-
- Handle<Object> old_value;
- bool should_enqueue_change_record = false;
- if (object->map()->is_observed()) {
- Maybe<bool> maybe = HasOwnElement(object, index);
- if (!maybe.has_value) return MaybeHandle<Object>();
- should_enqueue_change_record = maybe.value;
- if (should_enqueue_change_record) {
- if (!GetOwnElementAccessorPair(object, index).is_null()) {
- old_value = Handle<Object>::cast(factory->the_hole_value());
- } else {
- old_value = Object::GetElement(
- isolate, object, index).ToHandleChecked();
- }
- }
- }
-
- // Skip interceptor if forcing deletion.
- MaybeHandle<Object> maybe_result;
- if (object->HasIndexedInterceptor() && mode != FORCE_DELETION) {
- maybe_result = DeleteElementWithInterceptor(object, index);
+ auto cell = PropertyCell::InvalidateEntry(dictionary, entry);
+ cell->set_value(isolate->heap()->the_hole_value());
+ // TODO(ishell): InvalidateForDelete
+ cell->set_property_details(
+ cell->property_details().set_cell_type(PropertyCellType::kInvalidated));
} else {
- maybe_result = object->GetElementsAccessor()->Delete(object, index, mode);
- }
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION(isolate, result, maybe_result, Object);
+ Handle<NameDictionary> dictionary(object->property_dictionary());
+ DCHECK_NE(NameDictionary::kNotFound, entry);
- if (should_enqueue_change_record) {
- Maybe<bool> maybe = HasOwnElement(object, index);
- if (!maybe.has_value) return MaybeHandle<Object>();
- if (!maybe.value) {
- Handle<String> name = factory->Uint32ToString(index);
- RETURN_ON_EXCEPTION(
- isolate, EnqueueChangeRecord(object, "delete", name, old_value),
- Object);
- }
+ NameDictionary::DeleteProperty(dictionary, entry);
+ Handle<NameDictionary> new_properties =
+ NameDictionary::Shrink(dictionary, name);
+ object->set_properties(*new_properties);
}
-
- return result;
}
-MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
- Handle<Name> name,
- DeleteMode delete_mode) {
- // ECMA-262, 3rd, 8.6.2.5
- DCHECK(name->IsName());
+Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it,
+ LanguageMode language_mode) {
+ Isolate* isolate = it->isolate();
- uint32_t index = 0;
- if (name->AsArrayIndex(&index)) {
- return DeleteElement(object, index, delete_mode);
+ if (it->state() == LookupIterator::JSPROXY) {
+ return JSProxy::DeletePropertyOrElement(it->GetHolder<JSProxy>(),
+ it->GetName(), language_mode);
}
- // Skip interceptors on FORCE_DELETION.
- LookupIterator::Configuration config =
- delete_mode == FORCE_DELETION ? LookupIterator::HIDDEN_SKIP_INTERCEPTOR
- : LookupIterator::HIDDEN;
+ if (it->GetReceiver()->IsJSProxy()) {
+ if (it->state() != LookupIterator::NOT_FOUND) {
+ DCHECK_EQ(LookupIterator::DATA, it->state());
+ DCHECK(it->GetName()->IsPrivate());
+ it->Delete();
+ }
+ return Just(true);
+ }
+ Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver());
- LookupIterator it(object, name, config);
+ bool is_observed =
+ receiver->map()->is_observed() &&
+ (it->IsElement() || !isolate->IsInternallyUsedPropertyName(it->name()));
- bool is_observed = object->map()->is_observed() &&
- !it.isolate()->IsInternallyUsedPropertyName(name);
- Handle<Object> old_value = it.isolate()->factory()->the_hole_value();
+ Handle<Object> old_value = it->factory()->the_hole_value();
- for (; it.IsFound(); it.Next()) {
- switch (it.state()) {
+ for (; it->IsFound(); it->Next()) {
+ switch (it->state()) {
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
- if (it.HasAccess(v8::ACCESS_DELETE)) break;
- it.isolate()->ReportFailedAccessCheck(it.GetHolder<JSObject>(),
- v8::ACCESS_DELETE);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it.isolate(), Object);
- return it.isolate()->factory()->false_value();
+ if (it->HasAccess()) break;
+ isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>());
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ return Just(false);
case LookupIterator::INTERCEPTOR: {
- MaybeHandle<Object> maybe_result =
- JSObject::DeletePropertyWithInterceptor(it.GetHolder<JSObject>(),
- object, it.name());
- // Delete with interceptor succeeded. Return result.
- if (!maybe_result.is_null()) return maybe_result;
+ Maybe<bool> result = JSObject::DeletePropertyWithInterceptor(it);
// An exception was thrown in the interceptor. Propagate.
- if (it.isolate()->has_pending_exception()) return maybe_result;
+ if (isolate->has_pending_exception()) return Nothing<bool>();
+ // Delete with interceptor succeeded. Return result.
+ // TODO(neis): In strict mode, we should probably throw if the
+ // interceptor returns false.
+ if (result.IsJust()) return result;
break;
}
+ case LookupIterator::INTEGER_INDEXED_EXOTIC:
+ return Just(true);
case LookupIterator::DATA:
if (is_observed) {
- old_value = it.GetDataValue();
+ old_value = it->GetDataValue();
}
// Fall through.
case LookupIterator::ACCESSOR: {
- if (delete_mode != FORCE_DELETION && !it.IsConfigurable()) {
- // Fail if the property is not configurable.
- if (delete_mode == STRICT_DELETION) {
- Handle<Object> args[2] = {name, object};
- THROW_NEW_ERROR(it.isolate(),
- NewTypeError("strict_delete_property",
- HandleVector(args, arraysize(args))),
- Object);
+ if (!it->IsConfigurable() || receiver->map()->is_strong()) {
+ // Fail if the property is not configurable, or on a strong object.
+ if (is_strict(language_mode)) {
+ MessageTemplate::Template templ =
+ receiver->map()->is_strong()
+ ? MessageTemplate::kStrongDeleteProperty
+ : MessageTemplate::kStrictDeleteProperty;
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ templ, it->GetName(), receiver));
+ return Nothing<bool>();
}
- return it.isolate()->factory()->false_value();
+ return Just(false);
}
- PropertyNormalizationMode mode = object->map()->is_prototype_map()
- ? KEEP_INOBJECT_PROPERTIES
- : CLEAR_INOBJECT_PROPERTIES;
- Handle<JSObject> holder = it.GetHolder<JSObject>();
- // TODO(verwaest): Remove this temporary compatibility hack when blink
- // tests are updated.
- if (!holder.is_identical_to(object) &&
- !(object->IsJSGlobalProxy() && holder->IsJSGlobalObject())) {
- return it.isolate()->factory()->true_value();
- }
- NormalizeProperties(holder, mode, 0, "DeletingProperty");
- Handle<Object> result =
- DeleteNormalizedProperty(holder, name, delete_mode);
- ReoptimizeIfPrototype(holder);
+ it->Delete();
if (is_observed) {
- RETURN_ON_EXCEPTION(
- it.isolate(),
- EnqueueChangeRecord(object, "delete", name, old_value), Object);
+ RETURN_ON_EXCEPTION_VALUE(
+ isolate, JSObject::EnqueueChangeRecord(receiver, "delete",
+ it->GetName(), old_value),
+ Nothing<bool>());
}
- return result;
+ return Just(true);
}
}
}
- return it.isolate()->factory()->true_value();
+ return Just(true);
}
-MaybeHandle<Object> JSReceiver::DeleteElement(Handle<JSReceiver> object,
- uint32_t index,
- DeleteMode mode) {
- if (object->IsJSProxy()) {
- return JSProxy::DeleteElementWithHandler(
- Handle<JSProxy>::cast(object), index, mode);
- }
- return JSObject::DeleteElement(Handle<JSObject>::cast(object), index, mode);
+Maybe<bool> JSReceiver::DeleteElement(Handle<JSReceiver> object, uint32_t index,
+ LanguageMode language_mode) {
+ LookupIterator it(object->GetIsolate(), object, index,
+ LookupIterator::HIDDEN);
+ return DeleteProperty(&it, language_mode);
}
-MaybeHandle<Object> JSReceiver::DeleteProperty(Handle<JSReceiver> object,
- Handle<Name> name,
- DeleteMode mode) {
- if (object->IsJSProxy()) {
- return JSProxy::DeletePropertyWithHandler(
- Handle<JSProxy>::cast(object), name, mode);
+Maybe<bool> JSReceiver::DeleteProperty(Handle<JSReceiver> object,
+ Handle<Name> name,
+ LanguageMode language_mode) {
+ LookupIterator it(object, name, LookupIterator::HIDDEN);
+ return DeleteProperty(&it, language_mode);
+}
+
+
+Maybe<bool> JSReceiver::DeletePropertyOrElement(Handle<JSReceiver> object,
+ Handle<Name> name,
+ LanguageMode language_mode) {
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ name->GetIsolate(), object, name, LookupIterator::HIDDEN);
+ return DeleteProperty(&it, language_mode);
+}
+
+
+// ES6 7.1.14
+MaybeHandle<Object> ToPropertyKey(Isolate* isolate, Handle<Object> value) {
+ // 1. Let key be ToPrimitive(argument, hint String).
+ MaybeHandle<Object> maybe_key =
+ Object::ToPrimitive(value, ToPrimitiveHint::kString);
+ // 2. ReturnIfAbrupt(key).
+ Handle<Object> key;
+ if (!maybe_key.ToHandle(&key)) return key;
+ // 3. If Type(key) is Symbol, then return key.
+ if (key->IsSymbol()) return key;
+ // 4. Return ToString(key).
+ // Extending spec'ed behavior, we'd be happy to return an element index.
+ if (key->IsSmi()) return key;
+ if (key->IsHeapNumber()) {
+ uint32_t uint_value;
+ if (value->ToArrayLength(&uint_value) &&
+ uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) {
+ return handle(Smi::FromInt(static_cast<int>(uint_value)), isolate);
+ }
}
- return JSObject::DeleteProperty(Handle<JSObject>::cast(object), name, mode);
+ return Object::ToString(isolate, key);
+}
+
+
+// ES6 19.1.2.4
+// static
+Object* JSReceiver::DefineProperty(Isolate* isolate, Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> attributes) {
+ // 1. If Type(O) is not Object, throw a TypeError exception.
+ if (!object->IsJSReceiver()) {
+ Handle<String> fun_name =
+ isolate->factory()->InternalizeUtf8String("Object.defineProperty");
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name));
+ }
+ // 2. Let key be ToPropertyKey(P).
+ // 3. ReturnIfAbrupt(key).
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, key, ToPropertyKey(isolate, key));
+ // 4. Let desc be ToPropertyDescriptor(Attributes).
+ // 5. ReturnIfAbrupt(desc).
+ PropertyDescriptor desc;
+ if (!PropertyDescriptor::ToPropertyDescriptor(isolate, attributes, &desc)) {
+ return isolate->heap()->exception();
+ }
+ // 6. Let success be DefinePropertyOrThrow(O,key, desc).
+ Maybe<bool> success = DefineOwnProperty(
+ isolate, Handle<JSReceiver>::cast(object), key, &desc, THROW_ON_ERROR);
+ // 7. ReturnIfAbrupt(success).
+ MAYBE_RETURN(success, isolate->heap()->exception());
+ CHECK(success.FromJust());
+ // 8. Return O.
+ return *object;
+}
+
+
+// ES6 19.1.2.3.1
+// static
+MaybeHandle<Object> JSReceiver::DefineProperties(Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> properties) {
+ // 1. If Type(O) is not Object, throw a TypeError exception.
+ if (!object->IsJSReceiver()) {
+ Handle<String> fun_name =
+ isolate->factory()->InternalizeUtf8String("Object.defineProperties");
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name),
+ Object);
+ }
+ // 2. Let props be ToObject(Properties).
+ // 3. ReturnIfAbrupt(props).
+ Handle<JSReceiver> props;
+ if (!Object::ToObject(isolate, properties).ToHandle(&props)) {
+ THROW_NEW_ERROR(isolate,
+ NewTypeError(MessageTemplate::kUndefinedOrNullToObject),
+ Object);
+ }
+ // 4. Let keys be props.[[OwnPropertyKeys]]().
+ // 5. ReturnIfAbrupt(keys).
+ Handle<FixedArray> keys;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, keys,
+ JSReceiver::GetKeys(props, JSReceiver::OWN_ONLY, ALL_PROPERTIES), Object);
+ // 6. Let descriptors be an empty List.
+ int capacity = keys->length();
+ std::vector<PropertyDescriptor> descriptors(capacity);
+ size_t descriptors_index = 0;
+ // 7. Repeat for each element nextKey of keys in List order,
+ for (int i = 0; i < keys->length(); ++i) {
+ Handle<Object> next_key(keys->get(i), isolate);
+ // 7a. Let propDesc be props.[[GetOwnProperty]](nextKey).
+ // 7b. ReturnIfAbrupt(propDesc).
+ bool success = false;
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ isolate, props, next_key, &success, LookupIterator::HIDDEN);
+ DCHECK(success);
+ Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
+ if (!maybe.IsJust()) return MaybeHandle<Object>();
+ PropertyAttributes attrs = maybe.FromJust();
+ // 7c. If propDesc is not undefined and propDesc.[[Enumerable]] is true:
+ if (attrs == ABSENT) continue;
+ if (attrs & DONT_ENUM) continue;
+ // 7c i. Let descObj be Get(props, nextKey).
+ // 7c ii. ReturnIfAbrupt(descObj).
+ Handle<Object> desc_obj;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, desc_obj, Object::GetProperty(&it),
+ Object);
+ // 7c iii. Let desc be ToPropertyDescriptor(descObj).
+ success = PropertyDescriptor::ToPropertyDescriptor(
+ isolate, desc_obj, &descriptors[descriptors_index]);
+ // 7c iv. ReturnIfAbrupt(desc).
+ if (!success) return MaybeHandle<Object>();
+ // 7c v. Append the pair (a two element List) consisting of nextKey and
+ // desc to the end of descriptors.
+ descriptors[descriptors_index].set_name(next_key);
+ descriptors_index++;
+ }
+ // 8. For each pair from descriptors in list order,
+ for (size_t i = 0; i < descriptors_index; ++i) {
+ PropertyDescriptor* desc = &descriptors[i];
+ // 8a. Let P be the first element of pair.
+ // 8b. Let desc be the second element of pair.
+ // 8c. Let status be DefinePropertyOrThrow(O, P, desc).
+ Maybe<bool> status =
+ DefineOwnProperty(isolate, Handle<JSReceiver>::cast(object),
+ desc->name(), desc, THROW_ON_ERROR);
+ // 8d. ReturnIfAbrupt(status).
+ if (!status.IsJust()) return MaybeHandle<Object>();
+ CHECK(status.FromJust());
+ }
+ // 9. Return o.
+ return object;
+}
+
+
+// static
+Maybe<bool> JSReceiver::DefineOwnProperty(Isolate* isolate,
+ Handle<JSReceiver> object,
+ Handle<Object> key,
+ PropertyDescriptor* desc,
+ ShouldThrow should_throw) {
+ if (object->IsJSArray()) {
+ return JSArray::DefineOwnProperty(isolate, Handle<JSArray>::cast(object),
+ key, desc, should_throw);
+ }
+ if (object->IsJSProxy()) {
+ return JSProxy::DefineOwnProperty(isolate, Handle<JSProxy>::cast(object),
+ key, desc, should_throw);
+ }
+ // TODO(jkummerow): Support Modules (ES6 9.4.6.6)
+
+ // OrdinaryDefineOwnProperty, by virtue of calling
+ // DefineOwnPropertyIgnoreAttributes, can handle arguments (ES6 9.4.4.2)
+ // and IntegerIndexedExotics (ES6 9.4.5.3), with one exception:
+ // TODO(jkummerow): Setting an indexed accessor on a typed array should throw.
+ return OrdinaryDefineOwnProperty(isolate, Handle<JSObject>::cast(object), key,
+ desc, should_throw);
+}
+
+
+// static
+Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(Isolate* isolate,
+ Handle<JSObject> object,
+ Handle<Object> key,
+ PropertyDescriptor* desc,
+ ShouldThrow should_throw) {
+ bool success = false;
+ DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey...
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ isolate, object, key, &success, LookupIterator::HIDDEN);
+ DCHECK(success); // ...so creating a LookupIterator can't fail.
+
+ // Deal with access checks first.
+ if (it.state() == LookupIterator::ACCESS_CHECK) {
+ if (!it.HasAccess()) {
+ isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>());
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ return Just(true);
+ }
+ it.Next();
+ }
+
+ return OrdinaryDefineOwnProperty(&it, desc, should_throw);
+}
+
+
+// ES6 9.1.6.1
+// static
+Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it,
+ PropertyDescriptor* desc,
+ ShouldThrow should_throw) {
+ Isolate* isolate = it->isolate();
+ // 1. Let current be O.[[GetOwnProperty]](P).
+ // 2. ReturnIfAbrupt(current).
+ PropertyDescriptor current;
+ MAYBE_RETURN(GetOwnPropertyDescriptor(it, ¤t), Nothing<bool>());
+
+ // TODO(jkummerow/verwaest): It would be nice if we didn't have to reset
+ // the iterator every time. Currently, the reasons why we need it are:
+ // - handle interceptors correctly
+ // - handle accessors correctly (which might change the holder's map)
+ it->Restart();
+ // 3. Let extensible be the value of the [[Extensible]] internal slot of O.
+ Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver());
+ bool extensible = JSObject::IsExtensible(object);
+
+ return ValidateAndApplyPropertyDescriptor(isolate, it, extensible, desc,
+ ¤t, should_throw);
+}
+
+
+// ES6 9.1.6.2
+// static
+Maybe<bool> JSReceiver::IsCompatiblePropertyDescriptor(
+ Isolate* isolate, bool extensible, PropertyDescriptor* desc,
+ PropertyDescriptor* current, Handle<Name> property_name,
+ ShouldThrow should_throw) {
+ // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined,
+ // Extensible, Desc, Current).
+ return ValidateAndApplyPropertyDescriptor(
+ isolate, NULL, extensible, desc, current, should_throw, property_name);
+}
+
+
+// ES6 9.1.6.3
+// static
+Maybe<bool> JSReceiver::ValidateAndApplyPropertyDescriptor(
+ Isolate* isolate, LookupIterator* it, bool extensible,
+ PropertyDescriptor* desc, PropertyDescriptor* current,
+ ShouldThrow should_throw, Handle<Name> property_name) {
+ // We either need a LookupIterator, or a property name.
+ DCHECK((it == NULL) != property_name.is_null());
+ Handle<JSObject> object;
+ if (it != NULL) object = Handle<JSObject>::cast(it->GetReceiver());
+ bool desc_is_data_descriptor = PropertyDescriptor::IsDataDescriptor(desc);
+ bool desc_is_accessor_descriptor =
+ PropertyDescriptor::IsAccessorDescriptor(desc);
+ bool desc_is_generic_descriptor =
+ PropertyDescriptor::IsGenericDescriptor(desc);
+ // 1. (Assert)
+ // 2. If current is undefined, then
+ if (current->is_empty()) {
+ // 2a. If extensible is false, return false.
+ if (!extensible) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kDefineDisallowed,
+ it != NULL ? it->GetName() : property_name));
+ }
+ // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then:
+ // (This is equivalent to !IsAccessorDescriptor(desc).)
+ DCHECK((desc_is_generic_descriptor || desc_is_data_descriptor) ==
+ !desc_is_accessor_descriptor);
+ if (!desc_is_accessor_descriptor) {
+ // 2c i. If O is not undefined, create an own data property named P of
+ // object O whose [[Value]], [[Writable]], [[Enumerable]] and
+ // [[Configurable]] attribute values are described by Desc. If the value
+ // of an attribute field of Desc is absent, the attribute of the newly
+ // created property is set to its default value.
+ if (it != NULL) {
+ if (!desc->has_writable()) desc->set_writable(false);
+ if (!desc->has_enumerable()) desc->set_enumerable(false);
+ if (!desc->has_configurable()) desc->set_configurable(false);
+ Handle<Object> value(
+ desc->has_value()
+ ? desc->value()
+ : Handle<Object>::cast(isolate->factory()->undefined_value()));
+ MaybeHandle<Object> result =
+ JSObject::DefineOwnPropertyIgnoreAttributes(
+ it, value, desc->ToAttributes(), JSObject::DONT_FORCE_FIELD);
+ if (result.is_null()) return Nothing<bool>();
+ }
+ } else {
+ // 2d. Else Desc must be an accessor Property Descriptor,
+ DCHECK(desc_is_accessor_descriptor);
+ // 2d i. If O is not undefined, create an own accessor property named P
+ // of object O whose [[Get]], [[Set]], [[Enumerable]] and
+ // [[Configurable]] attribute values are described by Desc. If the value
+ // of an attribute field of Desc is absent, the attribute of the newly
+ // created property is set to its default value.
+ if (it != NULL) {
+ if (!desc->has_enumerable()) desc->set_enumerable(false);
+ if (!desc->has_configurable()) desc->set_configurable(false);
+ Handle<Object> getter(
+ desc->has_get()
+ ? desc->get()
+ : Handle<Object>::cast(isolate->factory()->null_value()));
+ Handle<Object> setter(
+ desc->has_set()
+ ? desc->set()
+ : Handle<Object>::cast(isolate->factory()->null_value()));
+ MaybeHandle<Object> result =
+ JSObject::DefineAccessor(it, getter, setter, desc->ToAttributes());
+ if (result.is_null()) return Nothing<bool>();
+ }
+ }
+ // 2e. Return true.
+ return Just(true);
+ }
+ // 3. Return true, if every field in Desc is absent.
+ // 4. Return true, if every field in Desc also occurs in current and the
+ // value of every field in Desc is the same value as the corresponding field
+ // in current when compared using the SameValue algorithm.
+ if ((!desc->has_enumerable() ||
+ desc->enumerable() == current->enumerable()) &&
+ (!desc->has_configurable() ||
+ desc->configurable() == current->configurable()) &&
+ (!desc->has_value() ||
+ (current->has_value() && current->value()->SameValue(*desc->value()))) &&
+ (!desc->has_writable() ||
+ (current->has_writable() && current->writable() == desc->writable())) &&
+ (!desc->has_get() ||
+ (current->has_get() && current->get()->SameValue(*desc->get()))) &&
+ (!desc->has_set() ||
+ (current->has_set() && current->set()->SameValue(*desc->set())))) {
+ return Just(true);
+ }
+ // 5. If the [[Configurable]] field of current is false, then
+ if (!current->configurable()) {
+ // 5a. Return false, if the [[Configurable]] field of Desc is true.
+ if (desc->has_configurable() && desc->configurable()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed,
+ it != NULL ? it->GetName() : property_name));
+ }
+ // 5b. Return false, if the [[Enumerable]] field of Desc is present and the
+ // [[Enumerable]] fields of current and Desc are the Boolean negation of
+ // each other.
+ if (desc->has_enumerable() && desc->enumerable() != current->enumerable()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed,
+ it != NULL ? it->GetName() : property_name));
+ }
+ }
+
+ bool current_is_data_descriptor =
+ PropertyDescriptor::IsDataDescriptor(current);
+ // 6. If IsGenericDescriptor(Desc) is true, no further validation is required.
+ if (desc_is_generic_descriptor) {
+ // Nothing to see here.
+
+ // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have
+ // different results, then:
+ } else if (current_is_data_descriptor != desc_is_data_descriptor) {
+ // 7a. Return false, if the [[Configurable]] field of current is false.
+ if (!current->configurable()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed,
+ it != NULL ? it->GetName() : property_name));
+ }
+ // 7b. If IsDataDescriptor(current) is true, then:
+ if (current_is_data_descriptor) {
+ // 7b i. If O is not undefined, convert the property named P of object O
+ // from a data property to an accessor property. Preserve the existing
+ // values of the converted property's [[Configurable]] and [[Enumerable]]
+ // attributes and set the rest of the property's attributes to their
+ // default values.
+ // --> Folded into step 10.
+ } else {
+ // 7c i. If O is not undefined, convert the property named P of object O
+ // from an accessor property to a data property. Preserve the existing
+ // values of the converted property’s [[Configurable]] and [[Enumerable]]
+ // attributes and set the rest of the property’s attributes to their
+ // default values.
+ // --> Folded into step 10.
+ }
+
+ // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both
+ // true, then:
+ } else if (current_is_data_descriptor && desc_is_data_descriptor) {
+ // 8a. If the [[Configurable]] field of current is false, then:
+ if (!current->configurable()) {
+ // [Strong mode] Disallow changing writable -> readonly for
+ // non-configurable properties.
+ if (it != NULL && current->writable() && desc->has_writable() &&
+ !desc->writable() && object->map()->is_strong()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kStrongRedefineDisallowed,
+ object, it->GetName()));
+ }
+ // 8a i. Return false, if the [[Writable]] field of current is false and
+ // the [[Writable]] field of Desc is true.
+ if (!current->writable() && desc->has_writable() && desc->writable()) {
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed,
+ it != NULL ? it->GetName() : property_name));
+ }
+ // 8a ii. If the [[Writable]] field of current is false, then:
+ if (!current->writable()) {
+ // 8a ii 1. Return false, if the [[Value]] field of Desc is present and
+ // SameValue(Desc.[[Value]], current.[[Value]]) is false.
+ if (desc->has_value() && !desc->value()->SameValue(*current->value())) {
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed,
+ it != NULL ? it->GetName() : property_name));
+ }
+ }
+ }
+ } else {
+ // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc)
+ // are both true,
+ DCHECK(PropertyDescriptor::IsAccessorDescriptor(current) &&
+ desc_is_accessor_descriptor);
+ // 9a. If the [[Configurable]] field of current is false, then:
+ if (!current->configurable()) {
+ // 9a i. Return false, if the [[Set]] field of Desc is present and
+ // SameValue(Desc.[[Set]], current.[[Set]]) is false.
+ if (desc->has_set() && !desc->set()->SameValue(*current->set())) {
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed,
+ it != NULL ? it->GetName() : property_name));
+ }
+ // 9a ii. Return false, if the [[Get]] field of Desc is present and
+ // SameValue(Desc.[[Get]], current.[[Get]]) is false.
+ if (desc->has_get() && !desc->get()->SameValue(*current->get())) {
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed,
+ it != NULL ? it->GetName() : property_name));
+ }
+ }
+ }
+
+ // 10. If O is not undefined, then:
+ if (it != NULL) {
+ // 10a. For each field of Desc that is present, set the corresponding
+ // attribute of the property named P of object O to the value of the field.
+ PropertyAttributes attrs = NONE;
+
+ if (desc->has_enumerable()) {
+ attrs = static_cast<PropertyAttributes>(
+ attrs | (desc->enumerable() ? NONE : DONT_ENUM));
+ } else {
+ attrs = static_cast<PropertyAttributes>(
+ attrs | (current->enumerable() ? NONE : DONT_ENUM));
+ }
+ if (desc->has_configurable()) {
+ attrs = static_cast<PropertyAttributes>(
+ attrs | (desc->configurable() ? NONE : DONT_DELETE));
+ } else {
+ attrs = static_cast<PropertyAttributes>(
+ attrs | (current->configurable() ? NONE : DONT_DELETE));
+ }
+ if (desc_is_data_descriptor ||
+ (desc_is_generic_descriptor && current_is_data_descriptor)) {
+ if (desc->has_writable()) {
+ attrs = static_cast<PropertyAttributes>(
+ attrs | (desc->writable() ? NONE : READ_ONLY));
+ } else {
+ attrs = static_cast<PropertyAttributes>(
+ attrs | (current->writable() ? NONE : READ_ONLY));
+ }
+ Handle<Object> value(
+ desc->has_value() ? desc->value()
+ : current->has_value()
+ ? current->value()
+ : Handle<Object>::cast(
+ isolate->factory()->undefined_value()));
+ MaybeHandle<Object> result = JSObject::DefineOwnPropertyIgnoreAttributes(
+ it, value, attrs, JSObject::DONT_FORCE_FIELD);
+ if (result.is_null()) return Nothing<bool>();
+ } else {
+ DCHECK(desc_is_accessor_descriptor ||
+ (desc_is_generic_descriptor &&
+ PropertyDescriptor::IsAccessorDescriptor(current)));
+ Handle<Object> getter(
+ desc->has_get()
+ ? desc->get()
+ : current->has_get()
+ ? current->get()
+ : Handle<Object>::cast(isolate->factory()->null_value()));
+ Handle<Object> setter(
+ desc->has_set()
+ ? desc->set()
+ : current->has_set()
+ ? current->set()
+ : Handle<Object>::cast(isolate->factory()->null_value()));
+ MaybeHandle<Object> result =
+ JSObject::DefineAccessor(it, getter, setter, attrs);
+ if (result.is_null()) return Nothing<bool>();
+ }
+ }
+
+ // 11. Return true.
+ return Just(true);
+}
+
+
+// static
+Maybe<bool> JSReceiver::CreateDataProperty(LookupIterator* it,
+ Handle<Object> value,
+ ShouldThrow should_throw) {
+ DCHECK(!it->check_prototype_chain());
+ Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
+ Isolate* isolate = receiver->GetIsolate();
+
+ if (receiver->IsJSObject()) {
+ return JSObject::CreateDataProperty(it, value); // Shortcut.
+ }
+
+ PropertyDescriptor new_desc;
+ new_desc.set_value(value);
+ new_desc.set_writable(true);
+ new_desc.set_enumerable(true);
+ new_desc.set_configurable(true);
+
+ return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(),
+ &new_desc, should_throw);
+}
+
+
+Maybe<bool> JSObject::CreateDataProperty(LookupIterator* it,
+ Handle<Object> value) {
+ DCHECK(it->GetReceiver()->IsJSObject());
+ MAYBE_RETURN(JSReceiver::GetPropertyAttributes(it), Nothing<bool>());
+
+ if (it->IsFound()) {
+ if (!it->IsConfigurable()) return Just(false);
+ } else {
+ if (!JSObject::IsExtensible(Handle<JSObject>::cast(it->GetReceiver())))
+ return Just(false);
+ }
+
+ RETURN_ON_EXCEPTION_VALUE(
+ it->isolate(),
+ DefineOwnPropertyIgnoreAttributes(it, value, NONE, DONT_FORCE_FIELD),
+ Nothing<bool>());
+
+ return Just(true);
+}
+
+
+// TODO(jkummerow): Consider unification with FastAsArrayLength() in
+// accessors.cc.
+bool PropertyKeyToArrayLength(Handle<Object> value, uint32_t* length) {
+ DCHECK(value->IsNumber() || value->IsName());
+ if (value->ToArrayLength(length)) return true;
+ if (value->IsString()) return String::cast(*value)->AsArrayIndex(length);
+ return false;
+}
+
+
+bool PropertyKeyToArrayIndex(Handle<Object> index_obj, uint32_t* output) {
+ return PropertyKeyToArrayLength(index_obj, output) && *output != kMaxUInt32;
+}
+
+
+// ES6 9.4.2.1
+// static
+Maybe<bool> JSArray::DefineOwnProperty(Isolate* isolate, Handle<JSArray> o,
+ Handle<Object> name,
+ PropertyDescriptor* desc,
+ ShouldThrow should_throw) {
+ // 1. Assert: IsPropertyKey(P) is true. ("P" is |name|.)
+ // 2. If P is "length", then:
+ // TODO(jkummerow): Check if we need slow string comparison.
+ if (*name == isolate->heap()->length_string()) {
+ // 2a. Return ArraySetLength(A, Desc).
+ return ArraySetLength(isolate, o, desc, should_throw);
+ }
+ // 3. Else if P is an array index, then:
+ uint32_t index = 0;
+ if (PropertyKeyToArrayIndex(name, &index)) {
+ // 3a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
+ PropertyDescriptor old_len_desc;
+ Maybe<bool> success = GetOwnPropertyDescriptor(
+ isolate, o, isolate->factory()->length_string(), &old_len_desc);
+ // 3b. (Assert)
+ DCHECK(success.FromJust());
+ USE(success);
+ // 3c. Let oldLen be oldLenDesc.[[Value]].
+ uint32_t old_len = 0;
+ CHECK(old_len_desc.value()->ToArrayLength(&old_len));
+ // 3d. Let index be ToUint32(P).
+ // (Already done above.)
+ // 3e. (Assert)
+ // 3f. If index >= oldLen and oldLenDesc.[[Writable]] is false,
+ // return false.
+ if (index >= old_len && old_len_desc.has_writable() &&
+ !old_len_desc.writable()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kDefineDisallowed, name));
+ }
+ // 3g. Let succeeded be OrdinaryDefineOwnProperty(A, P, Desc).
+ Maybe<bool> succeeded =
+ OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw);
+ // 3h. Assert: succeeded is not an abrupt completion.
+ // In our case, if should_throw == THROW_ON_ERROR, it can be!
+ // 3i. If succeeded is false, return false.
+ if (succeeded.IsNothing() || !succeeded.FromJust()) return succeeded;
+ // 3j. If index >= oldLen, then:
+ if (index >= old_len) {
+ // 3j i. Set oldLenDesc.[[Value]] to index + 1.
+ old_len_desc.set_value(isolate->factory()->NewNumberFromUint(index + 1));
+ // 3j ii. Let succeeded be
+ // OrdinaryDefineOwnProperty(A, "length", oldLenDesc).
+ succeeded = OrdinaryDefineOwnProperty(isolate, o,
+ isolate->factory()->length_string(),
+ &old_len_desc, should_throw);
+ // 3j iii. Assert: succeeded is true.
+ DCHECK(succeeded.FromJust());
+ USE(succeeded);
+ }
+ // 3k. Return true.
+ return Just(true);
+ }
+
+ // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
+ return OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw);
+}
+
+
+// Part of ES6 9.4.2.4 ArraySetLength.
+// static
+bool JSArray::AnythingToArrayLength(Isolate* isolate,
+ Handle<Object> length_object,
+ uint32_t* output) {
+ // Fast path: check numbers and strings that can be converted directly
+ // and unobservably.
+ if (length_object->ToArrayLength(output)) return true;
+ if (length_object->IsString() &&
+ Handle<String>::cast(length_object)->AsArrayIndex(output)) {
+ return true;
+ }
+ // Slow path: follow steps in ES6 9.4.2.4 "ArraySetLength".
+ // 3. Let newLen be ToUint32(Desc.[[Value]]).
+ Handle<Object> uint32_v;
+ if (!Object::ToUint32(isolate, length_object).ToHandle(&uint32_v)) {
+ // 4. ReturnIfAbrupt(newLen).
+ return false;
+ }
+ // 5. Let numberLen be ToNumber(Desc.[[Value]]).
+ Handle<Object> number_v;
+ if (!Object::ToNumber(length_object).ToHandle(&number_v)) {
+ // 6. ReturnIfAbrupt(newLen).
+ return false;
+ }
+ // 7. If newLen != numberLen, throw a RangeError exception.
+ if (uint32_v->Number() != number_v->Number()) {
+ Handle<Object> exception =
+ isolate->factory()->NewRangeError(MessageTemplate::kInvalidArrayLength);
+ isolate->Throw(*exception);
+ return false;
+ }
+ CHECK(uint32_v->ToArrayLength(output));
+ return true;
+}
+
+
+// ES6 9.4.2.4
+// static
+Maybe<bool> JSArray::ArraySetLength(Isolate* isolate, Handle<JSArray> a,
+ PropertyDescriptor* desc,
+ ShouldThrow should_throw) {
+ // 1. If the [[Value]] field of Desc is absent, then
+ if (!desc->has_value()) {
+ // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
+ return OrdinaryDefineOwnProperty(
+ isolate, a, isolate->factory()->length_string(), desc, should_throw);
+ }
+ // 2. Let newLenDesc be a copy of Desc.
+ // (Actual copying is not necessary.)
+ PropertyDescriptor* new_len_desc = desc;
+ // 3. - 7. Convert Desc.[[Value]] to newLen.
+ uint32_t new_len = 0;
+ if (!AnythingToArrayLength(isolate, desc->value(), &new_len)) {
+ DCHECK(isolate->has_pending_exception());
+ return Nothing<bool>();
+ }
+ // 8. Set newLenDesc.[[Value]] to newLen.
+ // (Done below, if needed.)
+ // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
+ PropertyDescriptor old_len_desc;
+ Maybe<bool> success = GetOwnPropertyDescriptor(
+ isolate, a, isolate->factory()->length_string(), &old_len_desc);
+ // 10. (Assert)
+ DCHECK(success.FromJust());
+ USE(success);
+ // 11. Let oldLen be oldLenDesc.[[Value]].
+ uint32_t old_len = 0;
+ CHECK(old_len_desc.value()->ToArrayLength(&old_len));
+ // 12. If newLen >= oldLen, then
+ if (new_len >= old_len) {
+ // 8. Set newLenDesc.[[Value]] to newLen.
+ // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
+ new_len_desc->set_value(isolate->factory()->NewNumberFromUint(new_len));
+ return OrdinaryDefineOwnProperty(isolate, a,
+ isolate->factory()->length_string(),
+ new_len_desc, should_throw);
+ }
+ // 13. If oldLenDesc.[[Writable]] is false, return false.
+ if (!old_len_desc.writable()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kRedefineDisallowed,
+ isolate->factory()->length_string()));
+ }
+ // 14. If newLenDesc.[[Writable]] is absent or has the value true,
+ // let newWritable be true.
+ bool new_writable = false;
+ if (!new_len_desc->has_writable() || new_len_desc->writable()) {
+ new_writable = true;
+ } else {
+ // 15. Else,
+ // 15a. Need to defer setting the [[Writable]] attribute to false in case
+ // any elements cannot be deleted.
+ // 15b. Let newWritable be false. (It's initialized as "false" anyway.)
+ // 15c. Set newLenDesc.[[Writable]] to true.
+ // (Not needed.)
+ }
+ // Most of steps 16 through 19 is implemented by JSArray::SetLength.
+ if (JSArray::ObservableSetLength(a, new_len).is_null()) {
+ DCHECK(isolate->has_pending_exception());
+ return Nothing<bool>();
+ }
+ // Steps 19d-ii, 20.
+ if (!new_writable) {
+ PropertyDescriptor readonly;
+ readonly.set_writable(false);
+ Maybe<bool> success = OrdinaryDefineOwnProperty(
+ isolate, a, isolate->factory()->length_string(), &readonly,
+ should_throw);
+ DCHECK(success.FromJust());
+ USE(success);
+ }
+ uint32_t actual_new_len = 0;
+ CHECK(a->length()->ToArrayLength(&actual_new_len));
+ // Steps 19d-v, 21. Return false if there were non-deletable elements.
+ bool result = actual_new_len == new_len;
+ if (!result) {
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kStrictDeleteProperty,
+ isolate->factory()->NewNumberFromUint(actual_new_len - 1),
+ a));
+ }
+ return Just(result);
+}
+
+
+// ES6 9.5.6
+// static
+Maybe<bool> JSProxy::DefineOwnProperty(Isolate* isolate, Handle<JSProxy> proxy,
+ Handle<Object> key,
+ PropertyDescriptor* desc,
+ ShouldThrow should_throw) {
+ STACK_CHECK(Nothing<bool>());
+ if (key->IsSymbol() && Handle<Symbol>::cast(key)->IsPrivate()) {
+ return AddPrivateProperty(isolate, proxy, Handle<Symbol>::cast(key), desc,
+ should_throw);
+ }
+ Handle<String> trap_name = isolate->factory()->defineProperty_string();
+ // 1. Assert: IsPropertyKey(P) is true.
+ DCHECK(key->IsName() || key->IsNumber());
+ // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+ Handle<Object> handler(proxy->handler(), isolate);
+ // 3. If handler is null, throw a TypeError exception.
+ // 4. Assert: Type(handler) is Object.
+ if (proxy->IsRevoked()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyRevoked, trap_name));
+ return Nothing<bool>();
+ }
+ // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ // 6. Let trap be ? GetMethod(handler, "defineProperty").
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap,
+ Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name),
+ Nothing<bool>());
+ // 7. If trap is undefined, then:
+ if (trap->IsUndefined()) {
+ // 7a. Return target.[[DefineOwnProperty]](P, Desc).
+ return JSReceiver::DefineOwnProperty(isolate, target, key, desc,
+ should_throw);
+ }
+ // 8. Let descObj be FromPropertyDescriptor(Desc).
+ Handle<Object> desc_obj = desc->ToObject(isolate);
+ // 9. Let booleanTrapResult be
+ // ToBoolean(? Call(trap, handler, «target, P, descObj»)).
+ Handle<Name> property_name =
+ key->IsName()
+ ? Handle<Name>::cast(key)
+ : Handle<Name>::cast(isolate->factory()->NumberToString(key));
+ // Do not leak private property names.
+ DCHECK(!property_name->IsPrivate());
+ Handle<Object> trap_result_obj;
+ Handle<Object> args[] = {target, property_name, desc_obj};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result_obj,
+ Execution::Call(isolate, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+ // 10. If booleanTrapResult is false, return false.
+ if (!trap_result_obj->BooleanValue()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor,
+ trap_name, property_name));
+ }
+ // 11. Let targetDesc be ? target.[[GetOwnProperty]](P).
+ PropertyDescriptor target_desc;
+ Maybe<bool> target_found =
+ JSReceiver::GetOwnPropertyDescriptor(isolate, target, key, &target_desc);
+ MAYBE_RETURN(target_found, Nothing<bool>());
+ // 12. Let extensibleTarget be ? IsExtensible(target).
+ Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN(maybe_extensible, Nothing<bool>());
+ bool extensible_target = maybe_extensible.FromJust();
+ // 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]]
+ // is false, then:
+ // 13a. Let settingConfigFalse be true.
+ // 14. Else let settingConfigFalse be false.
+ bool setting_config_false = desc->has_configurable() && !desc->configurable();
+ // 15. If targetDesc is undefined, then
+ if (!target_found.FromJust()) {
+ // 15a. If extensibleTarget is false, throw a TypeError exception.
+ if (!extensible_target) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyDefinePropertyNonExtensible, property_name));
+ return Nothing<bool>();
+ }
+ // 15b. If settingConfigFalse is true, throw a TypeError exception.
+ if (setting_config_false) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name));
+ return Nothing<bool>();
+ }
+ } else {
+ // 16. Else targetDesc is not undefined,
+ // 16a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc,
+ // targetDesc) is false, throw a TypeError exception.
+ Maybe<bool> valid =
+ IsCompatiblePropertyDescriptor(isolate, extensible_target, desc,
+ &target_desc, property_name, DONT_THROW);
+ MAYBE_RETURN(valid, Nothing<bool>());
+ if (!valid.FromJust()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyDefinePropertyIncompatible, property_name));
+ return Nothing<bool>();
+ }
+ // 16b. If settingConfigFalse is true and targetDesc.[[Configurable]] is
+ // true, throw a TypeError exception.
+ if (setting_config_false && target_desc.configurable()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name));
+ return Nothing<bool>();
+ }
+ }
+ // 17. Return true.
+ return Just(true);
+}
+
+
+// static
+Maybe<bool> JSProxy::AddPrivateProperty(Isolate* isolate, Handle<JSProxy> proxy,
+ Handle<Symbol> private_name,
+ PropertyDescriptor* desc,
+ ShouldThrow should_throw) {
+ // Despite the generic name, this can only add private data properties.
+ if (!PropertyDescriptor::IsDataDescriptor(desc) ||
+ desc->ToAttributes() != DONT_ENUM) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kProxyPrivate));
+ }
+ DCHECK(proxy->map()->is_dictionary_map());
+ Handle<Object> value =
+ desc->has_value()
+ ? desc->value()
+ : Handle<Object>::cast(isolate->factory()->undefined_value());
+
+ LookupIterator it(proxy, private_name);
+
+ if (it.IsFound()) {
+ DCHECK_EQ(LookupIterator::DATA, it.state());
+ DCHECK_EQ(DONT_ENUM, it.property_details().attributes());
+ it.WriteDataValue(value);
+ return Just(true);
+ }
+
+ Handle<NameDictionary> dict(proxy->property_dictionary());
+ PropertyDetails details(DONT_ENUM, DATA, 0, PropertyCellType::kNoCell);
+ Handle<NameDictionary> result =
+ NameDictionary::Add(dict, private_name, value, details);
+ if (!dict.is_identical_to(result)) proxy->set_properties(*result);
+ return Just(true);
+}
+
+
+// static
+Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(Isolate* isolate,
+ Handle<JSReceiver> object,
+ Handle<Object> key,
+ PropertyDescriptor* desc) {
+ bool success = false;
+ DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey...
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ isolate, object, key, &success, LookupIterator::HIDDEN);
+ DCHECK(success); // ...so creating a LookupIterator can't fail.
+ return GetOwnPropertyDescriptor(&it, desc);
+}
+
+
+// ES6 9.1.5.1
+// Returns true on success, false if the property didn't exist, nothing if
+// an exception was thrown.
+// static
+Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it,
+ PropertyDescriptor* desc) {
+ Isolate* isolate = it->isolate();
+ // "Virtual" dispatch.
+ if (it->IsFound() && it->GetHolder<JSReceiver>()->IsJSProxy()) {
+ return JSProxy::GetOwnPropertyDescriptor(isolate, it->GetHolder<JSProxy>(),
+ it->GetName(), desc);
+ }
+
+ // 1. (Assert)
+ // 2. If O does not have an own property with key P, return undefined.
+ Maybe<PropertyAttributes> maybe = JSObject::GetPropertyAttributes(it);
+ MAYBE_RETURN(maybe, Nothing<bool>());
+ PropertyAttributes attrs = maybe.FromJust();
+ if (attrs == ABSENT) return Just(false);
+ DCHECK(!isolate->has_pending_exception());
+
+ // 3. Let D be a newly created Property Descriptor with no fields.
+ DCHECK(desc->is_empty());
+ // 4. Let X be O's own property whose key is P.
+ // 5. If X is a data property, then
+ bool is_accessor_pair = it->state() == LookupIterator::ACCESSOR &&
+ it->GetAccessors()->IsAccessorPair();
+ if (!is_accessor_pair) {
+ // 5a. Set D.[[Value]] to the value of X's [[Value]] attribute.
+ Handle<Object> value;
+ if (!JSObject::GetProperty(it).ToHandle(&value)) {
+ DCHECK(isolate->has_pending_exception());
+ return Nothing<bool>();
+ }
+ desc->set_value(value);
+ // 5b. Set D.[[Writable]] to the value of X's [[Writable]] attribute
+ desc->set_writable((attrs & READ_ONLY) == 0);
+ } else {
+ // 6. Else X is an accessor property, so
+ Handle<AccessorPair> accessors =
+ Handle<AccessorPair>::cast(it->GetAccessors());
+ // 6a. Set D.[[Get]] to the value of X's [[Get]] attribute.
+ desc->set_get(handle(accessors->GetComponent(ACCESSOR_GETTER), isolate));
+ // 6b. Set D.[[Set]] to the value of X's [[Set]] attribute.
+ desc->set_set(handle(accessors->GetComponent(ACCESSOR_SETTER), isolate));
+ }
+
+ // 7. Set D.[[Enumerable]] to the value of X's [[Enumerable]] attribute.
+ desc->set_enumerable((attrs & DONT_ENUM) == 0);
+ // 8. Set D.[[Configurable]] to the value of X's [[Configurable]] attribute.
+ desc->set_configurable((attrs & DONT_DELETE) == 0);
+ // 9. Return D.
+ DCHECK(PropertyDescriptor::IsAccessorDescriptor(desc) !=
+ PropertyDescriptor::IsDataDescriptor(desc));
+ return Just(true);
+}
+
+
+// ES6 9.5.5
+// static
+Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate,
+ Handle<JSProxy> proxy,
+ Handle<Name> name,
+ PropertyDescriptor* desc) {
+ DCHECK(!name->IsPrivate());
+ STACK_CHECK(Nothing<bool>());
+
+ Handle<String> trap_name =
+ isolate->factory()->getOwnPropertyDescriptor_string();
+ // 1. (Assert)
+ // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+ Handle<Object> handler(proxy->handler(), isolate);
+ // 3. If handler is null, throw a TypeError exception.
+ // 4. Assert: Type(handler) is Object.
+ if (proxy->IsRevoked()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyRevoked, trap_name));
+ return Nothing<bool>();
+ }
+ // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ // 6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor").
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap,
+ Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name),
+ Nothing<bool>());
+ // 7. If trap is undefined, then
+ if (trap->IsUndefined()) {
+ // 7a. Return target.[[GetOwnProperty]](P).
+ return JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, desc);
+ }
+ // 8. Let trapResultObj be ? Call(trap, handler, «target, P»).
+ Handle<Object> trap_result_obj;
+ Handle<Object> args[] = {target, name};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result_obj,
+ Execution::Call(isolate, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+ // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a
+ // TypeError exception.
+ if (!trap_result_obj->IsJSReceiver() && !trap_result_obj->IsUndefined()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyGetOwnPropertyDescriptorInvalid, name));
+ return Nothing<bool>();
+ }
+ // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
+ PropertyDescriptor target_desc;
+ Maybe<bool> found =
+ JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
+ MAYBE_RETURN(found, Nothing<bool>());
+ // 11. If trapResultObj is undefined, then
+ if (trap_result_obj->IsUndefined()) {
+ // 11a. If targetDesc is undefined, return undefined.
+ if (!found.FromJust()) return Just(false);
+ // 11b. If targetDesc.[[Configurable]] is false, throw a TypeError
+ // exception.
+ if (!target_desc.configurable()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyGetOwnPropertyDescriptorUndefined, name));
+ return Nothing<bool>();
+ }
+ // 11c. Let extensibleTarget be ? IsExtensible(target).
+ Maybe<bool> extensible_target = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN(extensible_target, Nothing<bool>());
+ // 11d. (Assert)
+ // 11e. If extensibleTarget is false, throw a TypeError exception.
+ if (!extensible_target.FromJust()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyGetOwnPropertyDescriptorNonExtensible, name));
+ return Nothing<bool>();
+ }
+ // 11f. Return undefined.
+ return Just(false);
+ }
+ // 12. Let extensibleTarget be ? IsExtensible(target).
+ Maybe<bool> extensible_target = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN(extensible_target, Nothing<bool>());
+ // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
+ if (!PropertyDescriptor::ToPropertyDescriptor(isolate, trap_result_obj,
+ desc)) {
+ DCHECK(isolate->has_pending_exception());
+ return Nothing<bool>();
+ }
+ // 14. Call CompletePropertyDescriptor(resultDesc).
+ PropertyDescriptor::CompletePropertyDescriptor(isolate, desc);
+ // 15. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget,
+ // resultDesc, targetDesc).
+ Maybe<bool> valid =
+ IsCompatiblePropertyDescriptor(isolate, extensible_target.FromJust(),
+ desc, &target_desc, name, DONT_THROW);
+ MAYBE_RETURN(valid, Nothing<bool>());
+ // 16. If valid is false, throw a TypeError exception.
+ if (!valid.FromJust()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyGetOwnPropertyDescriptorIncompatible, name));
+ return Nothing<bool>();
+ }
+ // 17. If resultDesc.[[Configurable]] is false, then
+ if (!desc->configurable()) {
+ // 17a. If targetDesc is undefined or targetDesc.[[Configurable]] is true:
+ if (target_desc.is_empty() || target_desc.configurable()) {
+ // 17a i. Throw a TypeError exception.
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyGetOwnPropertyDescriptorNonConfigurable,
+ name));
+ return Nothing<bool>();
+ }
+ }
+ // 18. Return resultDesc.
+ return Just(true);
}
@@ -5324,7 +7438,7 @@
DisallowHeapAllocation no_allocation;
// Is the object the constructor for this object?
- if (map_of_this->constructor() == obj) {
+ if (map_of_this->GetConstructor() == obj) {
return true;
}
@@ -5345,7 +7459,6 @@
// Raw pixels and external arrays do not reference other
// objects.
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ELEMENTS: \
case TYPE##_ELEMENTS: \
break;
@@ -5365,7 +7478,8 @@
if (ReferencesObjectFromElements(elements, kind, obj)) return true;
break;
}
- case SLOPPY_ARGUMENTS_ELEMENTS: {
+ case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
+ case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
FixedArray* parameter_map = FixedArray::cast(elements());
// Check the mapped parameters.
int length = parameter_map->length();
@@ -5388,7 +7502,7 @@
Map* arguments_map =
heap->isolate()->context()->native_context()->sloppy_arguments_map();
JSFunction* arguments_function =
- JSFunction::cast(arguments_map->constructor());
+ JSFunction::cast(arguments_map->GetConstructor());
// Get the context and don't check if it is the native context.
JSFunction* f = JSFunction::cast(this);
@@ -5403,7 +7517,7 @@
if (context->get(i)->IsJSObject()) {
JSObject* ctxobj = JSObject::cast(context->get(i));
// If it is an arguments array check the content.
- if (ctxobj->map()->constructor() == arguments_function) {
+ if (ctxobj->map()->GetConstructor() == arguments_function) {
if (ctxobj->ReferencesObject(obj)) {
return true;
}
@@ -5415,13 +7529,13 @@
// 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 (FLAG_harmony_scoping && context->IsScriptContext()) {
+ if (context->IsScriptContext()) {
return false;
}
- return JSObject::cast(context->extension())->ReferencesObject(obj);
+ return context->extension_object()->ReferencesObject(obj);
}
}
@@ -5430,48 +7544,198 @@
}
-MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
- if (!object->map()->is_extensible()) return object;
+Maybe<bool> JSReceiver::SetIntegrityLevel(Handle<JSReceiver> receiver,
+ IntegrityLevel level,
+ ShouldThrow should_throw) {
+ DCHECK(level == SEALED || level == FROZEN);
- if (!object->HasSloppyArgumentsElements() && !object->map()->is_observed()) {
- return PreventExtensionsWithTransition<NONE>(object);
+ if (receiver->IsJSObject()) {
+ Handle<JSObject> object = Handle<JSObject>::cast(receiver);
+ if (!object->HasSloppyArgumentsElements() &&
+ !object->map()->is_observed() &&
+ (!object->map()->is_strong() || level == SEALED)) { // Fast path.
+ if (level == SEALED) {
+ return JSObject::PreventExtensionsWithTransition<SEALED>(object,
+ should_throw);
+ } else {
+ return JSObject::PreventExtensionsWithTransition<FROZEN>(object,
+ should_throw);
+ }
+ }
}
+ Isolate* isolate = receiver->GetIsolate();
+
+ MAYBE_RETURN(JSReceiver::PreventExtensions(receiver, should_throw),
+ Nothing<bool>());
+
+ Handle<FixedArray> keys;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, keys, JSReceiver::OwnPropertyKeys(receiver), Nothing<bool>());
+
+ PropertyDescriptor no_conf;
+ no_conf.set_configurable(false);
+
+ PropertyDescriptor no_conf_no_write;
+ no_conf_no_write.set_configurable(false);
+ no_conf_no_write.set_writable(false);
+
+ if (level == SEALED) {
+ for (int i = 0; i < keys->length(); ++i) {
+ Handle<Object> key(keys->get(i), isolate);
+ MAYBE_RETURN(
+ DefineOwnProperty(isolate, receiver, key, &no_conf, THROW_ON_ERROR),
+ Nothing<bool>());
+ }
+ return Just(true);
+ }
+
+ for (int i = 0; i < keys->length(); ++i) {
+ Handle<Object> key(keys->get(i), isolate);
+ PropertyDescriptor current_desc;
+ Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor(
+ isolate, receiver, key, ¤t_desc);
+ MAYBE_RETURN(owned, Nothing<bool>());
+ if (owned.FromJust()) {
+ PropertyDescriptor desc =
+ PropertyDescriptor::IsAccessorDescriptor(¤t_desc)
+ ? no_conf
+ : no_conf_no_write;
+ MAYBE_RETURN(
+ DefineOwnProperty(isolate, receiver, key, &desc, THROW_ON_ERROR),
+ Nothing<bool>());
+ }
+ }
+ return Just(true);
+}
+
+
+Maybe<bool> JSReceiver::TestIntegrityLevel(Handle<JSReceiver> object,
+ IntegrityLevel level) {
+ DCHECK(level == SEALED || level == FROZEN);
Isolate* isolate = object->GetIsolate();
- if (object->IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(
- object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return isolate->factory()->false_value();
+ Maybe<bool> extensible = JSReceiver::IsExtensible(object);
+ MAYBE_RETURN(extensible, Nothing<bool>());
+ if (extensible.FromJust()) return Just(false);
+
+ Handle<FixedArray> keys;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, keys, JSReceiver::OwnPropertyKeys(object), Nothing<bool>());
+
+ for (int i = 0; i < keys->length(); ++i) {
+ Handle<Object> key(keys->get(i), isolate);
+ PropertyDescriptor current_desc;
+ Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor(
+ isolate, object, key, ¤t_desc);
+ MAYBE_RETURN(owned, Nothing<bool>());
+ if (owned.FromJust()) {
+ if (current_desc.configurable()) return Just(false);
+ if (level == FROZEN &&
+ PropertyDescriptor::IsDataDescriptor(¤t_desc) &&
+ current_desc.writable()) {
+ return Just(false);
+ }
+ }
}
+ return Just(true);
+}
+
+
+Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object,
+ ShouldThrow should_throw) {
+ if (object->IsJSProxy()) {
+ return JSProxy::PreventExtensions(Handle<JSProxy>::cast(object),
+ should_throw);
+ }
+ DCHECK(object->IsJSObject());
+ return JSObject::PreventExtensions(Handle<JSObject>::cast(object),
+ should_throw);
+}
+
+
+Maybe<bool> JSProxy::PreventExtensions(Handle<JSProxy> proxy,
+ ShouldThrow should_throw) {
+ Isolate* isolate = proxy->GetIsolate();
+ STACK_CHECK(Nothing<bool>());
+ Factory* factory = isolate->factory();
+ Handle<String> trap_name = factory->preventExtensions_string();
+
+ if (proxy->IsRevoked()) {
+ isolate->Throw(
+ *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
+ return Nothing<bool>();
+ }
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
+
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
+ if (trap->IsUndefined()) {
+ return JSReceiver::PreventExtensions(target, should_throw);
+ }
+
+ Handle<Object> trap_result;
+ Handle<Object> args[] = {target};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result,
+ Execution::Call(isolate, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+ if (!trap_result->BooleanValue()) {
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
+ }
+
+ // Enforce the invariant.
+ Maybe<bool> target_result = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN(target_result, Nothing<bool>());
+ if (target_result.FromJust()) {
+ isolate->Throw(*factory->NewTypeError(
+ MessageTemplate::kProxyPreventExtensionsExtensible));
+ return Nothing<bool>();
+ }
+ return Just(true);
+}
+
+
+Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object,
+ ShouldThrow should_throw) {
+ Isolate* isolate = object->GetIsolate();
+
+ if (!object->HasSloppyArgumentsElements() && !object->map()->is_observed()) {
+ return PreventExtensionsWithTransition<NONE>(object, should_throw);
+ }
+
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayAccess(handle(isolate->context()), object)) {
+ isolate->ReportFailedAccessCheck(object);
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kNoAccess));
+ }
+
+ if (!object->map()->is_extensible()) return Just(true);
if (object->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return object;
+ if (iter.IsAtEnd()) return Just(true);
DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
- return PreventExtensions(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)));
+ return PreventExtensions(PrototypeIterator::GetCurrent<JSObject>(iter),
+ should_throw);
}
- // It's not possible to seal objects with external array elements
- if (object->HasExternalArrayElements() ||
- object->HasFixedTypedArrayElements()) {
- THROW_NEW_ERROR(isolate,
- NewTypeError("cant_prevent_ext_external_array_elements",
- HandleVector(&object, 1)),
- Object);
+ if (!object->HasFixedTypedArrayElements()) {
+ // If there are fast elements we normalize.
+ Handle<SeededNumberDictionary> dictionary = NormalizeElements(object);
+ DCHECK(object->HasDictionaryElements() ||
+ object->HasSlowArgumentsElements());
+
+ // Make sure that we never go back to fast case.
+ object->RequireSlowElements(*dictionary);
}
- // If there are fast elements we normalize.
- Handle<SeededNumberDictionary> dictionary = NormalizeElements(object);
- DCHECK(object->HasDictionaryElements() ||
- object->HasDictionaryArgumentsElements());
-
- // Make sure that we never go back to fast case.
- dictionary->set_requires_slow_elements();
-
// Do a map transition, other objects with this map may still
// be extensible.
// TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
@@ -5482,37 +7746,78 @@
DCHECK(!object->map()->is_extensible());
if (object->map()->is_observed()) {
- RETURN_ON_EXCEPTION(
+ RETURN_ON_EXCEPTION_VALUE(
isolate,
EnqueueChangeRecord(object, "preventExtensions", Handle<Name>(),
isolate->factory()->the_hole_value()),
- Object);
+ Nothing<bool>());
}
- return object;
+ return Just(true);
}
-Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
- Handle<JSObject> object) {
- DCHECK(!object->elements()->IsDictionary());
- Isolate* isolate = object->GetIsolate();
- int length = object->IsJSArray()
- ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
- : object->elements()->length();
- if (length > 0) {
- int capacity = 0;
- int used = 0;
- object->GetElementsCapacityAndUsage(&capacity, &used);
- Handle<SeededNumberDictionary> new_element_dictionary =
- SeededNumberDictionary::New(isolate, used);
-
- // Move elements to a dictionary; avoid calling NormalizeElements to avoid
- // unnecessary transitions.
- return CopyFastElementsToDictionary(handle(object->elements()), length,
- new_element_dictionary);
+Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) {
+ if (object->IsJSProxy()) {
+ return JSProxy::IsExtensible(Handle<JSProxy>::cast(object));
}
- // No existing elements, use a pre-allocated empty backing store
- return isolate->factory()->empty_slow_element_dictionary();
+ return Just(JSObject::IsExtensible(Handle<JSObject>::cast(object)));
+}
+
+
+Maybe<bool> JSProxy::IsExtensible(Handle<JSProxy> proxy) {
+ Isolate* isolate = proxy->GetIsolate();
+ STACK_CHECK(Nothing<bool>());
+ Factory* factory = isolate->factory();
+ Handle<String> trap_name = factory->isExtensible_string();
+
+ if (proxy->IsRevoked()) {
+ isolate->Throw(
+ *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
+ return Nothing<bool>();
+ }
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
+
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
+ if (trap->IsUndefined()) {
+ return JSReceiver::IsExtensible(target);
+ }
+
+ Handle<Object> trap_result;
+ Handle<Object> args[] = {target};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result,
+ Execution::Call(isolate, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+
+ // Enforce the invariant.
+ Maybe<bool> target_result = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN(target_result, Nothing<bool>());
+ if (target_result.FromJust() != trap_result->BooleanValue()) {
+ isolate->Throw(
+ *factory->NewTypeError(MessageTemplate::kProxyIsExtensibleInconsistent,
+ factory->ToBoolean(target_result.FromJust())));
+ return Nothing<bool>();
+ }
+ return target_result;
+}
+
+
+bool JSObject::IsExtensible(Handle<JSObject> object) {
+ Isolate* isolate = object->GetIsolate();
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayAccess(handle(isolate->context()), object)) {
+ return true;
+ }
+ if (object->IsJSGlobalProxy()) {
+ PrototypeIterator iter(isolate, *object);
+ if (iter.IsAtEnd()) return false;
+ DCHECK(iter.GetCurrent()->IsJSGlobalObject());
+ return iter.GetCurrent<JSObject>()->map()->is_extensible();
+ }
+ return object->map()->is_extensible();
}
@@ -5527,7 +7832,7 @@
PropertyDetails details = dictionary->DetailsAt(i);
int attrs = attributes;
// READ_ONLY is an invalid attribute for JS setters/getters.
- if ((attributes & READ_ONLY) && details.type() == CALLBACKS) {
+ if ((attributes & READ_ONLY) && details.type() == ACCESSOR_CONSTANT) {
Object* v = dictionary->ValueAt(i);
if (v->IsPropertyCell()) v = PropertyCell::cast(v)->value();
if (v->IsAccessorPair()) attrs &= ~READ_ONLY;
@@ -5541,8 +7846,8 @@
template <PropertyAttributes attrs>
-MaybeHandle<Object> JSObject::PreventExtensionsWithTransition(
- Handle<JSObject> object) {
+Maybe<bool> JSObject::PreventExtensionsWithTransition(
+ Handle<JSObject> object, ShouldThrow should_throw) {
STATIC_ASSERT(attrs == NONE || attrs == SEALED || attrs == FROZEN);
// Sealing/freezing sloppy arguments should be handled elsewhere.
@@ -5551,33 +7856,34 @@
Isolate* isolate = object->GetIsolate();
if (object->IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(
- object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return isolate->factory()->false_value();
+ !isolate->MayAccess(handle(isolate->context()), object)) {
+ isolate->ReportFailedAccessCheck(object);
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kNoAccess));
}
+ if (attrs == NONE && !object->map()->is_extensible()) return Just(true);
+
if (object->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return object;
+ if (iter.IsAtEnd()) return Just(true);
DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
return PreventExtensionsWithTransition<attrs>(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)));
- }
-
- // It's not possible to seal or freeze objects with external array elements
- if (object->HasExternalArrayElements() ||
- object->HasFixedTypedArrayElements()) {
- THROW_NEW_ERROR(isolate,
- NewTypeError("cant_prevent_ext_external_array_elements",
- HandleVector(&object, 1)),
- Object);
+ PrototypeIterator::GetCurrent<JSObject>(iter), should_throw);
}
Handle<SeededNumberDictionary> new_element_dictionary;
- if (!object->elements()->IsDictionary()) {
- new_element_dictionary = GetNormalizedElementDictionary(object);
+ if (!object->HasFixedTypedArrayElements() &&
+ !object->HasDictionaryElements()) {
+ int length =
+ object->IsJSArray()
+ ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
+ : object->elements()->length();
+ new_element_dictionary =
+ length == 0 ? isolate->factory()->empty_slow_element_dictionary()
+ : GetNormalizedElementDictionary(
+ object, handle(object->elements()));
}
Handle<Symbol> transition_marker;
@@ -5591,13 +7897,15 @@
}
Handle<Map> old_map(object->map(), isolate);
- int transition_index = old_map->SearchSpecialTransition(*transition_marker);
- if (transition_index != TransitionArray::kNotFound) {
- Handle<Map> transition_map(old_map->GetTransition(transition_index));
- DCHECK(transition_map->has_dictionary_elements());
+ Map* transition =
+ TransitionArray::SearchSpecial(*old_map, *transition_marker);
+ if (transition != NULL) {
+ Handle<Map> transition_map(transition, isolate);
+ DCHECK(transition_map->has_dictionary_elements() ||
+ transition_map->has_fixed_typed_array_elements());
DCHECK(!transition_map->is_extensible());
JSObject::MigrateToMap(object, transition_map);
- } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
+ } else if (TransitionArray::CanHaveMoreTransitions(old_map)) {
// Create a new descriptor array with the appropriate property attributes
Handle<Map> new_map = Map::CopyForPreventExtensions(
old_map, attrs, transition_marker, "CopyForPreventExtensions");
@@ -5613,14 +7921,32 @@
Handle<Map> new_map =
Map::Copy(handle(object->map()), "SlowCopyForPreventExtensions");
new_map->set_is_extensible(false);
- new_map->set_elements_kind(DICTIONARY_ELEMENTS);
+ if (!new_element_dictionary.is_null()) {
+ new_map->set_elements_kind(DICTIONARY_ELEMENTS);
+ }
JSObject::MigrateToMap(object, new_map);
if (attrs != NONE) {
- ApplyAttributesToDictionary(object->property_dictionary(), attrs);
+ if (object->IsJSGlobalObject()) {
+ ApplyAttributesToDictionary(object->global_dictionary(), attrs);
+ } else {
+ ApplyAttributesToDictionary(object->property_dictionary(), attrs);
+ }
}
}
+ // Both seal and preventExtensions always go through without modifications to
+ // typed array elements. Freeze works only if there are no actual elements.
+ if (object->HasFixedTypedArrayElements()) {
+ if (attrs == FROZEN &&
+ JSArrayBufferView::cast(*object)->byte_length()->Number() > 0) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kCannotFreezeArrayBufferView));
+ return Nothing<bool>();
+ }
+ return Just(true);
+ }
+
DCHECK(object->map()->has_dictionary_elements());
if (!new_element_dictionary.is_null()) {
object->set_elements(*new_element_dictionary);
@@ -5629,23 +7955,13 @@
if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) {
SeededNumberDictionary* dictionary = object->element_dictionary();
// Make sure we never go back to the fast case
- dictionary->set_requires_slow_elements();
+ object->RequireSlowElements(dictionary);
if (attrs != NONE) {
ApplyAttributesToDictionary(dictionary, attrs);
}
}
- return object;
-}
-
-
-MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
- return PreventExtensionsWithTransition<FROZEN>(object);
-}
-
-
-MaybeHandle<Object> JSObject::Seal(Handle<JSObject> object) {
- return PreventExtensionsWithTransition<SEALED>(object);
+ return Just(true);
}
@@ -5656,12 +7972,12 @@
Handle<Map> new_map;
Handle<Map> old_map(object->map(), isolate);
DCHECK(!old_map->is_observed());
- int transition_index =
- old_map->SearchSpecialTransition(isolate->heap()->observed_symbol());
- if (transition_index != TransitionArray::kNotFound) {
- new_map = handle(old_map->GetTransition(transition_index), isolate);
+ Map* transition = TransitionArray::SearchSpecial(
+ *old_map, isolate->heap()->observed_symbol());
+ if (transition != NULL) {
+ new_map = handle(transition, isolate);
DCHECK(new_map->is_observed());
- } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
+ } else if (TransitionArray::CanHaveMoreTransitions(old_map)) {
new_map = Map::CopyForObserved(old_map);
} else {
new_map = Map::Copy(old_map, "SlowObserved");
@@ -5767,7 +8083,7 @@
int limit = copy->map()->NumberOfOwnDescriptors();
for (int i = 0; i < limit; i++) {
PropertyDetails details = descriptors->GetDetails(i);
- if (details.type() != FIELD) continue;
+ if (details.type() != DATA) continue;
FieldIndex index = FieldIndex::ForDescriptor(copy->map(), i);
if (object->IsUnboxedDoubleField(index)) {
if (copying) {
@@ -5794,22 +8110,20 @@
}
}
} else {
- Handle<FixedArray> names =
- isolate->factory()->NewFixedArray(copy->NumberOfOwnProperties());
- copy->GetOwnPropertyNames(*names, 0);
+ // Only deep copy fields from the object literal expression.
+ // In particular, don't try to copy the length attribute of
+ // an array.
+ PropertyFilter filter = static_cast<PropertyFilter>(
+ ONLY_WRITABLE | ONLY_ENUMERABLE | ONLY_CONFIGURABLE);
+ KeyAccumulator accumulator(isolate, filter);
+ accumulator.NextPrototype();
+ copy->CollectOwnPropertyNames(&accumulator, filter);
+ Handle<FixedArray> names = accumulator.GetKeys();
for (int i = 0; i < names->length(); i++) {
- DCHECK(names->get(i)->IsString());
- Handle<String> key_string(String::cast(names->get(i)));
- Maybe<PropertyAttributes> maybe =
- JSReceiver::GetOwnPropertyAttributes(copy, key_string);
- DCHECK(maybe.has_value);
- PropertyAttributes attributes = maybe.value;
- // Only deep copy fields from the object literal expression.
- // In particular, don't try to copy the length attribute of
- // an array.
- if (attributes != NONE) continue;
+ DCHECK(names->get(i)->IsName());
+ Handle<Name> name(Name::cast(names->get(i)));
Handle<Object> value =
- Object::GetProperty(copy, key_string).ToHandleChecked();
+ Object::GetProperty(copy, name).ToHandleChecked();
if (value->IsJSObject()) {
Handle<JSObject> result;
ASSIGN_RETURN_ON_EXCEPTION(
@@ -5818,7 +8132,7 @@
JSObject);
if (copying) {
// Creating object copy for literals. No strict mode needed.
- JSObject::SetProperty(copy, key_string, result, SLOPPY).Assert();
+ JSObject::SetProperty(copy, name, result, SLOPPY).Assert();
}
}
}
@@ -5826,7 +8140,7 @@
// Deep copy own elements.
// Pixel elements cannot be created using an object literal.
- DCHECK(!copy->HasExternalArrayElements());
+ DCHECK(!copy->HasFixedTypedArrayElements());
switch (kind) {
case FAST_SMI_ELEMENTS:
case FAST_ELEMENTS:
@@ -5881,13 +8195,13 @@
}
break;
}
- case SLOPPY_ARGUMENTS_ELEMENTS:
+ case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
+ case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
UNIMPLEMENTED();
break;
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ELEMENTS: \
case TYPE##_ELEMENTS: \
TYPED_ARRAYS(TYPED_ARRAY_CASE)
@@ -5928,6 +8242,141 @@
}
+// 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);
+}
+
+
+// TODO(cbruni/jkummerow): Consider moving this into elements.cc.
+bool HasEnumerableElements(JSObject* object) {
+ if (object->IsJSValue()) {
+ Object* value = JSValue::cast(object)->value();
+ if (value->IsString()) {
+ if (String::cast(value)->length() > 0) return true;
+ }
+ }
+ switch (object->GetElementsKind()) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS: {
+ int length = object->IsJSArray()
+ ? Smi::cast(JSArray::cast(object)->length())->value()
+ : object->elements()->length();
+ return length > 0;
+ }
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS: {
+ FixedArray* elements = FixedArray::cast(object->elements());
+ int length = object->IsJSArray()
+ ? Smi::cast(JSArray::cast(object)->length())->value()
+ : elements->length();
+ for (int i = 0; i < length; i++) {
+ if (!elements->is_the_hole(i)) return true;
+ }
+ return false;
+ }
+ case FAST_HOLEY_DOUBLE_ELEMENTS: {
+ int length = object->IsJSArray()
+ ? Smi::cast(JSArray::cast(object)->length())->value()
+ : object->elements()->length();
+ // Zero-length arrays would use the empty FixedArray...
+ if (length == 0) return false;
+ // ...so only cast to FixedDoubleArray otherwise.
+ FixedDoubleArray* elements = FixedDoubleArray::cast(object->elements());
+ for (int i = 0; i < length; i++) {
+ if (!elements->is_the_hole(i)) return true;
+ }
+ return false;
+ }
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
+ case TYPE##_ELEMENTS:
+
+ TYPED_ARRAYS(TYPED_ARRAY_CASE)
+#undef TYPED_ARRAY_CASE
+ {
+ int length = object->elements()->length();
+ return length > 0;
+ }
+ case DICTIONARY_ELEMENTS: {
+ SeededNumberDictionary* elements =
+ SeededNumberDictionary::cast(object->elements());
+ return elements->NumberOfElementsFilterAttributes(ONLY_ENUMERABLE) > 0;
+ }
+ case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
+ case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
+ // We're approximating non-empty arguments objects here.
+ return true;
+ }
+ UNREACHABLE();
+ return true;
+}
+
+
// 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).
@@ -5938,39 +8387,21 @@
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 (HasEnumerableElements(current)) return false;
+ if (current != this && enum_length != 0) return false;
}
return true;
}
-static bool FilterKey(Object* key, PropertyAttributes filter) {
- if ((filter & SYMBOLIC) && key->IsSymbol()) {
- return true;
- }
-
- if ((filter & PRIVATE_SYMBOL) &&
- key->IsSymbol() && Symbol::cast(key)->is_private()) {
- return true;
- }
-
- if ((filter & STRING) && !key->IsSymbol()) {
- return true;
- }
-
- return false;
-}
-
-
int Map::NumberOfDescribedProperties(DescriptorFlag which,
- PropertyAttributes filter) {
+ PropertyFilter filter) {
int result = 0;
DescriptorArray* descs = instance_descriptors();
int limit = which == ALL_DESCRIPTORS
@@ -5978,7 +8409,7 @@
: NumberOfOwnDescriptors();
for (int i = 0; i < limit; i++) {
if ((descs->GetDetails(i).attributes() & filter) == 0 &&
- !FilterKey(descs->GetKey(i), filter)) {
+ !descs->GetKey(i)->FilterKey(filter)) {
result++;
}
}
@@ -5992,7 +8423,7 @@
DescriptorArray* descs = instance_descriptors();
for (int i = 0; i < number_of_own_descriptors; i++) {
PropertyDetails details = descs->GetDetails(i);
- if (details.type() == FIELD) {
+ if (details.location() == kField) {
int candidate = details.field_index() + details.field_width_in_words();
if (candidate > free_index) free_index = candidate;
}
@@ -6023,90 +8454,98 @@
}
-static Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
- bool cache_result) {
+namespace {
+
+Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
+ Handle<JSObject> object,
+ bool cache_enum_length) {
+ Handle<Map> map(object->map());
+ Handle<DescriptorArray> descs =
+ Handle<DescriptorArray>(map->instance_descriptors(), isolate);
+ int own_property_count = map->EnumLength();
+ // If the enum length of the given map is set to kInvalidEnumCache, this
+ // means that the map itself has never used the present enum cache. The
+ // first step to using the cache is to set the enum length of the map by
+ // counting the number of own descriptors that are ENUMERABLE_STRINGS.
+ if (own_property_count == kInvalidEnumCacheSentinel) {
+ own_property_count =
+ map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
+ } else {
+ DCHECK(
+ own_property_count ==
+ map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS));
+ }
+
+ if (descs->HasEnumCache()) {
+ Handle<FixedArray> keys(descs->GetEnumCache(), isolate);
+ // In case the number of properties required in the enum are actually
+ // present, we can reuse the enum cache. Otherwise, this means that the
+ // enum cache was generated for a previous (smaller) version of the
+ // Descriptor Array. In that case we regenerate the enum cache.
+ if (own_property_count <= keys->length()) {
+ isolate->counters()->enum_cache_hits()->Increment();
+ if (cache_enum_length) map->SetEnumLength(own_property_count);
+ return ReduceFixedArrayTo(keys, own_property_count);
+ }
+ }
+
+ if (descs->IsEmpty()) {
+ isolate->counters()->enum_cache_hits()->Increment();
+ if (cache_enum_length) map->SetEnumLength(0);
+ return isolate->factory()->empty_fixed_array();
+ }
+
+ isolate->counters()->enum_cache_misses()->Increment();
+
+ Handle<FixedArray> storage =
+ isolate->factory()->NewFixedArray(own_property_count);
+ Handle<FixedArray> indices =
+ isolate->factory()->NewFixedArray(own_property_count);
+
+ int size = map->NumberOfOwnDescriptors();
+ int index = 0;
+
+ for (int i = 0; i < size; i++) {
+ PropertyDetails details = descs->GetDetails(i);
+ Object* key = descs->GetKey(i);
+ if (details.IsDontEnum() || key->IsSymbol()) continue;
+ storage->set(index, key);
+ if (!indices.is_null()) {
+ if (details.type() != DATA) {
+ indices = Handle<FixedArray>();
+ } else {
+ FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
+ int load_by_field_index = field_index.GetLoadByFieldIndex();
+ indices->set(index, Smi::FromInt(load_by_field_index));
+ }
+ }
+ index++;
+ }
+ DCHECK(index == storage->length());
+
+ DescriptorArray::SetEnumCache(descs, isolate, storage, indices);
+ if (cache_enum_length) {
+ map->SetEnumLength(own_property_count);
+ }
+ return storage;
+}
+
+} // namespace
+
+
+Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object,
+ bool cache_enum_length) {
Isolate* isolate = object->GetIsolate();
if (object->HasFastProperties()) {
- int own_property_count = object->map()->EnumLength();
- // If the enum length of the given map is set to kInvalidEnumCache, this
- // means that the map itself has never used the present enum cache. The
- // first step to using the cache is to set the enum length of the map by
- // counting the number of own descriptors that are not DONT_ENUM or
- // SYMBOLIC.
- if (own_property_count == kInvalidEnumCacheSentinel) {
- own_property_count = object->map()->NumberOfDescribedProperties(
- OWN_DESCRIPTORS, DONT_SHOW);
- } else {
- DCHECK(own_property_count == object->map()->NumberOfDescribedProperties(
- OWN_DESCRIPTORS, DONT_SHOW));
+ return GetFastEnumPropertyKeys(isolate, object, cache_enum_length);
+ } else if (object->IsJSGlobalObject()) {
+ Handle<GlobalDictionary> dictionary(object->global_dictionary());
+ int length = dictionary->NumberOfEnumElements();
+ if (length == 0) {
+ return Handle<FixedArray>(isolate->heap()->empty_fixed_array());
}
-
- if (object->map()->instance_descriptors()->HasEnumCache()) {
- DescriptorArray* desc = object->map()->instance_descriptors();
- Handle<FixedArray> keys(desc->GetEnumCache(), isolate);
-
- // In case the number of properties required in the enum are actually
- // present, we can reuse the enum cache. Otherwise, this means that the
- // enum cache was generated for a previous (smaller) version of the
- // Descriptor Array. In that case we regenerate the enum cache.
- if (own_property_count <= keys->length()) {
- if (cache_result) object->map()->SetEnumLength(own_property_count);
- isolate->counters()->enum_cache_hits()->Increment();
- return ReduceFixedArrayTo(keys, own_property_count);
- }
- }
-
- Handle<Map> map(object->map());
-
- if (map->instance_descriptors()->IsEmpty()) {
- isolate->counters()->enum_cache_hits()->Increment();
- if (cache_result) map->SetEnumLength(0);
- return isolate->factory()->empty_fixed_array();
- }
-
- isolate->counters()->enum_cache_misses()->Increment();
-
- Handle<FixedArray> storage = isolate->factory()->NewFixedArray(
- own_property_count);
- Handle<FixedArray> indices = isolate->factory()->NewFixedArray(
- own_property_count);
-
- Handle<DescriptorArray> descs =
- Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate);
-
- int size = map->NumberOfOwnDescriptors();
- int index = 0;
-
- for (int i = 0; i < size; i++) {
- PropertyDetails details = descs->GetDetails(i);
- Object* key = descs->GetKey(i);
- if (!(details.IsDontEnum() || key->IsSymbol())) {
- storage->set(index, key);
- if (!indices.is_null()) {
- if (details.type() != FIELD) {
- indices = Handle<FixedArray>();
- } else {
- FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
- int load_by_field_index = field_index.GetLoadByFieldIndex();
- indices->set(index, Smi::FromInt(load_by_field_index));
- }
- }
- index++;
- }
- }
- DCHECK(index == storage->length());
-
- Handle<FixedArray> bridge_storage =
- isolate->factory()->NewFixedArray(
- DescriptorArray::kEnumCacheBridgeLength);
- DescriptorArray* desc = object->map()->instance_descriptors();
- desc->SetEnumCache(*bridge_storage,
- *storage,
- indices.is_null() ? Object::cast(Smi::FromInt(0))
- : Object::cast(*indices));
- if (cache_result) {
- object->map()->SetEnumLength(own_property_count);
- }
+ Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
+ dictionary->CopyEnumKeysTo(*storage);
return storage;
} else {
Handle<NameDictionary> dictionary(object->property_dictionary());
@@ -6121,74 +8560,87 @@
}
-MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
- KeyCollectionType type) {
- USE(ContainsOnlyValidKeys);
- Isolate* isolate = object->GetIsolate();
- Handle<FixedArray> content = isolate->factory()->empty_fixed_array();
- Handle<JSFunction> arguments_function(
- JSFunction::cast(isolate->sloppy_arguments_map()->constructor()));
+enum IndexedOrNamed { kIndexed, kNamed };
- // Only collect keys if access is permitted.
- for (PrototypeIterator iter(isolate, object,
- PrototypeIterator::START_AT_RECEIVER);
- !iter.IsAtEnd(); iter.Advance()) {
- if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
- Handle<JSProxy> proxy(JSProxy::cast(*PrototypeIterator::GetCurrent(iter)),
- isolate);
- Handle<Object> args[] = { proxy };
- Handle<Object> names;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, names,
- Execution::Call(isolate,
- isolate->proxy_enumerate(),
- object,
- arraysize(args),
- args),
- FixedArray);
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, content,
- FixedArray::AddKeysFromArrayLike(
- content, Handle<JSObject>::cast(names)),
- FixedArray);
- break;
+
+// Returns |true| on success, |nothing| on exception.
+template <class Callback, IndexedOrNamed type>
+static Maybe<bool> GetKeysFromInterceptor(Isolate* isolate,
+ Handle<JSReceiver> receiver,
+ Handle<JSObject> object,
+ PropertyFilter filter,
+ KeyAccumulator* accumulator) {
+ if (type == kIndexed) {
+ if (!object->HasIndexedInterceptor()) return Just(true);
+ } else {
+ if (!object->HasNamedInterceptor()) return Just(true);
+ }
+ Handle<InterceptorInfo> interceptor(type == kIndexed
+ ? object->GetIndexedInterceptor()
+ : object->GetNamedInterceptor(),
+ isolate);
+ if ((filter & ONLY_ALL_CAN_READ) && !interceptor->all_can_read()) {
+ return Just(true);
+ }
+ PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
+ *object);
+ v8::Local<v8::Object> result;
+ if (!interceptor->enumerator()->IsUndefined()) {
+ Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator());
+ const char* log_tag = type == kIndexed ? "interceptor-indexed-enum"
+ : "interceptor-named-enum";
+ LOG(isolate, ApiObjectAccess(log_tag, *object));
+ result = args.Call(enum_fun);
+ }
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ if (result.IsEmpty()) return Just(true);
+ DCHECK(v8::Utils::OpenHandle(*result)->IsJSArray() ||
+ (v8::Utils::OpenHandle(*result)->IsJSObject() &&
+ Handle<JSObject>::cast(v8::Utils::OpenHandle(*result))
+ ->HasSloppyArgumentsElements()));
+ // The accumulator takes care of string/symbol filtering.
+ if (type == kIndexed) {
+ accumulator->AddElementKeysFromInterceptor(
+ Handle<JSObject>::cast(v8::Utils::OpenHandle(*result)));
+ } else {
+ accumulator->AddKeys(
+ Handle<JSObject>::cast(v8::Utils::OpenHandle(*result)));
+ }
+ return Just(true);
+}
+
+
+// Returns |true| on success, |false| if prototype walking should be stopped,
+// |nothing| if an exception was thrown.
+static Maybe<bool> GetKeysFromJSObject(Isolate* isolate,
+ Handle<JSReceiver> receiver,
+ Handle<JSObject> object,
+ PropertyFilter* filter,
+ JSReceiver::KeyCollectionType type,
+ KeyAccumulator* accumulator) {
+ accumulator->NextPrototype();
+ // Check access rights if required.
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayAccess(handle(isolate->context()), object)) {
+ // The cross-origin spec says that [[Enumerate]] shall return an empty
+ // iterator when it doesn't have access...
+ if (type == JSReceiver::INCLUDE_PROTOS) {
+ return Just(false);
}
+ // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
+ DCHECK(type == JSReceiver::OWN_ONLY);
+ *filter = static_cast<PropertyFilter>(*filter | ONLY_ALL_CAN_READ);
+ }
- Handle<JSObject> current =
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
+ JSObject::CollectOwnElementKeys(object, accumulator, *filter);
- // Check access rights if required.
- if (current->IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(
- current, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
- isolate->ReportFailedAccessCheck(current, v8::ACCESS_KEYS);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, FixedArray);
- break;
- }
+ // Add the element keys from the interceptor.
+ Maybe<bool> success =
+ GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, kIndexed>(
+ isolate, receiver, object, *filter, accumulator);
+ MAYBE_RETURN(success, Nothing<bool>());
- // Compute the element keys.
- 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));
-
- // 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);
- }
- DCHECK(ContainsOnlyValidKeys(content));
- }
-
+ if (*filter == ENUMERABLE_STRINGS) {
// We can cache the computed property keys if access checks are
// not needed and no interceptors are involved.
//
@@ -6199,131 +8651,286 @@
// Wrapped strings have elements, but don't have an elements
// array or dictionary. So the fast inline test for whether to
// use the cache says yes, so we should not create a cache.
- bool cache_enum_keys =
- ((current->map()->constructor() != *arguments_function) &&
- !current->IsJSValue() &&
- !current->IsAccessCheckNeeded() &&
- !current->HasNamedInterceptor() &&
- !current->HasIndexedInterceptor());
+ Handle<JSFunction> arguments_function(
+ JSFunction::cast(isolate->sloppy_arguments_map()->GetConstructor()));
+ bool has_hidden_prototype = false;
+ Object* prototype = object->map()->prototype();
+ if (prototype->IsJSObject()) {
+ has_hidden_prototype =
+ JSObject::cast(prototype)->map()->is_hidden_prototype();
+ }
+ bool cache_enum_length =
+ ((object->map()->GetConstructor() != *arguments_function) &&
+ !object->IsJSValue() && !object->IsAccessCheckNeeded() &&
+ !object->HasNamedInterceptor() && !object->HasIndexedInterceptor() &&
+ !has_hidden_prototype);
// Compute the property keys and cache them if possible.
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, content,
- FixedArray::UnionOfKeys(
- content, GetEnumPropertyKeys(current, cache_enum_keys)),
- FixedArray);
- DCHECK(ContainsOnlyValidKeys(content));
-
- // 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);
- }
- DCHECK(ContainsOnlyValidKeys(content));
- }
-
- // If we only want own properties we bail out after the first
- // iteration.
- if (type == OWN_ONLY) break;
+ Handle<FixedArray> enum_keys =
+ JSObject::GetEnumPropertyKeys(object, cache_enum_length);
+ accumulator->AddKeys(enum_keys);
+ } else {
+ object->CollectOwnPropertyNames(accumulator, *filter);
}
- return content;
+
+ // Add the property keys from the interceptor.
+ success = GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback,
+ kNamed>(isolate, receiver, object, *filter,
+ accumulator);
+ MAYBE_RETURN(success, Nothing<bool>());
+ return Just(true);
}
-// Try to update an accessor in an elements dictionary. Return true if the
-// update succeeded, and false otherwise.
-static bool UpdateGetterSetterInDictionary(
- SeededNumberDictionary* dictionary,
- uint32_t index,
- Object* getter,
- Object* setter,
- PropertyAttributes attributes) {
- int entry = dictionary->FindEntry(index);
- if (entry != SeededNumberDictionary::kNotFound) {
- Object* result = dictionary->ValueAt(entry);
- PropertyDetails details = dictionary->DetailsAt(entry);
- if (details.type() == CALLBACKS && result->IsAccessorPair()) {
- DCHECK(details.IsConfigurable());
- if (details.attributes() != attributes) {
- dictionary->DetailsAtPut(
- entry,
- PropertyDetails(attributes, CALLBACKS, index));
+// Helper function for JSReceiver::GetKeys() below. Can be called recursively.
+// Returns |true| or |nothing|.
+static Maybe<bool> GetKeys_Internal(Isolate* isolate,
+ Handle<JSReceiver> receiver,
+ Handle<JSReceiver> object,
+ JSReceiver::KeyCollectionType type,
+ PropertyFilter filter,
+ KeyAccumulator* accumulator) {
+ PrototypeIterator::WhereToEnd end = type == JSReceiver::OWN_ONLY
+ ? PrototypeIterator::END_AT_NON_HIDDEN
+ : PrototypeIterator::END_AT_NULL;
+ for (PrototypeIterator iter(isolate, object,
+ PrototypeIterator::START_AT_RECEIVER);
+ !iter.IsAtEnd(end); iter.Advance()) {
+ Handle<JSReceiver> current =
+ PrototypeIterator::GetCurrent<JSReceiver>(iter);
+ Maybe<bool> result = Just(false); // Dummy initialization.
+ if (current->IsJSProxy()) {
+ if (type == JSReceiver::OWN_ONLY) {
+ result = JSProxy::OwnPropertyKeys(isolate, receiver,
+ Handle<JSProxy>::cast(current),
+ filter, accumulator);
+ } else {
+ DCHECK(type == JSReceiver::INCLUDE_PROTOS);
+ result = JSProxy::Enumerate(
+ isolate, receiver, Handle<JSProxy>::cast(current), accumulator);
}
- AccessorPair::cast(result)->SetComponents(getter, setter);
- return true;
+ } else {
+ DCHECK(current->IsJSObject());
+ result = GetKeysFromJSObject(isolate, receiver,
+ Handle<JSObject>::cast(current), &filter,
+ type, accumulator);
}
+ MAYBE_RETURN(result, Nothing<bool>());
+ if (!result.FromJust()) break; // |false| means "stop iterating".
}
- return false;
+ return Just(true);
}
-void JSObject::DefineElementAccessor(Handle<JSObject> object,
- uint32_t index,
- Handle<Object> getter,
- Handle<Object> setter,
- PropertyAttributes attributes) {
- switch (object->GetElementsKind()) {
- case FAST_SMI_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS:
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_HOLEY_ELEMENTS:
- case FAST_HOLEY_DOUBLE_ELEMENTS:
- break;
+// ES6 9.5.11
+// Returns false in case of exception.
+// static
+Maybe<bool> JSProxy::Enumerate(Isolate* isolate, Handle<JSReceiver> receiver,
+ Handle<JSProxy> proxy,
+ KeyAccumulator* accumulator) {
+ STACK_CHECK(Nothing<bool>());
+ // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+ Handle<Object> handler(proxy->handler(), isolate);
+ // 2. If handler is null, throw a TypeError exception.
+ // 3. Assert: Type(handler) is Object.
+ if (proxy->IsRevoked()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyRevoked,
+ isolate->factory()->enumerate_string()));
+ return Nothing<bool>();
+ }
+ // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ // 5. Let trap be ? GetMethod(handler, "enumerate").
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
+ isolate->factory()->enumerate_string()),
+ Nothing<bool>());
+ // 6. If trap is undefined, then
+ if (trap->IsUndefined()) {
+ // 6a. Return target.[[Enumerate]]().
+ return GetKeys_Internal(isolate, receiver, target, INCLUDE_PROTOS,
+ ENUMERABLE_STRINGS, accumulator);
+ }
+ // The "proxy_enumerate" helper calls the trap (steps 7 - 9), which returns
+ // a generator; it then iterates over that generator until it's exhausted
+ // and returns an array containing the generated values.
+ Handle<Object> trap_result_array;
+ Handle<Object> args[] = {trap, handler, target};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result_array,
+ Execution::Call(isolate, isolate->proxy_enumerate(),
+ isolate->factory()->undefined_value(), arraysize(args),
+ args),
+ Nothing<bool>());
+ accumulator->NextPrototype();
+ accumulator->AddKeysFromProxy(Handle<JSObject>::cast(trap_result_array));
+ return Just(true);
+}
-#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ELEMENTS: \
- case TYPE##_ELEMENTS: \
- TYPED_ARRAYS(TYPED_ARRAY_CASE)
-#undef TYPED_ARRAY_CASE
- // Ignore getters and setters on pixel and external array elements.
- return;
-
- case DICTIONARY_ELEMENTS:
- if (UpdateGetterSetterInDictionary(object->element_dictionary(),
- index,
- *getter,
- *setter,
- attributes)) {
- return;
- }
- break;
- case SLOPPY_ARGUMENTS_ELEMENTS: {
- // Ascertain whether we have read-only properties or an existing
- // getter/setter pair in an arguments elements dictionary backing
- // store.
- FixedArray* parameter_map = FixedArray::cast(object->elements());
- uint32_t length = parameter_map->length();
- Object* probe =
- index < (length - 2) ? parameter_map->get(index + 2) : NULL;
- if (probe == NULL || probe->IsTheHole()) {
- FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
- if (arguments->IsDictionary()) {
- SeededNumberDictionary* dictionary =
- SeededNumberDictionary::cast(arguments);
- if (UpdateGetterSetterInDictionary(dictionary,
- index,
- *getter,
- *setter,
- attributes)) {
- return;
- }
- }
- }
- break;
+// ES6 9.5.12
+// Returns |true| on success, |nothing| in case of exception.
+// static
+Maybe<bool> JSProxy::OwnPropertyKeys(Isolate* isolate,
+ Handle<JSReceiver> receiver,
+ Handle<JSProxy> proxy,
+ PropertyFilter filter,
+ KeyAccumulator* accumulator) {
+ STACK_CHECK(Nothing<bool>());
+ // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+ Handle<Object> handler(proxy->handler(), isolate);
+ // 2. If handler is null, throw a TypeError exception.
+ // 3. Assert: Type(handler) is Object.
+ if (proxy->IsRevoked()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyRevoked, isolate->factory()->ownKeys_string()));
+ return Nothing<bool>();
+ }
+ // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ // 5. Let trap be ? GetMethod(handler, "ownKeys").
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
+ isolate->factory()->ownKeys_string()),
+ Nothing<bool>());
+ // 6. If trap is undefined, then
+ if (trap->IsUndefined()) {
+ // 6a. Return target.[[OwnPropertyKeys]]().
+ return GetKeys_Internal(isolate, receiver, target, OWN_ONLY, filter,
+ accumulator);
+ }
+ // 7. Let trapResultArray be Call(trap, handler, «target»).
+ Handle<Object> trap_result_array;
+ Handle<Object> args[] = {target};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result_array,
+ Execution::Call(isolate, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+ // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray,
+ // «String, Symbol»).
+ Handle<FixedArray> trap_result;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result,
+ Object::CreateListFromArrayLike(isolate, trap_result_array,
+ ElementTypes::kStringAndSymbol),
+ Nothing<bool>());
+ // 9. Let extensibleTarget be ? IsExtensible(target).
+ Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN(maybe_extensible, Nothing<bool>());
+ bool extensible_target = maybe_extensible.FromJust();
+ // 10. Let targetKeys be ? target.[[OwnPropertyKeys]]().
+ Handle<FixedArray> target_keys;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_keys,
+ JSReceiver::OwnPropertyKeys(target),
+ Nothing<bool>());
+ // 11. (Assert)
+ // 12. Let targetConfigurableKeys be an empty List.
+ // To save memory, we're re-using target_keys and will modify it in-place.
+ Handle<FixedArray> target_configurable_keys = target_keys;
+ // 13. Let targetNonconfigurableKeys be an empty List.
+ Handle<FixedArray> target_nonconfigurable_keys =
+ isolate->factory()->NewFixedArray(target_keys->length());
+ int nonconfigurable_keys_length = 0;
+ // 14. Repeat, for each element key of targetKeys:
+ for (int i = 0; i < target_keys->length(); ++i) {
+ // 14a. Let desc be ? target.[[GetOwnProperty]](key).
+ PropertyDescriptor desc;
+ Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor(
+ isolate, target, handle(target_keys->get(i), isolate), &desc);
+ MAYBE_RETURN(found, Nothing<bool>());
+ // 14b. If desc is not undefined and desc.[[Configurable]] is false, then
+ if (found.FromJust() && !desc.configurable()) {
+ // 14b i. Append key as an element of targetNonconfigurableKeys.
+ target_nonconfigurable_keys->set(nonconfigurable_keys_length,
+ target_keys->get(i));
+ nonconfigurable_keys_length++;
+ // The key was moved, null it out in the original list.
+ target_keys->set(i, Smi::FromInt(0));
+ } else {
+ // 14c. Else,
+ // 14c i. Append key as an element of targetConfigurableKeys.
+ // (No-op, just keep it in |target_keys|.)
}
}
+ accumulator->NextPrototype(); // Prepare for accumulating keys.
+ // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty,
+ // then:
+ if (extensible_target && nonconfigurable_keys_length == 0) {
+ // 15a. Return trapResult.
+ return accumulator->AddKeysFromProxy(proxy, trap_result);
+ }
+ // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult.
+ Zone set_zone;
+ const int kPresent = 1;
+ const int kGone = 0;
+ IdentityMap<int> unchecked_result_keys(isolate->heap(), &set_zone);
+ int unchecked_result_keys_size = trap_result->length();
+ for (int i = 0; i < trap_result->length(); ++i) {
+ DCHECK(trap_result->get(i)->IsUniqueName());
+ unchecked_result_keys.Set(trap_result->get(i), kPresent);
+ }
+ // 17. Repeat, for each key that is an element of targetNonconfigurableKeys:
+ for (int i = 0; i < nonconfigurable_keys_length; ++i) {
+ Object* key = target_nonconfigurable_keys->get(i);
+ // 17a. If key is not an element of uncheckedResultKeys, throw a
+ // TypeError exception.
+ int* found = unchecked_result_keys.Find(key);
+ if (found == nullptr || *found == kGone) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate)));
+ return Nothing<bool>();
+ }
+ // 17b. Remove key from uncheckedResultKeys.
+ *found = kGone;
+ unchecked_result_keys_size--;
+ }
+ // 18. If extensibleTarget is true, return trapResult.
+ if (extensible_target) {
+ return accumulator->AddKeysFromProxy(proxy, trap_result);
+ }
+ // 19. Repeat, for each key that is an element of targetConfigurableKeys:
+ for (int i = 0; i < target_configurable_keys->length(); ++i) {
+ Object* key = target_configurable_keys->get(i);
+ if (key->IsSmi()) continue; // Zapped entry, was nonconfigurable.
+ // 19a. If key is not an element of uncheckedResultKeys, throw a
+ // TypeError exception.
+ int* found = unchecked_result_keys.Find(key);
+ if (found == nullptr || *found == kGone) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate)));
+ return Nothing<bool>();
+ }
+ // 19b. Remove key from uncheckedResultKeys.
+ *found = kGone;
+ unchecked_result_keys_size--;
+ }
+ // 20. If uncheckedResultKeys is not empty, throw a TypeError exception.
+ if (unchecked_result_keys_size != 0) {
+ DCHECK_GT(unchecked_result_keys_size, 0);
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyOwnKeysNonExtensible));
+ return Nothing<bool>();
+ }
+ // 21. Return trapResult.
+ return accumulator->AddKeysFromProxy(proxy, trap_result);
+}
+
+MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
+ KeyCollectionType type,
+ PropertyFilter filter,
+ GetKeysConversion keys_conversion) {
+ USE(ContainsOnlyValidKeys);
Isolate* isolate = object->GetIsolate();
- Handle<AccessorPair> accessors = isolate->factory()->NewAccessorPair();
- accessors->SetComponents(*getter, *setter);
-
- SetElementCallback(object, index, accessors, attributes);
+ KeyAccumulator accumulator(isolate, filter);
+ MAYBE_RETURN(
+ GetKeys_Internal(isolate, object, object, type, filter, &accumulator),
+ MaybeHandle<FixedArray>());
+ Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion);
+ DCHECK(ContainsOnlyValidKeys(keys));
+ return keys;
}
@@ -6333,14 +8940,23 @@
}
for (PrototypeIterator iter(this); !iter.IsAtEnd(); iter.Advance()) {
- if (iter.GetCurrent()->IsJSProxy()) {
- // Be conservative, don't walk into proxies.
+ // Be conservative, don't walk into proxies.
+ if (iter.GetCurrent()->IsJSProxy()) return true;
+ // String wrappers have non-configurable, non-writable elements.
+ if (iter.GetCurrent()->IsStringWrapper()) return true;
+ JSObject* current = iter.GetCurrent<JSObject>();
+
+ if (current->HasDictionaryElements() &&
+ current->element_dictionary()->requires_slow_elements()) {
return true;
}
- if (IsDictionaryElementsKind(
- JSObject::cast(iter.GetCurrent())->map()->elements_kind())) {
- return true;
+ if (current->HasSlowArgumentsElements()) {
+ FixedArray* parameter_map = FixedArray::cast(current->elements());
+ Object* arguments = parameter_map->get(1);
+ if (SeededNumberDictionary::cast(arguments)->requires_slow_elements()) {
+ return true;
+ }
}
}
@@ -6348,172 +8964,71 @@
}
-void JSObject::SetElementCallback(Handle<JSObject> object,
- uint32_t index,
- Handle<Object> structure,
- PropertyAttributes attributes) {
- Heap* heap = object->GetHeap();
- PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0);
-
- // Normalize elements to make this operation simple.
- bool had_dictionary_elements = object->HasDictionaryElements();
- Handle<SeededNumberDictionary> dictionary = NormalizeElements(object);
- DCHECK(object->HasDictionaryElements() ||
- object->HasDictionaryArgumentsElements());
- // Update the dictionary with the new CALLBACKS property.
- dictionary = SeededNumberDictionary::Set(dictionary, index, structure,
- details);
- dictionary->set_requires_slow_elements();
-
- // Update the dictionary backing store on the object.
- if (object->elements()->map() == heap->sloppy_arguments_elements_map()) {
- // Also delete any parameter alias.
- //
- // TODO(kmillikin): when deleting the last parameter alias we could
- // switch to a direct backing store without the parameter map. This
- // would allow GC of the context.
- FixedArray* parameter_map = FixedArray::cast(object->elements());
- if (index < static_cast<uint32_t>(parameter_map->length()) - 2) {
- parameter_map->set(index + 2, heap->the_hole_value());
- }
- parameter_map->set(1, *dictionary);
- } else {
- object->set_elements(*dictionary);
-
- if (!had_dictionary_elements) {
- // KeyedStoreICs (at least the non-generic ones) need a reset.
- heap->ClearAllICsByKind(Code::KEYED_STORE_IC);
- }
- }
-}
-
-
-void JSObject::SetPropertyCallback(Handle<JSObject> object,
- Handle<Name> name,
- Handle<Object> structure,
- PropertyAttributes attributes) {
- PropertyNormalizationMode mode = object->map()->is_prototype_map()
- ? KEEP_INOBJECT_PROPERTIES
- : CLEAR_INOBJECT_PROPERTIES;
- // Normalize object to make this operation simple.
- NormalizeProperties(object, mode, 0, "SetPropertyCallback");
-
- // For the global object allocate a new map to invalidate the global inline
- // caches which have a global property cell reference directly in the code.
- if (object->IsGlobalObject()) {
- Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
- DCHECK(new_map->is_dictionary_map());
-#if TRACE_MAPS
- if (FLAG_trace_maps) {
- PrintF("[TraceMaps: GlobalPropertyCallback from= %p to= %p ]\n",
- reinterpret_cast<void*>(object->map()),
- reinterpret_cast<void*>(*new_map));
- }
-#endif
- JSObject::MigrateToMap(object, new_map);
-
- // When running crankshaft, changing the map is not enough. We
- // need to deoptimize all functions that rely on this global
- // object.
- Deoptimizer::DeoptimizeGlobalObject(*object);
- }
-
- // Update the dictionary with the new CALLBACKS property.
- PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0);
- SetNormalizedProperty(object, name, structure, details);
-
- ReoptimizeIfPrototype(object);
-}
-
-
MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object,
Handle<Name> name,
Handle<Object> getter,
Handle<Object> setter,
PropertyAttributes attributes) {
Isolate* isolate = object->GetIsolate();
- // Check access rights if needed.
- if (object->IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) {
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return isolate->factory()->undefined_value();
+
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ isolate, object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
+ return DefineAccessor(&it, getter, setter, attributes);
+}
+
+
+MaybeHandle<Object> JSObject::DefineAccessor(LookupIterator* it,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes) {
+ Isolate* isolate = it->isolate();
+
+ if (it->state() == LookupIterator::ACCESS_CHECK) {
+ if (!it->HasAccess()) {
+ isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>());
+ RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
+ return isolate->factory()->undefined_value();
+ }
+ it->Next();
}
- if (object->IsJSGlobalProxy()) {
- PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return isolate->factory()->undefined_value();
- DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
- DefineAccessor(Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)),
- name, getter, setter, attributes);
- return isolate->factory()->undefined_value();
+ Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver());
+ // Ignore accessors on typed arrays.
+ if (it->IsElement() && object->HasFixedTypedArrayElements()) {
+ return it->factory()->undefined_value();
}
- // Make sure that the top context does not change when doing callbacks or
- // interceptor calls.
- AssertNoContextChange ncc(isolate);
-
- // Try to flatten before operating on the string.
- if (name->IsString()) name = String::Flatten(Handle<String>::cast(name));
-
- uint32_t index = 0;
- bool is_element = name->AsArrayIndex(&index);
-
Handle<Object> old_value = isolate->factory()->the_hole_value();
bool is_observed = object->map()->is_observed() &&
- !isolate->IsInternallyUsedPropertyName(name);
+ !isolate->IsInternallyUsedPropertyName(it->GetName());
bool preexists = false;
if (is_observed) {
- if (is_element) {
- Maybe<bool> maybe = HasOwnElement(object, index);
- // Workaround for a GCC 4.4.3 bug which leads to "‘preexists’ may be used
- // uninitialized in this function".
- if (!maybe.has_value) {
- DCHECK(false);
- return isolate->factory()->undefined_value();
- }
- preexists = maybe.value;
- if (preexists && GetOwnElementAccessorPair(object, index).is_null()) {
- old_value =
- Object::GetElement(isolate, object, index).ToHandleChecked();
- }
- } else {
- LookupIterator it(object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
- CHECK(GetPropertyAttributes(&it).has_value);
- preexists = it.IsFound();
- if (preexists && (it.state() == LookupIterator::DATA ||
- it.GetAccessors()->IsAccessorInfo())) {
- old_value = GetProperty(&it).ToHandleChecked();
- }
+ CHECK(GetPropertyAttributes(it).IsJust());
+ preexists = it->IsFound();
+ if (preexists && (it->state() == LookupIterator::DATA ||
+ it->GetAccessors()->IsAccessorInfo())) {
+ old_value = GetProperty(it).ToHandleChecked();
}
}
- if (is_element) {
- DefineElementAccessor(object, index, getter, setter, attributes);
- } else {
- DCHECK(getter->IsSpecFunction() || getter->IsUndefined() ||
- getter->IsNull());
- DCHECK(setter->IsSpecFunction() || setter->IsUndefined() ||
- setter->IsNull());
- // At least one of the accessors needs to be a new value.
- DCHECK(!getter->IsNull() || !setter->IsNull());
- LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
- if (it.state() == LookupIterator::ACCESS_CHECK) {
- // We already did an access check before. We do have access.
- it.Next();
- }
- if (!getter->IsNull()) {
- it.TransitionToAccessorProperty(ACCESSOR_GETTER, getter, attributes);
- }
- if (!setter->IsNull()) {
- it.TransitionToAccessorProperty(ACCESSOR_SETTER, setter, attributes);
- }
+ 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()) {
+ it->TransitionToAccessorProperty(ACCESSOR_GETTER, getter, attributes);
+ }
+ if (!setter->IsNull()) {
+ it->TransitionToAccessorProperty(ACCESSOR_SETTER, setter, attributes);
}
if (is_observed) {
+ // Make sure the top context isn't changed.
+ AssertNoContextChange ncc(isolate);
const char* type = preexists ? "reconfigure" : "add";
RETURN_ON_EXCEPTION(
- isolate, EnqueueChangeRecord(object, type, name, old_value), Object);
+ isolate, EnqueueChangeRecord(object, type, it->GetName(), old_value),
+ Object);
}
return isolate->factory()->undefined_value();
@@ -6523,79 +9038,40 @@
MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object,
Handle<AccessorInfo> info) {
Isolate* isolate = object->GetIsolate();
- Factory* factory = isolate->factory();
- Handle<Name> name(Name::cast(info->name()));
+ Handle<Name> name(Name::cast(info->name()), isolate);
- // Check access rights if needed.
- if (object->IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) {
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return factory->undefined_value();
- }
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ isolate, object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
- if (object->IsJSGlobalProxy()) {
- PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return object;
- DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
- return SetAccessor(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), info);
- }
-
- // Make sure that the top context does not change when doing callbacks or
- // interceptor calls.
- AssertNoContextChange ncc(isolate);
-
- // Try to flatten before operating on the string.
- if (name->IsString()) name = String::Flatten(Handle<String>::cast(name));
-
- uint32_t index = 0;
- bool is_element = name->AsArrayIndex(&index);
-
- if (is_element) {
- if (object->IsJSArray()) return factory->undefined_value();
-
- // Accessors overwrite previous callbacks (cf. with getters/setters).
- switch (object->GetElementsKind()) {
- case FAST_SMI_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS:
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_HOLEY_ELEMENTS:
- case FAST_HOLEY_DOUBLE_ELEMENTS:
- break;
-
-#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ELEMENTS: \
- case TYPE##_ELEMENTS: \
-
- TYPED_ARRAYS(TYPED_ARRAY_CASE)
-#undef TYPED_ARRAY_CASE
- // Ignore getters and setters on pixel and external array
- // elements.
- return factory->undefined_value();
-
- case DICTIONARY_ELEMENTS:
- break;
- case SLOPPY_ARGUMENTS_ELEMENTS:
- UNIMPLEMENTED();
- break;
+ // Duplicate ACCESS_CHECK outside of GetPropertyAttributes for the case that
+ // the FailedAccessCheckCallbackFunction doesn't throw an exception.
+ //
+ // TODO(verwaest): Force throw an exception if the callback doesn't, so we can
+ // remove reliance on default return values.
+ if (it.state() == LookupIterator::ACCESS_CHECK) {
+ if (!it.HasAccess()) {
+ isolate->ReportFailedAccessCheck(object);
+ RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
+ return it.factory()->undefined_value();
}
-
- SetElementCallback(object, index, info, info->property_attributes());
- } else {
- // Lookup the name.
- LookupIterator it(object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
- CHECK(GetPropertyAttributes(&it).has_value);
- // ES5 forbids turning a property into an accessor if it's not
- // configurable. See 8.6.1 (Table 5).
- if (it.IsFound() && (it.IsReadOnly() || !it.IsConfigurable())) {
- return factory->undefined_value();
- }
-
- SetPropertyCallback(object, name, info, info->property_attributes());
+ it.Next();
}
+ // Ignore accessors on typed arrays.
+ if (it.IsElement() && object->HasFixedTypedArrayElements()) {
+ return it.factory()->undefined_value();
+ }
+
+ CHECK(GetPropertyAttributes(&it).IsJust());
+
+ // ES5 forbids turning a property into an accessor if it's not
+ // configurable. See 8.6.1 (Table 5).
+ if (it.IsFound() && !it.IsConfigurable()) {
+ return it.factory()->undefined_value();
+ }
+
+ it.TransitionToAccessorPair(info, info->property_attributes());
+
return object;
}
@@ -6609,71 +9085,40 @@
// interceptor calls.
AssertNoContextChange ncc(isolate);
- // Make the lookup and include prototypes.
- uint32_t index = 0;
- if (name->AsArrayIndex(&index)) {
- for (PrototypeIterator iter(isolate, object,
- PrototypeIterator::START_AT_RECEIVER);
- !iter.IsAtEnd(); iter.Advance()) {
- Handle<Object> current = PrototypeIterator::GetCurrent(iter);
- // Check access rights if needed.
- if (current->IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(Handle<JSObject>::cast(current), name,
- v8::ACCESS_HAS)) {
- isolate->ReportFailedAccessCheck(Handle<JSObject>::cast(current),
- v8::ACCESS_HAS);
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ isolate, object, name, LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
+
+ for (; it.IsFound(); it.Next()) {
+ switch (it.state()) {
+ case LookupIterator::INTERCEPTOR:
+ case LookupIterator::NOT_FOUND:
+ case LookupIterator::TRANSITION:
+ UNREACHABLE();
+
+ case LookupIterator::ACCESS_CHECK:
+ if (it.HasAccess()) continue;
+ isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>());
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
return isolate->factory()->undefined_value();
- }
- if (current->IsJSObject() &&
- Handle<JSObject>::cast(current)->HasDictionaryElements()) {
- JSObject* js_object = JSObject::cast(*current);
- SeededNumberDictionary* dictionary = js_object->element_dictionary();
- int entry = dictionary->FindEntry(index);
- if (entry != SeededNumberDictionary::kNotFound) {
- Object* element = dictionary->ValueAt(entry);
- if (dictionary->DetailsAt(entry).type() == CALLBACKS &&
- element->IsAccessorPair()) {
- return handle(AccessorPair::cast(element)->GetComponent(component),
- isolate);
- }
- }
- }
- }
- } else {
- LookupIterator it(object, name,
- LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
- for (; it.IsFound(); it.Next()) {
- switch (it.state()) {
- case LookupIterator::INTERCEPTOR:
- case LookupIterator::NOT_FOUND:
- case LookupIterator::TRANSITION:
- UNREACHABLE();
+ case LookupIterator::JSPROXY:
+ return isolate->factory()->undefined_value();
- case LookupIterator::ACCESS_CHECK:
- if (it.HasAccess(v8::ACCESS_HAS)) continue;
- isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>(),
- v8::ACCESS_HAS);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return isolate->factory()->undefined_value();
-
- case LookupIterator::JSPROXY:
- return isolate->factory()->undefined_value();
-
- case LookupIterator::DATA:
- continue;
- case LookupIterator::ACCESSOR: {
- Handle<Object> maybe_pair = it.GetAccessors();
- if (maybe_pair->IsAccessorPair()) {
- return handle(
- AccessorPair::cast(*maybe_pair)->GetComponent(component),
- isolate);
- }
+ case LookupIterator::INTEGER_INDEXED_EXOTIC:
+ return isolate->factory()->undefined_value();
+ case LookupIterator::DATA:
+ continue;
+ case LookupIterator::ACCESSOR: {
+ Handle<Object> maybe_pair = it.GetAccessors();
+ if (maybe_pair->IsAccessorPair()) {
+ return handle(
+ AccessorPair::cast(*maybe_pair)->GetComponent(component),
+ isolate);
}
}
}
}
+
return isolate->factory()->undefined_value();
}
@@ -6684,7 +9129,7 @@
DescriptorArray* descs = map()->instance_descriptors();
bool value_is_number = value->IsNumber();
for (int i = 0; i < number_of_own_descriptors; i++) {
- if (descs->GetType(i) == FIELD) {
+ if (descs->GetType(i) == DATA) {
FieldIndex field_index = FieldIndex::ForDescriptor(map(), i);
if (IsUnboxedDoubleField(field_index)) {
if (value_is_number) {
@@ -6704,13 +9149,15 @@
return descs->GetKey(i);
}
}
- } else if (descs->GetType(i) == CONSTANT) {
+ } else if (descs->GetType(i) == DATA_CONSTANT) {
if (descs->GetConstant(i) == value) {
return descs->GetKey(i);
}
}
}
return GetHeap()->undefined_value();
+ } else if (IsJSGlobalObject()) {
+ return global_dictionary()->SlowReverseLookup(value);
} else {
return property_dictionary()->SlowReverseLookup(value);
}
@@ -6718,10 +9165,12 @@
Handle<Map> Map::RawCopy(Handle<Map> map, int instance_size) {
- Handle<Map> result = map->GetIsolate()->factory()->NewMap(
- map->instance_type(), instance_size);
- result->SetPrototype(handle(map->prototype(), map->GetIsolate()));
- result->set_constructor(map->constructor());
+ Isolate* isolate = map->GetIsolate();
+ Handle<Map> result =
+ isolate->factory()->NewMap(map->instance_type(), instance_size);
+ Handle<Object> prototype(map->prototype(), isolate);
+ Map::SetPrototype(result, prototype);
+ result->set_constructor_or_backpointer(map->GetConstructor());
result->set_bit_field(map->bit_field());
result->set_bit_field2(map->bit_field2());
int new_bit_field3 = map->bit_field3();
@@ -6733,7 +9182,8 @@
if (!map->is_dictionary_map()) {
new_bit_field3 = IsUnstable::update(new_bit_field3, false);
}
- new_bit_field3 = Counter::update(new_bit_field3, kRetainingCounterStart);
+ new_bit_field3 =
+ ConstructionCounter::update(new_bit_field3, kNoSlackTracking);
result->set_bit_field3(new_bit_field3);
return result;
}
@@ -6746,7 +9196,7 @@
Isolate* isolate = fast_map->GetIsolate();
Handle<Object> maybe_cache(isolate->native_context()->normalized_map_cache(),
isolate);
- bool use_cache = !maybe_cache->IsUndefined();
+ bool use_cache = !fast_map->is_prototype_map() && !maybe_cache->IsUndefined();
Handle<NormalizedMapCache> cache;
if (use_cache) cache = Handle<NormalizedMapCache>::cast(maybe_cache);
@@ -6759,15 +9209,28 @@
if (FLAG_enable_slow_asserts) {
// The cached map should match newly created normalized map bit-by-bit,
// except for the code cache, which can contain some ics which can be
- // applied to the shared map.
+ // applied to the shared map, dependent code and weak cell cache.
Handle<Map> fresh = Map::CopyNormalized(fast_map, mode);
- DCHECK(memcmp(fresh->address(),
- new_map->address(),
- Map::kCodeCacheOffset) == 0);
+ if (new_map->is_prototype_map()) {
+ // For prototype maps, the PrototypeInfo is not copied.
+ DCHECK(memcmp(fresh->address(), new_map->address(),
+ kTransitionsOrPrototypeInfoOffset) == 0);
+ DCHECK(fresh->raw_transitions() == Smi::FromInt(0));
+ STATIC_ASSERT(kDescriptorsOffset ==
+ kTransitionsOrPrototypeInfoOffset + kPointerSize);
+ DCHECK(memcmp(HeapObject::RawField(*fresh, kDescriptorsOffset),
+ HeapObject::RawField(*new_map, kDescriptorsOffset),
+ kCodeCacheOffset - kDescriptorsOffset) == 0);
+ } else {
+ DCHECK(memcmp(fresh->address(), new_map->address(),
+ Map::kCodeCacheOffset) == 0);
+ }
STATIC_ASSERT(Map::kDependentCodeOffset ==
Map::kCodeCacheOffset + kPointerSize);
- int offset = Map::kDependentCodeOffset + kPointerSize;
+ STATIC_ASSERT(Map::kWeakCellCacheOffset ==
+ Map::kDependentCodeOffset + kPointerSize);
+ int offset = Map::kWeakCellCacheOffset + kPointerSize;
DCHECK(memcmp(fresh->address() + offset,
new_map->address() + offset,
Map::kSize - offset) == 0);
@@ -6796,13 +9259,13 @@
PropertyNormalizationMode mode) {
int new_instance_size = map->instance_size();
if (mode == CLEAR_INOBJECT_PROPERTIES) {
- new_instance_size -= map->inobject_properties() * kPointerSize;
+ new_instance_size -= map->GetInObjectProperties() * kPointerSize;
}
Handle<Map> result = RawCopy(map, new_instance_size);
if (mode != CLEAR_INOBJECT_PROPERTIES) {
- result->set_inobject_properties(map->inobject_properties());
+ result->SetInObjectProperties(map->GetInObjectProperties());
}
result->set_dictionary_map(true);
@@ -6816,15 +9279,57 @@
}
+Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size,
+ int in_object_properties,
+ int unused_property_fields) {
+#ifdef DEBUG
+ Isolate* isolate = map->GetIsolate();
+ // Strict and strong function maps have Function as a constructor but the
+ // Function's initial map is a sloppy function map. Same holds for
+ // GeneratorFunction and its initial map.
+ Object* constructor = map->GetConstructor();
+ DCHECK(constructor->IsJSFunction());
+ DCHECK(*map == JSFunction::cast(constructor)->initial_map() ||
+ *map == *isolate->strict_function_map() ||
+ *map == *isolate->strong_function_map() ||
+ *map == *isolate->strict_generator_function_map() ||
+ *map == *isolate->strong_generator_function_map());
+#endif
+ // Initial maps must always own their descriptors and it's descriptor array
+ // does not contain descriptors that do not belong to the map.
+ DCHECK(map->owns_descriptors());
+ DCHECK_EQ(map->NumberOfOwnDescriptors(),
+ map->instance_descriptors()->number_of_descriptors());
+
+ Handle<Map> result = RawCopy(map, instance_size);
+
+ // Please note instance_type and instance_size are set when allocated.
+ result->SetInObjectProperties(in_object_properties);
+ result->set_unused_property_fields(unused_property_fields);
+
+ int number_of_own_descriptors = map->NumberOfOwnDescriptors();
+ if (number_of_own_descriptors > 0) {
+ // The copy will use the same descriptors array.
+ result->UpdateDescriptors(map->instance_descriptors(),
+ map->GetLayoutDescriptor());
+ result->SetNumberOfOwnDescriptors(number_of_own_descriptors);
+
+ DCHECK_EQ(result->NumberOfFields(),
+ in_object_properties - unused_property_fields);
+ }
+
+ return result;
+}
+
+
Handle<Map> Map::CopyDropDescriptors(Handle<Map> map) {
Handle<Map> result = RawCopy(map, map->instance_size());
// Please note instance_type and instance_size are set when allocated.
- result->set_inobject_properties(map->inobject_properties());
- result->set_unused_property_fields(map->unused_property_fields());
-
- result->set_pre_allocated_property_fields(
- map->pre_allocated_property_fields());
+ if (map->IsJSObjectMap()) {
+ result->SetInObjectProperties(map->GetInObjectProperties());
+ result->set_unused_property_fields(map->unused_property_fields());
+ }
result->ClearCodeCache(map->GetHeap());
map->NotifyLeafMapLayoutChange();
return result;
@@ -6837,8 +9342,8 @@
// Sanity check. This path is only to be taken if the map owns its descriptor
// array, implying that its NumberOfOwnDescriptors equals the number of
// descriptors in the descriptor array.
- DCHECK(map->NumberOfOwnDescriptors() ==
- map->instance_descriptors()->number_of_descriptors());
+ DCHECK_EQ(map->NumberOfOwnDescriptors(),
+ map->instance_descriptors()->number_of_descriptors());
Handle<Map> result = CopyDropDescriptors(map);
Handle<Name> name = descriptor->GetKey();
@@ -6849,15 +9354,15 @@
if (old_size == 0) {
descriptors = DescriptorArray::Allocate(map->GetIsolate(), 0, 1);
} else {
- EnsureDescriptorSlack(
- map, SlackForArraySize(old_size, kMaxNumberOfDescriptors));
+ int slack = SlackForArraySize(old_size, kMaxNumberOfDescriptors);
+ EnsureDescriptorSlack(map, slack);
descriptors = handle(map->instance_descriptors());
}
}
Handle<LayoutDescriptor> layout_descriptor =
FLAG_unbox_double_fields
- ? LayoutDescriptor::Append(map, descriptor->GetDetails())
+ ? LayoutDescriptor::ShareAppend(map, descriptor->GetDetails())
: handle(LayoutDescriptor::FastPointerLayout(), map->GetIsolate());
{
@@ -6888,11 +9393,12 @@
// static
void Map::TraceAllTransitions(Map* map) {
- if (!map->HasTransitionArray()) return;
- TransitionArray* transitions = map->transitions();
- for (int i = 0; i < transitions->number_of_transitions(); ++i) {
- Map* target = transitions->GetTarget(i);
- Map::TraceTransition("Transition", map, target, transitions->GetKey(i));
+ Object* transitions = map->raw_transitions();
+ int num_transitions = TransitionArray::NumberOfTransitions(transitions);
+ for (int i = -0; i < num_transitions; ++i) {
+ Map* target = TransitionArray::GetTarget(transitions, i);
+ Name* key = TransitionArray::GetKey(transitions, i);
+ Map::TraceTransition("Transition", map, target, key);
Map::TraceAllTransitions(target);
}
}
@@ -6902,26 +9408,22 @@
void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child,
Handle<Name> name, SimpleTransitionFlag flag) {
- parent->set_owns_descriptors(false);
+ if (!parent->GetBackPointer()->IsUndefined()) {
+ parent->set_owns_descriptors(false);
+ } else {
+ // |parent| is initial map and it must keep the ownership, there must be no
+ // descriptors in the descriptors array that do not belong to the map.
+ DCHECK(parent->owns_descriptors());
+ DCHECK_EQ(parent->NumberOfOwnDescriptors(),
+ parent->instance_descriptors()->number_of_descriptors());
+ }
if (parent->is_prototype_map()) {
DCHECK(child->is_prototype_map());
#if TRACE_MAPS
Map::TraceTransition("NoTransition", *parent, *child, *name);
#endif
} else {
- Handle<TransitionArray> transitions =
- TransitionArray::Insert(parent, name, child, flag);
- if (!parent->HasTransitionArray() ||
- *transitions != parent->transitions()) {
- parent->set_transitions(*transitions);
- }
- child->SetBackPointer(*parent);
- if (child->prototype()->IsJSObject()) {
- Handle<JSObject> proto(JSObject::cast(child->prototype()));
- if (!child->ShouldRegisterAsPrototypeUser(proto)) {
- JSObject::UnregisterPrototypeUser(proto, child);
- }
- }
+ TransitionArray::Insert(parent, name, child, flag);
#if TRACE_MAPS
Map::TraceTransition("Transition", *parent, *child, *name);
#endif
@@ -6939,7 +9441,8 @@
Handle<Map> result = CopyDropDescriptors(map);
if (!map->is_prototype_map()) {
- if (flag == INSERT_TRANSITION && map->CanHaveMoreTransitions()) {
+ if (flag == INSERT_TRANSITION &&
+ TransitionArray::CanHaveMoreTransitions(map)) {
result->InitializeDescriptors(*descriptors, *layout_descriptor);
Handle<Name> name;
@@ -6949,7 +9452,7 @@
int length = descriptors->number_of_descriptors();
for (int i = 0; i < length; i++) {
descriptors->SetRepresentation(i, Representation::Tagged());
- if (descriptors->GetDetails(i).type() == FIELD) {
+ if (descriptors->GetDetails(i).type() == DATA) {
descriptors->SetValue(i, HeapType::Any());
}
}
@@ -6963,7 +9466,8 @@
if (FLAG_trace_maps &&
// Mirror conditions above that did not call ConnectTransition().
(map->is_prototype_map() ||
- !(flag == INSERT_TRANSITION && map->CanHaveMoreTransitions()))) {
+ !(flag == INSERT_TRANSITION &&
+ TransitionArray::CanHaveMoreTransitions(map)))) {
PrintF("[TraceMaps: ReplaceDescriptors from= %p to= %p reason= %s ]\n",
reinterpret_cast<void*>(*map), reinterpret_cast<void*>(*result),
reason);
@@ -6974,118 +9478,229 @@
}
-// Since this method is used to rewrite an existing transition tree, it can
-// always insert transitions without checking.
-Handle<Map> Map::CopyInstallDescriptors(
- Handle<Map> map, int new_descriptor, Handle<DescriptorArray> descriptors,
+// Creates transition tree starting from |split_map| and adding all descriptors
+// starting from descriptor with index |split_map|.NumberOfOwnDescriptors().
+// The way how it is done is tricky because of GC and special descriptors
+// marking logic.
+Handle<Map> Map::AddMissingTransitions(
+ Handle<Map> split_map, Handle<DescriptorArray> descriptors,
Handle<LayoutDescriptor> full_layout_descriptor) {
DCHECK(descriptors->IsSortedNoDuplicates());
+ int split_nof = split_map->NumberOfOwnDescriptors();
+ int nof_descriptors = descriptors->number_of_descriptors();
+ DCHECK_LT(split_nof, nof_descriptors);
- Handle<Map> result = CopyDropDescriptors(map);
+ // Start with creating last map which will own full descriptors array.
+ // This is necessary to guarantee that GC will mark the whole descriptor
+ // array if any of the allocations happening below fail.
+ // Number of unused properties is temporarily incorrect and the layout
+ // descriptor could unnecessarily be in slow mode but we will fix after
+ // all the other intermediate maps are created.
+ Handle<Map> last_map = CopyDropDescriptors(split_map);
+ last_map->InitializeDescriptors(*descriptors, *full_layout_descriptor);
+ last_map->set_unused_property_fields(0);
- result->set_instance_descriptors(*descriptors);
- result->SetNumberOfOwnDescriptors(new_descriptor + 1);
+ // During creation of intermediate maps we violate descriptors sharing
+ // invariant since the last map is not yet connected to the transition tree
+ // we create here. But it is safe because GC never trims map's descriptors
+ // if there are no dead transitions from that map and this is exactly the
+ // case for all the intermediate maps we create here.
+ Handle<Map> map = split_map;
+ for (int i = split_nof; i < nof_descriptors - 1; ++i) {
+ Handle<Map> new_map = CopyDropDescriptors(map);
+ InstallDescriptors(map, new_map, i, descriptors, full_layout_descriptor);
+ map = new_map;
+ }
+ map->NotifyLeafMapLayoutChange();
+ InstallDescriptors(map, last_map, nof_descriptors - 1, descriptors,
+ full_layout_descriptor);
+ return last_map;
+}
- int unused_property_fields = map->unused_property_fields();
+
+// Since this method is used to rewrite an existing transition tree, it can
+// always insert transitions without checking.
+void Map::InstallDescriptors(Handle<Map> parent, Handle<Map> child,
+ int new_descriptor,
+ Handle<DescriptorArray> descriptors,
+ Handle<LayoutDescriptor> full_layout_descriptor) {
+ DCHECK(descriptors->IsSortedNoDuplicates());
+
+ child->set_instance_descriptors(*descriptors);
+ child->SetNumberOfOwnDescriptors(new_descriptor + 1);
+
+ int unused_property_fields = parent->unused_property_fields();
PropertyDetails details = descriptors->GetDetails(new_descriptor);
- if (details.type() == FIELD) {
- unused_property_fields = map->unused_property_fields() - 1;
+ if (details.location() == kField) {
+ unused_property_fields = parent->unused_property_fields() - 1;
if (unused_property_fields < 0) {
unused_property_fields += JSObject::kFieldsAdded;
}
}
- result->set_unused_property_fields(unused_property_fields);
+ child->set_unused_property_fields(unused_property_fields);
if (FLAG_unbox_double_fields) {
Handle<LayoutDescriptor> layout_descriptor =
- LayoutDescriptor::AppendIfFastOrUseFull(map, details,
+ LayoutDescriptor::AppendIfFastOrUseFull(parent, details,
full_layout_descriptor);
- result->set_layout_descriptor(*layout_descriptor);
- SLOW_DCHECK(result->layout_descriptor()->IsConsistentWithMap(*result));
- result->set_visitor_id(StaticVisitorBase::GetVisitorId(*result));
+ child->set_layout_descriptor(*layout_descriptor);
+#ifdef VERIFY_HEAP
+ // TODO(ishell): remove these checks from VERIFY_HEAP mode.
+ if (FLAG_verify_heap) {
+ CHECK(child->layout_descriptor()->IsConsistentWithMap(*child));
+ }
+#else
+ SLOW_DCHECK(child->layout_descriptor()->IsConsistentWithMap(*child));
+#endif
+ child->set_visitor_id(Heap::GetStaticVisitorIdForMap(*child));
}
Handle<Name> name = handle(descriptors->GetKey(new_descriptor));
- ConnectTransition(map, result, name, SIMPLE_PROPERTY_TRANSITION);
-
- return result;
+ ConnectTransition(parent, child, name, SIMPLE_PROPERTY_TRANSITION);
}
Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
TransitionFlag flag) {
+ Map* maybe_elements_transition_map = NULL;
if (flag == INSERT_TRANSITION) {
- DCHECK(!map->HasElementsTransition() ||
- ((map->elements_transition_map()->elements_kind() ==
- DICTIONARY_ELEMENTS ||
- IsExternalArrayElementsKind(
- map->elements_transition_map()->elements_kind())) &&
- (kind == DICTIONARY_ELEMENTS ||
- IsExternalArrayElementsKind(kind))));
+ maybe_elements_transition_map = map->ElementsTransitionMap();
+ DCHECK(maybe_elements_transition_map == NULL ||
+ (maybe_elements_transition_map->elements_kind() ==
+ DICTIONARY_ELEMENTS &&
+ kind == DICTIONARY_ELEMENTS));
DCHECK(!IsFastElementsKind(kind) ||
IsMoreGeneralElementsKindTransition(map->elements_kind(), kind));
DCHECK(kind != map->elements_kind());
}
bool insert_transition = flag == INSERT_TRANSITION &&
- map->CanHaveMoreTransitions() &&
- !map->HasElementsTransition();
+ TransitionArray::CanHaveMoreTransitions(map) &&
+ maybe_elements_transition_map == NULL;
- if (insert_transition && map->owns_descriptors()) {
- // In case the map owned its own descriptors, share the descriptors and
- // transfer ownership to the new map.
- Handle<Map> new_map = CopyDropDescriptors(map);
-
- ConnectElementsTransition(map, new_map);
-
+ if (insert_transition) {
+ Handle<Map> new_map = CopyForTransition(map, "CopyAsElementsKind");
new_map->set_elements_kind(kind);
- // The properties did not change, so reuse descriptors.
- new_map->InitializeDescriptors(map->instance_descriptors(),
- map->GetLayoutDescriptor());
+
+ Isolate* isolate = map->GetIsolate();
+ Handle<Name> name = isolate->factory()->elements_transition_symbol();
+ ConnectTransition(map, new_map, name, SPECIAL_TRANSITION);
return new_map;
}
- // In case the map did not own its own descriptors, a split is forced by
- // copying the map; creating a new descriptor array cell.
// Create a new free-floating map only if we are not allowed to store it.
Handle<Map> new_map = Copy(map, "CopyAsElementsKind");
-
new_map->set_elements_kind(kind);
-
- if (insert_transition) {
- ConnectElementsTransition(map, new_map);
- }
-
return new_map;
}
+Handle<Map> Map::AsLanguageMode(Handle<Map> initial_map,
+ LanguageMode language_mode, FunctionKind kind) {
+ DCHECK_EQ(JS_FUNCTION_TYPE, initial_map->instance_type());
+ // Initial map for sloppy mode function is stored in the function
+ // constructor. Initial maps for strict and strong modes are cached as
+ // special transitions using |strict_function_transition_symbol| and
+ // |strong_function_transition_symbol| respectively as a key.
+ if (language_mode == SLOPPY) return initial_map;
+ Isolate* isolate = initial_map->GetIsolate();
+ Factory* factory = isolate->factory();
+ Handle<Symbol> transition_symbol;
+
+ int map_index = Context::FunctionMapIndex(language_mode, kind);
+ Handle<Map> function_map(
+ Map::cast(isolate->native_context()->get(map_index)));
+
+ STATIC_ASSERT(LANGUAGE_END == 3);
+ switch (language_mode) {
+ case STRICT:
+ transition_symbol = factory->strict_function_transition_symbol();
+ break;
+ case STRONG:
+ transition_symbol = factory->strong_function_transition_symbol();
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ Map* maybe_transition =
+ TransitionArray::SearchSpecial(*initial_map, *transition_symbol);
+ if (maybe_transition != NULL) {
+ return handle(maybe_transition, isolate);
+ }
+ initial_map->NotifyLeafMapLayoutChange();
+
+ // Create new map taking descriptors from the |function_map| and all
+ // the other details from the |initial_map|.
+ Handle<Map> map =
+ Map::CopyInitialMap(function_map, initial_map->instance_size(),
+ initial_map->GetInObjectProperties(),
+ initial_map->unused_property_fields());
+ map->SetConstructor(initial_map->GetConstructor());
+ map->set_prototype(initial_map->prototype());
+
+ if (TransitionArray::CanHaveMoreTransitions(initial_map)) {
+ Map::ConnectTransition(initial_map, map, transition_symbol,
+ SPECIAL_TRANSITION);
+ }
+ return map;
+}
+
+
Handle<Map> Map::CopyForObserved(Handle<Map> map) {
DCHECK(!map->is_observed());
Isolate* isolate = map->GetIsolate();
- // In case the map owned its own descriptors, share the descriptors and
- // transfer ownership to the new map.
- Handle<Map> new_map;
- if (map->owns_descriptors()) {
- new_map = CopyDropDescriptors(map);
- } else {
- DCHECK(!map->is_prototype_map());
- new_map = Copy(map, "CopyForObserved");
+ bool insert_transition =
+ TransitionArray::CanHaveMoreTransitions(map) && !map->is_prototype_map();
+
+ if (insert_transition) {
+ Handle<Map> new_map = CopyForTransition(map, "CopyForObserved");
+ new_map->set_is_observed();
+
+ Handle<Name> name = isolate->factory()->observed_symbol();
+ ConnectTransition(map, new_map, name, SPECIAL_TRANSITION);
+ return new_map;
}
+ // Create a new free-floating map only if we are not allowed to store it.
+ Handle<Map> new_map = Map::Copy(map, "CopyForObserved");
new_map->set_is_observed();
+ return new_map;
+}
+
+
+Handle<Map> Map::CopyForTransition(Handle<Map> map, const char* reason) {
+ DCHECK(!map->is_prototype_map());
+ Handle<Map> new_map = CopyDropDescriptors(map);
+
if (map->owns_descriptors()) {
+ // In case the map owned its own descriptors, share the descriptors and
+ // transfer ownership to the new map.
// The properties did not change, so reuse descriptors.
new_map->InitializeDescriptors(map->instance_descriptors(),
map->GetLayoutDescriptor());
+ } else {
+ // In case the map did not own its own descriptors, a split is forced by
+ // copying the map; creating a new descriptor array cell.
+ Handle<DescriptorArray> descriptors(map->instance_descriptors());
+ int number_of_own_descriptors = map->NumberOfOwnDescriptors();
+ Handle<DescriptorArray> new_descriptors =
+ DescriptorArray::CopyUpTo(descriptors, number_of_own_descriptors);
+ Handle<LayoutDescriptor> new_layout_descriptor(map->GetLayoutDescriptor(),
+ map->GetIsolate());
+ new_map->InitializeDescriptors(*new_descriptors, *new_layout_descriptor);
}
- if (map->CanHaveMoreTransitions()) {
- Handle<Name> name = isolate->factory()->observed_symbol();
- ConnectTransition(map, new_map, name, SPECIAL_TRANSITION);
+#if TRACE_MAPS
+ if (FLAG_trace_maps) {
+ PrintF("[TraceMaps: CopyForTransition from= %p to= %p reason= %s ]\n",
+ reinterpret_cast<void*>(*map), reinterpret_cast<void*>(*new_map),
+ reason);
}
+#endif
+
return new_map;
}
@@ -7121,10 +9736,10 @@
JSObject::kHeaderSize + kPointerSize * inobject_properties;
// Adjust the map with the extra inobject properties.
- copy->set_inobject_properties(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;
}
@@ -7144,7 +9759,9 @@
map, new_desc, new_layout_descriptor, INSERT_TRANSITION,
transition_marker, reason, SPECIAL_TRANSITION);
new_map->set_is_extensible(false);
- new_map->set_elements_kind(DICTIONARY_ELEMENTS);
+ if (!IsFixedTypedArrayElementsKind(map->elements_kind())) {
+ new_map->set_elements_kind(DICTIONARY_ELEMENTS);
+ }
return new_map;
}
@@ -7152,17 +9769,17 @@
bool DescriptorArray::CanHoldValue(int descriptor, Object* value) {
PropertyDetails details = GetDetails(descriptor);
switch (details.type()) {
- case FIELD:
+ case DATA:
return value->FitsRepresentation(details.representation()) &&
GetFieldType(descriptor)->NowContains(value);
- case CONSTANT:
+ case DATA_CONSTANT:
DCHECK(GetConstant(descriptor) != value ||
value->FitsRepresentation(details.representation()));
return GetConstant(descriptor) == value;
- case ACCESSOR_FIELD:
- case CALLBACKS:
+ case ACCESSOR:
+ case ACCESSOR_CONSTANT:
return false;
}
@@ -7171,6 +9788,7 @@
}
+// static
Handle<Map> Map::PrepareForDataProperty(Handle<Map> map, int descriptor,
Handle<Object> value) {
// Dictionaries can store any property value.
@@ -7184,11 +9802,13 @@
if (descriptors->CanHoldValue(descriptor, *value)) return map;
Isolate* isolate = map->GetIsolate();
+ PropertyAttributes attributes =
+ descriptors->GetDetails(descriptor).attributes();
Representation representation = value->OptimalRepresentation();
Handle<HeapType> type = value->OptimalType(isolate, representation);
- return GeneralizeRepresentation(map, descriptor, representation, type,
- FORCE_IN_OBJECT);
+ return ReconfigureProperty(map, descriptor, kData, attributes, representation,
+ type, FORCE_FIELD);
}
@@ -7202,9 +9822,10 @@
// Migrate to the newest map before storing the property.
map = Update(map);
- int index = map->SearchTransition(DATA, *name, attributes);
- if (index != TransitionArray::kNotFound) {
- Handle<Map> transition(map->GetTransition(index));
+ Map* maybe_transition =
+ TransitionArray::SearchTransition(*map, kData, *name, attributes);
+ if (maybe_transition != NULL) {
+ Handle<Map> transition(maybe_transition);
int descriptor = transition->LastAdded();
DCHECK_EQ(attributes, transition->instance_descriptors()
@@ -7245,16 +9866,29 @@
}
-Handle<Map> Map::ReconfigureDataProperty(Handle<Map> map, int descriptor,
- PropertyAttributes attributes) {
+Handle<Map> Map::ReconfigureExistingProperty(Handle<Map> map, int descriptor,
+ PropertyKind kind,
+ PropertyAttributes attributes) {
// Dictionaries have to be reconfigured in-place.
DCHECK(!map->is_dictionary_map());
- // For now, give up on transitioning and just create a unique map.
- // TODO(verwaest/ishell): Cache transitions with different attributes.
- return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_IN_OBJECT,
- attributes,
- "GenAll_AttributesMismatch");
+ if (!map->GetBackPointer()->IsMap()) {
+ // There is no benefit from reconstructing transition tree for maps without
+ // back pointers.
+ return CopyGeneralizeAllRepresentations(
+ map, descriptor, FORCE_FIELD, kind, attributes,
+ "GenAll_AttributesMismatchProtoMap");
+ }
+
+ if (FLAG_trace_generalization) {
+ map->PrintReconfiguration(stdout, descriptor, kind, attributes);
+ }
+
+ Isolate* isolate = map->GetIsolate();
+ Handle<Map> new_map = ReconfigureProperty(
+ map, descriptor, kind, attributes, Representation::None(),
+ HeapType::None(isolate), FORCE_FIELD);
+ return new_map;
}
@@ -7266,12 +9900,7 @@
Isolate* isolate = name->GetIsolate();
// Dictionary maps can always have additional data properties.
- if (map->is_dictionary_map()) {
- // For global objects, property cells are inlined. We need to change the
- // map.
- if (map->IsGlobalObjectMap()) return Copy(map, "GlobalAccessor");
- return map;
- }
+ if (map->is_dictionary_map()) return map;
// Migrate to the newest map before transitioning to the new property.
map = Update(map);
@@ -7280,14 +9909,15 @@
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
- int index = map->SearchTransition(ACCESSOR, *name, attributes);
- if (index != TransitionArray::kNotFound) {
- Handle<Map> transition(map->GetTransition(index));
+ Map* maybe_transition =
+ TransitionArray::SearchTransition(*map, kAccessor, *name, attributes);
+ if (maybe_transition != NULL) {
+ Handle<Map> transition(maybe_transition, isolate);
DescriptorArray* descriptors = transition->instance_descriptors();
int descriptor = transition->LastAdded();
DCHECK(descriptors->GetKey(descriptor)->Equals(*name));
- DCHECK_EQ(ACCESSOR, descriptors->GetDetails(descriptor).kind());
+ DCHECK_EQ(kAccessor, descriptors->GetDetails(descriptor).kind());
DCHECK_EQ(attributes, descriptors->GetDetails(descriptor).attributes());
Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate);
@@ -7311,7 +9941,7 @@
return Map::Normalize(map, mode, "AccessorsOverwritingNonLast");
}
PropertyDetails old_details = old_descriptors->GetDetails(descriptor);
- if (old_details.type() != CALLBACKS) {
+ if (old_details.type() != ACCESSOR_CONSTANT) {
return Map::Normalize(map, mode, "AccessorsOverwritingNonAccessors");
}
@@ -7341,7 +9971,7 @@
pair->set(component, *accessor);
TransitionFlag flag = INSERT_TRANSITION;
- CallbacksDescriptor new_desc(name, pair, attributes);
+ AccessorConstantDescriptor new_desc(name, pair, attributes);
return Map::CopyInsertDescriptor(map, &new_desc, flag);
}
@@ -7354,19 +9984,21 @@
// Ensure the key is unique.
descriptor->KeyToUniqueName();
- if (flag == INSERT_TRANSITION &&
- map->owns_descriptors() &&
- map->CanHaveMoreTransitions()) {
+ // Share descriptors only if map owns descriptors and it not an initial map.
+ if (flag == INSERT_TRANSITION && map->owns_descriptors() &&
+ !map->GetBackPointer()->IsUndefined() &&
+ TransitionArray::CanHaveMoreTransitions(map)) {
return ShareDescriptor(map, descriptors, descriptor);
}
- Handle<DescriptorArray> new_descriptors = DescriptorArray::CopyUpTo(
- descriptors, map->NumberOfOwnDescriptors(), 1);
+ int nof = map->NumberOfOwnDescriptors();
+ Handle<DescriptorArray> new_descriptors =
+ DescriptorArray::CopyUpTo(descriptors, nof, 1);
new_descriptors->Append(descriptor);
Handle<LayoutDescriptor> new_layout_descriptor =
FLAG_unbox_double_fields
- ? LayoutDescriptor::Append(map, descriptor->GetDetails())
+ ? LayoutDescriptor::New(map, new_descriptors, nof + 1)
: handle(LayoutDescriptor::FastPointerLayout(), map->GetIsolate());
return CopyReplaceDescriptors(map, new_descriptors, new_layout_descriptor,
@@ -7414,7 +10046,6 @@
Handle<DescriptorArray> descriptors =
DescriptorArray::Allocate(desc->GetIsolate(), size, slack);
- DescriptorArray::WhitenessWitness witness(*descriptors);
if (attributes != NONE) {
for (int i = 0; i < size; ++i) {
@@ -7422,10 +10053,10 @@
Name* key = desc->GetKey(i);
PropertyDetails details = desc->GetDetails(i);
// Bulk attribute changes never affect private properties.
- if (!key->IsSymbol() || !Symbol::cast(key)->is_private()) {
+ if (!key->IsPrivate()) {
int mask = DONT_DELETE | DONT_ENUM;
// READ_ONLY is an invalid attribute for JS setters/getters.
- if (details.type() != CALLBACKS || !value->IsAccessorPair()) {
+ if (details.type() != ACCESSOR_CONSTANT || !value->IsAccessorPair()) {
mask |= READ_ONLY;
}
details = details.CopyAddAttributes(
@@ -7433,11 +10064,11 @@
}
Descriptor inner_desc(
handle(key), handle(value, desc->GetIsolate()), details);
- descriptors->Set(i, &inner_desc, witness);
+ descriptors->SetDescriptor(i, &inner_desc);
}
} else {
for (int i = 0; i < size; ++i) {
- descriptors->CopyFrom(i, *desc, witness);
+ descriptors->CopyFrom(i, *desc);
}
}
@@ -7447,6 +10078,22 @@
}
+bool DescriptorArray::IsEqualUpTo(DescriptorArray* desc, int nof_descriptors) {
+ for (int i = 0; i < nof_descriptors; i++) {
+ if (GetKey(i) != desc->GetKey(i) || GetValue(i) != desc->GetValue(i)) {
+ return false;
+ }
+ PropertyDetails details = GetDetails(i);
+ PropertyDetails other_details = desc->GetDetails(i);
+ if (details.type() != other_details.type() ||
+ !details.representation().Equals(other_details.representation())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
Handle<Map> Map::CopyReplaceDescriptor(Handle<Map> map,
Handle<DescriptorArray> descriptors,
Descriptor* descriptor,
@@ -7519,212 +10166,6 @@
}
-// An iterator over all map transitions in an descriptor array, reusing the
-// constructor field of the map while it is running. Negative values in
-// the constructor field indicate an active map transition iteration. The
-// original constructor is restored after iterating over all entries.
-class IntrusiveMapTransitionIterator {
- public:
- IntrusiveMapTransitionIterator(
- Map* map, TransitionArray* transition_array, Object* constructor)
- : map_(map),
- transition_array_(transition_array),
- constructor_(constructor) { }
-
- void StartIfNotStarted() {
- DCHECK(!(*IteratorField())->IsSmi() || IsIterating());
- if (!(*IteratorField())->IsSmi()) {
- DCHECK(*IteratorField() == constructor_);
- *IteratorField() = Smi::FromInt(-1);
- }
- }
-
- bool IsIterating() {
- return (*IteratorField())->IsSmi() &&
- Smi::cast(*IteratorField())->value() < 0;
- }
-
- Map* Next() {
- DCHECK(IsIterating());
- int value = Smi::cast(*IteratorField())->value();
- int index = -value - 1;
- int number_of_transitions = transition_array_->number_of_transitions();
- if (index < number_of_transitions) {
- *IteratorField() = Smi::FromInt(value - 1);
- return transition_array_->GetTarget(index);
- }
-
- *IteratorField() = constructor_;
- return NULL;
- }
-
- private:
- Object** IteratorField() {
- return HeapObject::RawField(map_, Map::kConstructorOffset);
- }
-
- Map* map_;
- TransitionArray* transition_array_;
- Object* constructor_;
-};
-
-
-// An iterator over all prototype transitions, reusing the constructor field
-// of the map while it is running. Positive values in the constructor field
-// indicate an active prototype transition iteration. The original constructor
-// is restored after iterating over all entries.
-class IntrusivePrototypeTransitionIterator {
- public:
- IntrusivePrototypeTransitionIterator(
- Map* map, HeapObject* proto_trans, Object* constructor)
- : map_(map), proto_trans_(proto_trans), constructor_(constructor) { }
-
- void StartIfNotStarted() {
- if (!(*IteratorField())->IsSmi()) {
- DCHECK(*IteratorField() == constructor_);
- *IteratorField() = Smi::FromInt(0);
- }
- }
-
- bool IsIterating() {
- return (*IteratorField())->IsSmi() &&
- Smi::cast(*IteratorField())->value() >= 0;
- }
-
- Map* Next() {
- DCHECK(IsIterating());
- int transitionNumber = Smi::cast(*IteratorField())->value();
- if (transitionNumber < NumberOfTransitions()) {
- *IteratorField() = Smi::FromInt(transitionNumber + 1);
- return GetTransition(transitionNumber);
- }
- *IteratorField() = constructor_;
- return NULL;
- }
-
- private:
- Object** IteratorField() {
- return HeapObject::RawField(map_, Map::kConstructorOffset);
- }
-
- int NumberOfTransitions() {
- FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
- Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset);
- return Smi::cast(num)->value();
- }
-
- Map* GetTransition(int transitionNumber) {
- FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
- return Map::cast(proto_trans->get(IndexFor(transitionNumber)));
- }
-
- int IndexFor(int transitionNumber) {
- return Map::kProtoTransitionHeaderSize +
- Map::kProtoTransitionMapOffset +
- transitionNumber * Map::kProtoTransitionElementsPerEntry;
- }
-
- Map* map_;
- HeapObject* proto_trans_;
- Object* constructor_;
-};
-
-
-// To traverse the transition tree iteratively, we have to store two kinds of
-// information in a map: The parent map in the traversal and which children of a
-// node have already been visited. To do this without additional memory, we
-// temporarily reuse two fields with known values:
-//
-// (1) The map of the map temporarily holds the parent, and is restored to the
-// meta map afterwards.
-//
-// (2) The info which children have already been visited depends on which part
-// of the map we currently iterate. We use the constructor field of the
-// map to store the current index. We can do that because the constructor
-// is the same for all involved maps.
-//
-// (a) If we currently follow normal map transitions, we temporarily store
-// the current index in the constructor field, and restore it to the
-// original constructor afterwards. Note that a single descriptor can
-// have 0, 1, or 2 transitions.
-//
-// (b) If we currently follow prototype transitions, we temporarily store
-// the current index in the constructor field, and restore it to the
-// original constructor afterwards.
-//
-// Note that the child iterator is just a concatenation of two iterators: One
-// iterating over map transitions and one iterating over prototype transisitons.
-class TraversableMap : public Map {
- public:
- // Record the parent in the traversal within this map. Note that this destroys
- // this map's map!
- void SetParent(TraversableMap* parent) { set_map_no_write_barrier(parent); }
-
- // Reset the current map's map, returning the parent previously stored in it.
- TraversableMap* GetAndResetParent() {
- TraversableMap* old_parent = static_cast<TraversableMap*>(map());
- set_map_no_write_barrier(GetHeap()->meta_map());
- return old_parent;
- }
-
- // If we have an unvisited child map, return that one and advance. If we have
- // none, return NULL and restore the overwritten constructor field.
- TraversableMap* ChildIteratorNext(Object* constructor) {
- if (!HasTransitionArray()) return NULL;
-
- TransitionArray* transition_array = transitions();
- if (transition_array->HasPrototypeTransitions()) {
- HeapObject* proto_transitions =
- transition_array->GetPrototypeTransitions();
- IntrusivePrototypeTransitionIterator proto_iterator(this,
- proto_transitions,
- constructor);
- proto_iterator.StartIfNotStarted();
- if (proto_iterator.IsIterating()) {
- Map* next = proto_iterator.Next();
- if (next != NULL) return static_cast<TraversableMap*>(next);
- }
- }
-
- IntrusiveMapTransitionIterator transition_iterator(this,
- transition_array,
- constructor);
- transition_iterator.StartIfNotStarted();
- if (transition_iterator.IsIterating()) {
- Map* next = transition_iterator.Next();
- if (next != NULL) return static_cast<TraversableMap*>(next);
- }
-
- return NULL;
- }
-};
-
-
-// Traverse the transition tree in postorder without using the C++ stack by
-// doing pointer reversal.
-void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
- // Make sure that we do not allocate in the callback.
- DisallowHeapAllocation no_allocation;
-
- TraversableMap* current = static_cast<TraversableMap*>(this);
- // Get the root constructor here to restore it later when finished iterating
- // over maps.
- Object* root_constructor = constructor();
- while (true) {
- TraversableMap* child = current->ChildIteratorNext(root_constructor);
- if (child != NULL) {
- child->SetParent(current);
- current = child;
- } else {
- TraversableMap* parent = current->GetAndResetParent();
- callback(current, data);
- if (current == this) break;
- current = parent;
- }
- }
-}
-
-
void CodeCache::Update(
Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) {
// The number of monomorphic stubs for normal load/store/call IC's can grow to
@@ -7793,10 +10234,11 @@
// Extend the code cache with some new entries (at least one). Must be a
// multiple of the entry size.
- int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
+ Isolate* isolate = cache->GetIsolate();
+ int new_length = length + (length >> 1) + kCodeCacheEntrySize;
new_length = new_length - new_length % kCodeCacheEntrySize;
DCHECK((new_length % kCodeCacheEntrySize) == 0);
- cache = FixedArray::CopySize(cache, new_length);
+ cache = isolate->factory()->CopyFixedArrayAndGrow(cache, new_length - length);
// Add the (name, code) pair to the new cache.
cache->set(length + kCodeCacheEntryNameOffset, *name);
@@ -7901,7 +10343,7 @@
CodeCacheHashTableKey(Handle<Name> name, Handle<Code> code)
: name_(name), flags_(code->flags()), code_(code) { }
- bool IsMatch(Object* other) OVERRIDE {
+ bool IsMatch(Object* other) override {
if (!other->IsFixedArray()) return false;
FixedArray* pair = FixedArray::cast(other);
Name* name = Name::cast(pair->get(0));
@@ -7916,16 +10358,16 @@
return name->Hash() ^ flags;
}
- uint32_t Hash() OVERRIDE { return NameFlagsHashHelper(*name_, flags_); }
+ uint32_t Hash() override { return NameFlagsHashHelper(*name_, flags_); }
- uint32_t HashForObject(Object* obj) OVERRIDE {
+ uint32_t HashForObject(Object* obj) override {
FixedArray* pair = FixedArray::cast(obj);
Name* name = Name::cast(pair->get(0));
Code* code = Code::cast(pair->get(1));
return NameFlagsHashHelper(name, code->flags());
}
- MUST_USE_RESULT Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
+ MUST_USE_RESULT Handle<Object> AsHandle(Isolate* isolate) override {
Handle<Code> code = code_.ToHandleChecked();
Handle<FixedArray> pair = isolate->factory()->NewFixedArray(2);
pair->set(0, *name_);
@@ -8029,7 +10471,7 @@
: maps_(maps),
code_flags_(code_flags) {}
- bool IsMatch(Object* other) OVERRIDE {
+ bool IsMatch(Object* other) override {
MapHandleList other_maps(kDefaultListAllocationSize);
int other_flags;
FromObject(other, &other_flags, &other_maps);
@@ -8064,18 +10506,16 @@
return hash;
}
- uint32_t Hash() OVERRIDE {
- return MapsHashHelper(maps_, code_flags_);
- }
+ uint32_t Hash() override { return MapsHashHelper(maps_, code_flags_); }
- uint32_t HashForObject(Object* obj) OVERRIDE {
+ uint32_t HashForObject(Object* obj) override {
MapHandleList other_maps(kDefaultListAllocationSize);
int other_flags;
FromObject(obj, &other_flags, &other_maps);
return MapsHashHelper(&other_maps, other_flags);
}
- MUST_USE_RESULT Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
+ MUST_USE_RESULT Handle<Object> AsHandle(Isolate* isolate) override {
// The maps in |maps_| must be copied to a newly allocated FixedArray,
// both because the referenced MapList is short-lived, and because C++
// objects can't be stored in the heap anyway.
@@ -8138,81 +10578,12 @@
void FixedArray::Shrink(int new_length) {
DCHECK(0 <= new_length && new_length <= length());
if (new_length < length()) {
- GetHeap()->RightTrimFixedArray<Heap::FROM_MUTATOR>(
+ GetHeap()->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(
this, length() - 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;
- ASSIGN_RETURN_ON_EXCEPTION(
- array->GetIsolate(), result,
- accessor->AddElementsToFixedArray(array, array, content, filter),
- FixedArray);
-
-#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) {
- ElementsAccessor* accessor = ElementsAccessor::ForArray(second);
- Handle<FixedArray> result;
- ASSIGN_RETURN_ON_EXCEPTION(
- first->GetIsolate(), result,
- accessor->AddElementsToFixedArray(
- Handle<Object>::null(), // receiver
- Handle<JSObject>::null(), // holder
- first, Handle<FixedArrayBase>::cast(second), ALL_KEYS),
- FixedArray);
-
-#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;
-}
-
-
-Handle<FixedArray> FixedArray::CopySize(
- Handle<FixedArray> array, int new_length, PretenureFlag pretenure) {
- Isolate* isolate = array->GetIsolate();
- if (new_length == 0) return isolate->factory()->empty_fixed_array();
- Handle<FixedArray> result =
- isolate->factory()->NewFixedArray(new_length, pretenure);
- // Copy the content
- DisallowHeapAllocation no_gc;
- int len = array->length();
- if (new_length < len) len = new_length;
- // We are taking the map from the old fixed array so the map is sure to
- // be an immortal immutable object.
- result->set_map_no_write_barrier(array->map());
- WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
- for (int i = 0; i < len; i++) {
- result->set(i, array->get(i), mode);
- }
- return result;
-}
-
-
void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
DisallowHeapAllocation no_gc;
WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
@@ -8249,66 +10620,105 @@
// static
-Handle<WeakFixedArray> WeakFixedArray::Add(
- Handle<Object> maybe_array, Handle<HeapObject> value,
- SearchForDuplicates search_for_duplicates) {
+Handle<WeakFixedArray> WeakFixedArray::Add(Handle<Object> maybe_array,
+ Handle<HeapObject> value,
+ int* assigned_index) {
Handle<WeakFixedArray> array =
(maybe_array.is_null() || !maybe_array->IsWeakFixedArray())
? Allocate(value->GetIsolate(), 1, Handle<WeakFixedArray>::null())
: Handle<WeakFixedArray>::cast(maybe_array);
-
- if (search_for_duplicates == kAddIfNotFound) {
- for (int i = 0; i < array->Length(); ++i) {
- if (array->Get(i) == *value) return array;
- }
- } else {
-#ifdef DEBUG
- for (int i = 0; i < array->Length(); ++i) {
- DCHECK_NE(*value, array->Get(i));
- }
-#endif
- }
-
// Try to store the new entry if there's room. Optimize for consecutive
// accesses.
int first_index = array->last_used_index();
- for (int i = first_index;;) {
- if (array->IsEmptySlot((i))) {
- WeakFixedArray::Set(array, i, value);
- return array;
+ int length = array->Length();
+ if (length > 0) {
+ for (int i = first_index;;) {
+ if (array->IsEmptySlot((i))) {
+ WeakFixedArray::Set(array, i, value);
+ if (assigned_index != NULL) *assigned_index = i;
+ return array;
+ }
+ if (FLAG_trace_weak_arrays) {
+ PrintF("[WeakFixedArray: searching for free slot]\n");
+ }
+ i = (i + 1) % length;
+ if (i == first_index) break;
}
- if (FLAG_trace_weak_arrays) {
- PrintF("[WeakFixedArray: searching for free slot]\n");
- }
- i = (i + 1) % array->Length();
- if (i == first_index) break;
}
// No usable slot found, grow the array.
- int new_length = array->Length() + (array->Length() >> 1) + 4;
+ int new_length = length == 0 ? 1 : length + (length >> 1) + 4;
Handle<WeakFixedArray> new_array =
Allocate(array->GetIsolate(), new_length, array);
if (FLAG_trace_weak_arrays) {
PrintF("[WeakFixedArray: growing to size %d ]\n", new_length);
}
- WeakFixedArray::Set(new_array, array->Length(), value);
+ WeakFixedArray::Set(new_array, length, value);
+ if (assigned_index != NULL) *assigned_index = length;
return new_array;
}
-void WeakFixedArray::Remove(Handle<HeapObject> value) {
+template <class CompactionCallback>
+void WeakFixedArray::Compact() {
+ FixedArray* array = FixedArray::cast(this);
+ int new_length = kFirstIndex;
+ for (int i = kFirstIndex; i < array->length(); i++) {
+ Object* element = array->get(i);
+ if (element->IsSmi()) continue;
+ if (WeakCell::cast(element)->cleared()) continue;
+ Object* value = WeakCell::cast(element)->value();
+ CompactionCallback::Callback(value, i - kFirstIndex,
+ new_length - kFirstIndex);
+ array->set(new_length++, element);
+ }
+ array->Shrink(new_length);
+ set_last_used_index(0);
+}
+
+
+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) {
+ DCHECK(value->IsMap() && Map::cast(value)->is_prototype_map());
+ Map* map = Map::cast(value);
+ DCHECK(map->prototype_info()->IsPrototypeInfo());
+ PrototypeInfo* proto_info = PrototypeInfo::cast(map->prototype_info());
+ DCHECK_EQ(old_index, proto_info->registry_slot());
+ proto_info->set_registry_slot(new_index);
+}
+
+
+template void WeakFixedArray::Compact<WeakFixedArray::NullCallback>();
+template void
+WeakFixedArray::Compact<JSObject::PrototypeRegistryCompactionCallback>();
+
+
+bool WeakFixedArray::Remove(Handle<HeapObject> value) {
+ if (Length() == 0) return false;
// Optimize for the most recently added element to be removed again.
int first_index = last_used_index();
for (int i = first_index;;) {
if (Get(i) == *value) {
- clear(i);
- // Users of WeakFixedArray should make sure that there are no duplicates,
- // they can use Add(..., kAddIfNotFound) if necessary.
- return;
+ Clear(i);
+ // Users of WeakFixedArray should make sure that there are no duplicates.
+ return true;
}
i = (i + 1) % Length();
- if (i == first_index) break;
+ if (i == first_index) return false;
}
+ UNREACHABLE();
}
@@ -8318,28 +10728,72 @@
DCHECK(0 <= size);
Handle<FixedArray> result =
isolate->factory()->NewUninitializedFixedArray(size + kFirstIndex);
- Handle<WeakFixedArray> casted_result = Handle<WeakFixedArray>::cast(result);
- if (initialize_from.is_null()) {
- for (int i = 0; i < result->length(); ++i) {
- result->set(i, Smi::FromInt(0));
- }
- } else {
+ int index = 0;
+ if (!initialize_from.is_null()) {
DCHECK(initialize_from->Length() <= size);
Handle<FixedArray> raw_source = Handle<FixedArray>::cast(initialize_from);
- int target_index = kFirstIndex;
- for (int source_index = kFirstIndex; source_index < raw_source->length();
- ++source_index) {
- // The act of allocating might have caused entries in the source array
- // to be cleared. Copy only what's needed.
- if (initialize_from->IsEmptySlot(source_index - kFirstIndex)) continue;
- result->set(target_index++, raw_source->get(source_index));
- }
- casted_result->set_last_used_index(target_index - 1 - kFirstIndex);
- for (; target_index < result->length(); ++target_index) {
- result->set(target_index, Smi::FromInt(0));
+ // Copy the entries without compacting, since the PrototypeInfo relies on
+ // the index of the entries not to change.
+ while (index < raw_source->length()) {
+ result->set(index, raw_source->get(index));
+ index++;
}
}
- return casted_result;
+ while (index < result->length()) {
+ result->set(index, Smi::FromInt(0));
+ index++;
+ }
+ return Handle<WeakFixedArray>::cast(result);
+}
+
+
+Handle<ArrayList> ArrayList::Add(Handle<ArrayList> array, Handle<Object> obj,
+ AddMode mode) {
+ int length = array->Length();
+ array = EnsureSpace(array, length + 1);
+ if (mode == kReloadLengthAfterAllocation) {
+ DCHECK(array->Length() <= length);
+ length = array->Length();
+ }
+ array->Set(length, *obj);
+ array->SetLength(length + 1);
+ return array;
+}
+
+
+Handle<ArrayList> ArrayList::Add(Handle<ArrayList> array, Handle<Object> obj1,
+ Handle<Object> obj2, AddMode mode) {
+ int length = array->Length();
+ array = EnsureSpace(array, length + 2);
+ if (mode == kReloadLengthAfterAllocation) {
+ length = array->Length();
+ }
+ array->Set(length, *obj1);
+ array->Set(length + 1, *obj2);
+ array->SetLength(length + 2);
+ return array;
+}
+
+
+bool ArrayList::IsFull() {
+ int capacity = length();
+ return kFirstIndex + Length() == capacity;
+}
+
+
+Handle<ArrayList> ArrayList::EnsureSpace(Handle<ArrayList> array, int length) {
+ int capacity = array->length();
+ bool empty = (capacity == 0);
+ if (capacity < kFirstIndex + length) {
+ Isolate* isolate = array->GetIsolate();
+ int new_capacity = kFirstIndex + length;
+ new_capacity = new_capacity + Max(new_capacity / 2, 2);
+ int grow_by = new_capacity - capacity;
+ array = Handle<ArrayList>::cast(
+ isolate->factory()->CopyFixedArrayAndGrow(array, grow_by));
+ if (empty) array->SetLength(0);
+ }
+ return array;
}
@@ -8352,7 +10806,7 @@
int size = number_of_descriptors + slack;
if (size == 0) return factory->empty_descriptor_array();
// Allocate the array of keys.
- Handle<FixedArray> result = factory->NewFixedArray(LengthFor(size));
+ Handle<FixedArray> result = factory->NewFixedArray(LengthFor(size), TENURED);
result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors));
result->set(kEnumCacheIndex, Smi::FromInt(0));
@@ -8371,36 +10825,40 @@
}
-void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
- FixedArray* new_cache,
- Object* new_index_cache) {
- DCHECK(bridge_storage->length() >= kEnumCacheBridgeLength);
- DCHECK(new_index_cache->IsSmi() || new_index_cache->IsFixedArray());
- DCHECK(!IsEmpty());
- DCHECK(!HasEnumCache() || new_cache->length() > GetEnumCache()->length());
- FixedArray::cast(bridge_storage)->
- set(kEnumCacheBridgeCacheIndex, new_cache);
- FixedArray::cast(bridge_storage)->
- set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
- set(kEnumCacheIndex, bridge_storage);
+// static
+void DescriptorArray::SetEnumCache(Handle<DescriptorArray> descriptors,
+ Isolate* isolate,
+ Handle<FixedArray> new_cache,
+ Handle<FixedArray> new_index_cache) {
+ DCHECK(!descriptors->IsEmpty());
+ FixedArray* bridge_storage;
+ bool needs_new_enum_cache = !descriptors->HasEnumCache();
+ if (needs_new_enum_cache) {
+ bridge_storage = *isolate->factory()->NewFixedArray(
+ DescriptorArray::kEnumCacheBridgeLength);
+ } else {
+ bridge_storage = FixedArray::cast(descriptors->get(kEnumCacheIndex));
+ }
+ bridge_storage->set(kEnumCacheBridgeCacheIndex, *new_cache);
+ bridge_storage->set(kEnumCacheBridgeIndicesCacheIndex,
+ new_index_cache.is_null() ? Object::cast(Smi::FromInt(0))
+ : *new_index_cache);
+ if (needs_new_enum_cache) {
+ descriptors->set(kEnumCacheIndex, bridge_storage);
+ }
}
-void DescriptorArray::CopyFrom(int index, DescriptorArray* src,
- const WhitenessWitness& witness) {
+void DescriptorArray::CopyFrom(int index, DescriptorArray* src) {
Object* value = src->GetValue(index);
PropertyDetails details = src->GetDetails(index);
Descriptor desc(handle(src->GetKey(index)),
handle(value, src->GetIsolate()),
details);
- Set(index, &desc, witness);
+ SetDescriptor(index, &desc);
}
-// We need the whiteness witness since sort will reshuffle the entries in the
-// descriptor array. If the descriptor array were to be black, the shuffling
-// would move a slot that was already recorded as pointing into an evacuation
-// candidate. This would result in missing updates upon evacuation.
void DescriptorArray::Sort() {
// In-place heap sort.
int len = number_of_descriptors();
@@ -8472,7 +10930,6 @@
Handle<DeoptimizationInputData> DeoptimizationInputData::New(
Isolate* isolate, int deopt_entry_count, PretenureFlag pretenure) {
- DCHECK(deopt_entry_count > 0);
return Handle<DeoptimizationInputData>::cast(
isolate->factory()->NewFixedArray(LengthFor(deopt_entry_count),
pretenure));
@@ -8494,6 +10951,58 @@
}
+// 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;
+ for (int i = 0; i < length(); i += kRangeEntrySize) {
+ int start_offset = Smi::cast(get(i + kRangeStartIndex))->value();
+ int end_offset = Smi::cast(get(i + kRangeEndIndex))->value();
+ int handler_field = Smi::cast(get(i + kRangeHandlerIndex))->value();
+ int handler_offset = HandlerOffsetField::decode(handler_field);
+ CatchPrediction prediction = HandlerPredictionField::decode(handler_field);
+ int stack_depth = Smi::cast(get(i + kRangeDepthIndex))->value();
+ if (pc_offset > start_offset && pc_offset <= end_offset) {
+ DCHECK_NE(start_offset, innermost_start);
+ if (start_offset < innermost_start) continue;
+ innermost_handler = handler_offset;
+ innermost_start = start_offset;
+ *stack_depth_out = stack_depth;
+ if (prediction_out) *prediction_out = prediction;
+ }
+ }
+ return innermost_handler;
+}
+
+
+// TODO(turbofan): Make sure table is sorted and use binary search.
+int HandlerTable::LookupReturn(int pc_offset, CatchPrediction* prediction_out) {
+ for (int i = 0; i < length(); i += kReturnEntrySize) {
+ int return_offset = Smi::cast(get(i + kReturnOffsetIndex))->value();
+ int handler_field = Smi::cast(get(i + kReturnHandlerIndex))->value();
+ if (pc_offset == return_offset) {
+ if (prediction_out) {
+ *prediction_out = HandlerPredictionField::decode(handler_field);
+ }
+ return HandlerOffsetField::decode(handler_field);
+ }
+ }
+ return -1;
+}
+
+
#ifdef DEBUG
bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
if (IsEmpty()) return other->IsEmpty();
@@ -8513,6 +11022,110 @@
}
+// 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();
@@ -8556,13 +11169,12 @@
}
-SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
- RobustnessFlag robust_flag,
- int offset,
- int length,
- int* length_return) {
+base::SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
+ RobustnessFlag robust_flag,
+ int offset, int length,
+ int* length_return) {
if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
- return SmartArrayPointer<char>(NULL);
+ return base::SmartArrayPointer<char>(NULL);
}
// Negative length means the to the end of the string.
if (length < 0) length = kMaxInt - offset;
@@ -8599,13 +11211,13 @@
last = character;
}
result[utf8_byte_position] = 0;
- return SmartArrayPointer<char>(result);
+ return base::SmartArrayPointer<char>(result);
}
-SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
- RobustnessFlag robust_flag,
- int* length_return) {
+base::SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
+ RobustnessFlag robust_flag,
+ int* length_return) {
return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
}
@@ -8631,9 +11243,10 @@
}
-SmartArrayPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
+base::SmartArrayPointer<uc16> String::ToWideCString(
+ RobustnessFlag robust_flag) {
if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
- return SmartArrayPointer<uc16>();
+ return base::SmartArrayPointer<uc16>();
}
StringCharacterStream stream(this);
@@ -8645,7 +11258,7 @@
result[i++] = character;
}
result[i] = 0;
- return SmartArrayPointer<uc16>(result);
+ return base::SmartArrayPointer<uc16>(result);
}
@@ -9009,20 +11622,20 @@
Vector<const SourceChar> src,
bool include_ending_line) {
const int src_len = src.length();
- StringSearch<uint8_t, SourceChar> search(isolate, STATIC_CHAR_VECTOR("\n"));
+ UnicodeCache* cache = isolate->unicode_cache();
+ for (int i = 0; i < src_len - 1; i++) {
+ SourceChar current = src[i];
+ SourceChar next = src[i + 1];
+ if (cache->IsLineTerminatorSequence(current, next)) line_ends->Add(i);
+ }
- // Find and record line ends.
- int position = 0;
- while (position != -1 && position < src_len) {
- position = search.Search(src, position);
- if (position != -1) {
- line_ends->Add(position);
- position++;
- } else if (include_ending_line) {
- // Even if the last line misses a line end, it is counted.
- line_ends->Add(src_len);
- return;
- }
+ if (src_len > 0 && cache->IsLineTerminatorSequence(src[src_len - 1], 0)) {
+ line_ends->Add(src_len - 1);
+ }
+ if (include_ending_line) {
+ // Include one character beyond the end of script. The rewriter uses that
+ // position for the implicit return statement.
+ line_ends->Add(src_len);
}
}
@@ -9303,20 +11916,66 @@
}
-bool String::MarkAsUndetectable() {
- if (StringShape(this).IsInternalized()) return false;
-
- Map* map = this->map();
- Heap* heap = GetHeap();
- if (map == heap->string_map()) {
- this->set_map(heap->undetectable_string_map());
- return true;
- } else if (map == heap->one_byte_string_map()) {
- this->set_map(heap->undetectable_one_byte_string_map());
- return true;
+// 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;
}
- // Rest cannot be marked as undetectable
- return false;
+
+ 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;
}
@@ -9330,10 +11989,10 @@
return false;
}
int i;
- unsigned remaining_in_str = static_cast<unsigned>(str_len);
+ size_t remaining_in_str = static_cast<size_t>(str_len);
const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(str.start());
for (i = 0; i < slen && remaining_in_str > 0; i++) {
- unsigned cursor = 0;
+ size_t cursor = 0;
uint32_t r = unibrow::Utf8::ValueOf(utf8_data, remaining_in_str, &cursor);
DCHECK(cursor > 0 && cursor <= remaining_in_str);
if (r > unibrow::Utf16::kMaxNonSurrogateCharCode) {
@@ -9440,17 +12099,10 @@
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);
- }
- heap->AdjustLiveBytes(start_of_string, -delta, Heap::FROM_MUTATOR);
+ // 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
// for the left-over space to avoid races with the sweeper thread.
@@ -9507,13 +12159,13 @@
// Start with a fake length which won't affect computation.
// It will be updated later.
StringHasher hasher(String::kMaxArrayIndexSize, seed);
- unsigned remaining = static_cast<unsigned>(vector_length);
+ size_t remaining = static_cast<size_t>(vector_length);
const uint8_t* stream = reinterpret_cast<const uint8_t*>(chars.start());
int utf16_length = 0;
bool is_index = true;
DCHECK(hasher.is_array_index_);
while (remaining > 0) {
- unsigned consumed = 0;
+ size_t consumed = 0;
uint32_t c = unibrow::Utf8::ValueOf(stream, remaining, &consumed);
DCHECK(consumed > 0 && consumed <= remaining);
stream += consumed;
@@ -9578,12 +12230,6 @@
}
-inline static uint32_t ObjectAddressForHashing(Object* object) {
- uint32_t value = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object));
- return value & MemoryChunk::kAlignmentMask;
-}
-
-
int Map::Hash() {
// For performance reasons we only hash the 3 most variable fields of a map:
// constructor, prototype and bit_field2. For predictability reasons we
@@ -9591,7 +12237,7 @@
// addresses.
// Shift away the tag.
- int hash = ObjectAddressForHashing(constructor()) >> 2;
+ int hash = ObjectAddressForHashing(GetConstructor()) >> 2;
// XOR-ing the prototype and constructor directly yields too many zero bits
// when the two pointers are close (which is fairly common).
@@ -9602,89 +12248,69 @@
}
-static bool CheckEquivalent(Map* first, Map* second) {
- return
- first->constructor() == second->constructor() &&
- first->prototype() == second->prototype() &&
- first->instance_type() == second->instance_type() &&
- first->bit_field() == second->bit_field() &&
- first->bit_field2() == second->bit_field2() &&
- first->has_instance_call_handler() == second->has_instance_call_handler();
+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->is_hidden_prototype() == second->is_hidden_prototype();
}
+} // namespace
+
bool Map::EquivalentToForTransition(Map* other) {
- return CheckEquivalent(this, other);
+ if (!CheckEquivalent(this, other)) return false;
+ if (instance_type() == JS_FUNCTION_TYPE) {
+ // JSFunctions require more checks to ensure that sloppy function is
+ // not equvalent to strict function.
+ int nof = Min(NumberOfOwnDescriptors(), other->NumberOfOwnDescriptors());
+ return instance_descriptors()->IsEqualUpTo(other->instance_descriptors(),
+ nof);
+ }
+ return true;
}
bool Map::EquivalentToForNormalization(Map* other,
PropertyNormalizationMode mode) {
- int properties = mode == CLEAR_INOBJECT_PROPERTIES
- ? 0 : other->inobject_properties();
- return CheckEquivalent(this, other) && inobject_properties() == properties;
+ int properties =
+ mode == CLEAR_INOBJECT_PROPERTIES ? 0 : other->GetInObjectProperties();
+ return CheckEquivalent(this, other) && bit_field2() == other->bit_field2() &&
+ GetInObjectProperties() == properties;
}
-void ConstantPoolArray::ConstantPoolIterateBody(ObjectVisitor* v) {
- // Unfortunately the serializer relies on pointers within an object being
- // visited in-order, so we have to iterate both the code and heap pointers in
- // the small section before doing so in the extended section.
- for (int s = 0; s <= final_section(); ++s) {
- LayoutSection section = static_cast<LayoutSection>(s);
- ConstantPoolArray::Iterator code_iter(this, ConstantPoolArray::CODE_PTR,
- section);
- while (!code_iter.is_finished()) {
- v->VisitCodeEntry(reinterpret_cast<Address>(
- RawFieldOfElementAt(code_iter.next_index())));
- }
-
- ConstantPoolArray::Iterator heap_iter(this, ConstantPoolArray::HEAP_PTR,
- section);
- while (!heap_iter.is_finished()) {
- v->VisitPointer(RawFieldOfElementAt(heap_iter.next_index()));
+bool JSFunction::Inlines(SharedFunctionInfo* candidate) {
+ DisallowHeapAllocation no_gc;
+ if (shared() == candidate) return true;
+ if (code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
+ DeoptimizationInputData* const data =
+ DeoptimizationInputData::cast(code()->deoptimization_data());
+ if (data->length() == 0) return false;
+ FixedArray* const literals = data->LiteralArray();
+ int const inlined_count = data->InlinedFunctionCount()->value();
+ for (int i = 0; i < inlined_count; ++i) {
+ if (SharedFunctionInfo::cast(literals->get(i)) == candidate) {
+ return true;
}
}
-}
-
-
-void ConstantPoolArray::ClearPtrEntries(Isolate* isolate) {
- Type type[] = { CODE_PTR, HEAP_PTR };
- Address default_value[] = {
- isolate->builtins()->builtin(Builtins::kIllegal)->entry(),
- reinterpret_cast<Address>(isolate->heap()->undefined_value()) };
-
- for (int i = 0; i < 2; ++i) {
- for (int s = 0; s <= final_section(); ++s) {
- LayoutSection section = static_cast<LayoutSection>(s);
- if (number_of_entries(type[i], section) > 0) {
- int offset = OffsetOfElementAt(first_index(type[i], section));
- MemsetPointer(
- reinterpret_cast<Address*>(HeapObject::RawField(this, offset)),
- default_value[i],
- number_of_entries(type[i], section));
- }
- }
- }
-}
-
-
-void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
- // Iterate over all fields in the body but take care in dealing with
- // the code entry.
- IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
- v->VisitCodeEntry(this->address() + kCodeEntryOffset);
- IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
+ return false;
}
void JSFunction::MarkForOptimization() {
Isolate* isolate = GetIsolate();
- DCHECK(isolate->use_crankshaft());
+ // Do not optimize if function contains break points.
+ if (shared()->HasDebugInfo()) return;
DCHECK(!IsOptimized());
DCHECK(shared()->allows_lazy_compilation() ||
- code()->optimizable());
- DCHECK(!shared()->is_generator());
+ !shared()->optimization_disabled());
+ DCHECK(!shared()->HasDebugInfo());
set_code_no_write_barrier(
isolate->builtins()->builtin(Builtins::kCompileOptimized));
// No write barrier required, since the builtin is part of the root set.
@@ -9699,19 +12325,17 @@
return;
}
if (isolate->concurrent_osr_enabled() &&
- isolate->optimizing_compiler_thread()->IsQueuedForOSR(this)) {
+ isolate->optimizing_compile_dispatcher()->IsQueuedForOSR(this)) {
// Do not attempt regular recompilation if we already queued this for OSR.
// TODO(yangguo): This is necessary so that we don't install optimized
// code on a function that is already optimized, since OSR and regular
// recompilation race. This goes away as soon as OSR becomes one-shot.
return;
}
- DCHECK(isolate->use_crankshaft());
DCHECK(!IsInOptimizationQueue());
- DCHECK(is_compiled() || isolate->DebuggerHasBreakPoints());
DCHECK(!IsOptimized());
- DCHECK(shared()->allows_lazy_compilation() || code()->optimizable());
- DCHECK(!shared()->is_generator());
+ DCHECK(shared()->allows_lazy_compilation() ||
+ !shared()->optimization_disabled());
DCHECK(isolate->concurrent_recompilation_enabled());
if (FLAG_trace_concurrent_recompilation) {
PrintF(" ** Marking ");
@@ -9719,276 +12343,490 @@
PrintF(" for concurrent recompilation.\n");
}
set_code_no_write_barrier(
- GetIsolate()->builtins()->builtin(Builtins::kCompileOptimizedConcurrent));
+ isolate->builtins()->builtin(Builtins::kCompileOptimizedConcurrent));
// No write barrier required, since the builtin is part of the root set.
}
-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());
+void SharedFunctionInfo::AddSharedCodeToOptimizedCodeMap(
+ Handle<SharedFunctionInfo> shared, Handle<Code> code) {
+ Isolate* isolate = shared->GetIsolate();
+ if (isolate->serializer_enabled()) return;
+ DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
+ // Empty code maps are unsupported.
+ if (!shared->OptimizedCodeMapIsCleared()) {
+ Handle<WeakCell> cell = isolate->factory()->NewWeakCell(code);
+ // A collection may have occured and cleared the optimized code map in the
+ // allocation above.
+ if (!shared->OptimizedCodeMapIsCleared()) {
+ shared->optimized_code_map()->set(kSharedCodeIndex, *cell);
+ }
}
-
- // 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::AddToOptimizedCodeMap(
- Handle<SharedFunctionInfo> shared,
- Handle<Context> native_context,
- Handle<Code> code,
- Handle<FixedArray> literals,
+void SharedFunctionInfo::AddToOptimizedCodeMapInternal(
+ Handle<SharedFunctionInfo> shared, Handle<Context> native_context,
+ Handle<HeapObject> code, Handle<LiteralsArray> literals,
BailoutId osr_ast_id) {
Isolate* isolate = shared->GetIsolate();
- DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
+ if (isolate->serializer_enabled()) return;
+ 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;
- if (value->IsSmi()) {
- // No optimized code map.
- DCHECK_EQ(0, Smi::cast(*value)->value());
- // Create 3 entries per context {context, code, literals}.
- new_code_map = isolate->factory()->NewFixedArray(kInitialLength);
- old_length = kEntriesStart;
+ int entry;
+
+ if (shared->OptimizedCodeMapIsCleared()) {
+ new_code_map = isolate->factory()->NewFixedArray(kInitialLength, TENURED);
+ new_code_map->set(kSharedCodeIndex, *isolate->factory()->empty_weak_cell(),
+ SKIP_WRITE_BARRIER);
+ entry = kEntriesStart;
} else {
- // Copy old map and append one new entry.
- Handle<FixedArray> old_code_map = Handle<FixedArray>::cast(value);
- DCHECK_EQ(-1, shared->SearchOptimizedCodeMap(*native_context, osr_ast_id));
- old_length = old_code_map->length();
- new_code_map = FixedArray::CopySize(
- old_code_map, old_length + kEntryLength);
- // Zap the old map for the sake of the heap verifier.
- if (Heap::ShouldZapGarbage()) {
- Object** data = old_code_map->data_start();
- MemsetPointer(data, isolate->heap()->the_hole_value(), old_length);
+ Handle<FixedArray> old_code_map(shared->optimized_code_map(), isolate);
+ entry = shared->SearchOptimizedCodeMapEntry(*native_context, osr_ast_id);
+ if (entry > kSharedCodeIndex) {
+ // Found an existing context-specific entry. If the user provided valid
+ // code, it must not contain any code.
+ DCHECK(code->IsUndefined() ||
+ WeakCell::cast(old_code_map->get(entry + kCachedCodeOffset))
+ ->cleared());
+
+ // Just set the code and literals to the entry.
+ if (!code->IsUndefined()) {
+ Handle<WeakCell> code_cell = isolate->factory()->NewWeakCell(code);
+ old_code_map->set(entry + kCachedCodeOffset, *code_cell);
+ }
+ Handle<WeakCell> literals_cell =
+ isolate->factory()->NewWeakCell(literals);
+ old_code_map->set(entry + kLiteralsOffset, *literals_cell);
+ return;
+ }
+
+ // Can we reuse an entry?
+ DCHECK(entry < kEntriesStart);
+ int length = old_code_map->length();
+ for (int i = kEntriesStart; i < length; i += kEntryLength) {
+ if (WeakCell::cast(old_code_map->get(i + kContextOffset))->cleared()) {
+ new_code_map = old_code_map;
+ entry = i;
+ break;
+ }
+ }
+
+ if (entry < kEntriesStart) {
+ // Copy old optimized code map and append one new entry.
+ new_code_map = isolate->factory()->CopyFixedArrayAndGrow(
+ old_code_map, kEntryLength, TENURED);
+ // TODO(mstarzinger): Temporary workaround. The allocation above might
+ // have flushed the optimized code map and the copy we created is full of
+ // holes. For now we just give up on adding the entry and pretend it got
+ // flushed.
+ if (shared->OptimizedCodeMapIsCleared()) return;
+ entry = old_code_map->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()));
+
+ Handle<WeakCell> code_cell = code->IsUndefined()
+ ? isolate->factory()->empty_weak_cell()
+ : isolate->factory()->NewWeakCell(code);
+ Handle<WeakCell> literals_cell = isolate->factory()->NewWeakCell(literals);
+ WeakCell* context_cell = native_context->self_weak_cell();
+
+ new_code_map->set(entry + kContextOffset, context_cell);
+ new_code_map->set(entry + kCachedCodeOffset, *code_cell);
+ new_code_map->set(entry + kLiteralsOffset, *literals_cell);
+ 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);
- DCHECK(new_code_map->get(i + kLiteralsOffset)->IsFixedArray());
+ WeakCell* cell = WeakCell::cast(new_code_map->get(i + kContextOffset));
+ DCHECK(cell->cleared() || cell->value()->IsNativeContext());
+ cell = WeakCell::cast(new_code_map->get(i + kCachedCodeOffset));
+ DCHECK(cell->cleared() ||
+ (cell->value()->IsCode() &&
+ Code::cast(cell->value())->kind() == Code::OPTIMIZED_FUNCTION));
+ cell = WeakCell::cast(new_code_map->get(i + kLiteralsOffset));
+ DCHECK(cell->cleared() || cell->value()->IsFixedArray());
DCHECK(new_code_map->get(i + kOsrAstIdOffset)->IsSmi());
}
#endif
- shared->set_optimized_code_map(*new_code_map);
-}
-
-FixedArray* SharedFunctionInfo::GetLiteralsFromOptimizedCodeMap(int index) {
- DCHECK(index > kEntriesStart);
- FixedArray* code_map = FixedArray::cast(optimized_code_map());
- if (!bound()) {
- FixedArray* cached_literals = FixedArray::cast(code_map->get(index + 1));
- DCHECK_NE(NULL, cached_literals);
- return cached_literals;
+ FixedArray* old_code_map = shared->optimized_code_map();
+ if (old_code_map != *new_code_map) {
+ shared->set_optimized_code_map(*new_code_map);
}
- return NULL;
-}
-
-
-Code* SharedFunctionInfo::GetCodeFromOptimizedCodeMap(int index) {
- DCHECK(index > kEntriesStart);
- FixedArray* code_map = FixedArray::cast(optimized_code_map());
- Code* code = Code::cast(code_map->get(index));
- DCHECK_NE(NULL, code);
- return code;
}
void SharedFunctionInfo::ClearOptimizedCodeMap() {
- FixedArray* code_map = FixedArray::cast(optimized_code_map());
-
- // If the next map link slot is already used then the function was
- // enqueued with code flushing and we remove it now.
- if (!code_map->get(kNextMapIndex)->IsUndefined()) {
- CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
- flusher->EvictOptimizedCodeMap(this);
- }
-
- DCHECK(code_map->get(kNextMapIndex)->IsUndefined());
- set_optimized_code_map(Smi::FromInt(0));
+ FixedArray* cleared_map = GetHeap()->cleared_optimized_code_map();
+ set_optimized_code_map(cleared_map, SKIP_WRITE_BARRIER);
}
void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code,
const char* reason) {
DisallowHeapAllocation no_gc;
- if (optimized_code_map()->IsSmi()) return;
+ if (OptimizedCodeMapIsCleared()) return;
- FixedArray* code_map = FixedArray::cast(optimized_code_map());
+ Heap* heap = GetHeap();
+ FixedArray* code_map = 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.
+ DCHECK(WeakCell::cast(code_map->get(src))->cleared() ||
+ WeakCell::cast(code_map->get(src))->value()->IsNativeContext());
+ if (WeakCell::cast(code_map->get(src + kCachedCodeOffset))->value() ==
+ 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(src + kCachedCodeOffset, heap->empty_weak_cell(),
+ SKIP_WRITE_BARRIER);
+ }
+
+ // 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 (WeakCell::cast(code_map->get(kSharedCodeIndex))->value() ==
+ optimized_code) {
+ // Evict context-independent code as well.
+ code_map->set(kSharedCodeIndex, heap->empty_weak_cell(),
+ SKIP_WRITE_BARRIER);
+ if (FLAG_trace_opt) {
+ PrintF("[evicting entry from optimizing code map (%s) for ", reason);
+ ShortPrint();
+ PrintF(" (context-independent code)]\n");
}
}
if (dst != length) {
// Always trim even when array is cleared because of heap verifier.
- GetHeap()->RightTrimFixedArray<Heap::FROM_MUTATOR>(code_map, length - dst);
- if (code_map->length() == kEntriesStart) ClearOptimizedCodeMap();
+ heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(code_map,
+ length - dst);
+ if (code_map->length() == kEntriesStart &&
+ WeakCell::cast(code_map->get(kSharedCodeIndex))->cleared()) {
+ ClearOptimizedCodeMap();
+ }
}
}
void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) {
- FixedArray* code_map = FixedArray::cast(optimized_code_map());
+ FixedArray* code_map = optimized_code_map();
DCHECK(shrink_by % kEntryLength == 0);
DCHECK(shrink_by <= code_map->length() - kEntriesStart);
// Always trim even when array is cleared because of heap verifier.
- GetHeap()->RightTrimFixedArray<Heap::FROM_GC>(code_map, shrink_by);
- if (code_map->length() == kEntriesStart) {
+ GetHeap()->RightTrimFixedArray<Heap::SEQUENTIAL_TO_SWEEPER>(code_map,
+ shrink_by);
+ if (code_map->length() == kEntriesStart &&
+ WeakCell::cast(code_map->get(kSharedCodeIndex))->cleared()) {
ClearOptimizedCodeMap();
}
}
-void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
- PrototypeOptimizationMode mode) {
- if (object->IsGlobalObject()) return;
- if (object->IsJSGlobalProxy()) return;
- if (mode == FAST_PROTOTYPE && !object->map()->is_prototype_map()) {
- // First normalize to ensure all JSFunctions are CONSTANT.
- JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
- "NormalizeAsPrototype");
- }
- bool has_just_copied_map = false;
- if (!object->HasFastProperties()) {
- JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
- has_just_copied_map = true;
- }
- if (mode == FAST_PROTOTYPE && object->HasFastProperties() &&
- !object->map()->is_prototype_map()) {
- if (!has_just_copied_map) {
- Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype");
- JSObject::MigrateToMap(object, new_map);
- }
- object->map()->set_is_prototype_map(true);
+static void GetMinInobjectSlack(Map* map, void* data) {
+ int slack = map->unused_property_fields();
+ if (*reinterpret_cast<int*>(data) > slack) {
+ *reinterpret_cast<int*>(data) = slack;
}
}
+static void ShrinkInstanceSize(Map* map, void* data) {
+ int slack = *reinterpret_cast<int*>(data);
+ map->SetInObjectProperties(map->GetInObjectProperties() - slack);
+ map->set_unused_property_fields(map->unused_property_fields() - slack);
+ map->set_instance_size(map->instance_size() - slack * kPointerSize);
+
+ // Visitor id might depend on the instance size, recalculate it.
+ map->set_visitor_id(Heap::GetStaticVisitorIdForMap(map));
+}
+
+
+void Map::CompleteInobjectSlackTracking() {
+ // Has to be an initial map.
+ DCHECK(GetBackPointer()->IsUndefined());
+
+ set_construction_counter(kNoSlackTracking);
+
+ int slack = unused_property_fields();
+ TransitionArray::TraverseTransitionTree(this, &GetMinInobjectSlack, &slack);
+ if (slack != 0) {
+ // Resize the initial map and all maps in its transition tree.
+ TransitionArray::TraverseTransitionTree(this, &ShrinkInstanceSize, &slack);
+ }
+}
+
+
+static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) {
+ DisallowHeapAllocation no_gc;
+ if (!object->HasFastProperties()) return false;
+ Map* map = object->map();
+ if (map->is_prototype_map()) return false;
+ DescriptorArray* descriptors = map->instance_descriptors();
+ for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
+ PropertyDetails details = descriptors->GetDetails(i);
+ if (details.location() == kDescriptor) continue;
+ if (details.representation().IsHeapObject() ||
+ details.representation().IsTagged()) {
+ FieldIndex index = FieldIndex::ForDescriptor(map, i);
+ if (object->RawFastPropertyAt(index)->IsJSFunction()) return true;
+ }
+ }
+ return false;
+}
+
+
+// static
+void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
+ PrototypeOptimizationMode mode) {
+ if (object->IsJSGlobalObject()) return;
+ if (mode == FAST_PROTOTYPE && PrototypeBenefitsFromNormalization(object)) {
+ // First normalize to ensure all JSFunctions are DATA_CONSTANT.
+ JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
+ "NormalizeAsPrototype");
+ }
+ Handle<Map> previous_map(object->map());
+ if (!object->HasFastProperties()) {
+ JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
+ }
+ if (!object->map()->is_prototype_map()) {
+ if (object->map() == *previous_map) {
+ Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype");
+ JSObject::MigrateToMap(object, new_map);
+ }
+ object->map()->set_is_prototype_map(true);
+
+ // Replace the pointer to the exact constructor with the Object function
+ // from the same context if undetectable from JS. This is to avoid keeping
+ // memory alive unnecessarily.
+ Object* maybe_constructor = object->map()->GetConstructor();
+ if (maybe_constructor->IsJSFunction()) {
+ JSFunction* constructor = JSFunction::cast(maybe_constructor);
+ Isolate* isolate = object->GetIsolate();
+ if (!constructor->shared()->IsApiFunction() &&
+ object->class_name() == isolate->heap()->Object_string()) {
+ Context* context = constructor->context()->native_context();
+ JSFunction* object_function = context->object_function();
+ object->map()->SetConstructor(object_function);
+ }
+ }
+ }
+}
+
+
+// static
void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
if (!object->map()->is_prototype_map()) return;
OptimizeAsPrototype(object, FAST_PROTOTYPE);
}
-void JSObject::RegisterPrototypeUser(Handle<JSObject> prototype,
- Handle<HeapObject> user) {
+// static
+void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
DCHECK(FLAG_track_prototype_users);
- Isolate* isolate = prototype->GetIsolate();
- Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
+ // Contract: In line with InvalidatePrototypeChains()'s requirements,
+ // leaf maps don't need to register as users, only prototypes do.
+ DCHECK(user->is_prototype_map());
- // Get prototype users array, create it if it doesn't exist yet.
- Handle<Object> maybe_array =
- JSObject::GetProperty(prototype, symbol).ToHandleChecked();
+ Handle<Map> current_user = user;
+ Handle<PrototypeInfo> current_user_info =
+ Map::GetOrCreatePrototypeInfo(user, isolate);
+ for (PrototypeIterator iter(user); !iter.IsAtEnd(); iter.Advance()) {
+ // Walk up the prototype chain as far as links haven't been registered yet.
+ if (current_user_info->registry_slot() != PrototypeInfo::UNREGISTERED) {
+ break;
+ }
+ Handle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter);
+ // Proxies on the prototype chain are not supported. They make it
+ // impossible to make any assumptions about the prototype chain anyway.
+ if (maybe_proto->IsJSProxy()) return;
+ Handle<JSObject> proto = Handle<JSObject>::cast(maybe_proto);
+ Handle<PrototypeInfo> proto_info =
+ Map::GetOrCreatePrototypeInfo(proto, isolate);
+ Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
+ int slot = 0;
+ Handle<WeakFixedArray> new_array =
+ WeakFixedArray::Add(maybe_registry, current_user, &slot);
+ current_user_info->set_registry_slot(slot);
+ if (!maybe_registry.is_identical_to(new_array)) {
+ proto_info->set_prototype_users(*new_array);
+ }
+ if (FLAG_trace_prototype_users) {
+ PrintF("Registering %p as a user of prototype %p (map=%p).\n",
+ reinterpret_cast<void*>(*current_user),
+ reinterpret_cast<void*>(*proto),
+ reinterpret_cast<void*>(proto->map()));
+ }
- Handle<WeakFixedArray> new_array = WeakFixedArray::Add(maybe_array, user);
- if (!maybe_array.is_identical_to(new_array)) {
- JSObject::SetOwnPropertyIgnoreAttributes(prototype, symbol, new_array,
- DONT_ENUM).Assert();
+ current_user = handle(proto->map(), isolate);
+ current_user_info = proto_info;
}
}
-void JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype,
- Handle<HeapObject> user) {
- Isolate* isolate = prototype->GetIsolate();
- Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
-
- Handle<Object> maybe_array =
- JSObject::GetProperty(prototype, symbol).ToHandleChecked();
- if (!maybe_array->IsWeakFixedArray()) return;
- Handle<WeakFixedArray>::cast(maybe_array)->Remove(user);
+// Can be called regardless of whether |user| was actually registered with
+// |prototype|. Returns true when there was a registration.
+// static
+bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
+ DCHECK(user->is_prototype_map());
+ // If it doesn't have a PrototypeInfo, it was never registered.
+ if (!user->prototype_info()->IsPrototypeInfo()) return false;
+ // If it had no prototype before, see if it had users that might expect
+ // registration.
+ if (!user->prototype()->IsJSObject()) {
+ Object* users =
+ PrototypeInfo::cast(user->prototype_info())->prototype_users();
+ return users->IsWeakFixedArray();
+ }
+ Handle<JSObject> prototype(JSObject::cast(user->prototype()), isolate);
+ Handle<PrototypeInfo> user_info =
+ Map::GetOrCreatePrototypeInfo(user, isolate);
+ int slot = user_info->registry_slot();
+ if (slot == PrototypeInfo::UNREGISTERED) return false;
+ DCHECK(prototype->map()->is_prototype_map());
+ Object* maybe_proto_info = prototype->map()->prototype_info();
+ // User knows its registry slot, prototype info and user registry must exist.
+ DCHECK(maybe_proto_info->IsPrototypeInfo());
+ Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(maybe_proto_info),
+ isolate);
+ Object* maybe_registry = proto_info->prototype_users();
+ DCHECK(maybe_registry->IsWeakFixedArray());
+ DCHECK(WeakFixedArray::cast(maybe_registry)->Get(slot) == *user);
+ WeakFixedArray::cast(maybe_registry)->Clear(slot);
+ if (FLAG_trace_prototype_users) {
+ PrintF("Unregistering %p as a user of prototype %p.\n",
+ reinterpret_cast<void*>(*user), reinterpret_cast<void*>(*prototype));
+ }
+ return true;
}
-void Map::SetPrototype(Handle<Object> prototype,
+static void InvalidatePrototypeChainsInternal(Map* map) {
+ if (!map->is_prototype_map()) return;
+ if (FLAG_trace_prototype_users) {
+ PrintF("Invalidating prototype map %p 's cell\n",
+ reinterpret_cast<void*>(map));
+ }
+ Object* maybe_proto_info = map->prototype_info();
+ if (!maybe_proto_info->IsPrototypeInfo()) return;
+ PrototypeInfo* proto_info = PrototypeInfo::cast(maybe_proto_info);
+ Object* maybe_cell = proto_info->validity_cell();
+ if (maybe_cell->IsCell()) {
+ // Just set the value; the cell will be replaced lazily.
+ Cell* cell = Cell::cast(maybe_cell);
+ cell->set_value(Smi::FromInt(Map::kPrototypeChainInvalid));
+ }
+
+ 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);
+ }
+}
+
+
+// static
+void JSObject::InvalidatePrototypeChains(Map* map) {
+ if (!FLAG_eliminate_prototype_chain_checks) return;
+ DisallowHeapAllocation no_gc;
+ InvalidatePrototypeChainsInternal(map);
+}
+
+
+// static
+Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<JSObject> prototype,
+ Isolate* isolate) {
+ Object* maybe_proto_info = prototype->map()->prototype_info();
+ if (maybe_proto_info->IsPrototypeInfo()) {
+ return handle(PrototypeInfo::cast(maybe_proto_info), isolate);
+ }
+ Handle<PrototypeInfo> proto_info = isolate->factory()->NewPrototypeInfo();
+ prototype->map()->set_prototype_info(*proto_info);
+ return proto_info;
+}
+
+
+// static
+Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<Map> prototype_map,
+ Isolate* isolate) {
+ Object* maybe_proto_info = prototype_map->prototype_info();
+ if (maybe_proto_info->IsPrototypeInfo()) {
+ return handle(PrototypeInfo::cast(maybe_proto_info), isolate);
+ }
+ Handle<PrototypeInfo> proto_info = isolate->factory()->NewPrototypeInfo();
+ prototype_map->set_prototype_info(*proto_info);
+ return proto_info;
+}
+
+
+// static
+Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
+ Isolate* isolate) {
+ Handle<Object> maybe_prototype(map->prototype(), isolate);
+ if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null();
+ Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype);
+ // Ensure the prototype is registered with its own prototypes so its cell
+ // will be invalidated when necessary.
+ JSObject::LazyRegisterPrototypeUser(handle(prototype->map(), isolate),
+ isolate);
+ Handle<PrototypeInfo> proto_info =
+ GetOrCreatePrototypeInfo(prototype, isolate);
+ Object* maybe_cell = proto_info->validity_cell();
+ // Return existing cell if it's still valid.
+ if (maybe_cell->IsCell()) {
+ Handle<Cell> cell(Cell::cast(maybe_cell), isolate);
+ if (cell->value() == Smi::FromInt(Map::kPrototypeChainValid)) {
+ return cell;
+ }
+ }
+ // Otherwise create a new cell.
+ Handle<Cell> cell = isolate->factory()->NewCell(
+ handle(Smi::FromInt(Map::kPrototypeChainValid), isolate));
+ proto_info->set_validity_cell(*cell);
+ return cell;
+}
+
+
+// static
+void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype,
PrototypeOptimizationMode proto_mode) {
- if (this->prototype()->IsJSObject() && FLAG_track_prototype_users) {
- Handle<JSObject> old_prototype(JSObject::cast(this->prototype()));
- JSObject::UnregisterPrototypeUser(old_prototype, handle(this));
- }
if (prototype->IsJSObject()) {
Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype);
- if (ShouldRegisterAsPrototypeUser(prototype_jsobj)) {
- JSObject::RegisterPrototypeUser(prototype_jsobj, handle(this));
- }
JSObject::OptimizeAsPrototype(prototype_jsobj, proto_mode);
}
WriteBarrierMode wb_mode =
prototype->IsNull() ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER;
- set_prototype(*prototype, wb_mode);
-}
-
-
-bool Map::ShouldRegisterAsPrototypeUser(Handle<JSObject> prototype) {
- if (!FLAG_track_prototype_users) return false;
- if (this->is_prototype_map()) return true;
- if (this->is_dictionary_map()) return false;
- Object* back = GetBackPointer();
- if (!back->IsMap()) return true;
- if (Map::cast(back)->prototype() != *prototype) return true;
- return false;
-}
-
-
-bool Map::CanUseOptimizationsBasedOnPrototypeRegistry() {
- if (!FLAG_track_prototype_users) return false;
- if (this->is_prototype_map()) return true;
- if (GetBackPointer()->IsMap()) return true;
- return false;
+ map->set_prototype(*prototype, wb_mode);
}
@@ -9996,29 +12834,26 @@
Handle<Context> native_context, Handle<Map> initial_map) {
// Replace all of the cached initial array maps in the native context with
// the appropriate transitioned elements kind maps.
- Factory* factory = native_context->GetIsolate()->factory();
- Handle<FixedArray> maps = factory->NewFixedArrayWithHoles(
- kElementsKindCount, TENURED);
-
+ Strength strength =
+ initial_map->is_strong() ? Strength::STRONG : Strength::WEAK;
Handle<Map> current_map = initial_map;
ElementsKind kind = current_map->elements_kind();
- DCHECK(kind == GetInitialFastElementsKind());
- maps->set(kind, *current_map);
+ DCHECK_EQ(GetInitialFastElementsKind(), kind);
+ native_context->set(Context::ArrayMapIndex(kind, strength), *current_map);
for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1;
i < kFastElementsKindCount; ++i) {
Handle<Map> new_map;
ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
- if (current_map->HasElementsTransition()) {
- new_map = handle(current_map->elements_transition_map());
- DCHECK(new_map->elements_kind() == next_kind);
+ if (Map* maybe_elements_transition = current_map->ElementsTransitionMap()) {
+ new_map = handle(maybe_elements_transition);
} else {
new_map = Map::CopyAsElementsKind(
current_map, next_kind, INSERT_TRANSITION);
}
- maps->set(next_kind, *new_map);
+ DCHECK_EQ(next_kind, new_map->elements_kind());
+ native_context->set(Context::ArrayMapIndex(next_kind, strength), *new_map);
current_map = new_map;
}
- native_context->set_js_array_maps(*maps);
return initial_map;
}
@@ -10036,9 +12871,7 @@
// copy containing the new prototype. Also complete any in-object
// slack tracking that is in progress at this point because it is
// still tracking the old copy.
- if (function->IsInobjectSlackTrackingInProgress()) {
- function->CompleteInobjectSlackTracking();
- }
+ function->CompleteInobjectSlackTrackingIfActive();
Handle<Map> initial_map(function->initial_map(), isolate);
@@ -10050,16 +12883,23 @@
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
- // initial map (and transitioned versions) in the native context.
- Context* native_context = function->context()->native_context();
- Object* array_function =
- native_context->get(Context::ARRAY_FUNCTION_INDEX);
+ // updated initial maps (and transitioned versions) in the native context.
+ Handle<Context> native_context(function->context()->native_context(),
+ isolate);
+ Handle<Object> array_function(
+ native_context->get(Context::ARRAY_FUNCTION_INDEX), isolate);
if (array_function->IsJSFunction() &&
- *function == JSFunction::cast(array_function)) {
- CacheInitialJSArrayMaps(handle(native_context, isolate), new_map);
+ *function == JSFunction::cast(*array_function)) {
+ CacheInitialJSArrayMaps(native_context, new_map);
+ Handle<Map> new_strong_map = Map::Copy(new_map, "SetInstancePrototype");
+ new_strong_map->set_is_strong();
+ CacheInitialJSArrayMaps(native_context, new_strong_map);
}
}
@@ -10071,6 +12911,11 @@
// needed. At that point, a new initial map is created and the
// prototype is put into the initial map where it belongs.
function->set_prototype_or_initial_map(*value);
+ if (value->IsJSObject()) {
+ // Optimize as prototype to detach it from its transition tree.
+ JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value),
+ FAST_PROTOTYPE);
+ }
}
isolate->heap()->ClearInstanceofCache();
}
@@ -10078,7 +12923,7 @@
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
@@ -10092,11 +12937,11 @@
Handle<Map> new_map = Map::Copy(handle(function->map()), "SetPrototype");
JSObject::MigrateToMap(function, new_map);
- new_map->set_constructor(*value);
+ new_map->SetConstructor(*value);
new_map->set_non_instance_prototype(true);
Isolate* isolate = new_map->GetIsolate();
construct_prototype = handle(
- isolate->context()->native_context()->initial_object_prototype(),
+ function->context()->native_context()->initial_object_prototype(),
isolate);
} else {
function->map()->set_non_instance_prototype(false);
@@ -10108,16 +12953,17 @@
bool JSFunction::RemovePrototype() {
Context* native_context = context()->native_context();
- Map* no_prototype_map = shared()->strict_mode() == SLOPPY
- ? native_context->sloppy_function_without_prototype_map()
- : native_context->strict_function_without_prototype_map();
+ Map* no_prototype_map =
+ is_strict(shared()->language_mode())
+ ? native_context->strict_function_without_prototype_map()
+ : native_context->sloppy_function_without_prototype_map();
if (map() == no_prototype_map) return true;
#ifdef DEBUG
- if (map() != (shared()->strict_mode() == SLOPPY
- ? native_context->sloppy_function_map()
- : native_context->strict_function_map())) {
+ if (map() != (is_strict(shared()->language_mode())
+ ? native_context->strict_function_map()
+ : native_context->sloppy_function_map())) {
return false;
}
#endif
@@ -10131,10 +12977,10 @@
void JSFunction::SetInitialMap(Handle<JSFunction> function, Handle<Map> map,
Handle<Object> prototype) {
if (map->prototype() != *prototype) {
- map->SetPrototype(prototype, FAST_PROTOTYPE);
+ Map::SetPrototype(map, prototype, FAST_PROTOTYPE);
}
function->set_prototype_or_initial_map(*map);
- map->set_constructor(*function);
+ map->SetConstructor(*function);
#if TRACE_MAPS
if (FLAG_trace_maps) {
PrintF("[TraceMaps: InitialMap map= %p SFI= %d_%s ]\n",
@@ -10145,25 +12991,102 @@
}
+#ifdef DEBUG
+namespace {
+
+bool CanSubclassHaveInobjectProperties(InstanceType instance_type) {
+ switch (instance_type) {
+ case JS_OBJECT_TYPE:
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
+ case JS_GENERATOR_OBJECT_TYPE:
+ case JS_MODULE_TYPE:
+ case JS_VALUE_TYPE:
+ case JS_DATE_TYPE:
+ case JS_ARRAY_TYPE:
+ case JS_MESSAGE_OBJECT_TYPE:
+ case JS_ARRAY_BUFFER_TYPE:
+ case JS_TYPED_ARRAY_TYPE:
+ case JS_DATA_VIEW_TYPE:
+ case JS_SET_TYPE:
+ case JS_MAP_TYPE:
+ case JS_SET_ITERATOR_TYPE:
+ case JS_MAP_ITERATOR_TYPE:
+ case JS_ITERATOR_RESULT_TYPE:
+ case JS_WEAK_MAP_TYPE:
+ case JS_WEAK_SET_TYPE:
+ case JS_PROMISE_TYPE:
+ case JS_REGEXP_TYPE:
+ case JS_FUNCTION_TYPE:
+ return true;
+
+ case JS_BOUND_FUNCTION_TYPE:
+ case JS_PROXY_TYPE:
+ case JS_GLOBAL_PROXY_TYPE:
+ case JS_GLOBAL_OBJECT_TYPE:
+ case FIXED_ARRAY_TYPE:
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ case ODDBALL_TYPE:
+ case FOREIGN_TYPE:
+ case MAP_TYPE:
+ case CODE_TYPE:
+ case CELL_TYPE:
+ case PROPERTY_CELL_TYPE:
+ case WEAK_CELL_TYPE:
+ case SYMBOL_TYPE:
+ case BYTECODE_ARRAY_TYPE:
+ case HEAP_NUMBER_TYPE:
+ case MUTABLE_HEAP_NUMBER_TYPE:
+ case SIMD128_VALUE_TYPE:
+ case FILLER_TYPE:
+ case BYTE_ARRAY_TYPE:
+ case FREE_SPACE_TYPE:
+ case SHARED_FUNCTION_INFO_TYPE:
+
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
+ case FIXED_##TYPE##_ARRAY_TYPE:
+#undef TYPED_ARRAY_CASE
+
+#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE:
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ // We must not end up here for these instance types at all.
+ UNREACHABLE();
+ // Fall through.
+ default:
+ return false;
+ }
+}
+
+} // namespace
+#endif
+
+
void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
+ DCHECK(function->IsConstructor() || function->shared()->is_generator());
if (function->has_initial_map()) return;
Isolate* isolate = function->GetIsolate();
+ // The constructor should be compiled for the optimization hints to be
+ // available.
+ Compiler::Compile(function, CLEAR_EXCEPTION);
+
// First create a new map with the size and number of in-object properties
// suggested by the function.
InstanceType instance_type;
- int instance_size;
- int in_object_properties;
if (function->shared()->is_generator()) {
instance_type = JS_GENERATOR_OBJECT_TYPE;
- instance_size = JSGeneratorObject::kSize;
- in_object_properties = 0;
} else {
instance_type = JS_OBJECT_TYPE;
- instance_size = function->shared()->CalculateInstanceSize();
- in_object_properties = function->shared()->CalculateInObjectProperties();
}
+ int instance_size;
+ int in_object_properties;
+ function->CalculateInstanceSize(instance_type, 0, &instance_size,
+ &in_object_properties);
+
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;
@@ -10172,35 +13095,125 @@
} else {
prototype = isolate->factory()->NewFunctionPrototype(function);
}
- map->set_inobject_properties(in_object_properties);
+ map->SetInObjectProperties(in_object_properties);
map->set_unused_property_fields(in_object_properties);
DCHECK(map->has_fast_object_elements());
// Finally link initial map and constructor function.
- JSFunction::SetInitialMap(function, map, Handle<JSReceiver>::cast(prototype));
-
- if (!function->shared()->is_generator()) {
- function->StartInobjectSlackTracking();
- }
+ DCHECK(prototype->IsJSReceiver());
+ JSFunction::SetInitialMap(function, map, prototype);
+ map->StartInobjectSlackTracking();
}
-void JSFunction::SetInstanceClassName(String* name) {
- shared()->set_instance_class_name(name);
+// static
+MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
+ Handle<JSFunction> constructor,
+ Handle<JSReceiver> new_target) {
+ EnsureHasInitialMap(constructor);
+
+ Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
+ if (*new_target == *constructor) return constructor_initial_map;
+
+ // Fast case, new.target is a subclass of constructor. The map is cacheable
+ // (and may already have been cached). new.target.prototype is guaranteed to
+ // be a JSReceiver.
+ if (new_target->IsJSFunction()) {
+ Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
+
+ // Check that |function|'s initial map still in sync with the |constructor|,
+ // otherwise we must create a new initial map for |function|.
+ if (function->has_initial_map() &&
+ function->initial_map()->GetConstructor() == *constructor) {
+ return handle(function->initial_map(), isolate);
+ }
+
+ // Create a new map with the size and number of in-object properties
+ // suggested by |function|.
+
+ // Link initial map and constructor function if the new.target is actually a
+ // subclass constructor.
+ if (IsSubclassConstructor(function->shared()->kind())) {
+ Handle<Object> prototype(function->instance_prototype(), isolate);
+ InstanceType instance_type = constructor_initial_map->instance_type();
+ DCHECK(CanSubclassHaveInobjectProperties(instance_type));
+ int internal_fields =
+ JSObject::GetInternalFieldCount(*constructor_initial_map);
+ int pre_allocated = constructor_initial_map->GetInObjectProperties() -
+ constructor_initial_map->unused_property_fields();
+ int instance_size;
+ int in_object_properties;
+ function->CalculateInstanceSizeForDerivedClass(
+ instance_type, internal_fields, &instance_size,
+ &in_object_properties);
+
+ int unused_property_fields = in_object_properties - pre_allocated;
+ Handle<Map> map =
+ Map::CopyInitialMap(constructor_initial_map, instance_size,
+ in_object_properties, unused_property_fields);
+ map->set_new_target_is_base(false);
+
+ JSFunction::SetInitialMap(function, map, prototype);
+ map->SetConstructor(*constructor);
+ map->StartInobjectSlackTracking();
+ return map;
+ }
+ }
+
+ // Slow path, new.target is either a proxy or can't cache the map.
+ // new.target.prototype is not guaranteed to be a JSReceiver, and may need to
+ // fall back to the intrinsicDefaultProto.
+ Handle<Object> prototype;
+ if (new_target->IsJSFunction()) {
+ Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
+ // Make sure the new.target.prototype is cached.
+ EnsureHasInitialMap(function);
+ prototype = handle(function->prototype(), isolate);
+ } else {
+ Handle<String> prototype_string = isolate->factory()->prototype_string();
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, prototype,
+ JSReceiver::GetProperty(new_target, prototype_string), Map);
+ // The above prototype lookup might change the constructor and its
+ // prototype, hence we have to reload the initial map.
+ EnsureHasInitialMap(constructor);
+ constructor_initial_map = handle(constructor->initial_map(), isolate);
+ }
+
+ // If prototype is not a JSReceiver, fetch the intrinsicDefaultProto from the
+ // correct realm. Rather than directly fetching the .prototype, we fetch the
+ // constructor that points to the .prototype. This relies on
+ // constructor.prototype being FROZEN for those constructors.
+ if (!prototype->IsJSReceiver()) {
+ Handle<Context> context;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, context,
+ JSReceiver::GetFunctionRealm(new_target), Map);
+ DCHECK(context->IsNativeContext());
+ Handle<Object> maybe_index = JSReceiver::GetDataProperty(
+ constructor, isolate->factory()->native_context_index_symbol());
+ int index = maybe_index->IsSmi() ? Smi::cast(*maybe_index)->value()
+ : Context::OBJECT_FUNCTION_INDEX;
+ Handle<JSFunction> realm_constructor(JSFunction::cast(context->get(index)));
+ prototype = handle(realm_constructor->prototype(), isolate);
+ }
+
+ Handle<Map> map = Map::CopyInitialMap(constructor_initial_map);
+ map->set_new_target_is_base(false);
+ DCHECK(prototype->IsJSReceiver());
+ if (map->prototype() != *prototype) {
+ Map::SetPrototype(map, prototype, FAST_PROTOTYPE);
+ }
+ map->SetConstructor(*constructor);
+ return map;
}
void JSFunction::PrintName(FILE* out) {
- SmartArrayPointer<char> name = shared()->DebugName()->ToCString();
+ base::SmartArrayPointer<char> name = shared()->DebugName()->ToCString();
PrintF(out, "%s", name.get());
}
-Context* JSFunction::NativeContextFromLiterals(FixedArray* literals) {
- return Context::cast(literals->get(JSFunction::kLiteralNativeContextIndex));
-}
-
-
// The filter is a pattern that matches function names in this way:
// "*" all; the default
// "-" all but the top-level function
@@ -10238,15 +13251,113 @@
}
-void Oddball::Initialize(Isolate* isolate,
- Handle<Oddball> oddball,
- const char* to_string,
- Handle<Object> to_number,
- byte kind) {
+Handle<String> JSFunction::GetName(Handle<JSFunction> function) {
+ Isolate* isolate = function->GetIsolate();
+ Handle<Object> name =
+ JSReceiver::GetDataProperty(function, isolate->factory()->name_string());
+ if (name->IsString()) return Handle<String>::cast(name);
+ return handle(function->shared()->DebugName(), isolate);
+}
+
+
+Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
+ Isolate* isolate = function->GetIsolate();
+ Handle<Object> name = JSReceiver::GetDataProperty(
+ function, isolate->factory()->display_name_string());
+ if (name->IsString()) return Handle<String>::cast(name);
+ return JSFunction::GetName(function);
+}
+
+
+namespace {
+
+char const kNativeCodeSource[] = "function () { [native code] }";
+
+
+Handle<String> NativeCodeFunctionSourceString(
+ Handle<SharedFunctionInfo> shared_info) {
+ Isolate* const isolate = shared_info->GetIsolate();
+ if (shared_info->name()->IsString()) {
+ IncrementalStringBuilder builder(isolate);
+ builder.AppendCString("function ");
+ builder.AppendString(handle(String::cast(shared_info->name()), isolate));
+ builder.AppendCString("() { [native code] }");
+ return builder.Finish().ToHandleChecked();
+ }
+ return isolate->factory()->NewStringFromAsciiChecked(kNativeCodeSource);
+}
+
+} // namespace
+
+
+// static
+Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) {
+ Isolate* const isolate = function->GetIsolate();
+ return isolate->factory()->NewStringFromAsciiChecked(kNativeCodeSource);
+}
+
+
+// static
+Handle<String> JSFunction::ToString(Handle<JSFunction> function) {
+ Isolate* const isolate = function->GetIsolate();
+ Handle<SharedFunctionInfo> shared_info(function->shared(), isolate);
+
+ // Check if {function} should hide its source code.
+ if (!shared_info->script()->IsScript() ||
+ Script::cast(shared_info->script())->hide_source()) {
+ return NativeCodeFunctionSourceString(shared_info);
+ }
+
+ // Check if we should print {function} as a class.
+ Handle<Object> class_start_position = JSReceiver::GetDataProperty(
+ function, isolate->factory()->class_start_position_symbol());
+ if (class_start_position->IsSmi()) {
+ Handle<Object> class_end_position = JSReceiver::GetDataProperty(
+ function, isolate->factory()->class_end_position_symbol());
+ Handle<String> script_source(
+ String::cast(Script::cast(shared_info->script())->source()), isolate);
+ return isolate->factory()->NewSubString(
+ script_source, Handle<Smi>::cast(class_start_position)->value(),
+ Handle<Smi>::cast(class_end_position)->value());
+ }
+
+ // Check if we have source code for the {function}.
+ if (!shared_info->HasSourceCode()) {
+ return NativeCodeFunctionSourceString(shared_info);
+ }
+
+ IncrementalStringBuilder builder(isolate);
+ if (!shared_info->is_arrow()) {
+ if (shared_info->is_concise_method()) {
+ if (shared_info->is_generator()) builder.AppendCharacter('*');
+ } else {
+ if (shared_info->is_generator()) {
+ builder.AppendCString("function* ");
+ } else {
+ builder.AppendCString("function ");
+ }
+ }
+ if (shared_info->name_should_print_as_anonymous()) {
+ builder.AppendCString("anonymous");
+ } else {
+ builder.AppendString(handle(String::cast(shared_info->name()), isolate));
+ }
+ }
+ builder.AppendString(Handle<String>::cast(shared_info->GetSourceCode()));
+ return builder.Finish().ToHandleChecked();
+}
+
+
+void Oddball::Initialize(Isolate* isolate, Handle<Oddball> oddball,
+ const char* to_string, Handle<Object> to_number,
+ const char* type_of, byte kind) {
Handle<String> internalized_to_string =
isolate->factory()->InternalizeUtf8String(to_string);
- oddball->set_to_string(*internalized_to_string);
+ Handle<String> internalized_type_of =
+ isolate->factory()->InternalizeUtf8String(type_of);
oddball->set_to_number(*to_number);
+ oddball->set_to_string(*internalized_to_string);
+ oddball->set_type_of(*internalized_type_of);
oddball->set_kind(kind);
}
@@ -10283,39 +13394,35 @@
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);
}
-int Script::GetLineNumberWithArray(int position) {
+int Script::GetLineNumberWithArray(int code_pos) {
DisallowHeapAllocation no_allocation;
- FixedArray* line_ends = FixedArray::cast(this->line_ends());
- int upper = line_ends->length() - 1;
- if (upper < 0) return -1;
- int offset = line_offset()->value();
+ DCHECK(line_ends()->IsFixedArray());
+ FixedArray* line_ends_array = FixedArray::cast(line_ends());
+ int line_ends_len = line_ends_array->length();
+ if (line_ends_len == 0) return -1;
- if (position > Smi::cast(line_ends->get(upper))->value()) {
- return upper + 1 + offset;
+ if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) {
+ return line_offset();
}
- if (position <= Smi::cast(line_ends->get(0))->value()) return offset;
- int lower = 1;
- // Binary search.
- while (true) {
- int mid = (lower + upper) / 2;
- if (position <= Smi::cast(line_ends->get(mid - 1))->value()) {
- upper = mid - 1;
- } else if (position > Smi::cast(line_ends->get(mid))->value()) {
- lower = mid + 1;
+ int left = 0;
+ int right = line_ends_len;
+ while (int half = (right - left) / 2) {
+ if ((Smi::cast(line_ends_array->get(left + half)))->value() > code_pos) {
+ right -= half;
} else {
- return mid + offset;
+ left += half;
}
}
- return -1;
+ return right + line_offset();
}
@@ -10352,11 +13459,11 @@
Handle<Object> property = Object::GetProperty(
script_wrapper, name_or_source_url_key).ToHandleChecked();
DCHECK(property->IsJSFunction());
- Handle<JSFunction> method = Handle<JSFunction>::cast(property);
Handle<Object> result;
// Do not check against pending exception, since this function may be called
// when an exception has already been pending.
- if (!Execution::TryCall(method, script_wrapper, 0, NULL).ToHandle(&result)) {
+ if (!Execution::TryCall(isolate, property, script_wrapper, 0, NULL)
+ .ToHandle(&result)) {
return isolate->factory()->undefined_value();
}
return result;
@@ -10366,6 +13473,7 @@
Handle<JSObject> Script::GetWrapper(Handle<Script> script) {
Isolate* isolate = script->GetIsolate();
if (!script->wrapper()->IsUndefined()) {
+ DCHECK(script->wrapper()->IsWeakCell());
Handle<WeakCell> cell(WeakCell::cast(script->wrapper()));
if (!cell->cleared()) {
// Return a handle for the existing script wrapper from the cache.
@@ -10388,6 +13496,103 @@
}
+MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo(
+ FunctionLiteral* fun) {
+ 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_(isolate->heap()->noscript_shared_function_infos()) {}
+
+
+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;
+ Isolate* isolate = shared->GetIsolate();
+
+ // Add shared function info to new script's list. If a collection occurs,
+ // the shared function info may be temporarily in two lists.
+ // This is okay because the gc-time processing of these lists can tolerate
+ // duplicates.
+ Handle<Object> list;
+ if (script_object->IsScript()) {
+ Handle<Script> script = Handle<Script>::cast(script_object);
+ list = handle(script->shared_function_infos(), isolate);
+ } else {
+ list = isolate->factory()->noscript_shared_function_infos();
+ }
+
+#ifdef DEBUG
+ {
+ WeakFixedArray::Iterator iterator(*list);
+ SharedFunctionInfo* next;
+ while ((next = iterator.Next<SharedFunctionInfo>())) {
+ DCHECK_NE(next, *shared);
+ }
+ }
+#endif // DEBUG
+ list = WeakFixedArray::Add(list, shared);
+
+ if (script_object->IsScript()) {
+ Handle<Script> script = Handle<Script>::cast(script_object);
+ script->set_shared_function_infos(*list);
+ } else {
+ isolate->heap()->SetRootNoScriptSharedFunctionInfos(*list);
+ }
+
+ // Remove shared function info from old script's list.
+ if (shared->script()->IsScript()) {
+ Script* old_script = Script::cast(shared->script());
+ if (old_script->shared_function_infos()->IsWeakFixedArray()) {
+ WeakFixedArray* list =
+ WeakFixedArray::cast(old_script->shared_function_infos());
+ list->Remove(shared);
+ }
+ } else {
+ // Remove shared function info from root array.
+ Object* list = isolate->heap()->noscript_shared_function_infos();
+ CHECK(WeakFixedArray::cast(list)->Remove(shared));
+ }
+
+ // Finally set new script.
+ shared->set_script(*script_object);
+}
+
+
String* SharedFunctionInfo::DebugName() {
Object* n = name();
if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
@@ -10412,10 +13617,7 @@
bool SharedFunctionInfo::IsInlineable() {
// Check that the function has a script associated with it.
if (!script()->IsScript()) return false;
- if (optimization_disabled()) return false;
- // If we never ran this (unlikely) then lets try to optimize it.
- if (code()->kind() != Code::FUNCTION) return true;
- return code()->optimizable();
+ return !optimization_disabled();
}
@@ -10424,19 +13626,56 @@
}
-int SharedFunctionInfo::CalculateInstanceSize() {
- int instance_size =
- JSObject::kHeaderSize +
- expected_nof_properties() * kPointerSize;
- if (instance_size > JSObject::kMaxInstanceSize) {
- instance_size = JSObject::kMaxInstanceSize;
- }
- return instance_size;
+namespace {
+
+void CalculateInstanceSizeHelper(InstanceType instance_type,
+ int requested_internal_fields,
+ int requested_in_object_properties,
+ int* instance_size,
+ int* in_object_properties) {
+ int header_size = JSObject::GetHeaderSize(instance_type);
+ DCHECK_LE(requested_internal_fields,
+ (JSObject::kMaxInstanceSize - header_size) >> kPointerSizeLog2);
+ *instance_size =
+ Min(header_size +
+ ((requested_internal_fields + requested_in_object_properties)
+ << kPointerSizeLog2),
+ JSObject::kMaxInstanceSize);
+ *in_object_properties = ((*instance_size - header_size) >> kPointerSizeLog2) -
+ requested_internal_fields;
+}
+
+} // namespace
+
+
+void JSFunction::CalculateInstanceSize(InstanceType instance_type,
+ int requested_internal_fields,
+ int* instance_size,
+ int* in_object_properties) {
+ CalculateInstanceSizeHelper(instance_type, requested_internal_fields,
+ shared()->expected_nof_properties(),
+ instance_size, in_object_properties);
}
-int SharedFunctionInfo::CalculateInObjectProperties() {
- return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
+void JSFunction::CalculateInstanceSizeForDerivedClass(
+ InstanceType instance_type, int requested_internal_fields,
+ int* instance_size, int* in_object_properties) {
+ Isolate* isolate = GetIsolate();
+ int expected_nof_properties = 0;
+ for (PrototypeIterator iter(isolate, this,
+ PrototypeIterator::START_AT_RECEIVER);
+ !iter.IsAtEnd(); iter.Advance()) {
+ JSFunction* func = iter.GetCurrent<JSFunction>();
+ SharedFunctionInfo* shared = func->shared();
+ expected_nof_properties += shared->expected_nof_properties();
+ if (!IsSubclassConstructor(shared->kind())) {
+ break;
+ }
+ }
+ CalculateInstanceSizeHelper(instance_type, requested_internal_fields,
+ expected_nof_properties, instance_size,
+ in_object_properties);
}
@@ -10517,12 +13756,8 @@
DCHECK(reason != kNoReason);
set_optimization_disabled(true);
set_disable_optimization_reason(reason);
- // Code should be the lazy compilation stub or else unoptimized. If the
- // latter, disable optimization for the code too.
+ // Code should be the lazy compilation stub or else unoptimized.
DCHECK(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN);
- if (code()->kind() == Code::FUNCTION) {
- code()->set_optimizable(false);
- }
PROFILE(GetIsolate(), CodeDisableOptEvent(code(), this));
if (FLAG_trace_opt) {
PrintF("[disabled optimization for ");
@@ -10532,6 +13767,39 @@
}
+void SharedFunctionInfo::InitFromFunctionLiteral(
+ Handle<SharedFunctionInfo> shared_info, FunctionLiteral* lit) {
+ shared_info->set_length(lit->scope()->default_function_length());
+ shared_info->set_internal_formal_parameter_count(lit->parameter_count());
+ shared_info->set_function_token_position(lit->function_token_position());
+ shared_info->set_start_position(lit->start_position());
+ shared_info->set_end_position(lit->end_position());
+ shared_info->set_is_expression(lit->is_expression());
+ shared_info->set_is_anonymous(lit->is_anonymous());
+ shared_info->set_inferred_name(*lit->inferred_name());
+ shared_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
+ shared_info->set_allows_lazy_compilation_without_context(
+ lit->AllowsLazyCompilationWithoutContext());
+ shared_info->set_language_mode(lit->language_mode());
+ shared_info->set_uses_arguments(lit->scope()->arguments() != NULL);
+ shared_info->set_has_duplicate_parameters(lit->has_duplicate_parameters());
+ shared_info->set_ast_node_count(lit->ast_node_count());
+ shared_info->set_is_function(lit->is_function());
+ if (lit->dont_optimize_reason() != kNoReason) {
+ shared_info->DisableOptimization(lit->dont_optimize_reason());
+ }
+ shared_info->set_dont_crankshaft(lit->flags() &
+ AstProperties::kDontCrankshaft);
+ shared_info->set_kind(lit->kind());
+ if (!IsConstructable(lit->kind(), lit->language_mode())) {
+ shared_info->set_construct_stub(
+ *shared_info->GetIsolate()->builtins()->ConstructedNonConstructable());
+ }
+ shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject());
+ shared_info->set_asm_function(lit->scope()->asm_function());
+}
+
+
bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) {
DCHECK(!id.IsNone());
Code* unoptimized = code();
@@ -10543,18 +13811,16 @@
}
-void JSFunction::StartInobjectSlackTracking() {
- DCHECK(has_initial_map() && !IsInobjectSlackTrackingInProgress());
-
- Map* map = initial_map();
+void Map::StartInobjectSlackTracking() {
+ DCHECK(!IsInobjectSlackTrackingInProgress());
// No tracking during the snapshot construction phase.
Isolate* isolate = GetIsolate();
if (isolate->serializer_enabled()) return;
- if (map->unused_property_fields() == 0) return;
+ if (unused_property_fields() == 0) return;
- map->set_counter(Map::kSlackTrackingCounterStart);
+ set_construction_counter(Map::kSlackTrackingCounterStart);
}
@@ -10570,7 +13836,6 @@
opt_count() >= FLAG_max_opt_count) {
// Re-enable optimizations if they were disabled due to opt_count limit.
set_optimization_disabled(false);
- code()->set_optimizable(true);
}
set_opt_count(0);
set_deopt_count(0);
@@ -10578,67 +13843,66 @@
}
-static void GetMinInobjectSlack(Map* map, void* data) {
- int slack = map->unused_property_fields();
- if (*reinterpret_cast<int*>(data) > slack) {
- *reinterpret_cast<int*>(data) = slack;
- }
-}
-
-
-static void ShrinkInstanceSize(Map* map, void* data) {
- int slack = *reinterpret_cast<int*>(data);
- map->set_inobject_properties(map->inobject_properties() - slack);
- map->set_unused_property_fields(map->unused_property_fields() - slack);
- 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));
-}
-
-
-void JSFunction::CompleteInobjectSlackTracking() {
- DCHECK(has_initial_map());
- Map* map = initial_map();
-
- DCHECK(map->counter() >= Map::kSlackTrackingCounterEnd - 1);
- map->set_counter(Map::kRetainingCounterStart);
-
- int slack = map->unused_property_fields();
- map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
- if (slack != 0) {
- // Resize the initial map and all maps in its transition tree.
- map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
- }
-}
-
-
-int 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());
- if (!FLAG_cache_optimized_code) return -1;
- Object* value = optimized_code_map();
- if (!value->IsSmi()) {
- FixedArray* optimized_code_map = FixedArray::cast(value);
+ if (!OptimizedCodeMapIsCleared()) {
+ FixedArray* optimized_code_map = this->optimized_code_map();
int length = optimized_code_map->length();
Smi* osr_ast_id_smi = Smi::FromInt(osr_ast_id.ToInt());
for (int i = kEntriesStart; i < length; i += kEntryLength) {
- if (optimized_code_map->get(i + kContextOffset) == native_context &&
+ if (WeakCell::cast(optimized_code_map->get(i + kContextOffset))
+ ->value() == native_context &&
optimized_code_map->get(i + kOsrAstIdOffset) == osr_ast_id_smi) {
- return i + kCachedCodeOffset;
+ return i;
}
}
- if (FLAG_trace_opt) {
- PrintF("[didn't find optimized code in optimized code map for ");
- ShortPrint();
- PrintF("]\n");
+ Object* shared_code =
+ WeakCell::cast(optimized_code_map->get(kSharedCodeIndex))->value();
+ if (shared_code->IsCode() && osr_ast_id.IsNone()) {
+ return kSharedCodeIndex;
}
}
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 = optimized_code_map();
+ if (entry == kSharedCodeIndex) {
+ // We know the weak cell isn't cleared because we made sure of it in
+ // SearchOptimizedCodeMapEntry and performed no allocations since that
+ // call.
+ result = {
+ Code::cast(WeakCell::cast(code_map->get(kSharedCodeIndex))->value()),
+ nullptr};
+ } else {
+ DCHECK_LE(entry + kEntryLength, code_map->length());
+ WeakCell* cell = WeakCell::cast(code_map->get(entry + kCachedCodeOffset));
+ WeakCell* literals_cell =
+ WeakCell::cast(code_map->get(entry + kLiteralsOffset));
+
+ result = {cell->cleared() ? nullptr : Code::cast(cell->value()),
+ literals_cell->cleared()
+ ? nullptr
+ : LiteralsArray::cast(literals_cell->value())};
+ }
+ }
+ if (FLAG_trace_opt && !OptimizedCodeMapIsCleared() &&
+ result.code == nullptr) {
+ PrintF("[didn't find optimized code in optimized code map for ");
+ ShortPrint();
+ PrintF("]\n");
+ }
+ return result;
+}
+
+
#define DECLARE_TAG(ignore1, name, ignore2) name,
const char* const VisitorSynchronization::kTags[
VisitorSynchronization::kNumberOfSyncTags] = {
@@ -10695,11 +13959,9 @@
void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
- DCHECK((RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
- rinfo->IsPatchedDebugBreakSlotSequence()));
- Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
+ DCHECK(RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence());
+ Object* target = Code::GetCodeFromTargetAddress(rinfo->debug_call_address());
Object* old_target = target;
VisitPointer(&target);
CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
@@ -10714,7 +13976,7 @@
void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) {
- Address p = rinfo->target_reference();
+ Address p = rinfo->target_external_reference();
VisitExternalReference(&p);
}
@@ -10743,9 +14005,9 @@
void Code::Relocate(intptr_t delta) {
for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
- it.rinfo()->apply(delta, SKIP_ICACHE_FLUSH);
+ it.rinfo()->apply(delta);
}
- CpuFeatures::FlushICache(instruction_start(), instruction_size());
+ Assembler::FlushICache(GetIsolate(), instruction_start(), instruction_size());
}
@@ -10796,10 +14058,10 @@
Code* code = Code::cast(*p);
it.rinfo()->set_code_age_stub(code, SKIP_ICACHE_FLUSH);
} else {
- it.rinfo()->apply(delta, SKIP_ICACHE_FLUSH);
+ it.rinfo()->apply(delta);
}
}
- CpuFeatures::FlushICache(instruction_start(), instruction_size());
+ Assembler::FlushICache(GetIsolate(), instruction_start(), instruction_size());
}
@@ -11030,7 +14292,6 @@
void Code::ClearInlineCaches(Code::Kind* kind) {
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
- RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) |
RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
for (RelocIterator it(this, mask); !it.done(); it.next()) {
RelocInfo* info = it.rinfo();
@@ -11050,6 +14311,11 @@
}
+void SharedFunctionInfo::ClearTypeFeedbackInfoAtGCTime() {
+ feedback_vector()->ClearSlotsAtGCTime(this);
+}
+
+
BailoutId Code::TranslatePcOffsetToAstId(uint32_t pc_offset) {
DisallowHeapAllocation no_gc;
DCHECK(kind() == FUNCTION);
@@ -11084,15 +14350,25 @@
}
-static Code::Age EffectiveAge(Code::Age age) {
- if (age == Code::kNotExecutedCodeAge) {
- // Treat that's never been executed as old immediately.
- age = Code::kIsOldCodeAge;
- } else if (age == Code::kExecutedOnceCodeAge) {
- // Pre-age code that has only been executed once.
- age = Code::kPreAgedCodeAge;
+// NextAge defines the Code::Age state transitions during a GC cycle.
+static Code::Age NextAge(Code::Age age) {
+ switch (age) {
+ case Code::kNotExecutedCodeAge: // Keep, until we've been executed.
+ case Code::kToBeExecutedOnceCodeAge: // Keep, until we've been executed.
+ case Code::kLastCodeAge: // Clamp at last Code::Age value.
+ return age;
+ case Code::kExecutedOnceCodeAge:
+ // Pre-age code that has only been executed once.
+ return static_cast<Code::Age>(Code::kPreAgedCodeAge + 1);
+ default:
+ return static_cast<Code::Age>(age + 1); // Default case: Increase age.
}
- return age;
+}
+
+
+// IsOldAge defines the collection criteria for a Code object.
+static bool IsOldAge(Code::Age age) {
+ return age >= Code::kIsOldCodeAge || age == Code::kNotExecutedCodeAge;
}
@@ -11102,6 +14378,15 @@
}
+void Code::MarkToBeExecutedOnce(Isolate* isolate) {
+ byte* sequence = FindCodeAgeSequence();
+ if (sequence != NULL) {
+ PatchPlatformCodeAge(isolate, sequence, kToBeExecutedOnceCodeAge,
+ NO_MARKING_PARITY);
+ }
+}
+
+
void Code::MakeOlder(MarkingParity current_parity) {
byte* sequence = FindCodeAgeSequence();
if (sequence != NULL) {
@@ -11109,19 +14394,16 @@
MarkingParity code_parity;
Isolate* isolate = GetIsolate();
GetCodeAgeAndParity(isolate, sequence, &age, &code_parity);
- age = EffectiveAge(age);
- if (age != kLastCodeAge && code_parity != current_parity) {
- PatchPlatformCodeAge(isolate,
- sequence,
- static_cast<Age>(age + 1),
- current_parity);
+ Age next_age = NextAge(age);
+ if (age != next_age && code_parity != current_parity) {
+ PatchPlatformCodeAge(isolate, sequence, next_age, current_parity);
}
}
}
bool Code::IsOld() {
- return GetAge() >= kIsOldCodeAge;
+ return IsOldAge(GetAge());
}
@@ -11136,11 +14418,6 @@
Code::Age Code::GetAge() {
- return EffectiveAge(GetRawAge());
-}
-
-
-Code::Age Code::GetRawAge() {
byte* sequence = FindCodeAgeSequence();
if (sequence == NULL) {
return kNoAgeCodeAge;
@@ -11184,6 +14461,12 @@
*parity = NO_MARKING_PARITY;
return;
}
+ stub = *builtins->MarkCodeAsToBeExecutedOnce();
+ if (code == stub) {
+ *age = kToBeExecutedOnceCodeAge;
+ *parity = NO_MARKING_PARITY;
+ return;
+ }
UNREACHABLE();
}
@@ -11208,6 +14491,10 @@
DCHECK(parity == NO_MARKING_PARITY);
return *builtins->MarkCodeAsExecutedTwice();
}
+ case kToBeExecutedOnceCodeAge: {
+ DCHECK(parity == NO_MARKING_PARITY);
+ return *builtins->MarkCodeAsToBeExecutedOnce();
+ }
default:
UNREACHABLE();
break;
@@ -11216,25 +14503,17 @@
}
-void Code::PrintDeoptLocation(FILE* out, int bailout_id) {
- const char* last_comment = NULL;
- int mask = RelocInfo::ModeMask(RelocInfo::COMMENT)
- | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
- for (RelocIterator it(this, mask); !it.done(); it.next()) {
- RelocInfo* info = it.rinfo();
- if (info->rmode() == RelocInfo::COMMENT) {
- last_comment = reinterpret_cast<const char*>(info->data());
- } else if (last_comment != NULL) {
- if ((bailout_id == Deoptimizer::GetDeoptimizationId(
- GetIsolate(), info->target_address(), Deoptimizer::EAGER)) ||
- (bailout_id == Deoptimizer::GetDeoptimizationId(
- GetIsolate(), info->target_address(), Deoptimizer::SOFT)) ||
- (bailout_id == Deoptimizer::GetDeoptimizationId(
- GetIsolate(), info->target_address(), Deoptimizer::LAZY))) {
- CHECK(RelocInfo::IsRuntimeEntry(info->rmode()));
- PrintF(out, " %s\n", last_comment);
- return;
- }
+void Code::PrintDeoptLocation(FILE* out, Address pc) {
+ Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(this, pc);
+ class SourcePosition pos = info.position;
+ if (info.deopt_reason != Deoptimizer::kNoReason || !pos.IsUnknown()) {
+ if (FLAG_hydrogen_track_positions) {
+ PrintF(out, " ;;; deoptimize at %d_%d: %s\n",
+ pos.inlining_id(), pos.position(),
+ Deoptimizer::GetDeoptReason(info.deopt_reason));
+ } else {
+ PrintF(out, " ;;; deoptimize at %d: %s\n", pos.raw(),
+ Deoptimizer::GetDeoptReason(info.deopt_reason));
}
}
}
@@ -11266,11 +14545,41 @@
}
+Handle<WeakCell> Code::WeakCellFor(Handle<Code> code) {
+ DCHECK(code->kind() == OPTIMIZED_FUNCTION);
+ WeakCell* raw_cell = code->CachedWeakCell();
+ if (raw_cell != NULL) return Handle<WeakCell>(raw_cell);
+ Handle<WeakCell> cell = code->GetIsolate()->factory()->NewWeakCell(code);
+ DeoptimizationInputData::cast(code->deoptimization_data())
+ ->SetWeakCellCache(*cell);
+ return cell;
+}
+
+
+WeakCell* Code::CachedWeakCell() {
+ DCHECK(kind() == OPTIMIZED_FUNCTION);
+ Object* weak_cell_cache =
+ DeoptimizationInputData::cast(deoptimization_data())->WeakCellCache();
+ if (weak_cell_cache->IsWeakCell()) {
+ DCHECK(this == WeakCell::cast(weak_cell_cache)->value());
+ return WeakCell::cast(weak_cell_cache);
+ }
+ return NULL;
+}
+
+
#ifdef ENABLE_DISASSEMBLER
void DeoptimizationInputData::DeoptimizationInputDataPrint(
std::ostream& os) { // NOLINT
disasm::NameConverter converter;
+ int const inlined_function_count = InlinedFunctionCount()->value();
+ os << "Inlined functions (count = " << inlined_function_count << ")\n";
+ for (int id = 0; id < inlined_function_count; ++id) {
+ Object* info = LiteralArray()->get(id);
+ os << " " << Brief(SharedFunctionInfo::cast(info)) << "\n";
+ }
+ os << "\n";
int deopt_count = DeoptCount();
os << "Deoptimization Input Data (deopt points = " << deopt_count << ")\n";
if (0 != deopt_count) {
@@ -11279,11 +14588,9 @@
os << "\n";
}
for (int i = 0; i < deopt_count; i++) {
- // TODO(svenpanne) Add some basic formatting to our streams.
- Vector<char> buf1 = Vector<char>::New(128);
- SNPrintF(buf1, "%6d %6d %6d %6d", i, AstId(i).ToInt(),
- ArgumentsStackHeight(i)->value(), Pc(i)->value());
- os << buf1.start();
+ os << std::setw(6) << i << " " << std::setw(6) << AstId(i).ToInt() << " "
+ << std::setw(6) << ArgumentsStackHeight(i)->value() << " "
+ << std::setw(6) << Pc(i)->value();
if (!FLAG_print_code_verbose) {
os << "\n";
@@ -11304,9 +14611,7 @@
while (iterator.HasNext() &&
Translation::BEGIN !=
(opcode = static_cast<Translation::Opcode>(iterator.Next()))) {
- Vector<char> buf2 = Vector<char>::New(128);
- SNPrintF(buf2, "%27s %s ", "", Translation::StringFor(opcode));
- os << buf2.start();
+ os << std::setw(31) << " " << Translation::StringFor(opcode) << " ";
switch (opcode) {
case Translation::BEGIN:
@@ -11315,16 +14620,28 @@
case Translation::JS_FRAME: {
int ast_id = iterator.Next();
- int function_id = iterator.Next();
+ int shared_info_id = iterator.Next();
unsigned height = iterator.Next();
- os << "{ast_id=" << ast_id << ", function=";
- if (function_id != Translation::kSelfLiteralId) {
- Object* function = LiteralArray()->get(function_id);
- os << Brief(JSFunction::cast(function)->shared()->DebugName());
- } else {
- os << "<self>";
- }
- os << ", height=" << height << "}";
+ Object* shared_info = LiteralArray()->get(shared_info_id);
+ os << "{ast_id=" << ast_id << ", function="
+ << Brief(SharedFunctionInfo::cast(shared_info)->DebugName())
+ << ", height=" << height << "}";
+ break;
+ }
+
+ case Translation::INTERPRETED_FRAME: {
+ int bytecode_offset = iterator.Next();
+ int shared_info_id = iterator.Next();
+ unsigned height = iterator.Next();
+ Object* shared_info = LiteralArray()->get(shared_info_id);
+ os << "{bytecode_offset=" << bytecode_offset << ", function="
+ << Brief(SharedFunctionInfo::cast(shared_info)->DebugName())
+ << ", height=" << height << "}";
+ break;
+ }
+
+ case Translation::JS_FRAME_FUNCTION: {
+ os << "{function}";
break;
}
@@ -11336,21 +14653,21 @@
case Translation::ARGUMENTS_ADAPTOR_FRAME:
case Translation::CONSTRUCT_STUB_FRAME: {
- int function_id = iterator.Next();
- JSFunction* function =
- JSFunction::cast(LiteralArray()->get(function_id));
+ int shared_info_id = iterator.Next();
+ Object* shared_info = LiteralArray()->get(shared_info_id);
unsigned height = iterator.Next();
- os << "{function=" << Brief(function->shared()->DebugName())
+ os << "{function="
+ << Brief(SharedFunctionInfo::cast(shared_info)->DebugName())
<< ", height=" << height << "}";
break;
}
case Translation::GETTER_STUB_FRAME:
case Translation::SETTER_STUB_FRAME: {
- int function_id = iterator.Next();
- JSFunction* function =
- JSFunction::cast(LiteralArray()->get(function_id));
- os << "{function=" << Brief(function->shared()->DebugName()) << "}";
+ int shared_info_id = iterator.Next();
+ Object* shared_info = LiteralArray()->get(shared_info_id);
+ os << "{function=" << Brief(SharedFunctionInfo::cast(shared_info)
+ ->DebugName()) << "}";
break;
}
@@ -11373,9 +14690,16 @@
break;
}
+ case Translation::BOOL_REGISTER: {
+ int reg_code = iterator.Next();
+ os << "{input=" << converter.NameOfCPURegister(reg_code)
+ << " (bool)}";
+ break;
+ }
+
case Translation::DOUBLE_REGISTER: {
int reg_code = iterator.Next();
- os << "{input=" << DoubleRegister::AllocationIndexToString(reg_code)
+ os << "{input=" << DoubleRegister::from_code(reg_code).ToString()
<< "}";
break;
}
@@ -11398,6 +14722,12 @@
break;
}
+ case Translation::BOOL_STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ os << "{input=" << input_slot_index << " (bool)}";
+ break;
+ }
+
case Translation::DOUBLE_STACK_SLOT: {
int input_slot_index = iterator.Next();
os << "{input=" << input_slot_index << "}";
@@ -11405,8 +14735,10 @@
}
case Translation::LITERAL: {
- unsigned literal_index = iterator.Next();
- os << "{literal_id=" << literal_index << "}";
+ int literal_index = iterator.Next();
+ Object* literal_value = LiteralArray()->get(literal_index);
+ os << "{literal_id=" << literal_index << " (" << Brief(literal_value)
+ << ")}";
break;
}
@@ -11438,13 +14770,39 @@
os << "ast id pc state\n";
for (int i = 0; i < this->DeoptPoints(); i++) {
int pc_and_state = this->PcAndState(i)->value();
- // TODO(svenpanne) Add some basic formatting to our streams.
- Vector<char> buf = Vector<char>::New(100);
- SNPrintF(buf, "%6d %8d %s\n", this->AstId(i).ToInt(),
- FullCodeGenerator::PcField::decode(pc_and_state),
- FullCodeGenerator::State2String(
- FullCodeGenerator::StateField::decode(pc_and_state)));
- os << buf.start();
+ os << std::setw(6) << this->AstId(i).ToInt() << " " << std::setw(8)
+ << FullCodeGenerator::PcField::decode(pc_and_state) << " "
+ << FullCodeGenerator::State2String(
+ FullCodeGenerator::StateField::decode(pc_and_state)) << "\n";
+ }
+}
+
+
+void HandlerTable::HandlerTableRangePrint(std::ostream& os) {
+ os << " from to hdlr\n";
+ for (int i = 0; i < length(); i += kRangeEntrySize) {
+ int pc_start = Smi::cast(get(i + kRangeStartIndex))->value();
+ int pc_end = Smi::cast(get(i + kRangeEndIndex))->value();
+ int handler_field = Smi::cast(get(i + kRangeHandlerIndex))->value();
+ int handler_offset = HandlerOffsetField::decode(handler_field);
+ CatchPrediction prediction = HandlerPredictionField::decode(handler_field);
+ int depth = Smi::cast(get(i + kRangeDepthIndex))->value();
+ os << " (" << std::setw(4) << pc_start << "," << std::setw(4) << pc_end
+ << ") -> " << std::setw(4) << handler_offset
+ << " (prediction=" << prediction << ", depth=" << depth << ")\n";
+ }
+}
+
+
+void HandlerTable::HandlerTableReturnPrint(std::ostream& os) {
+ os << " off hdlr (c)\n";
+ for (int i = 0; i < length(); i += kReturnEntrySize) {
+ int pc_offset = Smi::cast(get(i + kReturnOffsetIndex))->value();
+ int handler_field = Smi::cast(get(i + kReturnHandlerIndex))->value();
+ int handler_offset = HandlerOffsetField::decode(handler_field);
+ CatchPrediction prediction = HandlerPredictionField::decode(handler_field);
+ os << " " << std::setw(4) << pc_offset << " -> " << std::setw(4)
+ << handler_offset << " (prediction=" << prediction << ")\n";
}
}
@@ -11460,8 +14818,6 @@
case MEGAMORPHIC: return "MEGAMORPHIC";
case GENERIC: return "GENERIC";
case DEBUG_STUB: return "DEBUG_STUB";
- case DEFAULT:
- return "DEFAULT";
}
UNREACHABLE();
return NULL;
@@ -11481,7 +14837,8 @@
void Code::PrintExtraICState(std::ostream& os, // NOLINT
Kind kind, ExtraICState extra) {
os << "extra_ic_state = ";
- if ((kind == STORE_IC || kind == KEYED_STORE_IC) && (extra == STRICT)) {
+ if ((kind == STORE_IC || kind == KEYED_STORE_IC) &&
+ is_strict(static_cast<LanguageMode>(extra))) {
os << "STRICT\n";
} else {
os << extra << "\n";
@@ -11492,7 +14849,7 @@
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()) {
@@ -11512,25 +14869,53 @@
}
if ((name != NULL) && (name[0] != '\0')) {
os << "name = " << name << "\n";
+ } else if (kind() == BUILTIN) {
+ name = GetIsolate()->builtins()->Lookup(instruction_start());
+ if (name != NULL) {
+ os << "name = " << name << "\n";
+ }
}
if (kind() == OPTIMIZED_FUNCTION) {
os << "stack_slots = " << stack_slots() << "\n";
}
+ os << "compiler = " << (is_turbofanned()
+ ? "turbofan"
+ : is_crankshafted() ? "crankshaft"
+ : kind() == Code::FUNCTION
+ ? "full-codegen"
+ : "unknown") << "\n";
os << "Instructions (size = " << instruction_size() << ")\n";
{
Isolate* isolate = GetIsolate();
- int decode_size = is_crankshafted()
- ? static_cast<int>(safepoint_table_offset())
- : instruction_size();
- // If there might be a back edge table, stop before reaching it.
- if (kind() == Code::FUNCTION) {
- decode_size =
- Min(decode_size, static_cast<int>(back_edge_table_offset()));
- }
+ int size = instruction_size();
+ int safepoint_offset =
+ is_crankshafted() ? static_cast<int>(safepoint_table_offset()) : size;
+ int back_edge_offset = (kind() == Code::FUNCTION)
+ ? static_cast<int>(back_edge_table_offset())
+ : size;
+ int constant_pool_offset = FLAG_enable_embedded_constant_pool
+ ? this->constant_pool_offset()
+ : size;
+
+ // Stop before reaching any embedded tables
+ int code_size = Min(safepoint_offset, back_edge_offset);
+ code_size = Min(code_size, constant_pool_offset);
byte* begin = instruction_start();
- byte* end = begin + decode_size;
+ byte* end = begin + code_size;
Disassembler::Decode(isolate, &os, begin, end, this);
+
+ if (constant_pool_offset < size) {
+ int constant_pool_size = size - constant_pool_offset;
+ DCHECK((constant_pool_size & kPointerAlignmentMask) == 0);
+ os << "\nConstant Pool (size = " << constant_pool_size << ")\n";
+ Vector<char> buf = Vector<char>::New(50);
+ intptr_t* ptr = reinterpret_cast<intptr_t*>(begin + constant_pool_offset);
+ for (int i = 0; i < constant_pool_size; i += kPointerSize, ptr++) {
+ SNPrintF(buf, "%4d %08" V8PRIxPTR, i, *ptr);
+ os << static_cast<const void*>(ptr) << " " << buf.start() << "\n";
+ }
+ }
}
os << "\n";
@@ -11551,17 +14936,12 @@
for (unsigned i = 0; i < table.length(); i++) {
unsigned pc_offset = table.GetPcOffset(i);
os << static_cast<const void*>(instruction_start() + pc_offset) << " ";
- // TODO(svenpanne) Add some basic formatting to our streams.
- Vector<char> buf1 = Vector<char>::New(30);
- SNPrintF(buf1, "%4d", pc_offset);
- os << buf1.start() << " ";
+ os << std::setw(4) << pc_offset << " ";
table.PrintEntry(i, os);
os << " (sp -> fp) ";
SafepointEntry entry = table.GetEntry(i);
if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
- Vector<char> buf2 = Vector<char>::New(30);
- SNPrintF(buf2, "%6d", entry.deoptimization_index());
- os << buf2.start();
+ os << std::setw(6) << entry.deoptimization_index();
} else {
os << "<none>";
}
@@ -11583,10 +14963,9 @@
os << "ast_id pc_offset loop_depth\n";
for (uint32_t i = 0; i < back_edges.length(); i++) {
- Vector<char> buf = Vector<char>::New(100);
- SNPrintF(buf, "%6d %9u %10u\n", back_edges.ast_id(i).ToInt(),
- back_edges.pc_offset(i), back_edges.loop_depth(i));
- os << buf.start();
+ os << std::setw(6) << back_edges.ast_id(i).ToInt() << " "
+ << std::setw(9) << back_edges.pc_offset(i) << " " << std::setw(10)
+ << back_edges.loop_depth(i) << "\n";
}
os << "\n";
@@ -11600,123 +14979,65 @@
#endif
}
+ if (handler_table()->length() > 0) {
+ os << "Handler Table (size = " << handler_table()->Size() << ")\n";
+ if (kind() == FUNCTION) {
+ HandlerTable::cast(handler_table())->HandlerTableRangePrint(os);
+ } else if (kind() == OPTIMIZED_FUNCTION) {
+ HandlerTable::cast(handler_table())->HandlerTableReturnPrint(os);
+ }
+ os << "\n";
+ }
+
os << "RelocInfo (size = " << relocation_size() << ")\n";
for (RelocIterator it(this); !it.done(); it.next()) {
it.rinfo()->Print(GetIsolate(), os);
}
os << "\n";
-
-#ifdef OBJECT_PRINT
- if (FLAG_enable_ool_constant_pool) {
- ConstantPoolArray* pool = constant_pool();
- if (pool->length()) {
- os << "Constant Pool\n";
- pool->Print(os);
- os << "\n";
- }
- }
-#endif
}
#endif // ENABLE_DISASSEMBLER
-Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
- Handle<JSObject> object,
- int capacity,
- int length,
- SetFastElementsCapacitySmiMode smi_mode) {
- // We should never end in here with a pixel or external array.
- DCHECK(!object->HasExternalArrayElements());
+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);
- // Allocate a new fast elements backing store.
- Handle<FixedArray> new_elements =
- object->GetIsolate()->factory()->NewUninitializedFixedArray(capacity);
+ const uint8_t* first_bytecode_address = GetFirstBytecodeAddress();
+ int bytecode_size = 0;
+ for (int i = 0; i < this->length(); i += bytecode_size) {
+ const uint8_t* bytecode_start = &first_bytecode_address[i];
+ interpreter::Bytecode bytecode =
+ interpreter::Bytecodes::FromByte(bytecode_start[0]);
+ bytecode_size = interpreter::Bytecodes::Size(bytecode);
- ElementsKind elements_kind = object->GetElementsKind();
- ElementsKind new_elements_kind;
- // The resized array has FAST_*_SMI_ELEMENTS if the capacity mode forces it,
- // or if it's allowed and the old elements array contained only SMIs.
- bool has_fast_smi_elements =
- (smi_mode == kForceSmiElements) ||
- ((smi_mode == kAllowSmiElements) && object->HasFastSmiElements());
- if (has_fast_smi_elements) {
- if (IsHoleyElementsKind(elements_kind)) {
- new_elements_kind = FAST_HOLEY_SMI_ELEMENTS;
- } else {
- new_elements_kind = FAST_SMI_ELEMENTS;
+ SNPrintF(buf, "%p", bytecode_start);
+ os << buf.start() << " : ";
+ interpreter::Bytecodes::Decode(os, bytecode_start, parameter_count());
+
+ if (interpreter::Bytecodes::IsJumpConstantWide(bytecode)) {
+ DCHECK_EQ(bytecode_size, 3);
+ int index = static_cast<int>(ReadUnalignedUInt16(bytecode_start + 1));
+ int offset = Smi::cast(constant_pool()->get(index))->value();
+ SNPrintF(buf, " (%p)", bytecode_start + offset);
+ os << buf.start();
+ } else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
+ DCHECK_EQ(bytecode_size, 2);
+ int index = static_cast<int>(bytecode_start[1]);
+ int offset = Smi::cast(constant_pool()->get(index))->value();
+ SNPrintF(buf, " (%p)", bytecode_start + offset);
+ os << buf.start();
+ } else if (interpreter::Bytecodes::IsJump(bytecode)) {
+ DCHECK_EQ(bytecode_size, 2);
+ int offset = static_cast<int8_t>(bytecode_start[1]);
+ SNPrintF(buf, " (%p)", bytecode_start + offset);
+ os << buf.start();
}
- } else {
- if (IsHoleyElementsKind(elements_kind)) {
- new_elements_kind = FAST_HOLEY_ELEMENTS;
- } else {
- new_elements_kind = FAST_ELEMENTS;
- }
- }
- Handle<FixedArrayBase> old_elements(object->elements());
- ElementsAccessor* accessor = ElementsAccessor::ForKind(new_elements_kind);
- accessor->CopyElements(object, new_elements, elements_kind);
-
- if (elements_kind != SLOPPY_ARGUMENTS_ELEMENTS) {
- Handle<Map> new_map = (new_elements_kind != elements_kind)
- ? GetElementsTransitionMap(object, new_elements_kind)
- : handle(object->map());
- JSObject::ValidateElements(object);
- JSObject::SetMapAndElements(object, new_map, new_elements);
-
- // Transition through the allocation site as well if present.
- JSObject::UpdateAllocationSite(object, new_elements_kind);
- } else {
- Handle<FixedArray> parameter_map = Handle<FixedArray>::cast(old_elements);
- parameter_map->set(1, *new_elements);
+ os << "\n";
}
- if (FLAG_trace_elements_transitions) {
- PrintElementsTransition(stdout, object, elements_kind, old_elements,
- object->GetElementsKind(), new_elements);
- }
-
- if (object->IsJSArray()) {
- Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
- }
- return new_elements;
-}
-
-
-void JSObject::SetFastDoubleElementsCapacityAndLength(Handle<JSObject> object,
- int capacity,
- int length) {
- // We should never end in here with a pixel or external array.
- DCHECK(!object->HasExternalArrayElements());
-
- Handle<FixedArrayBase> elems =
- object->GetIsolate()->factory()->NewFixedDoubleArray(capacity);
-
- ElementsKind elements_kind = object->GetElementsKind();
- CHECK(elements_kind != SLOPPY_ARGUMENTS_ELEMENTS);
- ElementsKind new_elements_kind = elements_kind;
- if (IsHoleyElementsKind(elements_kind)) {
- new_elements_kind = FAST_HOLEY_DOUBLE_ELEMENTS;
- } else {
- new_elements_kind = FAST_DOUBLE_ELEMENTS;
- }
-
- Handle<Map> new_map = GetElementsTransitionMap(object, new_elements_kind);
-
- Handle<FixedArrayBase> old_elements(object->elements());
- ElementsAccessor* accessor = ElementsAccessor::ForKind(FAST_DOUBLE_ELEMENTS);
- accessor->CopyElements(object, elems, elements_kind);
-
- JSObject::ValidateElements(object);
- JSObject::SetMapAndElements(object, new_map, elems);
-
- if (FLAG_trace_elements_transitions) {
- PrintElementsTransition(stdout, object, elements_kind, old_elements,
- object->GetElementsKind(), elems);
- }
-
- if (object->IsJSArray()) {
- Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
- }
+ os << "Constant pool (size = " << constant_pool()->length() << ")\n";
+ constant_pool()->Print();
}
@@ -11728,96 +15049,43 @@
}
-void JSArray::Expand(Handle<JSArray> array, int required_size) {
- ElementsAccessor* accessor = array->GetElementsAccessor();
- accessor->SetCapacityAndLength(array, required_size, required_size);
-}
-
-
-// Returns false if the passed-in index is marked non-configurable,
-// which will cause the ES5 truncation operation to halt, and thus
-// no further old values need be collected.
+// Returns false if the passed-in index is marked non-configurable, which will
+// cause the truncation operation to halt, and thus no further old values need
+// be collected.
static bool GetOldValue(Isolate* isolate,
Handle<JSObject> object,
uint32_t index,
List<Handle<Object> >* old_values,
List<uint32_t>* indices) {
- Maybe<PropertyAttributes> maybe =
- JSReceiver::GetOwnElementAttribute(object, index);
- DCHECK(maybe.has_value);
- DCHECK(maybe.value != ABSENT);
- if (maybe.value == DONT_DELETE) return false;
- Handle<Object> value;
- if (!JSObject::GetOwnElementAccessorPair(object, index).is_null()) {
- value = Handle<Object>::cast(isolate->factory()->the_hole_value());
- } else {
- value = Object::GetElement(isolate, object, index).ToHandleChecked();
- }
+ LookupIterator it(isolate, object, index, LookupIterator::HIDDEN);
+ CHECK(JSReceiver::GetPropertyAttributes(&it).IsJust());
+ DCHECK(it.IsFound());
+ if (!it.IsConfigurable()) return false;
+ Handle<Object> value =
+ it.state() == LookupIterator::ACCESSOR
+ ? Handle<Object>::cast(isolate->factory()->the_hole_value())
+ : JSReceiver::GetDataProperty(&it);
old_values->Add(value);
indices->Add(index);
return true;
}
-MUST_USE_RESULT static MaybeHandle<Object> EnqueueSpliceRecord(
- Handle<JSArray> object, uint32_t index, Handle<JSArray> deleted,
- uint32_t add_count) {
- Isolate* isolate = object->GetIsolate();
- HandleScope scope(isolate);
- Handle<Object> index_object = isolate->factory()->NewNumberFromUint(index);
- Handle<Object> add_count_object =
- isolate->factory()->NewNumberFromUint(add_count);
- Handle<Object> args[] =
- { object, index_object, deleted, add_count_object };
-
- return Execution::Call(
- isolate, Handle<JSFunction>(isolate->observers_enqueue_splice()),
- isolate->factory()->undefined_value(), arraysize(args), args);
-}
-
-
-MUST_USE_RESULT static MaybeHandle<Object> BeginPerformSplice(
- Handle<JSArray> object) {
- Isolate* isolate = object->GetIsolate();
- HandleScope scope(isolate);
- Handle<Object> args[] = { object };
-
- return Execution::Call(
- isolate, Handle<JSFunction>(isolate->observers_begin_perform_splice()),
- isolate->factory()->undefined_value(), arraysize(args), args);
-}
-
-
-MUST_USE_RESULT static MaybeHandle<Object> EndPerformSplice(
- Handle<JSArray> object) {
- Isolate* isolate = object->GetIsolate();
- HandleScope scope(isolate);
- Handle<Object> args[] = { object };
-
- return Execution::Call(
- isolate, Handle<JSFunction>(isolate->observers_end_perform_splice()),
- isolate->factory()->undefined_value(), arraysize(args), args);
-}
-
-
-MaybeHandle<Object> JSArray::SetElementsLength(
- Handle<JSArray> array,
- Handle<Object> new_length_handle) {
- if (array->HasFastElements()) {
- // If the new array won't fit in a some non-trivial fraction of the max old
- // space size, then force it to go dictionary mode.
- int max_fast_array_size = static_cast<int>(
- (array->GetHeap()->MaxOldGenerationSize() / kDoubleSize) / 4);
- if (new_length_handle->IsNumber() &&
- NumberToInt32(*new_length_handle) >= max_fast_array_size) {
- NormalizeElements(array);
- }
- }
-
+void JSArray::SetLength(Handle<JSArray> array, uint32_t new_length) {
// We should never end in here with a pixel or external array.
- DCHECK(array->AllowsSetElementsLength());
+ DCHECK(array->AllowsSetLength());
+ if (array->SetLengthWouldNormalize(new_length)) {
+ JSObject::NormalizeElements(array);
+ }
+ array->GetElementsAccessor()->SetLength(array, new_length);
+}
+
+
+MaybeHandle<Object> JSArray::ObservableSetLength(Handle<JSArray> array,
+ uint32_t new_length) {
if (!array->map()->is_observed()) {
- return array->GetElementsAccessor()->SetLength(array, new_length_handle);
+ SetLength(array, new_length);
+ return array;
}
Isolate* isolate = array->GetIsolate();
@@ -11825,12 +15093,9 @@
List<Handle<Object> > old_values;
Handle<Object> old_length_handle(array->length(), isolate);
uint32_t old_length = 0;
- CHECK(old_length_handle->ToArrayIndex(&old_length));
- uint32_t new_length = 0;
- CHECK(new_length_handle->ToArrayIndex(&new_length));
+ CHECK(old_length_handle->ToArrayLength(&old_length));
- static const PropertyAttributes kNoAttrFilter = NONE;
- int num_elements = array->NumberOfOwnElements(kNoAttrFilter);
+ int num_elements = array->NumberOfOwnElements(ALL_PROPERTIES);
if (num_elements > 0) {
if (old_length == static_cast<uint32_t>(num_elements)) {
// Simple case for arrays without holes.
@@ -11842,7 +15107,7 @@
// TODO(rafaelw): For fast, sparse arrays, we can avoid iterating over
// the to-be-removed indices twice.
Handle<FixedArray> keys = isolate->factory()->NewFixedArray(num_elements);
- array->GetOwnElementKeys(*keys, kNoAttrFilter);
+ array->GetOwnElementKeys(*keys, ALL_PROPERTIES);
while (num_elements-- > 0) {
uint32_t index = NumberToUint32(keys->get(num_elements));
if (index < new_length) break;
@@ -11851,14 +15116,10 @@
}
}
- Handle<Object> hresult;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, hresult,
- array->GetElementsAccessor()->SetLength(array, new_length_handle),
- Object);
+ SetLength(array, new_length);
- CHECK(array->length()->ToArrayIndex(&new_length));
- if (old_length == new_length) return hresult;
+ CHECK(array->length()->ToArrayLength(&new_length));
+ if (old_length == new_length) return array;
RETURN_ON_EXCEPTION(isolate, BeginPerformSplice(array), Object);
@@ -11873,6 +15134,7 @@
old_values[i]),
Object);
}
+
RETURN_ON_EXCEPTION(isolate,
JSObject::EnqueueChangeRecord(
array, "update", isolate->factory()->length_string(),
@@ -11890,108 +15152,17 @@
// Skip deletions where the property was an accessor, leaving holes
// in the array of old values.
if (old_values[i]->IsTheHole()) continue;
- JSObject::SetOwnElement(deleted, indices[i] - index, old_values[i],
- SLOPPY).Assert();
+ JSObject::AddDataElement(deleted, indices[i] - index, old_values[i], NONE)
+ .Assert();
}
- SetProperty(deleted, isolate->factory()->length_string(),
- isolate->factory()->NewNumberFromUint(delete_count),
- STRICT).Assert();
+ JSArray::SetLength(deleted, delete_count);
}
RETURN_ON_EXCEPTION(
isolate, EnqueueSpliceRecord(array, index, deleted, add_count), Object);
- return hresult;
-}
-
-
-Handle<Map> Map::GetPrototypeTransition(Handle<Map> map,
- Handle<Object> prototype) {
- FixedArray* cache = map->GetPrototypeTransitions();
- int number_of_transitions = map->NumberOfProtoTransitions();
- const int proto_offset =
- kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset;
- const int map_offset = kProtoTransitionHeaderSize + kProtoTransitionMapOffset;
- const int step = kProtoTransitionElementsPerEntry;
- for (int i = 0; i < number_of_transitions; i++) {
- if (cache->get(proto_offset + i * step) == *prototype) {
- Object* result = cache->get(map_offset + i * step);
- return Handle<Map>(Map::cast(result));
- }
- }
- return Handle<Map>();
-}
-
-
-Handle<Map> Map::PutPrototypeTransition(Handle<Map> map,
- Handle<Object> prototype,
- Handle<Map> target_map) {
- DCHECK(target_map->IsMap());
- DCHECK(HeapObject::cast(*prototype)->map()->IsMap());
- // Don't cache prototype transition if this map is either shared, or a map of
- // a prototype.
- if (map->is_prototype_map()) return map;
- if (map->is_dictionary_map() || !FLAG_cache_prototype_transitions) return map;
-
- const int step = kProtoTransitionElementsPerEntry;
- const int header = kProtoTransitionHeaderSize;
-
- Handle<FixedArray> cache(map->GetPrototypeTransitions());
- int capacity = (cache->length() - header) / step;
- int transitions = map->NumberOfProtoTransitions() + 1;
-
- if (transitions > capacity) {
- if (capacity > kMaxCachedPrototypeTransitions) return map;
-
- // Grow array by factor 2 over and above what we need.
- cache = FixedArray::CopySize(cache, transitions * 2 * step + header);
-
- SetPrototypeTransitions(map, cache);
- }
-
- // Reload number of transitions as GC might shrink them.
- int last = map->NumberOfProtoTransitions();
- int entry = header + last * step;
-
- cache->set(entry + kProtoTransitionPrototypeOffset, *prototype);
- cache->set(entry + kProtoTransitionMapOffset, *target_map);
- map->SetNumberOfProtoTransitions(last + 1);
-
- return map;
-}
-
-
-void Map::ZapTransitions() {
- TransitionArray* transition_array = transitions();
- // TODO(mstarzinger): Temporarily use a slower version instead of the faster
- // MemsetPointer to investigate a crasher. Switch back to MemsetPointer.
- Object** data = transition_array->data_start();
- Object* the_hole = GetHeap()->the_hole_value();
- int length = transition_array->length();
- for (int i = 0; i < length; i++) {
- data[i] = the_hole;
- }
-}
-
-
-void Map::ZapPrototypeTransitions() {
- FixedArray* proto_transitions = GetPrototypeTransitions();
- MemsetPointer(proto_transitions->data_start(),
- GetHeap()->the_hole_value(),
- proto_transitions->length());
-}
-
-
-// static
-void Map::AddDependentCompilationInfo(Handle<Map> map,
- DependentCode::DependencyGroup group,
- CompilationInfo* info) {
- Handle<DependentCode> codes =
- DependentCode::Insert(handle(map->dependent_code(), info->isolate()),
- group, info->object_wrapper());
- if (*codes != map->dependent_code()) map->set_dependent_code(*codes);
- info->dependencies(group)->Add(map, info->zone());
+ return array;
}
@@ -11999,186 +15170,244 @@
void Map::AddDependentCode(Handle<Map> map,
DependentCode::DependencyGroup group,
Handle<Code> code) {
- Handle<DependentCode> codes = DependentCode::Insert(
- Handle<DependentCode>(map->dependent_code()), group, code);
+ Handle<WeakCell> cell = Code::WeakCellFor(code);
+ Handle<DependentCode> codes = DependentCode::InsertWeakCode(
+ Handle<DependentCode>(map->dependent_code()), group, cell);
if (*codes != map->dependent_code()) map->set_dependent_code(*codes);
}
-DependentCode::GroupStartIndexes::GroupStartIndexes(DependentCode* entries) {
- Recompute(entries);
+Handle<DependentCode> DependentCode::InsertCompilationDependencies(
+ Handle<DependentCode> entries, DependencyGroup group,
+ Handle<Foreign> info) {
+ return Insert(entries, group, info);
}
-void DependentCode::GroupStartIndexes::Recompute(DependentCode* entries) {
- start_indexes_[0] = 0;
- for (int g = 1; g <= kGroupCount; g++) {
- int count = entries->number_of_entries(static_cast<DependencyGroup>(g - 1));
- start_indexes_[g] = start_indexes_[g - 1] + count;
- }
-}
-
-
-DependentCode* DependentCode::ForObject(Handle<HeapObject> object,
- DependencyGroup group) {
- AllowDeferredHandleDereference dependencies_are_safe;
- if (group == DependentCode::kPropertyCellChangedGroup) {
- return Handle<PropertyCell>::cast(object)->dependent_code();
- } else if (group == DependentCode::kAllocationSiteTenuringChangedGroup ||
- group == DependentCode::kAllocationSiteTransitionChangedGroup) {
- return Handle<AllocationSite>::cast(object)->dependent_code();
- }
- return Handle<Map>::cast(object)->dependent_code();
+Handle<DependentCode> DependentCode::InsertWeakCode(
+ Handle<DependentCode> entries, DependencyGroup group,
+ Handle<WeakCell> code_cell) {
+ return Insert(entries, group, code_cell);
}
Handle<DependentCode> DependentCode::Insert(Handle<DependentCode> entries,
DependencyGroup group,
Handle<Object> object) {
- GroupStartIndexes starts(*entries);
- int start = starts.at(group);
- int end = starts.at(group + 1);
- int number_of_entries = starts.number_of_entries();
+ if (entries->length() == 0 || entries->group() > group) {
+ // There is no such group.
+ return DependentCode::New(group, object, entries);
+ }
+ if (entries->group() < group) {
+ // The group comes later in the list.
+ Handle<DependentCode> old_next(entries->next_link());
+ Handle<DependentCode> new_next = Insert(old_next, group, object);
+ if (!old_next.is_identical_to(new_next)) {
+ entries->set_next_link(*new_next);
+ }
+ return entries;
+ }
+ DCHECK_EQ(group, entries->group());
+ int count = entries->count();
// Check for existing entry to avoid duplicates.
- for (int i = start; i < end; i++) {
+ for (int i = 0; i < count; i++) {
if (entries->object_at(i) == *object) return entries;
}
- if (entries->length() < kCodesStartIndex + number_of_entries + 1) {
- int capacity = kCodesStartIndex + number_of_entries + 1;
- if (capacity > 5) capacity = capacity * 5 / 4;
- Handle<DependentCode> new_entries = Handle<DependentCode>::cast(
- FixedArray::CopySize(entries, capacity, TENURED));
- // The number of codes can change after GC.
- starts.Recompute(*entries);
- start = starts.at(group);
- end = starts.at(group + 1);
- number_of_entries = starts.number_of_entries();
- for (int i = 0; i < number_of_entries; i++) {
- entries->clear_at(i);
- }
- // If the old fixed array was empty, we need to reset counters of the
- // new array.
- if (number_of_entries == 0) {
- for (int g = 0; g < kGroupCount; g++) {
- new_entries->set_number_of_entries(static_cast<DependencyGroup>(g), 0);
- }
- }
- entries = new_entries;
+ if (entries->length() < kCodesStartIndex + count + 1) {
+ entries = EnsureSpace(entries);
+ // Count could have changed, reload it.
+ count = entries->count();
}
- entries->ExtendGroup(group);
- entries->set_object_at(end, *object);
- entries->set_number_of_entries(group, end + 1 - start);
+ entries->set_object_at(count, *object);
+ entries->set_count(count + 1);
return entries;
}
-void DependentCode::UpdateToFinishedCode(DependencyGroup group,
- CompilationInfo* info,
- Code* code) {
+Handle<DependentCode> DependentCode::New(DependencyGroup group,
+ Handle<Object> object,
+ Handle<DependentCode> next) {
+ Isolate* isolate = next->GetIsolate();
+ Handle<DependentCode> result = Handle<DependentCode>::cast(
+ isolate->factory()->NewFixedArray(kCodesStartIndex + 1, TENURED));
+ result->set_next_link(*next);
+ result->set_flags(GroupField::encode(group) | CountField::encode(1));
+ result->set_object_at(0, *object);
+ return result;
+}
+
+
+Handle<DependentCode> DependentCode::EnsureSpace(
+ Handle<DependentCode> entries) {
+ if (entries->Compact()) return entries;
+ Isolate* isolate = entries->GetIsolate();
+ int capacity = kCodesStartIndex + DependentCode::Grow(entries->count());
+ int grow_by = capacity - entries->length();
+ return Handle<DependentCode>::cast(
+ isolate->factory()->CopyFixedArrayAndGrow(entries, grow_by, TENURED));
+}
+
+
+bool DependentCode::Compact() {
+ int old_count = count();
+ int new_count = 0;
+ for (int i = 0; i < old_count; i++) {
+ Object* obj = object_at(i);
+ if (!obj->IsWeakCell() || !WeakCell::cast(obj)->cleared()) {
+ if (i != new_count) {
+ copy(i, new_count);
+ }
+ new_count++;
+ }
+ }
+ set_count(new_count);
+ for (int i = new_count; i < old_count; i++) {
+ clear_at(i);
+ }
+ return new_count < old_count;
+}
+
+
+void DependentCode::UpdateToFinishedCode(DependencyGroup group, Foreign* info,
+ WeakCell* code_cell) {
+ if (this->length() == 0 || this->group() > group) {
+ // There is no such group.
+ return;
+ }
+ if (this->group() < group) {
+ // The group comes later in the list.
+ next_link()->UpdateToFinishedCode(group, info, code_cell);
+ return;
+ }
+ DCHECK_EQ(group, this->group());
DisallowHeapAllocation no_gc;
- AllowDeferredHandleDereference get_object_wrapper;
- Foreign* info_wrapper = *info->object_wrapper();
- GroupStartIndexes starts(this);
- int start = starts.at(group);
- int end = starts.at(group + 1);
- for (int i = start; i < end; i++) {
- if (object_at(i) == info_wrapper) {
- set_object_at(i, code);
+ int count = this->count();
+ for (int i = 0; i < count; i++) {
+ if (object_at(i) == info) {
+ set_object_at(i, code_cell);
break;
}
}
-
#ifdef DEBUG
- for (int i = start; i < end; i++) {
- DCHECK(is_code_at(i) || compilation_info_at(i) != info);
+ for (int i = 0; i < count; i++) {
+ DCHECK(object_at(i) != info);
}
#endif
}
-void DependentCode::RemoveCompilationInfo(DependentCode::DependencyGroup group,
- CompilationInfo* info) {
+void DependentCode::RemoveCompilationDependencies(
+ DependentCode::DependencyGroup group, Foreign* info) {
+ if (this->length() == 0 || this->group() > group) {
+ // There is no such group.
+ return;
+ }
+ if (this->group() < group) {
+ // The group comes later in the list.
+ next_link()->RemoveCompilationDependencies(group, info);
+ return;
+ }
+ DCHECK_EQ(group, this->group());
DisallowHeapAllocation no_allocation;
- AllowDeferredHandleDereference get_object_wrapper;
- Foreign* info_wrapper = *info->object_wrapper();
- GroupStartIndexes starts(this);
- int start = starts.at(group);
- int end = starts.at(group + 1);
+ int old_count = count();
// Find compilation info wrapper.
int info_pos = -1;
- for (int i = start; i < end; i++) {
- if (object_at(i) == info_wrapper) {
+ for (int i = 0; i < old_count; i++) {
+ if (object_at(i) == info) {
info_pos = i;
break;
}
}
if (info_pos == -1) return; // Not found.
- int gap = info_pos;
- // Use the last of each group to fill the gap in the previous group.
- for (int i = group; i < kGroupCount; i++) {
- int last_of_group = starts.at(i + 1) - 1;
- DCHECK(last_of_group >= gap);
- if (last_of_group == gap) continue;
- copy(last_of_group, gap);
- gap = last_of_group;
+ // Use the last code to fill the gap.
+ if (info_pos < old_count - 1) {
+ copy(old_count - 1, info_pos);
}
- DCHECK(gap == starts.number_of_entries() - 1);
- clear_at(gap); // Clear last gap.
- set_number_of_entries(group, end - start - 1);
+ clear_at(old_count - 1);
+ set_count(old_count - 1);
#ifdef DEBUG
- for (int i = start; i < end - 1; i++) {
- DCHECK(is_code_at(i) || compilation_info_at(i) != info);
+ for (int i = 0; i < old_count - 1; i++) {
+ DCHECK(object_at(i) != info);
}
#endif
}
-bool DependentCode::Contains(DependencyGroup group, Code* code) {
- GroupStartIndexes starts(this);
- int start = starts.at(group);
- int end = starts.at(group + 1);
- for (int i = start; i < end; i++) {
- if (object_at(i) == code) return true;
+bool DependentCode::Contains(DependencyGroup group, WeakCell* code_cell) {
+ if (this->length() == 0 || this->group() > group) {
+ // There is no such group.
+ return false;
+ }
+ if (this->group() < group) {
+ // The group comes later in the list.
+ return next_link()->Contains(group, code_cell);
+ }
+ DCHECK_EQ(group, this->group());
+ int count = this->count();
+ for (int i = 0; i < count; i++) {
+ if (object_at(i) == code_cell) return true;
}
return false;
}
+bool DependentCode::IsEmpty(DependencyGroup group) {
+ if (this->length() == 0 || this->group() > group) {
+ // There is no such group.
+ return true;
+ }
+ if (this->group() < group) {
+ // The group comes later in the list.
+ return next_link()->IsEmpty(group);
+ }
+ DCHECK_EQ(group, this->group());
+ return count() == 0;
+}
+
+
bool DependentCode::MarkCodeForDeoptimization(
Isolate* isolate,
DependentCode::DependencyGroup group) {
+ if (this->length() == 0 || this->group() > group) {
+ // There is no such group.
+ return false;
+ }
+ if (this->group() < group) {
+ // The group comes later in the list.
+ return next_link()->MarkCodeForDeoptimization(isolate, group);
+ }
+ DCHECK_EQ(group, this->group());
DisallowHeapAllocation no_allocation_scope;
- DependentCode::GroupStartIndexes starts(this);
- int start = starts.at(group);
- int end = starts.at(group + 1);
- int code_entries = starts.number_of_entries();
- if (start == end) return false;
-
// Mark all the code that needs to be deoptimized.
bool marked = false;
- for (int i = start; i < end; i++) {
- if (is_code_at(i)) {
- Code* code = code_at(i);
+ bool invalidate_embedded_objects = group == kWeakCodeGroup;
+ int count = this->count();
+ for (int i = 0; i < count; i++) {
+ Object* obj = object_at(i);
+ if (obj->IsWeakCell()) {
+ WeakCell* cell = WeakCell::cast(obj);
+ if (cell->cleared()) continue;
+ Code* code = Code::cast(cell->value());
if (!code->marked_for_deoptimization()) {
SetMarkedForDeoptimization(code, group);
+ if (invalidate_embedded_objects) {
+ code->InvalidateEmbeddedObjects();
+ }
marked = true;
}
} else {
- CompilationInfo* info = compilation_info_at(i);
- info->AbortDueToDependencyChange();
+ DCHECK(obj->IsForeign());
+ CompilationDependencies* info =
+ reinterpret_cast<CompilationDependencies*>(
+ Foreign::cast(obj)->foreign_address());
+ info->Abort();
}
}
- // Compact the array by moving all subsequent groups to fill in the new holes.
- for (int src = end, dst = start; src < code_entries; src++, dst++) {
- copy(src, dst);
- }
- // Now the holes are at the end of the array, zap them for heap-verifier.
- int removed = end - start;
- for (int i = code_entries - removed; i < code_entries; i++) {
+ for (int i = 0; i < count; i++) {
clear_at(i);
}
- set_number_of_entries(group, 0);
+ set_count(0);
return marked;
}
@@ -12189,7 +15418,6 @@
DCHECK(AllowCodeDependencyChange::IsAllowed());
DisallowHeapAllocation no_allocation_scope;
bool marked = MarkCodeForDeoptimization(isolate, group);
-
if (marked) Deoptimizer::DeoptimizeMarkedCode(isolate);
}
@@ -12218,8 +15446,6 @@
return "transition";
case kPrototypeCheckGroup:
return "prototype-check";
- case kElementsCantBeAddedGroup:
- return "elements-cant-be-added";
case kPropertyCellChangedGroup:
return "property-cell-changed";
case kFieldTypeGroup:
@@ -12239,28 +15465,187 @@
Handle<Map> Map::TransitionToPrototype(Handle<Map> map,
Handle<Object> prototype,
PrototypeOptimizationMode mode) {
- Handle<Map> new_map = GetPrototypeTransition(map, prototype);
+ Handle<Map> new_map = TransitionArray::GetPrototypeTransition(map, prototype);
if (new_map.is_null()) {
new_map = Copy(map, "TransitionToPrototype");
- PutPrototypeTransition(map, prototype, new_map);
- new_map->SetPrototype(prototype, mode);
+ TransitionArray::PutPrototypeTransition(map, prototype, new_map);
+ Map::SetPrototype(new_map, prototype, mode);
}
return new_map;
}
-MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> object,
- Handle<Object> value,
- bool from_javascript) {
+Maybe<bool> JSReceiver::SetPrototype(Handle<JSReceiver> object,
+ Handle<Object> value, bool from_javascript,
+ ShouldThrow should_throw) {
+ if (object->IsJSProxy()) {
+ return JSProxy::SetPrototype(Handle<JSProxy>::cast(object), value,
+ from_javascript, should_throw);
+ }
+ return JSObject::SetPrototype(Handle<JSObject>::cast(object), value,
+ from_javascript, should_throw);
+}
+
+
+// ES6: 9.5.2 [[SetPrototypeOf]] (V)
+// static
+Maybe<bool> JSProxy::SetPrototype(Handle<JSProxy> proxy, Handle<Object> value,
+ bool from_javascript,
+ ShouldThrow should_throw) {
+ Isolate* isolate = proxy->GetIsolate();
+ STACK_CHECK(Nothing<bool>());
+ Handle<Name> trap_name = isolate->factory()->setPrototypeOf_string();
+ // 1. Assert: Either Type(V) is Object or Type(V) is Null.
+ DCHECK(value->IsJSReceiver() || value->IsNull());
+ // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+ Handle<Object> handler(proxy->handler(), isolate);
+ // 3. If handler is null, throw a TypeError exception.
+ // 4. Assert: Type(handler) is Object.
+ if (proxy->IsRevoked()) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxyRevoked, trap_name));
+ return Nothing<bool>();
+ }
+ // 5. Let target be the value of the [[ProxyTarget]] internal slot.
+ Handle<JSReceiver> target(proxy->target(), isolate);
+ // 6. Let trap be ? GetMethod(handler, "getPrototypeOf").
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap,
+ Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name),
+ Nothing<bool>());
+ // 7. If trap is undefined, then return target.[[SetPrototypeOf]]().
+ if (trap->IsUndefined()) {
+ return JSReceiver::SetPrototype(target, value, from_javascript,
+ should_throw);
+ }
+ // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, V»)).
+ Handle<Object> argv[] = {target, value};
+ Handle<Object> trap_result;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, trap_result,
+ Execution::Call(isolate, trap, handler, arraysize(argv), argv),
+ Nothing<bool>());
+ bool bool_trap_result = trap_result->BooleanValue();
+ // 9. Let extensibleTarget be ? IsExtensible(target).
+ Maybe<bool> is_extensible = JSReceiver::IsExtensible(target);
+ if (is_extensible.IsNothing()) return Nothing<bool>();
+ // 10. If extensibleTarget is true, return booleanTrapResult.
+ if (is_extensible.FromJust()) {
+ if (bool_trap_result) return Just(true);
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
+ }
+ // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
+ Handle<Object> target_proto;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_proto,
+ Object::GetPrototype(isolate, target),
+ Nothing<bool>());
+ // 12. If booleanTrapResult is true and SameValue(V, targetProto) is false,
+ // throw a TypeError exception.
+ if (bool_trap_result && !value->SameValue(*target_proto)) {
+ isolate->Throw(*isolate->factory()->NewTypeError(
+ MessageTemplate::kProxySetPrototypeOfNonExtensible));
+ return Nothing<bool>();
+ }
+ // 13. Return booleanTrapResult.
+ if (bool_trap_result) return Just(true);
+ RETURN_FAILURE(
+ isolate, should_throw,
+ NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name));
+}
+
+
+Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object,
+ Handle<Object> value, bool from_javascript,
+ ShouldThrow should_throw) {
+ Isolate* isolate = object->GetIsolate();
+
+ const bool observed = from_javascript && object->map()->is_observed();
+ Handle<Object> old_value;
+ if (observed) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, old_value,
+ Object::GetPrototype(isolate, object),
+ Nothing<bool>());
+ }
+
+ Maybe<bool> result =
+ SetPrototypeUnobserved(object, value, from_javascript, should_throw);
+ MAYBE_RETURN(result, Nothing<bool>());
+
+ if (result.FromJust() && observed) {
+ Handle<Object> new_value;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, new_value,
+ Object::GetPrototype(isolate, object),
+ Nothing<bool>());
+ if (!new_value->SameValue(*old_value)) {
+ RETURN_ON_EXCEPTION_VALUE(
+ isolate, JSObject::EnqueueChangeRecord(
+ object, "setPrototype",
+ isolate->factory()->proto_string(), old_value),
+ Nothing<bool>());
+ }
+ }
+
+ return result;
+}
+
+
+Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object,
+ Handle<Object> value,
+ bool from_javascript,
+ ShouldThrow should_throw) {
#ifdef DEBUG
int size = object->Size();
#endif
Isolate* isolate = object->GetIsolate();
+
+ if (from_javascript) {
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayAccess(handle(isolate->context()), object)) {
+ isolate->ReportFailedAccessCheck(object);
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kNoAccess));
+ }
+ } else {
+ DCHECK(!object->IsAccessCheckNeeded());
+ }
+
+ // Strong objects may not have their prototype set via __proto__ or
+ // setPrototypeOf.
+ if (from_javascript && object->map()->is_strong()) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kStrongSetProto, object));
+ }
Heap* heap = isolate->heap();
// Silently ignore the change if value is not a JSObject or null.
// SpiderMonkey behaves this way.
- if (!value->IsJSReceiver() && !value->IsNull()) return value;
+ if (!value->IsJSReceiver() && !value->IsNull()) return Just(true);
+
+ bool dictionary_elements_in_chain =
+ object->map()->DictionaryElementsInPrototypeChainOnly();
+
+ bool all_extensible = object->map()->is_extensible();
+ Handle<JSObject> real_receiver = object;
+ if (from_javascript) {
+ // Find the first object in the chain whose prototype object is not
+ // hidden.
+ PrototypeIterator iter(isolate, real_receiver);
+ while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) {
+ // Casting to JSObject is fine because hidden prototypes are never
+ // JSProxies.
+ real_receiver = PrototypeIterator::GetCurrent<JSObject>(iter);
+ iter.Advance();
+ all_extensible = all_extensible && real_receiver->map()->is_extensible();
+ }
+ }
+ Handle<Map> map(real_receiver->map());
+
+ // Nothing to do if prototype is already set.
+ if (map->prototype() == *value) return Just(true);
// From 8.6.2 Object Internal Methods
// ...
@@ -12270,48 +15655,27 @@
// Implementation specific extensions that modify [[Class]], [[Prototype]]
// or [[Extensible]] must not violate the invariants defined in the preceding
// paragraph.
- if (!object->map()->is_extensible()) {
- Handle<Object> args[] = { object };
- THROW_NEW_ERROR(isolate, NewTypeError("non_extensible_proto",
- HandleVector(args, arraysize(args))),
- Object);
+ if (!all_extensible) {
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kNonExtensibleProto, object));
}
- // Before we can set the prototype we need to be sure
- // prototype cycles are prevented.
- // It is sufficient to validate that the receiver is not in the new prototype
- // chain.
+ // Before we can set the prototype we need to be sure prototype cycles are
+ // prevented. It is sufficient to validate that the receiver is not in the
+ // new prototype chain.
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,
- NewError("cyclic_proto", HandleVector<Object>(NULL, 0)),
- Object);
- }
- }
-
- bool dictionary_elements_in_chain =
- object->map()->DictionaryElementsInPrototypeChainOnly();
- Handle<JSObject> real_receiver = object;
-
- if (from_javascript) {
- // Find the first object in the chain whose prototype object is not
- // 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));
- iter.Advance();
+ RETURN_FAILURE(isolate, should_throw,
+ NewTypeError(MessageTemplate::kCyclicProto));
}
}
// Set the new prototype of the object.
- Handle<Map> map(real_receiver->map());
- // Nothing to do if prototype is already set.
- if (map->prototype() == *value) return value;
+ isolate->UpdateArrayProtectorOnSetPrototype(real_receiver);
PrototypeOptimizationMode mode =
from_javascript ? REGULAR_PROTOTYPE : FAST_PROTOTYPE;
@@ -12324,12 +15688,12 @@
// 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);
+ TypeFeedbackVector::ClearAllKeyedStoreICs(isolate);
}
heap->ClearInstanceofCache();
DCHECK(size == object->Size());
- return value;
+ return Just(true);
}
@@ -12346,832 +15710,222 @@
}
-MaybeHandle<AccessorPair> JSObject::GetOwnElementAccessorPair(
- Handle<JSObject> object,
- uint32_t index) {
- if (object->IsJSGlobalProxy()) {
- PrototypeIterator iter(object->GetIsolate(), object);
- if (iter.IsAtEnd()) return MaybeHandle<AccessorPair>();
- DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
- return GetOwnElementAccessorPair(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index);
- }
-
- // Check for lookup interceptor.
- if (object->HasIndexedInterceptor()) return MaybeHandle<AccessorPair>();
-
- return object->GetElementsAccessor()->GetAccessorPair(object, object, index);
+ElementsAccessor* JSObject::GetElementsAccessor() {
+ return ElementsAccessor::ForKind(GetElementsKind());
}
-MaybeHandle<Object> JSObject::SetElementWithInterceptor(
- Handle<JSObject> object,
- uint32_t index,
- Handle<Object> value,
- PropertyAttributes attributes,
- StrictMode strict_mode,
- bool check_prototype,
- SetPropertyMode set_mode) {
- Isolate* isolate = object->GetIsolate();
-
- // Make sure that the top context does not change when doing
- // callbacks or interceptor calls.
- AssertNoContextChange ncc(isolate);
-
- Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor());
- if (!interceptor->setter()->IsUndefined()) {
- v8::IndexedPropertySetterCallback setter =
- v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter());
- LOG(isolate,
- ApiIndexedPropertyAccess("interceptor-indexed-set", *object, index));
- PropertyCallbackArguments args(isolate, interceptor->data(), *object,
- *object);
- v8::Handle<v8::Value> result =
- args.Call(setter, index, v8::Utils::ToLocal(value));
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- if (!result.IsEmpty()) return value;
+void JSObject::ValidateElements(Handle<JSObject> object) {
+#ifdef ENABLE_SLOW_DCHECKS
+ if (FLAG_enable_slow_asserts) {
+ ElementsAccessor* accessor = object->GetElementsAccessor();
+ accessor->Validate(object);
}
-
- return SetElementWithoutInterceptor(object, index, value, attributes,
- strict_mode,
- check_prototype,
- set_mode);
+#endif
}
-MaybeHandle<Object> JSObject::GetElementWithCallback(
- Handle<JSObject> object,
- Handle<Object> receiver,
- Handle<Object> structure,
- uint32_t index,
- Handle<Object> holder) {
- Isolate* isolate = object->GetIsolate();
- DCHECK(!structure->IsForeign());
- // api style callbacks.
- if (structure->IsExecutableAccessorInfo()) {
- Handle<ExecutableAccessorInfo> data =
- Handle<ExecutableAccessorInfo>::cast(structure);
- Object* fun_obj = data->getter();
- v8::AccessorNameGetterCallback call_fun =
- v8::ToCData<v8::AccessorNameGetterCallback>(fun_obj);
- if (call_fun == NULL) return isolate->factory()->undefined_value();
- Handle<JSObject> holder_handle = Handle<JSObject>::cast(holder);
- Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
- Handle<String> key = isolate->factory()->NumberToString(number);
- LOG(isolate, ApiNamedPropertyAccess("load", *holder_handle, *key));
- PropertyCallbackArguments
- args(isolate, data->data(), *receiver, *holder_handle);
- v8::Handle<v8::Value> result = args.Call(call_fun, v8::Utils::ToLocal(key));
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- if (result.IsEmpty()) return isolate->factory()->undefined_value();
- Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
- result_internal->VerifyApiCallResultType();
- // Rebox handle before return.
- return handle(*result_internal, isolate);
- }
-
- // __defineGetter__ callback
- if (structure->IsAccessorPair()) {
- Handle<Object> getter(Handle<AccessorPair>::cast(structure)->getter(),
- isolate);
- if (getter->IsSpecFunction()) {
- // TODO(rossberg): nicer would be to cast to some JSCallable here...
- return GetPropertyWithDefinedGetter(
- receiver, Handle<JSReceiver>::cast(getter));
- }
- // Getter is not a function.
- return isolate->factory()->undefined_value();
- }
-
- if (structure->IsDeclaredAccessorInfo()) {
- return GetDeclaredAccessorProperty(
- receiver, Handle<DeclaredAccessorInfo>::cast(structure), isolate);
- }
-
- UNREACHABLE();
- return MaybeHandle<Object>();
-}
-
-
-MaybeHandle<Object> JSObject::SetElementWithCallback(
- Handle<Object> object, Handle<Object> structure, uint32_t index,
- Handle<Object> value, Handle<JSObject> holder, StrictMode strict_mode) {
- Isolate* isolate = holder->GetIsolate();
-
- // We should never get here to initialize a const with the hole
- // value since a const declaration would conflict with the setter.
- DCHECK(!value->IsTheHole());
- DCHECK(!structure->IsForeign());
- if (structure->IsExecutableAccessorInfo()) {
- // api style callbacks
- Handle<ExecutableAccessorInfo> data =
- Handle<ExecutableAccessorInfo>::cast(structure);
- Object* call_obj = data->setter();
- v8::AccessorNameSetterCallback call_fun =
- v8::ToCData<v8::AccessorNameSetterCallback>(call_obj);
- if (call_fun == NULL) return value;
- Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
- Handle<String> key(isolate->factory()->NumberToString(number));
- LOG(isolate, ApiNamedPropertyAccess("store", *holder, *key));
- PropertyCallbackArguments
- args(isolate, data->data(), *object, *holder);
- args.Call(call_fun,
- v8::Utils::ToLocal(key),
- v8::Utils::ToLocal(value));
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return value;
- }
-
- if (structure->IsAccessorPair()) {
- Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate);
- if (setter->IsSpecFunction()) {
- // TODO(rossberg): nicer would be to cast to some JSCallable here...
- return SetPropertyWithDefinedSetter(
- object, Handle<JSReceiver>::cast(setter), value);
- } else {
- if (strict_mode == SLOPPY) return value;
- Handle<Object> key(isolate->factory()->NewNumberFromUint(index));
- Handle<Object> args[2] = { key, holder };
- THROW_NEW_ERROR(
- isolate, NewTypeError("no_setter_in_callback", HandleVector(args, 2)),
- Object);
- }
- }
-
- // TODO(dcarney): Handle correctly.
- if (structure->IsDeclaredAccessorInfo()) return value;
-
- UNREACHABLE();
- return MaybeHandle<Object>();
-}
-
-
-bool JSObject::HasFastArgumentsElements() {
- Heap* heap = GetHeap();
- if (!elements()->IsFixedArray()) return false;
- FixedArray* elements = FixedArray::cast(this->elements());
- if (elements->map() != heap->sloppy_arguments_elements_map()) {
+static bool ShouldConvertToSlowElements(JSObject* object, uint32_t capacity,
+ uint32_t index,
+ uint32_t* new_capacity) {
+ STATIC_ASSERT(JSObject::kMaxUncheckedOldFastElementsLength <=
+ JSObject::kMaxUncheckedFastElementsLength);
+ if (index < capacity) {
+ *new_capacity = capacity;
return false;
}
- FixedArray* arguments = FixedArray::cast(elements->get(1));
- return !arguments->IsDictionary();
-}
-
-
-bool JSObject::HasDictionaryArgumentsElements() {
- Heap* heap = GetHeap();
- if (!elements()->IsFixedArray()) return false;
- FixedArray* elements = FixedArray::cast(this->elements());
- if (elements->map() != heap->sloppy_arguments_elements_map()) {
+ if (index - capacity >= JSObject::kMaxGap) return true;
+ *new_capacity = JSObject::NewElementsCapacity(index + 1);
+ DCHECK_LT(index, *new_capacity);
+ if (*new_capacity <= JSObject::kMaxUncheckedOldFastElementsLength ||
+ (*new_capacity <= JSObject::kMaxUncheckedFastElementsLength &&
+ object->GetHeap()->InNewSpace(object))) {
return false;
}
- FixedArray* arguments = FixedArray::cast(elements->get(1));
- return arguments->IsDictionary();
+ // If the fast-case backing storage takes up roughly three times as
+ // much space (in machine words) as a dictionary backing storage
+ // would, the object should have slow elements.
+ int used_elements = object->GetFastElementsUsage();
+ int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) *
+ SeededNumberDictionary::kEntrySize;
+ return 3 * static_cast<uint32_t>(dictionary_size) <= *new_capacity;
}
-// Adding n elements in fast case is O(n*n).
-// Note: revisit design to have dual undefined values to capture absent
-// elements.
-MaybeHandle<Object> JSObject::SetFastElement(Handle<JSObject> object,
+bool JSObject::WouldConvertToSlowElements(uint32_t index) {
+ if (HasFastElements()) {
+ Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
+ uint32_t capacity = static_cast<uint32_t>(backing_store->length());
+ uint32_t new_capacity;
+ return ShouldConvertToSlowElements(this, capacity, index, &new_capacity);
+ }
+ return false;
+}
+
+
+static ElementsKind BestFittingFastElementsKind(JSObject* object) {
+ if (object->HasSloppyArgumentsElements()) {
+ return FAST_SLOPPY_ARGUMENTS_ELEMENTS;
+ }
+ DCHECK(object->HasDictionaryElements());
+ SeededNumberDictionary* dictionary = object->element_dictionary();
+ ElementsKind kind = FAST_HOLEY_SMI_ELEMENTS;
+ for (int i = 0; i < dictionary->Capacity(); i++) {
+ Object* key = dictionary->KeyAt(i);
+ if (key->IsNumber()) {
+ Object* value = dictionary->ValueAt(i);
+ if (!value->IsNumber()) return FAST_HOLEY_ELEMENTS;
+ if (!value->IsSmi()) {
+ if (!FLAG_unbox_double_arrays) return FAST_HOLEY_ELEMENTS;
+ kind = FAST_HOLEY_DOUBLE_ELEMENTS;
+ }
+ }
+ }
+ return kind;
+}
+
+
+static bool ShouldConvertToFastElements(JSObject* object,
+ SeededNumberDictionary* dictionary,
+ uint32_t index,
+ uint32_t* new_capacity) {
+ // If properties with non-standard attributes or accessors were added, we
+ // cannot go back to fast elements.
+ if (dictionary->requires_slow_elements()) return false;
+
+ // Adding a property with this index will require slow elements.
+ if (index >= static_cast<uint32_t>(Smi::kMaxValue)) return false;
+
+ if (object->IsJSArray()) {
+ Object* length = JSArray::cast(object)->length();
+ if (!length->IsSmi()) return false;
+ *new_capacity = static_cast<uint32_t>(Smi::cast(length)->value());
+ } else {
+ *new_capacity = dictionary->max_number_key() + 1;
+ }
+ *new_capacity = Max(index + 1, *new_capacity);
+
+ uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
+ SeededNumberDictionary::kEntrySize;
+
+ // Turn fast if the dictionary only saves 50% space.
+ return 2 * dictionary_size >= *new_capacity;
+}
+
+
+// static
+MaybeHandle<Object> JSObject::AddDataElement(Handle<JSObject> object,
uint32_t index,
Handle<Object> value,
- StrictMode strict_mode,
- bool check_prototype) {
- DCHECK(object->HasFastSmiOrObjectElements() ||
- object->HasFastArgumentsElements());
-
- Isolate* isolate = object->GetIsolate();
-
- // Array optimizations rely on the prototype lookups of Array objects always
- // returning undefined. If there is a store to the initial prototype object,
- // make sure all of these optimizations are invalidated.
- if (isolate->is_initial_object_prototype(*object) ||
- isolate->is_initial_array_prototype(*object)) {
- object->map()->dependent_code()->DeoptimizeDependentCodeGroup(isolate,
- DependentCode::kElementsCantBeAddedGroup);
- }
-
- Handle<FixedArray> backing_store(FixedArray::cast(object->elements()));
- if (backing_store->map() ==
- isolate->heap()->sloppy_arguments_elements_map()) {
- backing_store = handle(FixedArray::cast(backing_store->get(1)));
- } else {
- backing_store = EnsureWritableFastElements(object);
- }
- uint32_t capacity = static_cast<uint32_t>(backing_store->length());
-
- if (check_prototype &&
- (index >= capacity || backing_store->get(index)->IsTheHole())) {
- bool found;
- MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes(
- object, index, value, &found, strict_mode);
- if (found) return result;
- }
-
- uint32_t new_capacity = capacity;
- // Check if the length property of this object needs to be updated.
- uint32_t array_length = 0;
- bool must_update_array_length = false;
- bool introduces_holes = true;
- if (object->IsJSArray()) {
- CHECK(Handle<JSArray>::cast(object)->length()->ToArrayIndex(&array_length));
- introduces_holes = index > array_length;
- if (index >= array_length) {
- must_update_array_length = true;
- array_length = index + 1;
- }
- } else {
- introduces_holes = index >= capacity;
- }
-
- // If the array is growing, and it's not growth by a single element at the
- // end, make sure that the ElementsKind is HOLEY.
- ElementsKind elements_kind = object->GetElementsKind();
- if (introduces_holes &&
- IsFastElementsKind(elements_kind) &&
- !IsFastHoleyElementsKind(elements_kind)) {
- ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind);
- TransitionElementsKind(object, transitioned_kind);
- }
-
- // Check if the capacity of the backing store needs to be increased, or if
- // a transition to slow elements is necessary.
- if (index >= capacity) {
- bool convert_to_slow = true;
- if ((index - capacity) < kMaxGap) {
- new_capacity = NewElementsCapacity(index + 1);
- DCHECK(new_capacity > index);
- if (!object->ShouldConvertToSlowElements(new_capacity)) {
- convert_to_slow = false;
- }
- }
- if (convert_to_slow) {
- NormalizeElements(object);
- return SetDictionaryElement(object, index, value, NONE, strict_mode,
- check_prototype);
- }
- }
- // Convert to fast double elements if appropriate.
- if (object->HasFastSmiElements() && !value->IsSmi() && value->IsNumber()) {
- // Consider fixing the boilerplate as well if we have one.
- ElementsKind to_kind = IsHoleyElementsKind(elements_kind)
- ? FAST_HOLEY_DOUBLE_ELEMENTS
- : FAST_DOUBLE_ELEMENTS;
-
- UpdateAllocationSite(object, to_kind);
-
- SetFastDoubleElementsCapacityAndLength(object, new_capacity, array_length);
- FixedDoubleArray::cast(object->elements())->set(index, value->Number());
- JSObject::ValidateElements(object);
- return value;
- }
- // Change elements kind from Smi-only to generic FAST if necessary.
- if (object->HasFastSmiElements() && !value->IsSmi()) {
- ElementsKind kind = object->HasFastHoleyElements()
- ? FAST_HOLEY_ELEMENTS
- : FAST_ELEMENTS;
-
- UpdateAllocationSite(object, kind);
- Handle<Map> new_map = GetElementsTransitionMap(object, kind);
- JSObject::MigrateToMap(object, new_map);
- DCHECK(IsFastObjectElementsKind(object->GetElementsKind()));
- }
- // Increase backing store capacity if that's been decided previously.
- if (new_capacity != capacity) {
- SetFastElementsCapacitySmiMode smi_mode =
- value->IsSmi() && object->HasFastSmiElements()
- ? kAllowSmiElements
- : kDontAllowSmiElements;
- Handle<FixedArray> new_elements =
- SetFastElementsCapacityAndLength(object, new_capacity, array_length,
- smi_mode);
- new_elements->set(index, *value);
- JSObject::ValidateElements(object);
- return value;
- }
-
- // Finally, set the new element and length.
- DCHECK(object->elements()->IsFixedArray());
- backing_store->set(index, *value);
- if (must_update_array_length) {
- Handle<JSArray>::cast(object)->set_length(Smi::FromInt(array_length));
- }
+ PropertyAttributes attributes) {
+ MAYBE_RETURN_NULL(
+ AddDataElement(object, index, value, attributes, THROW_ON_ERROR));
return value;
}
-MaybeHandle<Object> JSObject::SetDictionaryElement(
- Handle<JSObject> object,
- uint32_t index,
- Handle<Object> value,
- PropertyAttributes attributes,
- StrictMode strict_mode,
- bool check_prototype,
- SetPropertyMode set_mode) {
- DCHECK(object->HasDictionaryElements() ||
- object->HasDictionaryArgumentsElements());
+// static
+Maybe<bool> JSObject::AddDataElement(Handle<JSObject> object, uint32_t index,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ ShouldThrow should_throw) {
+ DCHECK(object->map()->is_extensible());
+
Isolate* isolate = object->GetIsolate();
- // Insert element in the dictionary.
- Handle<FixedArray> elements(FixedArray::cast(object->elements()));
- bool is_arguments =
- (elements->map() == isolate->heap()->sloppy_arguments_elements_map());
- Handle<SeededNumberDictionary> dictionary(is_arguments
- ? SeededNumberDictionary::cast(elements->get(1))
- : SeededNumberDictionary::cast(*elements));
+ uint32_t old_length = 0;
+ uint32_t new_capacity = 0;
- int entry = dictionary->FindEntry(index);
- if (entry != SeededNumberDictionary::kNotFound) {
- Handle<Object> element(dictionary->ValueAt(entry), isolate);
- PropertyDetails details = dictionary->DetailsAt(entry);
- if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) {
- return SetElementWithCallback(object, element, index, value, object,
- strict_mode);
- } else {
- dictionary->UpdateMaxNumberKey(index);
- // If a value has not been initialized we allow writing to it even if it
- // is read-only (a declared const that has not been initialized). If a
- // value is being defined we skip attribute checks completely.
- if (set_mode == DEFINE_PROPERTY) {
- details =
- PropertyDetails(attributes, FIELD, details.dictionary_index());
- dictionary->DetailsAtPut(entry, details);
- } else if (details.IsReadOnly() && !element->IsTheHole()) {
- if (strict_mode == SLOPPY) {
- return isolate->factory()->undefined_value();
- } else {
- Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
- Handle<Object> args[2] = { number, object };
- THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
- HandleVector(args, 2)),
- Object);
- }
- }
- // Elements of the arguments object in slow mode might be slow aliases.
- if (is_arguments && element->IsAliasedArgumentsEntry()) {
- Handle<AliasedArgumentsEntry> entry =
- Handle<AliasedArgumentsEntry>::cast(element);
- Handle<Context> context(Context::cast(elements->get(0)));
- int context_index = entry->aliased_context_slot();
- DCHECK(!context->get(context_index)->IsTheHole());
- context->set(context_index, *value);
- // For elements that are still writable we keep slow aliasing.
- if (!details.IsReadOnly()) value = element;
- }
- dictionary->ValueAtPut(entry, *value);
- }
- } else {
- // Index not already used. Look for an accessor in the prototype chain.
- // Can cause GC!
- if (check_prototype) {
- bool found;
- MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes(
- object, index, value, &found, strict_mode);
- if (found) return result;
- }
-
- // When we set the is_extensible flag to false we always force the
- // element into dictionary mode (and force them to stay there).
- if (!object->map()->is_extensible()) {
- if (strict_mode == SLOPPY) {
- return isolate->factory()->undefined_value();
- } else {
- Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
- Handle<String> name = isolate->factory()->NumberToString(number);
- Handle<Object> args[1] = { name };
- THROW_NEW_ERROR(isolate, NewTypeError("object_not_extensible",
- HandleVector(args, 1)),
- Object);
- }
- }
-
- PropertyDetails details(attributes, FIELD, 0);
- Handle<SeededNumberDictionary> new_dictionary =
- SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
- details);
- if (*dictionary != *new_dictionary) {
- if (is_arguments) {
- elements->set(1, *new_dictionary);
- } else {
- object->set_elements(*new_dictionary);
- }
- dictionary = new_dictionary;
- }
- }
-
- // Update the array length if this JSObject is an array.
- if (object->IsJSArray()) {
- JSArray::JSArrayUpdateLengthFromIndex(Handle<JSArray>::cast(object), index,
- value);
- }
-
- // Attempt to put this object back in fast case.
- if (object->ShouldConvertToFastElements()) {
- uint32_t new_length = 0;
- if (object->IsJSArray()) {
- CHECK(Handle<JSArray>::cast(object)->length()->ToArrayIndex(&new_length));
- } else {
- new_length = dictionary->max_number_key() + 1;
- }
- bool has_smi_only_elements = false;
- bool should_convert_to_fast_double_elements =
- object->ShouldConvertToFastDoubleElements(&has_smi_only_elements);
- SetFastElementsCapacitySmiMode smi_mode =
- has_smi_only_elements ? kForceSmiElements : kAllowSmiElements;
-
- if (should_convert_to_fast_double_elements) {
- SetFastDoubleElementsCapacityAndLength(object, new_length, new_length);
- } else {
- SetFastElementsCapacityAndLength(object, new_length, new_length,
- smi_mode);
- }
- JSObject::ValidateElements(object);
-#ifdef DEBUG
- if (FLAG_trace_normalization) {
- OFStream os(stdout);
- os << "Object elements are fast case again:\n";
- object->Print(os);
- }
-#endif
- }
- return value;
-}
-
-MaybeHandle<Object> JSObject::SetFastDoubleElement(
- Handle<JSObject> object,
- uint32_t index,
- Handle<Object> value,
- StrictMode strict_mode,
- bool check_prototype) {
- DCHECK(object->HasFastDoubleElements());
-
- Handle<FixedArrayBase> base_elms(FixedArrayBase::cast(object->elements()));
- uint32_t elms_length = static_cast<uint32_t>(base_elms->length());
-
- // If storing to an element that isn't in the array, pass the store request
- // up the prototype chain before storing in the receiver's elements.
- if (check_prototype &&
- (index >= elms_length ||
- Handle<FixedDoubleArray>::cast(base_elms)->is_the_hole(index))) {
- bool found;
- MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes(
- object, index, value, &found, strict_mode);
- if (found) return result;
- }
-
- // If the value object is not a heap number, switch to fast elements and try
- // again.
- bool value_is_smi = value->IsSmi();
- bool introduces_holes = true;
- uint32_t length = elms_length;
- if (object->IsJSArray()) {
- CHECK(Handle<JSArray>::cast(object)->length()->ToArrayIndex(&length));
- introduces_holes = index > length;
- } else {
- introduces_holes = index >= elms_length;
- }
-
- if (!value->IsNumber()) {
- SetFastElementsCapacityAndLength(object, elms_length, length,
- kDontAllowSmiElements);
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION(
- object->GetIsolate(), result,
- SetFastElement(object, index, value, strict_mode, check_prototype),
- Object);
- JSObject::ValidateElements(object);
- return result;
- }
-
- double double_value = value_is_smi
- ? static_cast<double>(Handle<Smi>::cast(value)->value())
- : Handle<HeapNumber>::cast(value)->value();
-
- // If the array is growing, and it's not growth by a single element at the
- // end, make sure that the ElementsKind is HOLEY.
- ElementsKind elements_kind = object->GetElementsKind();
- if (introduces_holes && !IsFastHoleyElementsKind(elements_kind)) {
- ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind);
- TransitionElementsKind(object, transitioned_kind);
- }
-
- // Check whether there is extra space in the fixed array.
- if (index < elms_length) {
- Handle<FixedDoubleArray> elms(FixedDoubleArray::cast(object->elements()));
- elms->set(index, double_value);
- if (object->IsJSArray()) {
- // Update the length of the array if needed.
- uint32_t array_length = 0;
- CHECK(
- Handle<JSArray>::cast(object)->length()->ToArrayIndex(&array_length));
- if (index >= array_length) {
- Handle<JSArray>::cast(object)->set_length(Smi::FromInt(index + 1));
- }
- }
- return value;
- }
-
- // Allow gap in fast case.
- if ((index - elms_length) < kMaxGap) {
- // Try allocating extra space.
- int new_capacity = NewElementsCapacity(index+1);
- if (!object->ShouldConvertToSlowElements(new_capacity)) {
- DCHECK(static_cast<uint32_t>(new_capacity) > index);
- SetFastDoubleElementsCapacityAndLength(object, new_capacity, index + 1);
- FixedDoubleArray::cast(object->elements())->set(index, double_value);
- JSObject::ValidateElements(object);
- return value;
- }
- }
-
- // Otherwise default to slow case.
- DCHECK(object->HasFastDoubleElements());
- DCHECK(object->map()->has_fast_double_elements());
- DCHECK(object->elements()->IsFixedDoubleArray() ||
- object->elements()->length() == 0);
-
- NormalizeElements(object);
- DCHECK(object->HasDictionaryElements());
- return SetElement(object, index, value, NONE, strict_mode, check_prototype);
-}
-
-
-MaybeHandle<Object> JSReceiver::SetElement(Handle<JSReceiver> object,
- uint32_t index,
- Handle<Object> value,
- PropertyAttributes attributes,
- StrictMode strict_mode) {
- if (object->IsJSProxy()) {
- return JSProxy::SetElementWithHandler(
- Handle<JSProxy>::cast(object), object, index, value, strict_mode);
- }
- return JSObject::SetElement(
- Handle<JSObject>::cast(object), index, value, attributes, strict_mode);
-}
-
-
-MaybeHandle<Object> JSObject::SetOwnElement(Handle<JSObject> object,
- uint32_t index,
- Handle<Object> value,
- StrictMode strict_mode) {
- DCHECK(!object->HasExternalArrayElements());
- return JSObject::SetElement(object, index, value, NONE, strict_mode, false);
-}
-
-
-MaybeHandle<Object> JSObject::SetElement(Handle<JSObject> object,
- uint32_t index,
- Handle<Object> value,
- PropertyAttributes attributes,
- StrictMode strict_mode,
- bool check_prototype,
- SetPropertyMode set_mode) {
- Isolate* isolate = object->GetIsolate();
-
- if (object->HasExternalArrayElements() ||
- object->HasFixedTypedArrayElements()) {
- if (!value->IsNumber() && !value->IsUndefined()) {
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, value,
- Execution::ToNumber(isolate, value), Object);
- }
- }
-
- // Check access rights if needed.
- if (object->IsAccessCheckNeeded()) {
- if (!isolate->MayIndexedAccess(object, index, v8::ACCESS_SET)) {
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- return value;
- }
- }
-
- if (object->IsJSGlobalProxy()) {
- PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return value;
- DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
- return SetElement(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index,
- value, attributes, strict_mode, check_prototype, set_mode);
- }
-
- // Don't allow element properties to be redefined for external arrays.
- if ((object->HasExternalArrayElements() ||
- object->HasFixedTypedArrayElements()) &&
- set_mode == DEFINE_PROPERTY) {
- Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
- Handle<Object> args[] = { object, number };
- THROW_NEW_ERROR(isolate, NewTypeError("redef_external_array_element",
- HandleVector(args, arraysize(args))),
- Object);
- }
-
- // Normalize the elements to enable attributes on the property.
- if ((attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) {
- Handle<SeededNumberDictionary> dictionary = NormalizeElements(object);
- // Make sure that we never go back to fast case.
- dictionary->set_requires_slow_elements();
- }
-
- if (!object->map()->is_observed()) {
- return object->HasIndexedInterceptor()
- ? SetElementWithInterceptor(object, index, value, attributes,
- strict_mode, check_prototype, set_mode)
- : SetElementWithoutInterceptor(object, index, value, attributes,
- strict_mode, check_prototype, set_mode);
- }
-
- Maybe<PropertyAttributes> maybe =
- JSReceiver::GetOwnElementAttribute(object, index);
- if (!maybe.has_value) return MaybeHandle<Object>();
- PropertyAttributes old_attributes = maybe.value;
-
- Handle<Object> old_value = isolate->factory()->the_hole_value();
Handle<Object> old_length_handle;
+ if (object->IsJSArray()) {
+ CHECK(JSArray::cast(*object)->length()->ToArrayLength(&old_length));
+ if (object->map()->is_observed()) {
+ old_length_handle = handle(JSArray::cast(*object)->length(), isolate);
+ }
+ }
+
+ ElementsKind kind = object->GetElementsKind();
+ FixedArrayBase* elements = object->elements();
+ ElementsKind dictionary_kind = DICTIONARY_ELEMENTS;
+ if (IsSloppyArgumentsElements(kind)) {
+ elements = FixedArrayBase::cast(FixedArray::cast(elements)->get(1));
+ dictionary_kind = SLOW_SLOPPY_ARGUMENTS_ELEMENTS;
+ }
+
+ if (attributes != NONE) {
+ kind = dictionary_kind;
+ } else if (elements->IsSeededNumberDictionary()) {
+ kind = ShouldConvertToFastElements(*object,
+ SeededNumberDictionary::cast(elements),
+ index, &new_capacity)
+ ? BestFittingFastElementsKind(*object)
+ : dictionary_kind; // Overwrite in case of arguments.
+ } else if (ShouldConvertToSlowElements(
+ *object, static_cast<uint32_t>(elements->length()), index,
+ &new_capacity)) {
+ kind = dictionary_kind;
+ }
+
+ ElementsKind to = value->OptimalElementsKind();
+ if (IsHoleyElementsKind(kind) || !object->IsJSArray() || index > old_length) {
+ to = GetHoleyElementsKind(to);
+ kind = GetHoleyElementsKind(kind);
+ }
+ to = GetMoreGeneralElementsKind(kind, to);
+ ElementsAccessor* accessor = ElementsAccessor::ForKind(to);
+ accessor->Add(object, index, value, attributes, new_capacity);
+
+ uint32_t new_length = old_length;
Handle<Object> new_length_handle;
-
- if (old_attributes != ABSENT) {
- if (GetOwnElementAccessorPair(object, index).is_null()) {
- old_value = Object::GetElement(isolate, object, index).ToHandleChecked();
- }
- } else if (object->IsJSArray()) {
- // Store old array length in case adding an element grows the array.
- old_length_handle = handle(Handle<JSArray>::cast(object)->length(),
- isolate);
+ if (object->IsJSArray() && index >= old_length) {
+ new_length = index + 1;
+ new_length_handle = isolate->factory()->NewNumberFromUint(new_length);
+ JSArray::cast(*object)->set_length(*new_length_handle);
}
- // Check for lookup interceptor
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, result,
- object->HasIndexedInterceptor()
- ? SetElementWithInterceptor(
- object, index, value, attributes,
- strict_mode, check_prototype, set_mode)
- : SetElementWithoutInterceptor(
- object, index, value, attributes,
- strict_mode, check_prototype, set_mode),
- Object);
+ if (!old_length_handle.is_null() && new_length != old_length) {
+ // |old_length_handle| is kept null above unless the object is observed.
+ DCHECK(object->map()->is_observed());
+ Handle<JSArray> array = Handle<JSArray>::cast(object);
+ Handle<String> name = isolate->factory()->Uint32ToString(index);
- Handle<String> name = isolate->factory()->Uint32ToString(index);
- maybe = GetOwnElementAttribute(object, index);
- if (!maybe.has_value) return MaybeHandle<Object>();
- PropertyAttributes new_attributes = maybe.value;
-
- if (old_attributes == ABSENT) {
- if (object->IsJSArray() &&
- !old_length_handle->SameValue(
- Handle<JSArray>::cast(object)->length())) {
- new_length_handle = handle(Handle<JSArray>::cast(object)->length(),
- isolate);
- uint32_t old_length = 0;
- uint32_t new_length = 0;
- CHECK(old_length_handle->ToArrayIndex(&old_length));
- CHECK(new_length_handle->ToArrayIndex(&new_length));
-
- RETURN_ON_EXCEPTION(
- isolate, BeginPerformSplice(Handle<JSArray>::cast(object)), Object);
- RETURN_ON_EXCEPTION(
- isolate, EnqueueChangeRecord(object, "add", name, old_value), Object);
- RETURN_ON_EXCEPTION(
- isolate, EnqueueChangeRecord(object, "update",
- isolate->factory()->length_string(),
- old_length_handle),
- Object);
- RETURN_ON_EXCEPTION(
- isolate, EndPerformSplice(Handle<JSArray>::cast(object)), Object);
- Handle<JSArray> deleted = isolate->factory()->NewJSArray(0);
- RETURN_ON_EXCEPTION(
- isolate,
- EnqueueSpliceRecord(Handle<JSArray>::cast(object), old_length,
- deleted, new_length - old_length),
- Object);
- } else {
- RETURN_ON_EXCEPTION(
- isolate, EnqueueChangeRecord(object, "add", name, old_value), Object);
- }
- } else if (old_value->IsTheHole()) {
- RETURN_ON_EXCEPTION(
- isolate, EnqueueChangeRecord(object, "reconfigure", name, old_value),
- Object);
- } else {
- Handle<Object> new_value =
- Object::GetElement(isolate, object, index).ToHandleChecked();
- bool value_changed = !old_value->SameValue(*new_value);
- if (old_attributes != new_attributes) {
- if (!value_changed) old_value = isolate->factory()->the_hole_value();
- RETURN_ON_EXCEPTION(
- isolate, EnqueueChangeRecord(object, "reconfigure", name, old_value),
- Object);
- } else if (value_changed) {
- RETURN_ON_EXCEPTION(
- isolate, EnqueueChangeRecord(object, "update", name, old_value),
- Object);
- }
+ RETURN_ON_EXCEPTION_VALUE(isolate, BeginPerformSplice(array),
+ Nothing<bool>());
+ RETURN_ON_EXCEPTION_VALUE(
+ isolate, EnqueueChangeRecord(array, "add", name,
+ isolate->factory()->the_hole_value()),
+ Nothing<bool>());
+ RETURN_ON_EXCEPTION_VALUE(
+ isolate, EnqueueChangeRecord(array, "update",
+ isolate->factory()->length_string(),
+ old_length_handle),
+ Nothing<bool>());
+ RETURN_ON_EXCEPTION_VALUE(isolate, EndPerformSplice(array),
+ Nothing<bool>());
+ Handle<JSArray> deleted = isolate->factory()->NewJSArray(0);
+ RETURN_ON_EXCEPTION_VALUE(isolate,
+ EnqueueSpliceRecord(array, old_length, deleted,
+ new_length - old_length),
+ Nothing<bool>());
+ } else if (object->map()->is_observed()) {
+ Handle<String> name = isolate->factory()->Uint32ToString(index);
+ RETURN_ON_EXCEPTION_VALUE(
+ isolate, EnqueueChangeRecord(object, "add", name,
+ isolate->factory()->the_hole_value()),
+ Nothing<bool>());
}
- return result;
+ return Just(true);
}
-MaybeHandle<Object> JSObject::SetElementWithoutInterceptor(
- Handle<JSObject> object,
- uint32_t index,
- Handle<Object> value,
- PropertyAttributes attributes,
- StrictMode strict_mode,
- bool check_prototype,
- SetPropertyMode set_mode) {
- DCHECK(object->HasDictionaryElements() ||
- object->HasDictionaryArgumentsElements() ||
- (attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0);
- Isolate* isolate = object->GetIsolate();
- if (FLAG_trace_external_array_abuse &&
- IsExternalArrayElementsKind(object->GetElementsKind())) {
- CheckArrayAbuse(object, "external elements write", index);
- }
- if (FLAG_trace_js_array_abuse &&
- !IsExternalArrayElementsKind(object->GetElementsKind())) {
- if (object->IsJSArray()) {
- CheckArrayAbuse(object, "elements write", index, true);
- }
- }
- if (object->IsJSArray() && JSArray::WouldChangeReadOnlyLength(
- Handle<JSArray>::cast(object), index)) {
- if (strict_mode == SLOPPY) {
- return value;
- } else {
- return JSArray::ReadOnlyLengthError(Handle<JSArray>::cast(object));
- }
- }
- switch (object->GetElementsKind()) {
- case FAST_SMI_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_HOLEY_ELEMENTS:
- return SetFastElement(object, index, value, strict_mode, check_prototype);
- case FAST_DOUBLE_ELEMENTS:
- case FAST_HOLEY_DOUBLE_ELEMENTS:
- return SetFastDoubleElement(object, index, value, strict_mode,
- check_prototype);
-
-#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ELEMENTS: { \
- Handle<External##Type##Array> array( \
- External##Type##Array::cast(object->elements())); \
- return External##Type##Array::SetValue(array, index, value); \
- } \
- case TYPE##_ELEMENTS: { \
- Handle<Fixed##Type##Array> array( \
- Fixed##Type##Array::cast(object->elements())); \
- return Fixed##Type##Array::SetValue(array, index, value); \
- }
-
- TYPED_ARRAYS(TYPED_ARRAY_CASE)
-
-#undef TYPED_ARRAY_CASE
-
- case DICTIONARY_ELEMENTS:
- return SetDictionaryElement(object, index, value, attributes, strict_mode,
- check_prototype,
- set_mode);
- case SLOPPY_ARGUMENTS_ELEMENTS: {
- Handle<FixedArray> parameter_map(FixedArray::cast(object->elements()));
- uint32_t length = parameter_map->length();
- Handle<Object> probe = index < length - 2 ?
- Handle<Object>(parameter_map->get(index + 2), isolate) :
- Handle<Object>();
- if (!probe.is_null() && !probe->IsTheHole()) {
- Handle<Context> context(Context::cast(parameter_map->get(0)));
- int context_index = Handle<Smi>::cast(probe)->value();
- DCHECK(!context->get(context_index)->IsTheHole());
- context->set(context_index, *value);
- // Redefining attributes of an aliased element destroys fast aliasing.
- if (set_mode == SET_PROPERTY || attributes == NONE) return value;
- parameter_map->set_the_hole(index + 2);
- // For elements that are still writable we re-establish slow aliasing.
- if ((attributes & READ_ONLY) == 0) {
- value = Handle<Object>::cast(
- isolate->factory()->NewAliasedArgumentsEntry(context_index));
- }
- }
- Handle<FixedArray> arguments(FixedArray::cast(parameter_map->get(1)));
- if (arguments->IsDictionary()) {
- return SetDictionaryElement(object, index, value, attributes,
- strict_mode,
- check_prototype,
- set_mode);
- } else {
- return SetFastElement(object, index, value, strict_mode,
- check_prototype);
- }
- }
- }
- // All possible cases have been handled above. Add a return to avoid the
- // complaints from the compiler.
- UNREACHABLE();
- return isolate->factory()->null_value();
+bool JSArray::SetLengthWouldNormalize(uint32_t new_length) {
+ if (!HasFastElements()) return false;
+ uint32_t capacity = static_cast<uint32_t>(elements()->length());
+ uint32_t new_capacity;
+ return JSArray::SetLengthWouldNormalize(GetHeap(), new_length) &&
+ ShouldConvertToSlowElements(this, capacity, new_length - 1,
+ &new_capacity);
}
@@ -13222,7 +15976,7 @@
// If the array is huge, it's not likely to be defined in a local
// function, so we shouldn't make new instances of it very often.
uint32_t length = 0;
- CHECK(transition_info->length()->ToArrayIndex(&length));
+ CHECK(transition_info->length()->ToArrayLength(&length));
if (length <= kMaximumArrayBytesToPretransition) {
if (FLAG_trace_track_allocation_sites) {
bool is_nested = site->IsNestedSite();
@@ -13259,41 +16013,6 @@
}
-// static
-void AllocationSite::RegisterForDeoptOnTenureChange(Handle<AllocationSite> site,
- CompilationInfo* info) {
- AddDependentCompilationInfo(
- site, DependentCode::kAllocationSiteTenuringChangedGroup, info);
-}
-
-
-// static
-void AllocationSite::RegisterForDeoptOnTransitionChange(
- Handle<AllocationSite> site, CompilationInfo* info) {
- // Do nothing if the object doesn't have any useful element transitions left.
- ElementsKind kind =
- site->SitePointsToLiteral()
- ? JSObject::cast(site->transition_info())->GetElementsKind()
- : site->GetElementsKind();
- if (AllocationSite::GetMode(kind) == TRACK_ALLOCATION_SITE) {
- AddDependentCompilationInfo(
- site, DependentCode::kAllocationSiteTransitionChangedGroup, info);
- }
-}
-
-
-// static
-void AllocationSite::AddDependentCompilationInfo(
- Handle<AllocationSite> site, DependentCode::DependencyGroup group,
- CompilationInfo* info) {
- Handle<DependentCode> dep(site->dependent_code());
- Handle<DependentCode> codes =
- DependentCode::Insert(dep, group, info->object_wrapper());
- if (*codes != site->dependent_code()) site->set_dependent_code(*codes);
- info->dependencies(group)->Add(Handle<HeapObject>(*site), info->zone());
-}
-
-
const char* AllocationSite::PretenureDecisionName(PretenureDecision decision) {
switch (decision) {
case kUndecided: return "undecided";
@@ -13330,25 +16049,23 @@
void JSObject::TransitionElementsKind(Handle<JSObject> object,
ElementsKind to_kind) {
- ElementsKind from_kind = object->map()->elements_kind();
+ ElementsKind from_kind = object->GetElementsKind();
if (IsFastHoleyElementsKind(from_kind)) {
to_kind = GetHoleyElementsKind(to_kind);
}
if (from_kind == to_kind) return;
- // Don't update the site if to_kind isn't fast
- if (IsFastElementsKind(to_kind)) {
- UpdateAllocationSite(object, to_kind);
- }
- Isolate* isolate = object->GetIsolate();
- if (object->elements() == isolate->heap()->empty_fixed_array() ||
- (IsFastSmiOrObjectElementsKind(from_kind) &&
- IsFastSmiOrObjectElementsKind(to_kind)) ||
- (from_kind == FAST_DOUBLE_ELEMENTS &&
- to_kind == FAST_HOLEY_DOUBLE_ELEMENTS)) {
- DCHECK(from_kind != TERMINAL_FAST_ELEMENTS_KIND);
+ // This method should never be called for any other case.
+ DCHECK(IsFastElementsKind(from_kind));
+ DCHECK(IsFastElementsKind(to_kind));
+ DCHECK_NE(TERMINAL_FAST_ELEMENTS_KIND, from_kind);
+
+ UpdateAllocationSite(object, to_kind);
+ if (object->elements() == object->GetHeap()->empty_fixed_array() ||
+ IsFastDoubleElementsKind(from_kind) ==
+ IsFastDoubleElementsKind(to_kind)) {
// No change is needed to the elements() buffer, the transition
// only requires a map change.
Handle<Map> new_map = GetElementsTransitionMap(object, to_kind);
@@ -13357,42 +16074,14 @@
Handle<FixedArrayBase> elms(object->elements());
PrintElementsTransition(stdout, object, from_kind, elms, to_kind, elms);
}
- return;
+ } else {
+ DCHECK((IsFastSmiElementsKind(from_kind) &&
+ IsFastDoubleElementsKind(to_kind)) ||
+ (IsFastDoubleElementsKind(from_kind) &&
+ IsFastObjectElementsKind(to_kind)));
+ uint32_t c = static_cast<uint32_t>(object->elements()->length());
+ ElementsAccessor::ForKind(to_kind)->GrowCapacityAndConvert(object, c);
}
-
- Handle<FixedArrayBase> elms(object->elements());
- uint32_t capacity = static_cast<uint32_t>(elms->length());
- uint32_t length = capacity;
-
- if (object->IsJSArray()) {
- Object* raw_length = Handle<JSArray>::cast(object)->length();
- if (raw_length->IsUndefined()) {
- // If length is undefined, then JSArray is being initialized and has no
- // elements, assume a length of zero.
- length = 0;
- } else {
- CHECK(raw_length->ToArrayIndex(&length));
- }
- }
-
- if (IsFastSmiElementsKind(from_kind) &&
- IsFastDoubleElementsKind(to_kind)) {
- SetFastDoubleElementsCapacityAndLength(object, capacity, length);
- JSObject::ValidateElements(object);
- return;
- }
-
- if (IsFastDoubleElementsKind(from_kind) &&
- IsFastObjectElementsKind(to_kind)) {
- SetFastElementsCapacityAndLength(object, capacity, length,
- kDontAllowSmiElements);
- JSObject::ValidateElements(object);
- return;
- }
-
- // This method should never be called for any other case than the ones
- // handled above.
- UNREACHABLE();
}
@@ -13410,31 +16099,6 @@
}
-void JSArray::JSArrayUpdateLengthFromIndex(Handle<JSArray> array,
- uint32_t index,
- Handle<Object> value) {
- uint32_t old_len = 0;
- CHECK(array->length()->ToArrayIndex(&old_len));
- // Check to see if we need to update the length. For now, we make
- // sure that the length stays within 32-bits (unsigned).
- if (index >= old_len && index != 0xffffffff) {
- Handle<Object> len = array->GetIsolate()->factory()->NewNumber(
- static_cast<double>(index) + 1);
- array->set_length(*len);
- }
-}
-
-
-bool JSArray::IsReadOnlyLengthDescriptor(Handle<Map> jsarray_map) {
- Isolate* isolate = jsarray_map->GetIsolate();
- DCHECK(!jsarray_map->is_dictionary_map());
- LookupResult lookup(isolate);
- Handle<Name> length_string = isolate->factory()->length_string();
- jsarray_map->LookupDescriptor(NULL, *length_string, &lookup);
- return lookup.IsReadOnly();
-}
-
-
bool JSArray::HasReadOnlyLength(Handle<JSArray> array) {
LookupIterator it(array, array->GetIsolate()->factory()->length_string(),
LookupIterator::OWN_SKIP_INTERCEPTOR);
@@ -13448,243 +16112,53 @@
bool JSArray::WouldChangeReadOnlyLength(Handle<JSArray> array,
uint32_t index) {
uint32_t length = 0;
- CHECK(array->length()->ToArrayIndex(&length));
+ CHECK(array->length()->ToArrayLength(&length));
if (length <= index) return HasReadOnlyLength(array);
return false;
}
-MaybeHandle<Object> JSArray::ReadOnlyLengthError(Handle<JSArray> array) {
- Isolate* isolate = array->GetIsolate();
- Handle<Name> length = isolate->factory()->length_string();
- Handle<Object> args[2] = { length, array };
- THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
- HandleVector(args, arraysize(args))),
- Object);
-}
-
-
-MaybeHandle<Object> JSObject::GetElementWithInterceptor(Handle<JSObject> object,
- Handle<Object> receiver,
- uint32_t index,
- bool check_prototype) {
- Isolate* isolate = object->GetIsolate();
-
- // Make sure that the top context does not change when doing
- // callbacks or interceptor calls.
- AssertNoContextChange ncc(isolate);
-
- Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor(), isolate);
- if (!interceptor->getter()->IsUndefined()) {
- v8::IndexedPropertyGetterCallback getter =
- v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter());
- LOG(isolate,
- ApiIndexedPropertyAccess("interceptor-indexed-get", *object, index));
- PropertyCallbackArguments
- args(isolate, interceptor->data(), *receiver, *object);
- v8::Handle<v8::Value> result = args.Call(getter, index);
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- if (!result.IsEmpty()) {
- Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
- result_internal->VerifyApiCallResultType();
- // Rebox handle before return.
- return handle(*result_internal, isolate);
- }
- }
-
- if (!check_prototype) return MaybeHandle<Object>();
-
- ElementsAccessor* handler = object->GetElementsAccessor();
- Handle<Object> result;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, result, handler->Get(receiver, object, index),
- Object);
- if (!result->IsTheHole()) return result;
-
- PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return isolate->factory()->undefined_value();
- return Object::GetElementWithReceiver(
- isolate, PrototypeIterator::GetCurrent(iter), receiver, index);
-}
-
-
-bool JSObject::HasDenseElements() {
- int capacity = 0;
+template <typename BackingStore>
+static int FastHoleyElementsUsage(JSObject* object, BackingStore* store) {
+ int limit = object->IsJSArray()
+ ? Smi::cast(JSArray::cast(object)->length())->value()
+ : store->length();
int used = 0;
- GetElementsCapacityAndUsage(&capacity, &used);
- return (capacity == 0) || (used > (capacity / 2));
+ for (int i = 0; i < limit; ++i) {
+ if (!store->is_the_hole(i)) ++used;
+ }
+ return used;
}
-void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
- *capacity = 0;
- *used = 0;
-
- FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements());
- FixedArray* backing_store = NULL;
+int JSObject::GetFastElementsUsage() {
+ FixedArrayBase* store = elements();
switch (GetElementsKind()) {
- case SLOPPY_ARGUMENTS_ELEMENTS:
- backing_store_base =
- FixedArray::cast(FixedArray::cast(backing_store_base)->get(1));
- backing_store = FixedArray::cast(backing_store_base);
- if (backing_store->IsDictionary()) {
- SeededNumberDictionary* dictionary =
- SeededNumberDictionary::cast(backing_store);
- *capacity = dictionary->Capacity();
- *used = dictionary->NumberOfElements();
- break;
- }
- // Fall through.
case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
- if (IsJSArray()) {
- *capacity = backing_store_base->length();
- *used = Smi::cast(JSArray::cast(this)->length())->value();
- break;
- }
- // Fall through if packing is not guaranteed.
+ return IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value()
+ : store->length();
+ case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
+ store = FixedArray::cast(FixedArray::cast(store)->get(1));
+ // Fall through.
case FAST_HOLEY_SMI_ELEMENTS:
case FAST_HOLEY_ELEMENTS:
- backing_store = FixedArray::cast(backing_store_base);
- *capacity = backing_store->length();
- for (int i = 0; i < *capacity; ++i) {
- if (!backing_store->get(i)->IsTheHole()) ++(*used);
- }
- break;
- case DICTIONARY_ELEMENTS: {
- SeededNumberDictionary* dictionary = element_dictionary();
- *capacity = dictionary->Capacity();
- *used = dictionary->NumberOfElements();
- break;
- }
- case FAST_DOUBLE_ELEMENTS:
- if (IsJSArray()) {
- *capacity = backing_store_base->length();
- *used = Smi::cast(JSArray::cast(this)->length())->value();
- break;
- }
- // Fall through if packing is not guaranteed.
- case FAST_HOLEY_DOUBLE_ELEMENTS: {
- *capacity = elements()->length();
- if (*capacity == 0) break;
- FixedDoubleArray * elms = FixedDoubleArray::cast(elements());
- for (int i = 0; i < *capacity; i++) {
- if (!elms->is_the_hole(i)) ++(*used);
- }
- break;
- }
+ return FastHoleyElementsUsage(this, FixedArray::cast(store));
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ if (elements()->length() == 0) return 0;
+ return FastHoleyElementsUsage(this, FixedDoubleArray::cast(store));
+ case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ELEMENTS: \
case TYPE##_ELEMENTS: \
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
- {
- // External arrays are considered 100% used.
- FixedArrayBase* external_array = FixedArrayBase::cast(elements());
- *capacity = external_array->length();
- *used = external_array->length();
- break;
- }
+ UNREACHABLE();
}
-}
-
-
-bool JSObject::WouldConvertToSlowElements(Handle<Object> key) {
- uint32_t index;
- if (HasFastElements() && key->ToArrayIndex(&index)) {
- Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
- uint32_t capacity = static_cast<uint32_t>(backing_store->length());
- if (index >= capacity) {
- if ((index - capacity) >= kMaxGap) return true;
- uint32_t new_capacity = NewElementsCapacity(index + 1);
- return ShouldConvertToSlowElements(new_capacity);
- }
- }
- return false;
-}
-
-
-bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
- STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
- kMaxUncheckedFastElementsLength);
- if (new_capacity <= kMaxUncheckedOldFastElementsLength ||
- (new_capacity <= kMaxUncheckedFastElementsLength &&
- GetHeap()->InNewSpace(this))) {
- return false;
- }
- // If the fast-case backing storage takes up roughly three times as
- // much space (in machine words) as a dictionary backing storage
- // would, the object should have slow elements.
- int old_capacity = 0;
- int used_elements = 0;
- GetElementsCapacityAndUsage(&old_capacity, &used_elements);
- int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) *
- SeededNumberDictionary::kEntrySize;
- return 3 * dictionary_size <= new_capacity;
-}
-
-
-bool JSObject::ShouldConvertToFastElements() {
- DCHECK(HasDictionaryElements() || HasDictionaryArgumentsElements());
- // If the elements are sparse, we should not go back to fast case.
- if (!HasDenseElements()) return false;
- // An object requiring access checks is never allowed to have fast
- // elements. If it had fast elements we would skip security checks.
- if (IsAccessCheckNeeded()) return false;
- // Observed objects may not go to fast mode because they rely on map checks,
- // and for fast element accesses we sometimes check element kinds only.
- if (map()->is_observed()) return false;
-
- FixedArray* elements = FixedArray::cast(this->elements());
- SeededNumberDictionary* dictionary = NULL;
- if (elements->map() == GetHeap()->sloppy_arguments_elements_map()) {
- dictionary = SeededNumberDictionary::cast(elements->get(1));
- } else {
- dictionary = SeededNumberDictionary::cast(elements);
- }
- // If an element has been added at a very high index in the elements
- // dictionary, we cannot go back to fast case.
- if (dictionary->requires_slow_elements()) return false;
- // If the dictionary backing storage takes up roughly half as much
- // space (in machine words) as a fast-case backing storage would,
- // the object should have fast elements.
- uint32_t array_size = 0;
- if (IsJSArray()) {
- CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size));
- } else {
- array_size = dictionary->max_number_key();
- }
- uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
- SeededNumberDictionary::kEntrySize;
- return 2 * dictionary_size >= array_size;
-}
-
-
-bool JSObject::ShouldConvertToFastDoubleElements(
- bool* has_smi_only_elements) {
- *has_smi_only_elements = false;
- if (HasSloppyArgumentsElements()) return false;
- if (FLAG_unbox_double_arrays) {
- DCHECK(HasDictionaryElements());
- SeededNumberDictionary* dictionary = element_dictionary();
- bool found_double = false;
- for (int i = 0; i < dictionary->Capacity(); i++) {
- Object* key = dictionary->KeyAt(i);
- if (key->IsNumber()) {
- Object* value = dictionary->ValueAt(i);
- if (!value->IsNumber()) return false;
- if (!value->IsSmi()) {
- found_double = true;
- }
- }
- }
- *has_smi_only_elements = !found_double;
- return found_double;
- } else {
- return false;
- }
+ return 0;
}
@@ -13696,17 +16170,17 @@
#ifdef OBJECT_PRINT
template <typename Derived, typename Shape, typename Key>
void Dictionary<Derived, Shape, Key>::Print(std::ostream& os) { // NOLINT
- int capacity = DerivedHashTable::Capacity();
+ int capacity = this->Capacity();
for (int i = 0; i < capacity; i++) {
- Object* k = DerivedHashTable::KeyAt(i);
- if (DerivedHashTable::IsKey(k)) {
- os << " ";
+ Object* k = this->KeyAt(i);
+ if (this->IsKey(k)) {
+ os << "\n ";
if (k->IsString()) {
String::cast(k)->StringPrint(os);
} else {
os << Brief(k);
}
- os << ": " << Brief(ValueAt(i)) << " " << DetailsAt(i) << "\n";
+ os << ": " << Brief(this->ValueAt(i)) << " " << this->DetailsAt(i);
}
}
}
@@ -13716,13 +16190,13 @@
template<typename Derived, typename Shape, typename Key>
void Dictionary<Derived, Shape, Key>::CopyValuesTo(FixedArray* elements) {
int pos = 0;
- int capacity = DerivedHashTable::Capacity();
+ int capacity = this->Capacity();
DisallowHeapAllocation no_gc;
WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
for (int i = 0; i < capacity; i++) {
- Object* k = Dictionary::KeyAt(i);
- if (Dictionary::IsKey(k)) {
- elements->set(pos++, ValueAt(i), mode);
+ Object* k = this->KeyAt(i);
+ if (this->IsKey(k)) {
+ elements->set(pos++, this->ValueAt(i), mode);
}
}
DCHECK(pos == elements->length());
@@ -13731,7 +16205,7 @@
InterceptorInfo* JSObject::GetNamedInterceptor() {
DCHECK(map()->has_named_interceptor());
- JSFunction* constructor = JSFunction::cast(map()->constructor());
+ JSFunction* constructor = JSFunction::cast(map()->GetConstructor());
DCHECK(constructor->shared()->IsApiFunction());
Object* result =
constructor->shared()->get_api_func_data()->named_property_handler();
@@ -13739,155 +16213,82 @@
}
-InterceptorInfo* JSObject::GetIndexedInterceptor() {
- DCHECK(map()->has_indexed_interceptor());
- JSFunction* constructor = JSFunction::cast(map()->constructor());
- DCHECK(constructor->shared()->IsApiFunction());
- Object* result =
- constructor->shared()->get_api_func_data()->indexed_property_handler();
- return InterceptorInfo::cast(result);
-}
+MaybeHandle<Object> JSObject::GetPropertyWithInterceptor(LookupIterator* it,
+ 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);
-
-MaybeHandle<Object> JSObject::GetPropertyWithInterceptor(
- Handle<JSObject> holder,
- Handle<Object> receiver,
- Handle<Name> name) {
- Isolate* isolate = holder->GetIsolate();
-
- Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor(), isolate);
- if (interceptor->getter()->IsUndefined()) return MaybeHandle<Object>();
-
- if (name->IsSymbol() && !interceptor->can_intercept_symbols()) {
- return MaybeHandle<Object>();
+ DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state());
+ Handle<InterceptorInfo> interceptor = it->GetInterceptor();
+ if (interceptor->getter()->IsUndefined()) {
+ return isolate->factory()->undefined_value();
}
- v8::GenericNamedPropertyGetterCallback getter =
- v8::ToCData<v8::GenericNamedPropertyGetterCallback>(
- interceptor->getter());
- LOG(isolate,
- ApiNamedPropertyAccess("interceptor-named-get", *holder, *name));
- PropertyCallbackArguments
- args(isolate, interceptor->data(), *receiver, *holder);
- v8::Handle<v8::Value> result = args.Call(getter, v8::Utils::ToLocal(name));
- RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
- if (result.IsEmpty()) return MaybeHandle<Object>();
+ Handle<JSObject> holder = it->GetHolder<JSObject>();
+ v8::Local<v8::Value> result;
+ PropertyCallbackArguments args(isolate, interceptor->data(),
+ *it->GetReceiver(), *holder);
+ if (it->IsElement()) {
+ uint32_t index = it->index();
+ v8::IndexedPropertyGetterCallback getter =
+ v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-get", *holder, index));
+ 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());
+ LOG(isolate,
+ ApiNamedPropertyAccess("interceptor-named-get", *holder, *name));
+ result = args.Call(getter, v8::Utils::ToLocal(name));
+ }
+
+ RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
+ if (result.IsEmpty()) return isolate->factory()->undefined_value();
Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
result_internal->VerifyApiCallResultType();
+ *done = true;
// Rebox handle before return
return handle(*result_internal, isolate);
}
-// Compute the property keys from the interceptor.
-MaybeHandle<JSObject> JSObject::GetKeysForNamedInterceptor(
- Handle<JSObject> object, Handle<JSReceiver> receiver) {
- Isolate* isolate = receiver->GetIsolate();
- Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor());
- PropertyCallbackArguments
- args(isolate, interceptor->data(), *receiver, *object);
- v8::Handle<v8::Object> result;
- if (!interceptor->enumerator()->IsUndefined()) {
- v8::GenericNamedPropertyEnumeratorCallback enum_fun =
- v8::ToCData<v8::GenericNamedPropertyEnumeratorCallback>(
- interceptor->enumerator());
- LOG(isolate, ApiObjectAccess("interceptor-named-enum", *object));
- result = args.Call(enum_fun);
- }
- if (result.IsEmpty()) return MaybeHandle<JSObject>();
-#if ENABLE_EXTRA_CHECKS
- CHECK(v8::Utils::OpenHandle(*result)->IsJSArray() ||
- v8::Utils::OpenHandle(*result)->HasSloppyArgumentsElements());
-#endif
- // Rebox before returning.
- return handle(*v8::Utils::OpenHandle(*result), isolate);
-}
-
-
-// Compute the element keys from the interceptor.
-MaybeHandle<JSObject> JSObject::GetKeysForIndexedInterceptor(
- Handle<JSObject> object, Handle<JSReceiver> receiver) {
- Isolate* isolate = receiver->GetIsolate();
- Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor());
- PropertyCallbackArguments
- args(isolate, interceptor->data(), *receiver, *object);
- v8::Handle<v8::Object> result;
- if (!interceptor->enumerator()->IsUndefined()) {
- v8::IndexedPropertyEnumeratorCallback enum_fun =
- v8::ToCData<v8::IndexedPropertyEnumeratorCallback>(
- interceptor->enumerator());
- LOG(isolate, ApiObjectAccess("interceptor-indexed-enum", *object));
- result = args.Call(enum_fun);
- }
- if (result.IsEmpty()) return MaybeHandle<JSObject>();
-#if ENABLE_EXTRA_CHECKS
- CHECK(v8::Utils::OpenHandle(*result)->IsJSArray() ||
- v8::Utils::OpenHandle(*result)->HasSloppyArgumentsElements());
-#endif
- // Rebox before returning.
- return handle(*v8::Utils::OpenHandle(*result), isolate);
-}
-
-
Maybe<bool> JSObject::HasRealNamedProperty(Handle<JSObject> object,
- Handle<Name> key) {
- LookupIterator it(object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
- Maybe<PropertyAttributes> maybe_result = GetPropertyAttributes(&it);
- if (!maybe_result.has_value) return Maybe<bool>();
- return maybe(it.IsFound());
+ Handle<Name> name) {
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ name->GetIsolate(), object, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
+ return HasProperty(&it);
}
Maybe<bool> JSObject::HasRealElementProperty(Handle<JSObject> object,
uint32_t index) {
Isolate* isolate = object->GetIsolate();
- HandleScope scope(isolate);
- // Check access rights if needed.
- if (object->IsAccessCheckNeeded()) {
- if (!isolate->MayIndexedAccess(object, index, v8::ACCESS_HAS)) {
- isolate->ReportFailedAccessCheck(object, v8::ACCESS_HAS);
- RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe<bool>());
- return maybe(false);
- }
- }
-
- if (object->IsJSGlobalProxy()) {
- HandleScope scope(isolate);
- PrototypeIterator iter(isolate, object);
- if (iter.IsAtEnd()) return maybe(false);
- DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
- return HasRealElementProperty(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index);
- }
-
- Maybe<PropertyAttributes> result =
- GetElementAttributeWithoutInterceptor(object, object, index, false);
- if (!result.has_value) return Maybe<bool>();
- return maybe(result.value != ABSENT);
+ LookupIterator it(isolate, object, index,
+ LookupIterator::OWN_SKIP_INTERCEPTOR);
+ return HasProperty(&it);
}
Maybe<bool> JSObject::HasRealNamedCallbackProperty(Handle<JSObject> object,
- Handle<Name> key) {
- LookupIterator it(object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
+ Handle<Name> name) {
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ name->GetIsolate(), object, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
Maybe<PropertyAttributes> maybe_result = GetPropertyAttributes(&it);
- if (!maybe_result.has_value) return Maybe<bool>();
- return maybe(it.state() == LookupIterator::ACCESSOR);
-}
-
-
-int JSObject::NumberOfOwnProperties(PropertyAttributes filter) {
- if (HasFastProperties()) {
- Map* map = this->map();
- if (filter == NONE) return map->NumberOfOwnDescriptors();
- if (filter & DONT_ENUM) {
- int result = map->EnumLength();
- if (result != kInvalidEnumCacheSentinel) return result;
- }
- return map->NumberOfDescribedProperties(OWN_DESCRIPTORS, filter);
- }
- return property_dictionary()->NumberOfElementsFilterAttributes(filter);
+ return maybe_result.IsJust() ? Just(it.state() == LookupIterator::ACCESSOR)
+ : Nothing<bool>();
}
@@ -14004,52 +16405,90 @@
}
-// Fill in the names of own properties into the supplied storage. The main
-// purpose of this function is to provide reflection information for the object
-// mirrors.
-void JSObject::GetOwnPropertyNames(
- FixedArray* storage, int index, PropertyAttributes filter) {
- DCHECK(storage->length() >= (NumberOfOwnProperties(filter) - index));
+void JSObject::CollectOwnPropertyNames(KeyAccumulator* keys,
+ PropertyFilter filter) {
if (HasFastProperties()) {
int real_size = map()->NumberOfOwnDescriptors();
- DescriptorArray* descs = map()->instance_descriptors();
+ Handle<DescriptorArray> descs(map()->instance_descriptors());
for (int i = 0; i < real_size; i++) {
- if ((descs->GetDetails(i).attributes() & filter) == 0 &&
- !FilterKey(descs->GetKey(i), filter)) {
- storage->set(index++, descs->GetKey(i));
+ PropertyDetails details = descs->GetDetails(i);
+ if ((details.attributes() & filter) != 0) continue;
+ if (filter & ONLY_ALL_CAN_READ) {
+ if (details.kind() != kAccessor) continue;
+ Object* accessors = descs->GetValue(i);
+ if (!accessors->IsAccessorInfo()) continue;
+ if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
}
+ Name* key = descs->GetKey(i);
+ if (key->FilterKey(filter)) continue;
+ keys->AddKey(key);
}
+ } else if (IsJSGlobalObject()) {
+ GlobalDictionary::CollectKeysTo(handle(global_dictionary()), keys, filter);
} else {
- property_dictionary()->CopyKeysTo(storage,
- index,
- filter,
- NameDictionary::UNSORTED);
+ NameDictionary::CollectKeysTo(handle(property_dictionary()), keys, filter);
}
}
-int JSObject::NumberOfOwnElements(PropertyAttributes filter) {
+int JSObject::NumberOfOwnElements(PropertyFilter filter) {
+ // Fast case for objects with no elements.
+ if (!IsJSValue() && HasFastElements()) {
+ uint32_t length =
+ IsJSArray()
+ ? static_cast<uint32_t>(
+ Smi::cast(JSArray::cast(this)->length())->value())
+ : static_cast<uint32_t>(FixedArrayBase::cast(elements())->length());
+ if (length == 0) return 0;
+ }
+ // Compute the number of enumerable elements.
return GetOwnElementKeys(NULL, filter);
}
-int JSObject::NumberOfEnumElements() {
- // Fast case for objects with no elements.
- if (!IsJSValue() && HasFastObjectElements()) {
- uint32_t length = IsJSArray() ?
- static_cast<uint32_t>(
- Smi::cast(JSArray::cast(this)->length())->value()) :
- static_cast<uint32_t>(FixedArray::cast(elements())->length());
- if (length == 0) return 0;
+void JSObject::CollectOwnElementKeys(Handle<JSObject> object,
+ KeyAccumulator* keys,
+ PropertyFilter filter) {
+ if (filter & SKIP_STRINGS) return;
+ uint32_t string_keys = 0;
+
+ // If this is a String wrapper, add the string indices first,
+ // as they're guaranteed to precede the elements in numerical order
+ // and ascending order is required by ECMA-262, 6th, 9.1.12.
+ if (object->IsJSValue()) {
+ Object* val = JSValue::cast(*object)->value();
+ if (val->IsString() && (filter & ONLY_ALL_CAN_READ) == 0) {
+ String* str = String::cast(val);
+ string_keys = str->length();
+ for (uint32_t i = 0; i < string_keys; i++) {
+ keys->AddKey(i);
+ }
+ }
}
- // Compute the number of enumerable elements.
- return NumberOfOwnElements(static_cast<PropertyAttributes>(DONT_ENUM));
+ ElementsAccessor* accessor = object->GetElementsAccessor();
+ accessor->CollectElementIndices(object, keys, kMaxUInt32, filter, 0);
}
-int JSObject::GetOwnElementKeys(FixedArray* storage,
- PropertyAttributes filter) {
+int JSObject::GetOwnElementKeys(FixedArray* storage, PropertyFilter filter) {
int counter = 0;
+
+ // If this is a String wrapper, add the string indices first,
+ // as they're guaranteed to precede the elements in numerical order
+ // and ascending order is required by ECMA-262, 6th, 9.1.12.
+ if (IsJSValue()) {
+ Object* val = JSValue::cast(this)->value();
+ if (val->IsString()) {
+ String* str = String::cast(val);
+ if (storage) {
+ for (int i = 0; i < str->length(); i++) {
+ storage->set(counter + i, Smi::FromInt(i));
+ }
+ }
+ counter += str->length();
+ }
+ }
+
switch (GetElementsKind()) {
case FAST_SMI_ELEMENTS:
case FAST_ELEMENTS:
@@ -14087,7 +16526,6 @@
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ELEMENTS: \
case TYPE##_ELEMENTS: \
TYPED_ARRAYS(TYPED_ARRAY_CASE)
@@ -14106,14 +16544,14 @@
case DICTIONARY_ELEMENTS: {
if (storage != NULL) {
- element_dictionary()->CopyKeysTo(storage,
- filter,
+ element_dictionary()->CopyKeysTo(storage, counter, filter,
SeededNumberDictionary::SORTED);
}
counter += element_dictionary()->NumberOfElementsFilterAttributes(filter);
break;
}
- case SLOPPY_ARGUMENTS_ELEMENTS: {
+ case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
+ case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
FixedArray* parameter_map = FixedArray::cast(elements());
int mapped_length = parameter_map->length() - 2;
FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
@@ -14123,8 +16561,8 @@
SeededNumberDictionary* dictionary =
SeededNumberDictionary::cast(arguments);
if (storage != NULL) {
- dictionary->CopyKeysTo(
- storage, filter, SeededNumberDictionary::UNSORTED);
+ dictionary->CopyKeysTo(storage, counter, filter,
+ SeededNumberDictionary::UNSORTED);
}
counter += dictionary->NumberOfElementsFilterAttributes(filter);
for (int i = 0; i < mapped_length; ++i) {
@@ -14156,25 +16594,41 @@
}
}
- if (this->IsJSValue()) {
- Object* val = JSValue::cast(this)->value();
- if (val->IsString()) {
- String* str = String::cast(val);
- if (storage) {
- for (int i = 0; i < str->length(); i++) {
- storage->set(counter + i, Smi::FromInt(i));
- }
- }
- counter += str->length();
- }
- }
DCHECK(!storage || storage->length() == counter);
return counter;
}
-int JSObject::GetEnumElementKeys(FixedArray* storage) {
- return GetOwnElementKeys(storage, static_cast<PropertyAttributes>(DONT_ENUM));
+MaybeHandle<String> Object::ObjectProtoToString(Isolate* isolate,
+ Handle<Object> object) {
+ if (object->IsUndefined()) return isolate->factory()->undefined_to_string();
+ if (object->IsNull()) return isolate->factory()->null_to_string();
+
+ Handle<JSReceiver> receiver;
+ CHECK(Object::ToObject(isolate, object).ToHandle(&receiver));
+
+ Handle<String> tag;
+ if (FLAG_harmony_tostring) {
+ Handle<Object> to_string_tag;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, to_string_tag,
+ GetProperty(receiver, isolate->factory()->to_string_tag_symbol()),
+ String);
+ if (to_string_tag->IsString()) {
+ tag = Handle<String>::cast(to_string_tag);
+ }
+ }
+
+ if (tag.is_null()) {
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, tag,
+ JSReceiver::BuiltinStringTag(receiver), String);
+ }
+
+ IncrementalStringBuilder builder(isolate);
+ builder.AppendCString("[object ");
+ builder.AppendString(tag);
+ builder.AppendCharacter(']');
+ return builder.Finish();
}
@@ -14206,16 +16660,14 @@
// StringSharedKeys are used as keys in the eval cache.
class StringSharedKey : public HashTableKey {
public:
- StringSharedKey(Handle<String> source,
- Handle<SharedFunctionInfo> shared,
- StrictMode strict_mode,
- int scope_position)
+ StringSharedKey(Handle<String> source, Handle<SharedFunctionInfo> shared,
+ LanguageMode language_mode, int scope_position)
: source_(source),
shared_(shared),
- strict_mode_(strict_mode),
- scope_position_(scope_position) { }
+ language_mode_(language_mode),
+ scope_position_(scope_position) {}
- bool IsMatch(Object* other) OVERRIDE {
+ bool IsMatch(Object* other) override {
DisallowHeapAllocation no_allocation;
if (!other->IsFixedArray()) {
if (!other->IsNumber()) return false;
@@ -14225,10 +16677,10 @@
FixedArray* other_array = FixedArray::cast(other);
SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0));
if (shared != *shared_) return false;
- int strict_unchecked = Smi::cast(other_array->get(2))->value();
- DCHECK(strict_unchecked == SLOPPY || strict_unchecked == STRICT);
- StrictMode strict_mode = static_cast<StrictMode>(strict_unchecked);
- if (strict_mode != strict_mode_) return false;
+ int language_unchecked = Smi::cast(other_array->get(2))->value();
+ DCHECK(is_valid_language_mode(language_unchecked));
+ LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked);
+ if (language_mode != language_mode_) return false;
int scope_position = Smi::cast(other_array->get(3))->value();
if (scope_position != scope_position_) return false;
String* source = String::cast(other_array->get(1));
@@ -14237,7 +16689,7 @@
static uint32_t StringSharedHashHelper(String* source,
SharedFunctionInfo* shared,
- StrictMode strict_mode,
+ LanguageMode language_mode,
int scope_position) {
uint32_t hash = source->Hash();
if (shared->HasSourceCode()) {
@@ -14248,18 +16700,20 @@
// collection.
Script* script(Script::cast(shared->script()));
hash ^= String::cast(script->source())->Hash();
- if (strict_mode == STRICT) hash ^= 0x8000;
+ STATIC_ASSERT(LANGUAGE_END == 3);
+ if (is_strict(language_mode)) hash ^= 0x8000;
+ if (is_strong(language_mode)) hash ^= 0x10000;
hash += scope_position;
}
return hash;
}
- uint32_t Hash() OVERRIDE {
- return StringSharedHashHelper(*source_, *shared_, strict_mode_,
+ uint32_t Hash() override {
+ return StringSharedHashHelper(*source_, *shared_, language_mode_,
scope_position_);
}
- uint32_t HashForObject(Object* obj) OVERRIDE {
+ uint32_t HashForObject(Object* obj) override {
DisallowHeapAllocation no_allocation;
if (obj->IsNumber()) {
return static_cast<uint32_t>(obj->Number());
@@ -14267,20 +16721,20 @@
FixedArray* other_array = FixedArray::cast(obj);
SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0));
String* source = String::cast(other_array->get(1));
- int strict_unchecked = Smi::cast(other_array->get(2))->value();
- DCHECK(strict_unchecked == SLOPPY || strict_unchecked == STRICT);
- StrictMode strict_mode = static_cast<StrictMode>(strict_unchecked);
+ int language_unchecked = Smi::cast(other_array->get(2))->value();
+ DCHECK(is_valid_language_mode(language_unchecked));
+ LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked);
int scope_position = Smi::cast(other_array->get(3))->value();
- return StringSharedHashHelper(
- source, shared, strict_mode, scope_position);
+ return StringSharedHashHelper(source, shared, language_mode,
+ scope_position);
}
- Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
+ Handle<Object> AsHandle(Isolate* isolate) override {
Handle<FixedArray> array = isolate->factory()->NewFixedArray(4);
array->set(0, *shared_);
array->set(1, *source_);
- array->set(2, Smi::FromInt(strict_mode_));
+ array->set(2, Smi::FromInt(language_mode_));
array->set(3, Smi::FromInt(scope_position_));
return array;
}
@@ -14288,38 +16742,223 @@
private:
Handle<String> source_;
Handle<SharedFunctionInfo> shared_;
- StrictMode strict_mode_;
+ LanguageMode language_mode_;
int scope_position_;
};
+namespace {
+
+JSRegExp::Flags RegExpFlagsFromString(Handle<String> flags, bool* success) {
+ JSRegExp::Flags value = JSRegExp::kNone;
+ int length = flags->length();
+ // A longer flags string cannot be valid.
+ if (length > 5) return JSRegExp::Flags(0);
+ for (int i = 0; i < length; i++) {
+ JSRegExp::Flag flag = JSRegExp::kNone;
+ switch (flags->Get(i)) {
+ case 'g':
+ flag = JSRegExp::kGlobal;
+ break;
+ case 'i':
+ flag = JSRegExp::kIgnoreCase;
+ break;
+ case 'm':
+ flag = JSRegExp::kMultiline;
+ break;
+ case 'u':
+ if (!FLAG_harmony_unicode_regexps) return JSRegExp::Flags(0);
+ flag = JSRegExp::kUnicode;
+ break;
+ case 'y':
+ if (!FLAG_harmony_regexps) return JSRegExp::Flags(0);
+ flag = JSRegExp::kSticky;
+ break;
+ default:
+ return JSRegExp::Flags(0);
+ }
+ // Duplicate flag.
+ if (value & flag) return JSRegExp::Flags(0);
+ value |= flag;
+ }
+ *success = true;
+ return value;
+}
+
+} // namespace
+
+
+// static
+MaybeHandle<JSRegExp> JSRegExp::New(Handle<String> pattern, Flags flags) {
+ Isolate* isolate = pattern->GetIsolate();
+ Handle<JSFunction> constructor = isolate->regexp_function();
+ Handle<JSRegExp> regexp =
+ Handle<JSRegExp>::cast(isolate->factory()->NewJSObject(constructor));
+
+ return JSRegExp::Initialize(regexp, pattern, flags);
+}
+
+
+// static
+MaybeHandle<JSRegExp> JSRegExp::New(Handle<String> pattern,
+ Handle<String> flags_string) {
+ Isolate* isolate = pattern->GetIsolate();
+ bool success = false;
+ Flags flags = RegExpFlagsFromString(flags_string, &success);
+ if (!success) {
+ THROW_NEW_ERROR(
+ isolate,
+ NewSyntaxError(MessageTemplate::kInvalidRegExpFlags, flags_string),
+ JSRegExp);
+ }
+ return New(pattern, flags);
+}
+
+
+// static
+Handle<JSRegExp> JSRegExp::Copy(Handle<JSRegExp> regexp) {
+ Isolate* const isolate = regexp->GetIsolate();
+ return Handle<JSRegExp>::cast(isolate->factory()->CopyJSObject(regexp));
+}
+
+
+template <typename Char>
+inline int CountRequiredEscapes(Handle<String> source) {
+ DisallowHeapAllocation no_gc;
+ int escapes = 0;
+ Vector<const Char> src = source->GetCharVector<Char>();
+ for (int i = 0; i < src.length(); i++) {
+ if (src[i] == '/' && (i == 0 || src[i - 1] != '\\')) escapes++;
+ }
+ return escapes;
+}
+
+
+template <typename Char, typename StringType>
+inline Handle<StringType> WriteEscapedRegExpSource(Handle<String> source,
+ Handle<StringType> result) {
+ DisallowHeapAllocation no_gc;
+ Vector<const Char> src = source->GetCharVector<Char>();
+ Vector<Char> dst(result->GetChars(), result->length());
+ int s = 0;
+ int d = 0;
+ while (s < src.length()) {
+ if (src[s] == '/' && (s == 0 || src[s - 1] != '\\')) dst[d++] = '\\';
+ dst[d++] = src[s++];
+ }
+ DCHECK_EQ(result->length(), d);
+ return result;
+}
+
+
+MaybeHandle<String> EscapeRegExpSource(Isolate* isolate,
+ Handle<String> source) {
+ String::Flatten(source);
+ if (source->length() == 0) return isolate->factory()->query_colon_string();
+ bool one_byte = source->IsOneByteRepresentationUnderneath();
+ int escapes = one_byte ? CountRequiredEscapes<uint8_t>(source)
+ : CountRequiredEscapes<uc16>(source);
+ if (escapes == 0) return source;
+ int length = source->length() + escapes;
+ if (one_byte) {
+ Handle<SeqOneByteString> result;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
+ isolate->factory()->NewRawOneByteString(length),
+ String);
+ return WriteEscapedRegExpSource<uint8_t>(source, result);
+ } else {
+ Handle<SeqTwoByteString> result;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
+ isolate->factory()->NewRawTwoByteString(length),
+ String);
+ return WriteEscapedRegExpSource<uc16>(source, result);
+ }
+}
+
+
+// static
+MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp,
+ Handle<String> source,
+ Handle<String> flags_string) {
+ Isolate* isolate = source->GetIsolate();
+ bool success = false;
+ Flags flags = RegExpFlagsFromString(flags_string, &success);
+ if (!success) {
+ THROW_NEW_ERROR(
+ isolate,
+ NewSyntaxError(MessageTemplate::kInvalidRegExpFlags, flags_string),
+ JSRegExp);
+ }
+ return Initialize(regexp, source, flags);
+}
+
+
+// static
+MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp,
+ Handle<String> source, Flags flags) {
+ Isolate* isolate = regexp->GetIsolate();
+ Factory* factory = isolate->factory();
+ // If source is the empty string we set it to "(?:)" instead as
+ // suggested by ECMA-262, 5th, section 15.10.4.1.
+ if (source->length() == 0) source = factory->query_colon_string();
+
+ Handle<String> escaped_source;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, escaped_source,
+ EscapeRegExpSource(isolate, source), JSRegExp);
+
+ regexp->set_source(*escaped_source);
+ regexp->set_flags(Smi::FromInt(flags));
+
+ Map* map = regexp->map();
+ Object* constructor = map->GetConstructor();
+ if (constructor->IsJSFunction() &&
+ JSFunction::cast(constructor)->initial_map() == map) {
+ // If we still have the original map, set in-object properties directly.
+ regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
+ Smi::FromInt(0), SKIP_WRITE_BARRIER);
+ } else {
+ // Map has changed, so use generic, but slower, method.
+ PropertyAttributes writable =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
+ JSObject::SetOwnPropertyIgnoreAttributes(
+ regexp, factory->last_index_string(),
+ Handle<Smi>(Smi::FromInt(0), isolate), writable)
+ .Check();
+ }
+
+ RETURN_ON_EXCEPTION(isolate, RegExpImpl::Compile(regexp, source, flags),
+ JSRegExp);
+
+ return regexp;
+}
+
+
// RegExpKey carries the source and flags of a regular expression as key.
class RegExpKey : public HashTableKey {
public:
RegExpKey(Handle<String> string, JSRegExp::Flags flags)
- : string_(string),
- flags_(Smi::FromInt(flags.value())) { }
+ : string_(string), flags_(Smi::FromInt(flags)) {}
// Rather than storing the key in the hash table, a pointer to the
// stored value is stored where the key should be. IsMatch then
// compares the search key to the found object, rather than comparing
// a key to a key.
- bool IsMatch(Object* obj) OVERRIDE {
+ bool IsMatch(Object* obj) override {
FixedArray* val = FixedArray::cast(obj);
return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
&& (flags_ == val->get(JSRegExp::kFlagsIndex));
}
- uint32_t Hash() OVERRIDE { return RegExpHash(*string_, flags_); }
+ uint32_t Hash() override { return RegExpHash(*string_, flags_); }
- Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
+ Handle<Object> AsHandle(Isolate* isolate) override {
// Plain hash maps, which is where regexp keys are used, don't
// use this function.
UNREACHABLE();
return MaybeHandle<Object>().ToHandleChecked();
}
- uint32_t HashForObject(Object* obj) OVERRIDE {
+ uint32_t HashForObject(Object* obj) override {
FixedArray* val = FixedArray::cast(obj);
return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
Smi::cast(val->get(JSRegExp::kFlagsIndex)));
@@ -14365,17 +17004,17 @@
explicit InternalizedStringKey(Handle<String> string)
: string_(string) { }
- bool IsMatch(Object* string) OVERRIDE {
+ bool IsMatch(Object* string) override {
return String::cast(string)->Equals(*string_);
}
- uint32_t Hash() OVERRIDE { return string_->Hash(); }
+ uint32_t Hash() override { return string_->Hash(); }
- uint32_t HashForObject(Object* other) OVERRIDE {
+ uint32_t HashForObject(Object* other) override {
return String::cast(other)->Hash();
}
- Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
+ Handle<Object> AsHandle(Isolate* isolate) override {
// Internalize the string if possible.
MaybeHandle<Map> maybe_map =
isolate->factory()->InternalizedStringMapForString(string_);
@@ -14400,15 +17039,14 @@
template<typename Derived, typename Shape, typename Key>
void HashTable<Derived, Shape, Key>::IteratePrefix(ObjectVisitor* v) {
- IteratePointers(v, 0, kElementsStartOffset);
+ BodyDescriptorBase::IteratePointers(this, 0, kElementsStartOffset, v);
}
template<typename Derived, typename Shape, typename Key>
void HashTable<Derived, Shape, Key>::IterateElements(ObjectVisitor* v) {
- IteratePointers(v,
- kElementsStartOffset,
- kHeaderSize + length() * kPointerSize);
+ BodyDescriptorBase::IteratePointers(this, kElementsStartOffset,
+ kHeaderSize + length() * kPointerSize, v);
}
@@ -14420,6 +17058,7 @@
PretenureFlag pretenure) {
DCHECK(0 <= at_least_space_for);
DCHECK(!capacity_option || base::bits::IsPowerOfTwo32(at_least_space_for));
+
int capacity = (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY)
? at_least_space_for
: ComputeCapacity(at_least_space_for);
@@ -14441,9 +17080,10 @@
// Find entry for key otherwise return kNotFound.
-int NameDictionary::FindEntry(Handle<Name> key) {
+template <typename Derived, typename Shape>
+int NameDictionaryBase<Derived, Shape>::FindEntry(Handle<Name> key) {
if (!key->IsUniqueName()) {
- return DerivedHashTable::FindEntry(key);
+ return DerivedDictionary::FindEntry(key);
}
// Optimized for unique names. Knowledge of the key type allows:
@@ -14456,13 +17096,13 @@
// boost a certain style of code).
// EnsureCapacity will guarantee the hash table is never full.
- uint32_t capacity = Capacity();
- uint32_t entry = FirstProbe(key->Hash(), capacity);
+ uint32_t capacity = this->Capacity();
+ uint32_t entry = Derived::FirstProbe(key->Hash(), capacity);
uint32_t count = 1;
while (true) {
- int index = EntryToIndex(entry);
- Object* element = get(index);
+ int index = Derived::EntryToIndex(entry);
+ Object* element = this->get(index);
if (element->IsUndefined()) break; // Empty entry.
if (*key == element) return entry;
if (!element->IsUniqueName() &&
@@ -14470,13 +17110,13 @@
Name::cast(element)->Equals(*key)) {
// Replace a key that is a non-internalized string by the equivalent
// internalized string for faster further lookups.
- set(index, *key);
+ this->set(index, *key);
return entry;
}
DCHECK(element->IsTheHole() || !Name::cast(element)->Equals(*key));
- entry = NextProbe(entry, count++, capacity);
+ entry = Derived::NextProbe(entry, count++, capacity);
}
- return kNotFound;
+ return Derived::kNotFound;
}
@@ -14497,12 +17137,12 @@
}
// Rehash the elements.
- int capacity = Capacity();
+ int capacity = this->Capacity();
for (int i = 0; i < capacity; i++) {
uint32_t from_index = EntryToIndex(i);
- Object* k = get(from_index);
+ Object* k = this->get(from_index);
if (IsKey(k)) {
- uint32_t hash = HashTable::HashForObject(key, k);
+ uint32_t hash = this->HashForObject(key, k);
uint32_t insertion_index =
EntryToIndex(new_table->FindInsertionEntry(hash));
for (int j = 0; j < Shape::kEntrySize; j++) {
@@ -14521,8 +17161,8 @@
Object* k,
int probe,
uint32_t expected) {
- uint32_t hash = HashTable::HashForObject(key, k);
- uint32_t capacity = Capacity();
+ uint32_t hash = this->HashForObject(key, k);
+ uint32_t capacity = this->Capacity();
uint32_t entry = FirstProbe(hash, capacity);
for (int i = 1; i < probe; i++) {
if (entry == expected) return expected;
@@ -14593,14 +17233,8 @@
Isolate* isolate = table->GetIsolate();
int capacity = table->Capacity();
int nof = table->NumberOfElements() + n;
- int nod = table->NumberOfDeletedElements();
- // Return if:
- // 50% is still free after adding n elements and
- // at most 50% of the free elements are deleted elements.
- if (nod <= (capacity - nof) >> 1) {
- int needed_free = nof >> 1;
- if (nof + needed_free <= capacity) return table;
- }
+
+ if (table->HasSufficientCapacity(n)) return table;
const int kMinCapacityForPretenure = 256;
bool should_pretenure = pretenure == TENURED ||
@@ -14617,6 +17251,22 @@
}
+template <typename Derived, typename Shape, typename Key>
+bool HashTable<Derived, Shape, Key>::HasSufficientCapacity(int n) {
+ int capacity = Capacity();
+ int nof = NumberOfElements() + n;
+ int nod = NumberOfDeletedElements();
+ // Return true if:
+ // 50% is still free after adding n elements and
+ // at most 50% of the free elements are deleted elements.
+ if (nod <= (capacity - nof) >> 1) {
+ int needed_free = nof >> 1;
+ if (nof + needed_free <= capacity) return true;
+ }
+ return false;
+}
+
+
template<typename Derived, typename Shape, typename Key>
Handle<Derived> HashTable<Derived, Shape, Key>::Shrink(Handle<Derived> table,
Key key) {
@@ -14681,6 +17331,9 @@
template class Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >;
+template class Dictionary<GlobalDictionary, GlobalDictionaryShape,
+ Handle<Name> >;
+
template class Dictionary<SeededNumberDictionary,
SeededNumberDictionaryShape,
uint32_t>;
@@ -14701,6 +17354,10 @@
Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
New(Isolate*, int n, PretenureFlag pretenure);
+template Handle<GlobalDictionary>
+Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name> >::New(
+ Isolate*, int n, PretenureFlag pretenure);
+
template Handle<SeededNumberDictionary>
Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
AtPut(Handle<SeededNumberDictionary>, uint32_t, Handle<Object>);
@@ -14717,22 +17374,13 @@
Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
SlowReverseLookup(Object* value);
-template void
-Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
- CopyKeysTo(
- FixedArray*,
- PropertyAttributes,
- Dictionary<SeededNumberDictionary,
- SeededNumberDictionaryShape,
- uint32_t>::SortMode);
-
template Handle<Object>
Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::DeleteProperty(
- Handle<NameDictionary>, int, JSObject::DeleteMode);
+ Handle<NameDictionary>, int);
template Handle<Object>
-Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
- DeleteProperty(Handle<SeededNumberDictionary>, int, JSObject::DeleteMode);
+Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape,
+ uint32_t>::DeleteProperty(Handle<SeededNumberDictionary>, int);
template Handle<NameDictionary>
HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >::
@@ -14746,22 +17394,15 @@
HashTable<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
Shrink(Handle<SeededNumberDictionary>, uint32_t);
-template void Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
- CopyKeysTo(
- FixedArray*,
- int,
- PropertyAttributes,
- Dictionary<
- NameDictionary, NameDictionaryShape, Handle<Name> >::SortMode);
-
-template int
-Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
- NumberOfElementsFilterAttributes(PropertyAttributes);
-
template Handle<NameDictionary>
Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::Add(
Handle<NameDictionary>, Handle<Name>, Handle<Object>, PropertyDetails);
+template Handle<GlobalDictionary>
+ Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name> >::Add(
+ Handle<GlobalDictionary>, Handle<Name>, Handle<Object>,
+ PropertyDetails);
+
template Handle<FixedArray> Dictionary<
NameDictionary, NameDictionaryShape,
Handle<Name> >::BuildIterationIndicesArray(Handle<NameDictionary>);
@@ -14770,10 +17411,6 @@
NameDictionary, NameDictionaryShape,
Handle<Name> >::GenerateNewEnumerationIndices(Handle<NameDictionary>);
-template int
-Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
- NumberOfElementsFilterAttributes(PropertyAttributes);
-
template Handle<SeededNumberDictionary>
Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
Add(Handle<SeededNumberDictionary>,
@@ -14796,24 +17433,22 @@
Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>::
EnsureCapacity(Handle<UnseededNumberDictionary>, int, uint32_t);
+template void Dictionary<NameDictionary, NameDictionaryShape,
+ Handle<Name> >::SetRequiresCopyOnCapacityChange();
+
template Handle<NameDictionary>
Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
EnsureCapacity(Handle<NameDictionary>, int, Handle<Name>);
-template
-int Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
- NumberOfEnumElements();
-
-template
-int Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
- NumberOfEnumElements();
-
template bool Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape,
uint32_t>::HasComplexElements();
template int HashTable<SeededNumberDictionary, SeededNumberDictionaryShape,
uint32_t>::FindEntry(uint32_t);
+template int NameDictionaryBase<NameDictionary, NameDictionaryShape>::FindEntry(
+ Handle<Name>);
+
Handle<Object> JSObject::PrepareSlowElementsForSort(
Handle<JSObject> object, uint32_t limit) {
@@ -14845,7 +17480,7 @@
HandleScope scope(isolate);
Handle<Object> value(dict->ValueAt(i), isolate);
PropertyDetails details = dict->DetailsAt(i);
- if (details.type() == CALLBACKS || details.IsReadOnly()) {
+ if (details.type() == ACCESSOR_CONSTANT || details.IsReadOnly()) {
// Bail out and do the sorting of undefineds and array holes in JS.
// Also bail out if the element is not supposed to be moved.
return bailout;
@@ -14861,7 +17496,7 @@
return bailout;
} else {
Handle<Object> result = SeededNumberDictionary::AddNumberEntry(
- new_dict, pos, value, details);
+ new_dict, pos, value, details, object->map()->is_prototype_map());
DCHECK(result.is_identical_to(new_dict));
USE(result);
pos++;
@@ -14872,14 +17507,14 @@
return bailout;
} else {
Handle<Object> result = SeededNumberDictionary::AddNumberEntry(
- new_dict, key, value, details);
+ new_dict, key, value, details, object->map()->is_prototype_map());
DCHECK(result.is_identical_to(new_dict));
USE(result);
}
}
uint32_t result = pos;
- PropertyDetails no_details(NONE, FIELD, 0);
+ PropertyDetails no_details = PropertyDetails::Empty();
while (undefs > 0) {
if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
// Adding an entry with the key beyond smi-range requires
@@ -14888,7 +17523,8 @@
}
HandleScope scope(isolate);
Handle<Object> result = SeededNumberDictionary::AddNumberEntry(
- new_dict, pos, isolate->factory()->undefined_value(), no_details);
+ new_dict, pos, isolate->factory()->undefined_value(), no_details,
+ object->map()->is_prototype_map());
DCHECK(result.is_identical_to(new_dict));
USE(result);
pos++;
@@ -14935,8 +17571,7 @@
JSObject::ValidateElements(object);
JSObject::SetMapAndElements(object, new_map, fast_elements);
- } else if (object->HasExternalArrayElements() ||
- object->HasFixedTypedArrayElements()) {
+ } else if (object->HasFixedTypedArrayElements()) {
// Typed arrays cannot have holes or undefined elements.
return handle(Smi::FromInt(
FixedArrayBase::cast(object->elements())->length()), isolate);
@@ -14952,7 +17587,7 @@
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);
@@ -15039,7 +17674,6 @@
ExternalArrayType JSTypedArray::type() {
switch (elements()->map()->instance_type()) {
#define INSTANCE_TYPE_TO_ARRAY_TYPE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ARRAY_TYPE: \
case FIXED_##TYPE##_ARRAY_TYPE: \
return kExternal##Type##Array;
@@ -15055,9 +17689,9 @@
size_t JSTypedArray::element_size() {
switch (elements()->map()->instance_type()) {
-#define INSTANCE_TYPE_TO_ELEMENT_SIZE(Type, type, TYPE, ctype, size) \
- case EXTERNAL_##TYPE##_ARRAY_TYPE: \
- return size;
+#define INSTANCE_TYPE_TO_ELEMENT_SIZE(Type, type, TYPE, ctype, size) \
+ case FIXED_##TYPE##_ARRAY_TYPE: \
+ return size;
TYPED_ARRAYS(INSTANCE_TYPE_TO_ELEMENT_SIZE)
#undef INSTANCE_TYPE_TO_ELEMENT_SIZE
@@ -15069,213 +17703,40 @@
}
-Handle<Object> ExternalUint8ClampedArray::SetValue(
- Handle<ExternalUint8ClampedArray> array,
- uint32_t index,
- Handle<Object> value) {
- uint8_t clamped_value = 0;
- if (index < static_cast<uint32_t>(array->length())) {
- if (value->IsSmi()) {
- int int_value = Handle<Smi>::cast(value)->value();
- if (int_value < 0) {
- clamped_value = 0;
- } else if (int_value > 255) {
- clamped_value = 255;
- } else {
- clamped_value = static_cast<uint8_t>(int_value);
- }
- } else if (value->IsHeapNumber()) {
- double double_value = Handle<HeapNumber>::cast(value)->value();
- if (!(double_value > 0)) {
- // NaN and less than zero clamp to zero.
- clamped_value = 0;
- } else if (double_value > 255) {
- // Greater than 255 clamp to 255.
- clamped_value = 255;
- } else {
- // Other doubles are rounded to the nearest integer.
- clamped_value = static_cast<uint8_t>(lrint(double_value));
- }
- } else {
- // Clamp undefined to zero (default). All other types have been
- // converted to a number type further up in the call chain.
- DCHECK(value->IsUndefined());
- }
- array->set(index, clamped_value);
- }
- return handle(Smi::FromInt(clamped_value), array->GetIsolate());
-}
-
-
-template<typename ExternalArrayClass, typename ValueType>
-static Handle<Object> ExternalArrayIntSetter(
- Isolate* isolate,
- Handle<ExternalArrayClass> receiver,
- uint32_t index,
- Handle<Object> value) {
- ValueType cast_value = 0;
- if (index < static_cast<uint32_t>(receiver->length())) {
- if (value->IsSmi()) {
- int int_value = Handle<Smi>::cast(value)->value();
- cast_value = static_cast<ValueType>(int_value);
- } else if (value->IsHeapNumber()) {
- double double_value = Handle<HeapNumber>::cast(value)->value();
- cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
- } else {
- // Clamp undefined to zero (default). All other types have been
- // converted to a number type further up in the call chain.
- DCHECK(value->IsUndefined());
- }
- receiver->set(index, cast_value);
- }
- return isolate->factory()->NewNumberFromInt(cast_value);
-}
-
-
-Handle<Object> ExternalInt8Array::SetValue(Handle<ExternalInt8Array> array,
- uint32_t index,
- Handle<Object> value) {
- return ExternalArrayIntSetter<ExternalInt8Array, int8_t>(
- array->GetIsolate(), array, index, value);
-}
-
-
-Handle<Object> ExternalUint8Array::SetValue(Handle<ExternalUint8Array> array,
- uint32_t index,
- Handle<Object> value) {
- return ExternalArrayIntSetter<ExternalUint8Array, uint8_t>(
- array->GetIsolate(), array, index, value);
-}
-
-
-Handle<Object> ExternalInt16Array::SetValue(Handle<ExternalInt16Array> array,
- uint32_t index,
- Handle<Object> value) {
- return ExternalArrayIntSetter<ExternalInt16Array, int16_t>(
- array->GetIsolate(), array, index, value);
-}
-
-
-Handle<Object> ExternalUint16Array::SetValue(Handle<ExternalUint16Array> array,
- uint32_t index,
- Handle<Object> value) {
- return ExternalArrayIntSetter<ExternalUint16Array, uint16_t>(
- array->GetIsolate(), array, index, value);
-}
-
-
-Handle<Object> ExternalInt32Array::SetValue(Handle<ExternalInt32Array> array,
- uint32_t index,
- Handle<Object> value) {
- return ExternalArrayIntSetter<ExternalInt32Array, int32_t>(
- array->GetIsolate(), array, index, value);
-}
-
-
-Handle<Object> ExternalUint32Array::SetValue(
- Handle<ExternalUint32Array> array,
- uint32_t index,
- Handle<Object> value) {
- uint32_t cast_value = 0;
- if (index < static_cast<uint32_t>(array->length())) {
- if (value->IsSmi()) {
- int int_value = Handle<Smi>::cast(value)->value();
- cast_value = static_cast<uint32_t>(int_value);
- } else if (value->IsHeapNumber()) {
- double double_value = Handle<HeapNumber>::cast(value)->value();
- cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
- } else {
- // Clamp undefined to zero (default). All other types have been
- // converted to a number type further up in the call chain.
- DCHECK(value->IsUndefined());
- }
- array->set(index, cast_value);
- }
- return array->GetIsolate()->factory()->NewNumberFromUint(cast_value);
-}
-
-
-Handle<Object> ExternalFloat32Array::SetValue(
- Handle<ExternalFloat32Array> array,
- uint32_t index,
- Handle<Object> value) {
- float cast_value = static_cast<float>(base::OS::nan_value());
- if (index < static_cast<uint32_t>(array->length())) {
- if (value->IsSmi()) {
- int int_value = Handle<Smi>::cast(value)->value();
- cast_value = static_cast<float>(int_value);
- } else if (value->IsHeapNumber()) {
- double double_value = Handle<HeapNumber>::cast(value)->value();
- cast_value = static_cast<float>(double_value);
- } else {
- // Clamp undefined to NaN (default). All other types have been
- // converted to a number type further up in the call chain.
- DCHECK(value->IsUndefined());
- }
- array->set(index, cast_value);
- }
- return array->GetIsolate()->factory()->NewNumber(cast_value);
-}
-
-
-Handle<Object> ExternalFloat64Array::SetValue(
- Handle<ExternalFloat64Array> array,
- uint32_t index,
- Handle<Object> value) {
- double double_value = base::OS::nan_value();
- if (index < static_cast<uint32_t>(array->length())) {
- if (value->IsNumber()) {
- double_value = value->Number();
- } else {
- // Clamp undefined to NaN (default). All other types have been
- // converted to a number type further up in the call chain.
- DCHECK(value->IsUndefined());
- }
- array->set(index, double_value);
- }
- return array->GetIsolate()->factory()->NewNumber(double_value);
-}
-
-
-void GlobalObject::InvalidatePropertyCell(Handle<GlobalObject> global,
- Handle<Name> name) {
+void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global,
+ Handle<Name> name) {
DCHECK(!global->HasFastProperties());
- Isolate* isolate = global->GetIsolate();
- int entry = global->property_dictionary()->FindEntry(name);
- if (entry != NameDictionary::kNotFound) {
- Handle<PropertyCell> cell(
- PropertyCell::cast(global->property_dictionary()->ValueAt(entry)));
-
- Handle<Object> value(cell->value(), isolate);
- Handle<PropertyCell> new_cell = isolate->factory()->NewPropertyCell(value);
- global->property_dictionary()->ValueAtPut(entry, *new_cell);
-
- Handle<Object> hole = global->GetIsolate()->factory()->the_hole_value();
- PropertyCell::SetValueInferType(cell, hole);
- }
+ auto dictionary = handle(global->global_dictionary());
+ int entry = dictionary->FindEntry(name);
+ if (entry == GlobalDictionary::kNotFound) return;
+ PropertyCell::InvalidateEntry(dictionary, entry);
}
+// TODO(ishell): rename to EnsureEmptyPropertyCell or something.
Handle<PropertyCell> JSGlobalObject::EnsurePropertyCell(
- Handle<JSGlobalObject> global,
- Handle<Name> name) {
+ Handle<JSGlobalObject> global, Handle<Name> name) {
DCHECK(!global->HasFastProperties());
- int entry = global->property_dictionary()->FindEntry(name);
- if (entry == NameDictionary::kNotFound) {
- Isolate* isolate = global->GetIsolate();
- Handle<PropertyCell> cell = isolate->factory()->NewPropertyCell(
- isolate->factory()->the_hole_value());
- PropertyDetails details(NONE, FIELD, 0);
- details = details.AsDeleted();
- Handle<NameDictionary> dictionary = NameDictionary::Add(
- handle(global->property_dictionary()), name, cell, details);
- global->set_properties(*dictionary);
+ auto dictionary = handle(global->global_dictionary());
+ int entry = dictionary->FindEntry(name);
+ Handle<PropertyCell> cell;
+ if (entry != GlobalDictionary::kNotFound) {
+ // This call should be idempotent.
+ DCHECK(dictionary->ValueAt(entry)->IsPropertyCell());
+ cell = handle(PropertyCell::cast(dictionary->ValueAt(entry)));
+ DCHECK(cell->property_details().cell_type() ==
+ PropertyCellType::kUninitialized ||
+ cell->property_details().cell_type() ==
+ PropertyCellType::kInvalidated);
+ DCHECK(cell->value()->IsTheHole());
return cell;
- } else {
- Object* value = global->property_dictionary()->ValueAt(entry);
- DCHECK(value->IsPropertyCell());
- return handle(PropertyCell::cast(value));
}
+ Isolate* isolate = global->GetIsolate();
+ cell = isolate->factory()->NewPropertyCell();
+ PropertyDetails details(NONE, DATA, 0, PropertyCellType::kUninitialized);
+ dictionary = GlobalDictionary::Add(dictionary, name, cell, details);
+ global->set_properties(*dictionary);
+ return cell;
}
@@ -15315,7 +17776,7 @@
#endif
}
- bool IsMatch(Object* o) OVERRIDE {
+ bool IsMatch(Object* o) override {
if (!o->IsString()) return false;
String* other = String::cast(o);
if (other->length() != 2) return false;
@@ -15323,13 +17784,13 @@
return other->Get(1) == c2_;
}
- uint32_t Hash() OVERRIDE { return hash_; }
- uint32_t HashForObject(Object* key) OVERRIDE {
+ uint32_t Hash() override { return hash_; }
+ uint32_t HashForObject(Object* key) override {
if (!key->IsString()) return 0;
return String::cast(key)->Hash();
}
- Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
+ Handle<Object> AsHandle(Isolate* isolate) override {
// The TwoCharHashTableKey is only used for looking in the string
// table, not for adding to it.
UNREACHABLE();
@@ -15392,7 +17853,7 @@
// 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);
}
@@ -15426,17 +17887,25 @@
table->set(EntryToIndex(entry), *string);
table->ElementAdded();
- isolate->factory()->set_string_table(table);
+ isolate->heap()->SetRootStringTable(*table);
return Handle<String>::cast(string);
}
+String* StringTable::LookupKeyIfExists(Isolate* isolate, HashTableKey* key) {
+ Handle<StringTable> table = isolate->factory()->string_table();
+ int entry = table->FindEntry(key);
+ if (entry != kNotFound) return String::cast(table->KeyAt(entry));
+ return NULL;
+}
+
+
Handle<Object> CompilationCacheTable::Lookup(Handle<String> src,
- Handle<Context> context) {
+ Handle<Context> context,
+ LanguageMode language_mode) {
Isolate* isolate = GetIsolate();
Handle<SharedFunctionInfo> shared(context->closure()->shared());
- StringSharedKey key(src, shared, FLAG_use_strict ? STRICT : SLOPPY,
- RelocInfo::kNoPosition);
+ StringSharedKey key(src, shared, language_mode, RelocInfo::kNoPosition);
int entry = FindEntry(&key);
if (entry == kNotFound) return isolate->factory()->undefined_value();
int index = EntryToIndex(entry);
@@ -15447,11 +17916,11 @@
Handle<Object> CompilationCacheTable::LookupEval(
Handle<String> src, Handle<SharedFunctionInfo> outer_info,
- StrictMode strict_mode, int scope_position) {
+ LanguageMode language_mode, int scope_position) {
Isolate* isolate = GetIsolate();
// Cache key is the tuple (source, outer shared function info, scope position)
// to unambiguously identify the context chain the cached eval code assumes.
- StringSharedKey key(src, outer_info, strict_mode, scope_position);
+ StringSharedKey key(src, outer_info, language_mode, scope_position);
int entry = FindEntry(&key);
if (entry == kNotFound) return isolate->factory()->undefined_value();
int index = EntryToIndex(entry);
@@ -15473,11 +17942,10 @@
Handle<CompilationCacheTable> CompilationCacheTable::Put(
Handle<CompilationCacheTable> cache, Handle<String> src,
- Handle<Context> context, Handle<Object> value) {
+ Handle<Context> context, LanguageMode language_mode, Handle<Object> value) {
Isolate* isolate = cache->GetIsolate();
Handle<SharedFunctionInfo> shared(context->closure()->shared());
- StringSharedKey key(src, shared, FLAG_use_strict ? STRICT : SLOPPY,
- RelocInfo::kNoPosition);
+ StringSharedKey key(src, shared, language_mode, RelocInfo::kNoPosition);
{
Handle<Object> k = key.AsHandle(isolate);
DisallowHeapAllocation no_allocation_scope;
@@ -15505,7 +17973,7 @@
Handle<SharedFunctionInfo> outer_info, Handle<SharedFunctionInfo> value,
int scope_position) {
Isolate* isolate = cache->GetIsolate();
- StringSharedKey key(src, outer_info, value->strict_mode(), scope_position);
+ StringSharedKey key(src, outer_info, value->language_mode(), scope_position);
{
Handle<Object> k = key.AsHandle(isolate);
DisallowHeapAllocation no_allocation_scope;
@@ -15593,7 +18061,7 @@
public:
explicit StringsKey(Handle<FixedArray> strings) : strings_(strings) { }
- bool IsMatch(Object* strings) OVERRIDE {
+ bool IsMatch(Object* strings) override {
FixedArray* o = FixedArray::cast(strings);
int len = strings_->length();
if (o->length() != len) return false;
@@ -15603,9 +18071,9 @@
return true;
}
- uint32_t Hash() OVERRIDE { return HashForObject(*strings_); }
+ uint32_t Hash() override { return HashForObject(*strings_); }
- uint32_t HashForObject(Object* obj) OVERRIDE {
+ uint32_t HashForObject(Object* obj) override {
FixedArray* strings = FixedArray::cast(obj);
int len = strings->length();
uint32_t hash = 0;
@@ -15615,7 +18083,7 @@
return hash;
}
- Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { return strings_; }
+ Handle<Object> AsHandle(Isolate* isolate) override { return strings_; }
private:
Handle<FixedArray> strings_;
@@ -15686,8 +18154,7 @@
int enum_index = PropertyDetails::kInitialIndex + i;
PropertyDetails details = dictionary->DetailsAt(index);
- PropertyDetails new_details =
- PropertyDetails(details.attributes(), details.type(), enum_index);
+ PropertyDetails new_details = details.set_index(enum_index);
dictionary->DetailsAtPut(index, new_details);
}
@@ -15697,7 +18164,17 @@
}
-template<typename Derived, typename Shape, typename Key>
+template <typename Derived, typename Shape, typename Key>
+void Dictionary<Derived, Shape, Key>::SetRequiresCopyOnCapacityChange() {
+ DCHECK_EQ(0, DerivedHashTable::NumberOfElements());
+ DCHECK_EQ(0, DerivedHashTable::NumberOfDeletedElements());
+ // Make sure that HashTable::EnsureCapacity will create a copy.
+ DerivedHashTable::SetNumberOfDeletedElements(DerivedHashTable::Capacity());
+ DCHECK(!DerivedHashTable::HasSufficientCapacity(1));
+}
+
+
+template <typename Derived, typename Shape, typename Key>
Handle<Derived> Dictionary<Derived, Shape, Key>::EnsureCapacity(
Handle<Derived> dictionary, int n, Key key) {
// Check whether there are enough enumeration indices to add n elements.
@@ -15710,17 +18187,12 @@
}
-template<typename Derived, typename Shape, typename Key>
+template <typename Derived, typename Shape, typename Key>
Handle<Object> Dictionary<Derived, Shape, Key>::DeleteProperty(
- Handle<Derived> dictionary,
- int entry,
- JSObject::DeleteMode mode) {
+ Handle<Derived> dictionary, int entry) {
Factory* factory = dictionary->GetIsolate()->factory();
PropertyDetails details = dictionary->DetailsAt(entry);
- // Ignore attributes if forcing a deletion.
- if (!details.IsConfigurable() && mode != JSReceiver::FORCE_DELETION) {
- return factory->false_value();
- }
+ if (!details.IsConfigurable()) return factory->false_value();
dictionary->SetEntry(
entry, factory->the_hole_value(), factory->the_hole_value());
@@ -15745,7 +18217,7 @@
#ifdef DEBUG
USE(Shape::AsHandle(dictionary->GetIsolate(), key));
#endif
- PropertyDetails details(NONE, FIELD, 0);
+ PropertyDetails details = PropertyDetails::Empty();
AddEntry(dictionary, key, value, details, dictionary->Hash(key));
return dictionary;
@@ -15781,13 +18253,11 @@
uint32_t entry = dictionary->FindInsertionEntry(hash);
// Insert element at empty or deleted entry
- if (!details.IsDeleted() &&
- details.dictionary_index() == 0 &&
- Shape::kIsEnumerable) {
+ if (details.dictionary_index() == 0 && Shape::kIsEnumerable) {
// Assign an enumeration index to the property and update
// SetNextEnumerationIndex.
int index = dictionary->NextEnumerationIndex();
- details = PropertyDetails(details.attributes(), details.type(), index);
+ details = details.set_index(index);
dictionary->SetNextEnumerationIndex(index + 1);
}
dictionary->SetEntry(entry, k, value, details);
@@ -15797,7 +18267,8 @@
}
-void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) {
+void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key,
+ bool used_as_prototype) {
DisallowHeapAllocation no_allocation;
// If the dictionary requires slow elements an element has already
// been added at a high index.
@@ -15805,6 +18276,10 @@
// Check if this index is high enough that we should require slow
// elements.
if (key > kRequiresSlowElementsLimit) {
+ if (used_as_prototype) {
+ // TODO(verwaest): Remove this hack.
+ TypeFeedbackVector::ClearAllKeyedStoreICs(GetIsolate());
+ }
set_requires_slow_elements();
return;
}
@@ -15818,11 +18293,9 @@
Handle<SeededNumberDictionary> SeededNumberDictionary::AddNumberEntry(
- Handle<SeededNumberDictionary> dictionary,
- uint32_t key,
- Handle<Object> value,
- PropertyDetails details) {
- dictionary->UpdateMaxNumberKey(key);
+ Handle<SeededNumberDictionary> dictionary, uint32_t key,
+ Handle<Object> value, PropertyDetails details, bool used_as_prototype) {
+ dictionary->UpdateMaxNumberKey(key, used_as_prototype);
SLOW_DCHECK(dictionary->FindEntry(key) == kNotFound);
return Add(dictionary, key, value, details);
}
@@ -15833,15 +18306,14 @@
uint32_t key,
Handle<Object> value) {
SLOW_DCHECK(dictionary->FindEntry(key) == kNotFound);
- return Add(dictionary, key, value, PropertyDetails(NONE, FIELD, 0));
+ return Add(dictionary, key, value, PropertyDetails::Empty());
}
Handle<SeededNumberDictionary> SeededNumberDictionary::AtNumberPut(
- Handle<SeededNumberDictionary> dictionary,
- uint32_t key,
- Handle<Object> value) {
- dictionary->UpdateMaxNumberKey(key);
+ Handle<SeededNumberDictionary> dictionary, uint32_t key,
+ Handle<Object> value, bool used_as_prototype) {
+ dictionary->UpdateMaxNumberKey(key, used_as_prototype);
return AtPut(dictionary, key, value);
}
@@ -15855,18 +18327,14 @@
Handle<SeededNumberDictionary> SeededNumberDictionary::Set(
- Handle<SeededNumberDictionary> dictionary,
- uint32_t key,
- Handle<Object> value,
- PropertyDetails details) {
+ Handle<SeededNumberDictionary> dictionary, uint32_t key,
+ Handle<Object> value, PropertyDetails details, bool used_as_prototype) {
int entry = dictionary->FindEntry(key);
if (entry == kNotFound) {
- return AddNumberEntry(dictionary, key, value, details);
+ return AddNumberEntry(dictionary, key, value, details, used_as_prototype);
}
// Preserve enumeration index.
- details = PropertyDetails(details.attributes(),
- details.type(),
- dictionary->DetailsAt(entry).dictionary_index());
+ details = details.set_index(dictionary->DetailsAt(entry).dictionary_index());
Handle<Object> object_key =
SeededNumberDictionaryShape::AsHandle(dictionary->GetIsolate(), key);
dictionary->SetEntry(entry, object_key, value, details);
@@ -15887,17 +18355,16 @@
}
-
-template<typename Derived, typename Shape, typename Key>
+template <typename Derived, typename Shape, typename Key>
int Dictionary<Derived, Shape, Key>::NumberOfElementsFilterAttributes(
- PropertyAttributes filter) {
- int capacity = DerivedHashTable::Capacity();
+ PropertyFilter filter) {
+ int capacity = this->Capacity();
int result = 0;
for (int i = 0; i < capacity; i++) {
- Object* k = DerivedHashTable::KeyAt(i);
- if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) {
- PropertyDetails details = DetailsAt(i);
- if (details.IsDeleted()) continue;
+ Object* k = this->KeyAt(i);
+ if (this->IsKey(k) && !k->FilterKey(filter)) {
+ if (this->IsDeleted(i)) continue;
+ PropertyDetails details = this->DetailsAt(i);
PropertyAttributes attr = details.attributes();
if ((attr & filter) == 0) result++;
}
@@ -15906,121 +18373,136 @@
}
-template<typename Derived, typename Shape, typename Key>
-int Dictionary<Derived, Shape, Key>::NumberOfEnumElements() {
- return NumberOfElementsFilterAttributes(
- static_cast<PropertyAttributes>(DONT_ENUM | SYMBOLIC));
-}
-
-
template <typename Derived, typename Shape, typename Key>
bool Dictionary<Derived, Shape, Key>::HasComplexElements() {
- int capacity = DerivedHashTable::Capacity();
+ int capacity = this->Capacity();
for (int i = 0; i < capacity; i++) {
- Object* k = DerivedHashTable::KeyAt(i);
- if (DerivedHashTable::IsKey(k) && !FilterKey(k, NONE)) {
- PropertyDetails details = DetailsAt(i);
- if (details.IsDeleted()) continue;
- if (details.type() == CALLBACKS) return true;
+ Object* k = this->KeyAt(i);
+ if (this->IsKey(k) && !k->FilterKey(ALL_PROPERTIES)) {
+ if (this->IsDeleted(i)) continue;
+ PropertyDetails details = this->DetailsAt(i);
+ if (details.type() == ACCESSOR_CONSTANT) return true;
PropertyAttributes attr = details.attributes();
- if (attr & (READ_ONLY | DONT_DELETE | DONT_ENUM)) return true;
+ if (attr & ALL_ATTRIBUTES_MASK) return true;
}
}
return false;
}
-template <typename Derived, typename Shape, typename Key>
-void Dictionary<Derived, Shape, Key>::CopyKeysTo(
- FixedArray* storage, PropertyAttributes filter,
- typename Dictionary<Derived, Shape, Key>::SortMode sort_mode) {
- DCHECK(storage->length() >= NumberOfElementsFilterAttributes(filter));
- int capacity = DerivedHashTable::Capacity();
- int index = 0;
- for (int i = 0; i < capacity; i++) {
- Object* k = DerivedHashTable::KeyAt(i);
- if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) {
- PropertyDetails details = DetailsAt(i);
- if (details.IsDeleted()) continue;
- PropertyAttributes attr = details.attributes();
- if ((attr & filter) == 0) storage->set(index++, k);
- }
- }
- if (sort_mode == Dictionary::SORTED) {
- storage->SortPairs(storage, index);
- }
- DCHECK(storage->length() >= index);
-}
-
-
+template <typename Dictionary>
struct EnumIndexComparator {
- explicit EnumIndexComparator(NameDictionary* dict) : dict(dict) { }
+ explicit EnumIndexComparator(Dictionary* dict) : dict(dict) {}
bool operator() (Smi* a, Smi* b) {
PropertyDetails da(dict->DetailsAt(a->value()));
PropertyDetails db(dict->DetailsAt(b->value()));
return da.dictionary_index() < db.dictionary_index();
}
- NameDictionary* dict;
+ Dictionary* dict;
};
-void NameDictionary::CopyEnumKeysTo(FixedArray* storage) {
+template <typename Derived, typename Shape, typename Key>
+void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo(FixedArray* storage) {
int length = storage->length();
- int capacity = Capacity();
+ int capacity = this->Capacity();
int properties = 0;
for (int i = 0; i < capacity; i++) {
- Object* k = KeyAt(i);
- if (IsKey(k) && !k->IsSymbol()) {
- PropertyDetails details = DetailsAt(i);
- if (details.IsDeleted() || details.IsDontEnum()) continue;
- storage->set(properties, Smi::FromInt(i));
- properties++;
- if (properties == length) break;
- }
+ Object* k = this->KeyAt(i);
+ if (this->IsKey(k) && !k->IsSymbol()) {
+ PropertyDetails details = this->DetailsAt(i);
+ if (details.IsDontEnum() || this->IsDeleted(i)) continue;
+ storage->set(properties, Smi::FromInt(i));
+ properties++;
+ if (properties == length) break;
+ }
}
CHECK_EQ(length, properties);
- EnumIndexComparator cmp(this);
+ EnumIndexComparator<Derived> cmp(static_cast<Derived*>(this));
Smi** start = reinterpret_cast<Smi**>(storage->GetFirstElementAddress());
std::sort(start, start + length, cmp);
for (int i = 0; i < length; i++) {
int index = Smi::cast(storage->get(i))->value();
- storage->set(i, KeyAt(index));
+ storage->set(i, this->KeyAt(index));
}
}
-template<typename Derived, typename Shape, typename Key>
-void Dictionary<Derived, Shape, Key>::CopyKeysTo(
- FixedArray* storage,
- int index,
- PropertyAttributes filter,
+template <typename Derived, typename Shape, typename Key>
+int Dictionary<Derived, Shape, Key>::CopyKeysTo(
+ FixedArray* storage, int index, PropertyFilter filter,
typename Dictionary<Derived, Shape, Key>::SortMode sort_mode) {
DCHECK(storage->length() >= NumberOfElementsFilterAttributes(filter));
- int capacity = DerivedHashTable::Capacity();
+ int start_index = index;
+ int capacity = this->Capacity();
for (int i = 0; i < capacity; i++) {
- Object* k = DerivedHashTable::KeyAt(i);
- if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) {
- PropertyDetails details = DetailsAt(i);
- if (details.IsDeleted()) continue;
- PropertyAttributes attr = details.attributes();
- if ((attr & filter) == 0) storage->set(index++, k);
- }
+ Object* k = this->KeyAt(i);
+ if (!this->IsKey(k) || k->FilterKey(filter)) continue;
+ if (this->IsDeleted(i)) continue;
+ PropertyDetails details = this->DetailsAt(i);
+ PropertyAttributes attr = details.attributes();
+ if ((attr & filter) != 0) continue;
+ storage->set(index++, k);
}
if (sort_mode == Dictionary::SORTED) {
storage->SortPairs(storage, index);
}
DCHECK(storage->length() >= index);
+ return index - start_index;
+}
+
+
+template <typename Derived, typename Shape, typename Key>
+void Dictionary<Derived, Shape, Key>::CollectKeysTo(
+ Handle<Dictionary<Derived, Shape, Key> > dictionary, KeyAccumulator* keys,
+ PropertyFilter filter) {
+ int capacity = dictionary->Capacity();
+ Handle<FixedArray> array =
+ keys->isolate()->factory()->NewFixedArray(dictionary->NumberOfElements());
+ int array_size = 0;
+
+ {
+ DisallowHeapAllocation no_gc;
+ Dictionary<Derived, Shape, Key>* raw_dict = *dictionary;
+ for (int i = 0; i < capacity; i++) {
+ Object* k = raw_dict->KeyAt(i);
+ if (!raw_dict->IsKey(k) || k->FilterKey(filter)) continue;
+ if (raw_dict->IsDeleted(i)) continue;
+ PropertyDetails details = raw_dict->DetailsAt(i);
+ if ((details.attributes() & filter) != 0) continue;
+ if (filter & ONLY_ALL_CAN_READ) {
+ if (details.kind() != kAccessor) continue;
+ Object* accessors = raw_dict->ValueAt(i);
+ if (accessors->IsPropertyCell()) {
+ accessors = PropertyCell::cast(accessors)->value();
+ }
+ if (!accessors->IsAccessorInfo()) continue;
+ if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
+ }
+ array->set(array_size++, Smi::FromInt(i));
+ }
+
+ EnumIndexComparator<Derived> cmp(static_cast<Derived*>(raw_dict));
+ Smi** start = reinterpret_cast<Smi**>(array->GetFirstElementAddress());
+ std::sort(start, start + array_size, cmp);
+ }
+
+ for (int i = 0; i < array_size; i++) {
+ int index = Smi::cast(array->get(i))->value();
+ keys->AddKey(dictionary->KeyAt(index));
+ }
}
// Backwards lookup (slow).
template<typename Derived, typename Shape, typename Key>
Object* Dictionary<Derived, Shape, Key>::SlowReverseLookup(Object* value) {
- int capacity = DerivedHashTable::Capacity();
+ int capacity = this->Capacity();
for (int i = 0; i < capacity; i++) {
- Object* k = DerivedHashTable::KeyAt(i);
- if (Dictionary::IsKey(k)) {
- Object* e = ValueAt(i);
+ Object* k = this->KeyAt(i);
+ if (this->IsKey(k)) {
+ Object* e = this->ValueAt(i);
+ // TODO(dcarney): this should be templatized.
if (e->IsPropertyCell()) {
e = PropertyCell::cast(e)->value();
}
@@ -16032,18 +18514,34 @@
}
+Object* ObjectHashTable::Lookup(Isolate* isolate, Handle<Object> key,
+ int32_t hash) {
+ DisallowHeapAllocation no_gc;
+ DCHECK(IsKey(*key));
+
+ int entry = FindEntry(isolate, key, hash);
+ if (entry == kNotFound) return isolate->heap()->the_hole_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
Object* ObjectHashTable::Lookup(Handle<Object> key) {
DisallowHeapAllocation no_gc;
DCHECK(IsKey(*key));
+ Isolate* isolate = GetIsolate();
+
// If the object does not have an identity hash, it was never used as a key.
Object* hash = key->GetHash();
if (hash->IsUndefined()) {
- return GetHeap()->the_hole_value();
+ return isolate->heap()->the_hole_value();
}
- int entry = FindEntry(key);
- if (entry == kNotFound) return GetHeap()->the_hole_value();
- return get(EntryToIndex(entry) + 1);
+ return Lookup(isolate, key, Smi::cast(hash)->value());
+}
+
+
+Object* ObjectHashTable::Lookup(Handle<Object> key, int32_t hash) {
+ return Lookup(GetIsolate(), key, hash);
}
@@ -16054,11 +18552,23 @@
DCHECK(!value->IsTheHole());
Isolate* isolate = table->GetIsolate();
-
// Make sure the key object has an identity hash code.
- Handle<Smi> hash = Object::GetOrCreateHash(isolate, key);
+ int32_t hash = Object::GetOrCreateHash(isolate, key)->value();
- int entry = table->FindEntry(key);
+ return Put(table, key, value, hash);
+}
+
+
+Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table,
+ Handle<Object> key,
+ Handle<Object> value,
+ int32_t hash) {
+ DCHECK(table->IsKey(*key));
+ DCHECK(!value->IsTheHole());
+
+ Isolate* isolate = table->GetIsolate();
+
+ int entry = table->FindEntry(isolate, key, hash);
// Key is already in table, just overwrite value.
if (entry != kNotFound) {
@@ -16068,9 +18578,7 @@
// Check whether the hash table should be extended.
table = EnsureCapacity(table, 1, key);
- table->AddEntry(table->FindInsertionEntry(hash->value()),
- *key,
- *value);
+ table->AddEntry(table->FindInsertionEntry(hash), *key, *value);
return table;
}
@@ -16086,7 +18594,17 @@
return table;
}
- int entry = table->FindEntry(key);
+ return Remove(table, key, was_present, Smi::cast(hash)->value());
+}
+
+
+Handle<ObjectHashTable> ObjectHashTable::Remove(Handle<ObjectHashTable> table,
+ Handle<Object> key,
+ bool* was_present,
+ int32_t hash) {
+ DCHECK(table->IsKey(*key));
+
+ int entry = table->FindEntry(table->GetIsolate(), key, hash);
if (entry == kNotFound) {
*was_present = false;
return table;
@@ -16112,7 +18630,7 @@
}
-Object* WeakHashTable::Lookup(Handle<Object> key) {
+Object* WeakHashTable::Lookup(Handle<HeapObject> key) {
DisallowHeapAllocation no_gc;
DCHECK(IsKey(*key));
int entry = FindEntry(key);
@@ -16122,36 +18640,31 @@
Handle<WeakHashTable> WeakHashTable::Put(Handle<WeakHashTable> table,
- Handle<Object> key,
- Handle<Object> value) {
+ Handle<HeapObject> key,
+ Handle<HeapObject> value) {
DCHECK(table->IsKey(*key));
int entry = table->FindEntry(key);
// Key is already in table, just overwrite value.
if (entry != kNotFound) {
- // TODO(ulan): Skipping write barrier is a temporary solution to avoid
- // memory leaks. Remove this once we have special visitor for weak fixed
- // arrays.
- table->set(EntryToValueIndex(entry), *value, SKIP_WRITE_BARRIER);
+ table->set(EntryToValueIndex(entry), *value);
return table;
}
+ Handle<WeakCell> key_cell = key->GetIsolate()->factory()->NewWeakCell(key);
+
// Check whether the hash table should be extended.
table = EnsureCapacity(table, 1, key, TENURED);
- table->AddEntry(table->FindInsertionEntry(table->Hash(key)), key, value);
+ table->AddEntry(table->FindInsertionEntry(table->Hash(key)), key_cell, value);
return table;
}
-void WeakHashTable::AddEntry(int entry,
- Handle<Object> key,
- Handle<Object> value) {
+void WeakHashTable::AddEntry(int entry, Handle<WeakCell> key_cell,
+ Handle<HeapObject> value) {
DisallowHeapAllocation no_allocation;
- // TODO(ulan): Skipping write barrier is a temporary solution to avoid
- // memory leaks. Remove this once we have special visitor for weak fixed
- // arrays.
- set(EntryToIndex(entry), *key, SKIP_WRITE_BARRIER);
- set(EntryToValueIndex(entry), *value, SKIP_WRITE_BARRIER);
+ set(EntryToIndex(entry), *key_cell);
+ set(EntryToValueIndex(entry), *value);
ElementAdded();
}
@@ -16228,18 +18741,46 @@
return new_table;
}
-
-template<class Derived, class Iterator, int entrysize>
-Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Remove(
- Handle<Derived> table, Handle<Object> key, bool* was_present) {
- int entry = table->FindEntry(key);
- if (entry == kNotFound) {
- *was_present = false;
- return 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);
}
- *was_present = true;
- table->RemoveEntry(entry);
- return Shrink(table);
+ 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;
}
@@ -16288,61 +18829,6 @@
}
-template <class Derived, class Iterator, int entrysize>
-int OrderedHashTable<Derived, Iterator, entrysize>::FindEntry(
- Handle<Object> key, int hash) {
- DCHECK(!IsObsolete());
-
- DisallowHeapAllocation no_gc;
- DCHECK(!key->IsTheHole());
- for (int entry = HashToEntry(hash); entry != kNotFound;
- entry = ChainAt(entry)) {
- Object* candidate = KeyAt(entry);
- if (candidate->SameValueZero(*key))
- return entry;
- }
- return kNotFound;
-}
-
-
-template <class Derived, class Iterator, int entrysize>
-int OrderedHashTable<Derived, Iterator, entrysize>::FindEntry(
- Handle<Object> key) {
- DisallowHeapAllocation no_gc;
- Object* hash = key->GetHash();
- if (!hash->IsSmi()) return kNotFound;
- return FindEntry(key, Smi::cast(hash)->value());
-}
-
-
-template <class Derived, class Iterator, int entrysize>
-int OrderedHashTable<Derived, Iterator, entrysize>::AddEntry(int hash) {
- DCHECK(!IsObsolete());
-
- int entry = UsedCapacity();
- int bucket = HashToBucket(hash);
- int index = EntryToIndex(entry);
- Object* chain_entry = get(kHashTableStartIndex + bucket);
- set(kHashTableStartIndex + bucket, Smi::FromInt(entry));
- set(index + kChainOffset, chain_entry);
- SetNumberOfElements(NumberOfElements() + 1);
- return index;
-}
-
-
-template<class Derived, class Iterator, int entrysize>
-void OrderedHashTable<Derived, Iterator, entrysize>::RemoveEntry(int entry) {
- DCHECK(!IsObsolete());
-
- int index = EntryToIndex(entry);
- for (int i = 0; i < entrysize; ++i) {
- set_the_hole(index + i);
- }
- SetNumberOfElements(NumberOfElements() - 1);
- SetNumberOfDeletedElements(NumberOfDeletedElements() + 1);
-}
-
-
template Handle<OrderedHashSet>
OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Allocate(
Isolate* isolate, int capacity, PretenureFlag pretenure);
@@ -16359,20 +18845,8 @@
OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Clear(
Handle<OrderedHashSet> table);
-template Handle<OrderedHashSet>
-OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Remove(
- Handle<OrderedHashSet> table, Handle<Object> key, bool* was_present);
-
-template int OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::FindEntry(
- Handle<Object> key, int hash);
-template int OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::FindEntry(
- Handle<Object> key);
-
-template int
-OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::AddEntry(int hash);
-
-template void
-OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::RemoveEntry(int entry);
+template bool OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::HasKey(
+ Handle<OrderedHashSet> table, Handle<Object> key);
template Handle<OrderedHashMap>
@@ -16391,68 +18865,8 @@
OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Clear(
Handle<OrderedHashMap> table);
-template Handle<OrderedHashMap>
-OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Remove(
- Handle<OrderedHashMap> table, Handle<Object> key, bool* was_present);
-
-template int OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::FindEntry(
- Handle<Object> key, int hash);
-template int OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::FindEntry(
- Handle<Object> key);
-
-template int
-OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::AddEntry(int hash);
-
-template void
-OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::RemoveEntry(int entry);
-
-
-bool OrderedHashSet::Contains(Handle<Object> key) {
- return FindEntry(key) != kNotFound;
-}
-
-
-Handle<OrderedHashSet> OrderedHashSet::Add(Handle<OrderedHashSet> table,
- Handle<Object> key) {
- int hash = GetOrCreateHash(table->GetIsolate(), key)->value();
- if (table->FindEntry(key, hash) != kNotFound) return table;
-
- table = EnsureGrowable(table);
-
- int index = table->AddEntry(hash);
- table->set(index, *key);
- return table;
-}
-
-
-Object* OrderedHashMap::Lookup(Handle<Object> key) {
- DisallowHeapAllocation no_gc;
- int entry = FindEntry(key);
- if (entry == kNotFound) return GetHeap()->the_hole_value();
- return ValueAt(entry);
-}
-
-
-Handle<OrderedHashMap> OrderedHashMap::Put(Handle<OrderedHashMap> table,
- Handle<Object> key,
- Handle<Object> value) {
- DCHECK(!key->IsTheHole());
-
- int hash = GetOrCreateHash(table->GetIsolate(), key)->value();
- int entry = table->FindEntry(key, hash);
-
- if (entry != kNotFound) {
- table->set(table->EntryToIndex(entry) + kValueOffset, *value);
- return table;
- }
-
- table = EnsureGrowable(table);
-
- int index = table->AddEntry(hash);
- table->set(index, *key);
- table->set(index + kValueOffset, *value);
- return table;
-}
+template bool OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::HasKey(
+ Handle<OrderedHashMap> table, Handle<Object> key);
template<class Derived, class TableType>
@@ -16559,55 +18973,71 @@
OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Transition();
-DeclaredAccessorDescriptorIterator::DeclaredAccessorDescriptorIterator(
- DeclaredAccessorDescriptor* descriptor)
- : array_(descriptor->serialized_data()->GetDataStartAddress()),
- length_(descriptor->serialized_data()->length()),
- offset_(0) {
+void JSSet::Initialize(Handle<JSSet> set, Isolate* isolate) {
+ Handle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet();
+ set->set_table(*table);
}
-const DeclaredAccessorDescriptorData*
- DeclaredAccessorDescriptorIterator::Next() {
- DCHECK(offset_ < length_);
- uint8_t* ptr = &array_[offset_];
- DCHECK(reinterpret_cast<uintptr_t>(ptr) % sizeof(uintptr_t) == 0);
- const DeclaredAccessorDescriptorData* data =
- reinterpret_cast<const DeclaredAccessorDescriptorData*>(ptr);
- offset_ += sizeof(*data);
- DCHECK(offset_ <= length_);
- return data;
+void JSSet::Clear(Handle<JSSet> set) {
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()));
+ table = OrderedHashSet::Clear(table);
+ set->set_table(*table);
}
-Handle<DeclaredAccessorDescriptor> DeclaredAccessorDescriptor::Create(
- Isolate* isolate,
- const DeclaredAccessorDescriptorData& descriptor,
- Handle<DeclaredAccessorDescriptor> previous) {
- int previous_length =
- previous.is_null() ? 0 : previous->serialized_data()->length();
- int length = sizeof(descriptor) + previous_length;
- Handle<ByteArray> serialized_descriptor =
- isolate->factory()->NewByteArray(length);
- Handle<DeclaredAccessorDescriptor> value =
- isolate->factory()->NewDeclaredAccessorDescriptor();
- value->set_serialized_data(*serialized_descriptor);
- // Copy in the data.
- {
- DisallowHeapAllocation no_allocation;
- uint8_t* array = serialized_descriptor->GetDataStartAddress();
- if (previous_length != 0) {
- uint8_t* previous_array =
- previous->serialized_data()->GetDataStartAddress();
- MemCopy(array, previous_array, previous_length);
- array += previous_length;
- }
- DCHECK(reinterpret_cast<uintptr_t>(array) % sizeof(uintptr_t) == 0);
- DeclaredAccessorDescriptorData* data =
- reinterpret_cast<DeclaredAccessorDescriptorData*>(array);
- *data = descriptor;
+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) {
+ 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());
}
- return value;
+}
+
+
+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;
}
@@ -16691,10 +19121,9 @@
// 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);
@@ -16703,12 +19132,14 @@
// Get the break point objects for a code position.
-Object* DebugInfo::GetBreakPointObjects(int code_position) {
+Handle<Object> DebugInfo::GetBreakPointObjects(int code_position) {
Object* break_point_info = GetBreakPointInfo(code_position);
if (break_point_info->IsUndefined()) {
- return GetHeap()->undefined_value();
+ return GetIsolate()->factory()->undefined_value();
}
- return BreakPointInfo::cast(break_point_info)->break_point_objects();
+ return Handle<Object>(
+ BreakPointInfo::cast(break_point_info)->break_point_objects(),
+ GetIsolate());
}
@@ -16727,22 +19158,22 @@
}
-Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
- Handle<Object> break_point_object) {
- Heap* heap = debug_info->GetHeap();
- if (debug_info->break_points()->IsUndefined()) return heap->undefined_value();
- for (int i = 0; i < debug_info->break_points()->length(); i++) {
- if (!debug_info->break_points()->get(i)->IsUndefined()) {
- Handle<BreakPointInfo> break_point_info =
- Handle<BreakPointInfo>(BreakPointInfo::cast(
- debug_info->break_points()->get(i)));
- if (BreakPointInfo::HasBreakPointObject(break_point_info,
- break_point_object)) {
- return *break_point_info;
+Handle<Object> DebugInfo::FindBreakPointInfo(
+ Handle<DebugInfo> debug_info, Handle<Object> break_point_object) {
+ Isolate* isolate = debug_info->GetIsolate();
+ if (!debug_info->break_points()->IsUndefined()) {
+ for (int i = 0; i < debug_info->break_points()->length(); i++) {
+ if (!debug_info->break_points()->get(i)->IsUndefined()) {
+ Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>(
+ BreakPointInfo::cast(debug_info->break_points()->get(i)), isolate);
+ if (BreakPointInfo::HasBreakPointObject(break_point_info,
+ break_point_object)) {
+ return break_point_info;
+ }
}
}
}
- return heap->undefined_value();
+ return isolate->factory()->undefined_value();
}
@@ -16754,7 +19185,7 @@
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;
}
}
@@ -16866,6 +19297,39 @@
}
+// static
+MaybeHandle<JSDate> JSDate::New(Handle<JSFunction> constructor,
+ Handle<JSReceiver> new_target, double tv) {
+ Isolate* const isolate = constructor->GetIsolate();
+ Handle<JSObject> result;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
+ JSObject::New(constructor, new_target), JSDate);
+ if (-DateCache::kMaxTimeInMs <= tv && tv <= DateCache::kMaxTimeInMs) {
+ tv = DoubleToInteger(tv) + 0.0;
+ } else {
+ tv = std::numeric_limits<double>::quiet_NaN();
+ }
+ Handle<Object> value = isolate->factory()->NewNumber(tv);
+ Handle<JSDate>::cast(result)->SetValue(*value, std::isnan(tv));
+ return Handle<JSDate>::cast(result);
+}
+
+
+// static
+double JSDate::CurrentTimeValue(Isolate* isolate) {
+ if (FLAG_log_timer_events || FLAG_prof_cpp) LOG(isolate, CurrentTimeEvent());
+
+ // According to ECMA-262, section 15.9.1, page 117, the precision of
+ // the number in a Date object representing a particular instant in
+ // time is milliseconds. Therefore, we floor the result of getting
+ // the OS time.
+ return Floor(FLAG_verify_predictable
+ ? isolate->heap()->MonotonicallyIncreasingTimeInMs()
+ : base::OS::TimeCurrentMillis());
+}
+
+
+// static
Object* JSDate::GetField(Object* object, Smi* index) {
return JSDate::cast(object)->DoGetField(
static_cast<FieldIndex>(index->value()));
@@ -16958,6 +19422,16 @@
}
+// static
+Handle<Object> JSDate::SetValue(Handle<JSDate> date, double v) {
+ Isolate* const isolate = date->GetIsolate();
+ Handle<Object> value = isolate->factory()->NewNumber(v);
+ bool value_is_nan = std::isnan(v);
+ date->SetValue(*value, value_is_nan);
+ return value;
+}
+
+
void JSDate::SetValue(Object* value, bool is_value_nan) {
set_value(value);
if (is_value_nan) {
@@ -16976,6 +19450,27 @@
}
+// 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);
@@ -17001,40 +19496,62 @@
CHECK(is_external());
set_backing_store(NULL);
set_byte_length(Smi::FromInt(0));
+ set_was_neutered(true);
}
-void JSArrayBufferView::NeuterView() {
- CHECK(JSArrayBuffer::cast(buffer())->is_neuterable());
- set_byte_offset(Smi::FromInt(0));
- set_byte_length(Smi::FromInt(0));
-}
-
-
-void JSDataView::Neuter() {
- NeuterView();
-}
-
-
-void JSTypedArray::Neuter() {
- NeuterView();
- set_length(Smi::FromInt(0));
- set_elements(GetHeap()->EmptyExternalArrayForMap(map()));
-}
-
-
-static ElementsKind FixedToExternalElementsKind(ElementsKind elements_kind) {
- switch (elements_kind) {
-#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case TYPE##_ELEMENTS: return EXTERNAL_##TYPE##_ELEMENTS;
-
- TYPED_ARRAYS(TYPED_ARRAY_CASE)
-#undef TYPED_ARRAY_CASE
-
- default:
- UNREACHABLE();
- return FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND;
+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;
}
@@ -17046,104 +19563,186 @@
DCHECK(IsFixedTypedArrayElementsKind(map->elements_kind()));
- Handle<Map> new_map = Map::TransitionElementsTo(
- map,
- FixedToExternalElementsKind(map->elements_kind()));
-
- Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
Handle<FixedTypedArrayBase> fixed_typed_array(
FixedTypedArrayBase::cast(typed_array->elements()));
- Runtime::SetupArrayBufferAllocatingData(isolate, buffer,
- fixed_typed_array->DataSize(), false);
+
+ Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(typed_array->buffer()),
+ isolate);
+ void* backing_store =
+ isolate->array_buffer_allocator()->AllocateUninitialized(
+ fixed_typed_array->DataSize());
+ buffer->set_is_external(false);
+ 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());
- Handle<ExternalArray> new_elements =
- isolate->factory()->NewExternalArray(
+ Handle<FixedTypedArrayBase> new_elements =
+ isolate->factory()->NewFixedTypedArrayWithExternalPointer(
fixed_typed_array->length(), typed_array->type(),
static_cast<uint8_t*>(buffer->backing_store()));
- buffer->set_weak_first_view(*typed_array);
- DCHECK(typed_array->weak_next() == isolate->heap()->undefined_value());
- typed_array->set_buffer(*buffer);
- JSObject::SetMapAndElements(typed_array, new_map, new_elements);
+ typed_array->set_elements(*new_elements);
return buffer;
}
Handle<JSArrayBuffer> JSTypedArray::GetBuffer() {
- Handle<Object> result(buffer(), GetIsolate());
- if (*result != Smi::FromInt(0)) {
- DCHECK(IsExternalArrayElementsKind(map()->elements_kind()));
- return Handle<JSArrayBuffer>::cast(result);
+ Handle<JSArrayBuffer> array_buffer(JSArrayBuffer::cast(buffer()),
+ GetIsolate());
+ if (array_buffer->was_neutered() ||
+ array_buffer->backing_store() != nullptr) {
+ return array_buffer;
}
Handle<JSTypedArray> self(this);
return MaterializeArrayBuffer(self);
}
-HeapType* PropertyCell::type() {
- return static_cast<HeapType*>(type_raw());
-}
-
-
-void PropertyCell::set_type(HeapType* type, WriteBarrierMode ignored) {
- DCHECK(IsPropertyCell());
- set_type_raw(type, ignored);
-}
-
-
-Handle<HeapType> PropertyCell::UpdatedType(Handle<PropertyCell> cell,
- Handle<Object> value) {
- Isolate* isolate = cell->GetIsolate();
- Handle<HeapType> old_type(cell->type(), isolate);
- Handle<HeapType> new_type = HeapType::Constant(value, isolate);
-
- if (new_type->Is(old_type)) return old_type;
-
+Handle<PropertyCell> PropertyCell::InvalidateEntry(
+ Handle<GlobalDictionary> dictionary, int entry) {
+ Isolate* isolate = dictionary->GetIsolate();
+ // Swap with a copy.
+ DCHECK(dictionary->ValueAt(entry)->IsPropertyCell());
+ Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry)));
+ auto new_cell = isolate->factory()->NewPropertyCell();
+ new_cell->set_value(cell->value());
+ dictionary->ValueAtPut(entry, *new_cell);
+ bool is_the_hole = cell->value()->IsTheHole();
+ // Cell is officially mutable henceforth.
+ PropertyDetails details = cell->property_details();
+ details = details.set_cell_type(is_the_hole ? PropertyCellType::kInvalidated
+ : PropertyCellType::kMutable);
+ new_cell->set_property_details(details);
+ // Old cell is ready for invalidation.
+ if (is_the_hole) {
+ cell->set_value(isolate->heap()->undefined_value());
+ } else {
+ cell->set_value(isolate->heap()->the_hole_value());
+ }
+ details = details.set_cell_type(PropertyCellType::kInvalidated);
+ cell->set_property_details(details);
cell->dependent_code()->DeoptimizeDependentCodeGroup(
isolate, DependentCode::kPropertyCellChangedGroup);
-
- if (old_type->Is(HeapType::None()) || old_type->Is(HeapType::Undefined())) {
- return new_type;
- }
-
- return HeapType::Any(isolate);
+ return new_cell;
}
-Handle<Object> PropertyCell::SetValueInferType(Handle<PropertyCell> cell,
- Handle<Object> value) {
- // Heuristic: if a small-ish string is stored in a previously uninitialized
- // property cell, internalize it.
- const int kMaxLengthForInternalization = 200;
- if ((cell->type()->Is(HeapType::None()) ||
- cell->type()->Is(HeapType::Undefined())) &&
- value->IsString() &&
- Handle<String>::cast(value)->length() <= kMaxLengthForInternalization) {
- value = cell->GetIsolate()->factory()->InternalizeString(
- Handle<String>::cast(value));
+PropertyCellConstantType PropertyCell::GetConstantType() {
+ if (value()->IsSmi()) return PropertyCellConstantType::kSmi;
+ return PropertyCellConstantType::kStableMap;
+}
+
+
+static bool RemainsConstantType(Handle<PropertyCell> cell,
+ Handle<Object> value) {
+ // TODO(dcarney): double->smi and smi->double transition from kConstant
+ if (cell->value()->IsSmi() && value->IsSmi()) {
+ return true;
+ } else if (cell->value()->IsHeapObject() && value->IsHeapObject()) {
+ return HeapObject::cast(cell->value())->map() ==
+ HeapObject::cast(*value)->map() &&
+ HeapObject::cast(*value)->map()->is_stable();
}
+ return false;
+}
+
+
+PropertyCellType PropertyCell::UpdatedType(Handle<PropertyCell> cell,
+ Handle<Object> value,
+ PropertyDetails details) {
+ PropertyCellType type = details.cell_type();
+ DCHECK(!value->IsTheHole());
+ if (cell->value()->IsTheHole()) {
+ switch (type) {
+ // Only allow a cell to transition once into constant state.
+ case PropertyCellType::kUninitialized:
+ if (value->IsUndefined()) return PropertyCellType::kUndefined;
+ return PropertyCellType::kConstant;
+ case PropertyCellType::kInvalidated:
+ return PropertyCellType::kMutable;
+ default:
+ UNREACHABLE();
+ return PropertyCellType::kMutable;
+ }
+ }
+ switch (type) {
+ case PropertyCellType::kUndefined:
+ return PropertyCellType::kConstant;
+ case PropertyCellType::kConstant:
+ if (*value == cell->value()) return PropertyCellType::kConstant;
+ // Fall through.
+ case PropertyCellType::kConstantType:
+ if (RemainsConstantType(cell, value)) {
+ return PropertyCellType::kConstantType;
+ }
+ // Fall through.
+ case PropertyCellType::kMutable:
+ return PropertyCellType::kMutable;
+ }
+ UNREACHABLE();
+ return PropertyCellType::kMutable;
+}
+
+
+void PropertyCell::UpdateCell(Handle<GlobalDictionary> dictionary, int entry,
+ Handle<Object> value, PropertyDetails details) {
+ DCHECK(!value->IsTheHole());
+ DCHECK(dictionary->ValueAt(entry)->IsPropertyCell());
+ Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry)));
+ const PropertyDetails original_details = cell->property_details();
+ // Data accesses could be cached in ics or optimized code.
+ bool invalidate =
+ original_details.kind() == kData && details.kind() == kAccessor;
+ int index = original_details.dictionary_index();
+ PropertyCellType old_type = original_details.cell_type();
+ // Preserve the enumeration index unless the property was deleted or never
+ // initialized.
+ if (cell->value()->IsTheHole()) {
+ index = dictionary->NextEnumerationIndex();
+ dictionary->SetNextEnumerationIndex(index + 1);
+ // Negative lookup cells must be invalidated.
+ invalidate = true;
+ }
+ DCHECK(index > 0);
+ details = details.set_index(index);
+
+ PropertyCellType new_type = UpdatedType(cell, value, original_details);
+ if (invalidate) cell = PropertyCell::InvalidateEntry(dictionary, entry);
+
+ // Install new property details and cell value.
+ details = details.set_cell_type(new_type);
+ cell->set_property_details(details);
cell->set_value(*value);
- if (!HeapType::Any()->Is(cell->type())) {
- Handle<HeapType> new_type = UpdatedType(cell, value);
- cell->set_type(*new_type);
+
+ // Deopt when transitioning from a constant type.
+ if (!invalidate && (old_type != new_type ||
+ original_details.IsReadOnly() != details.IsReadOnly())) {
+ Isolate* isolate = dictionary->GetIsolate();
+ cell->dependent_code()->DeoptimizeDependentCodeGroup(
+ isolate, DependentCode::kPropertyCellChangedGroup);
}
- return value;
}
// static
-void PropertyCell::AddDependentCompilationInfo(Handle<PropertyCell> cell,
- CompilationInfo* info) {
- Handle<DependentCode> codes =
- DependentCode::Insert(handle(cell->dependent_code(), info->isolate()),
- DependentCode::kPropertyCellChangedGroup,
- info->object_wrapper());
- if (*codes != cell->dependent_code()) cell->set_dependent_code(*codes);
- info->dependencies(DependentCode::kPropertyCellChangedGroup)->Add(
- cell, info->zone());
+void PropertyCell::SetValueWithInvalidation(Handle<PropertyCell> cell,
+ Handle<Object> new_value) {
+ if (cell->value() != *new_value) {
+ cell->set_value(*new_value);
+ Isolate* isolate = cell->GetIsolate();
+ cell->dependent_code()->DeoptimizeDependentCodeGroup(
+ isolate, DependentCode::kPropertyCellChangedGroup);
+ }
}
-} } // namespace v8::internal
+} // namespace internal
+} // namespace v8