| Index: src/objects.cc
|
| ===================================================================
|
| --- src/objects.cc (revision 9808)
|
| +++ src/objects.cc (working copy)
|
| @@ -55,6 +55,54 @@
|
| namespace v8 {
|
| namespace internal {
|
|
|
| +void PrintElementsKind(FILE* out, ElementsKind kind) {
|
| + switch (kind) {
|
| + case FAST_SMI_ONLY_ELEMENTS:
|
| + PrintF(out, "FAST_SMI_ONLY_ELEMENTS");
|
| + break;
|
| + case FAST_ELEMENTS:
|
| + PrintF(out, "FAST_ELEMENTS");
|
| + break;
|
| + case FAST_DOUBLE_ELEMENTS:
|
| + PrintF(out, "FAST_DOUBLE_ELEMENTS");
|
| + break;
|
| + case DICTIONARY_ELEMENTS:
|
| + PrintF(out, "DICTIONARY_ELEMENTS");
|
| + break;
|
| + case NON_STRICT_ARGUMENTS_ELEMENTS:
|
| + PrintF(out, "NON_STRICT_ARGUMENTS_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_BYTE_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_BYTE_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_UNSIGNED_BYTE_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_SHORT_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_SHORT_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_UNSIGNED_SHORT_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_INT_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_INT_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_UNSIGNED_INT_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_UNSIGNED_INT_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_FLOAT_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_FLOAT_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_DOUBLE_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS");
|
| + break;
|
| + case EXTERNAL_PIXEL_ELEMENTS:
|
| + PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS");
|
| + break;
|
| + }
|
| +}
|
| +
|
| +
|
| // Getters and setters are stored in a fixed array property. These are
|
| // constants for their indices.
|
| const int kGetterIndex = 0;
|
| @@ -154,7 +202,7 @@
|
| MaybeObject* Object::GetPropertyWithReceiver(Object* receiver,
|
| String* name,
|
| PropertyAttributes* attributes) {
|
| - LookupResult result;
|
| + LookupResult result(name->GetIsolate());
|
| Lookup(name, &result);
|
| MaybeObject* value = GetProperty(receiver, &result, name, attributes);
|
| ASSERT(*attributes <= ABSENT);
|
| @@ -234,6 +282,14 @@
|
| }
|
|
|
|
|
| +Handle<Object> Object::GetElement(Handle<Object> object, uint32_t index) {
|
| + Isolate* isolate = object->IsHeapObject()
|
| + ? Handle<HeapObject>::cast(object)->GetIsolate()
|
| + : Isolate::Current();
|
| + CALL_HEAP_FUNCTION(isolate, object->GetElement(index), Object);
|
| +}
|
| +
|
| +
|
| MaybeObject* JSProxy::GetElementWithHandler(Object* receiver,
|
| uint32_t index) {
|
| String* name;
|
| @@ -310,7 +366,7 @@
|
| case FIELD:
|
| case CONSTANT_FUNCTION: {
|
| // Search ALL_CAN_READ accessors in prototype chain.
|
| - LookupResult r;
|
| + LookupResult r(GetIsolate());
|
| result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
|
| if (r.IsProperty()) {
|
| return GetPropertyWithFailedAccessCheck(receiver,
|
| @@ -323,7 +379,7 @@
|
| case INTERCEPTOR: {
|
| // If the object has an interceptor, try real named properties.
|
| // No access check in GetPropertyAttributeWithInterceptor.
|
| - LookupResult r;
|
| + LookupResult r(GetIsolate());
|
| result->holder()->LookupRealNamedProperty(name, &r);
|
| if (r.IsProperty()) {
|
| return GetPropertyWithFailedAccessCheck(receiver,
|
| @@ -370,7 +426,7 @@
|
| case CONSTANT_FUNCTION: {
|
| if (!continue_search) break;
|
| // Search ALL_CAN_READ accessors in prototype chain.
|
| - LookupResult r;
|
| + LookupResult r(GetIsolate());
|
| result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
|
| if (r.IsProperty()) {
|
| return GetPropertyAttributeWithFailedAccessCheck(receiver,
|
| @@ -384,7 +440,7 @@
|
| case INTERCEPTOR: {
|
| // If the object has an interceptor, try real named properties.
|
| // No access check in GetPropertyAttributeWithInterceptor.
|
| - LookupResult r;
|
| + LookupResult r(GetIsolate());
|
| if (continue_search) {
|
| result->holder()->LookupRealNamedProperty(name, &r);
|
| } else {
|
| @@ -404,7 +460,7 @@
|
| }
|
| }
|
|
|
| - GetHeap()->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| + GetIsolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| return ABSENT;
|
| }
|
|
|
| @@ -528,6 +584,21 @@
|
| }
|
|
|
|
|
| +Handle<Object> Object::GetProperty(Handle<Object> object,
|
| + Handle<Object> receiver,
|
| + LookupResult* result,
|
| + Handle<String> key,
|
| + PropertyAttributes* attributes) {
|
| + Isolate* isolate = object->IsHeapObject()
|
| + ? Handle<HeapObject>::cast(object)->GetIsolate()
|
| + : Isolate::Current();
|
| + CALL_HEAP_FUNCTION(
|
| + isolate,
|
| + object->GetProperty(*receiver, result, *key, attributes),
|
| + Object);
|
| +}
|
| +
|
| +
|
| MaybeObject* Object::GetProperty(Object* receiver,
|
| LookupResult* result,
|
| String* name,
|
| @@ -700,6 +771,49 @@
|
| }
|
|
|
|
|
| +MaybeObject* Object::GetHash(CreationFlag flag) {
|
| + // The object is either a number, a string, an odd-ball,
|
| + // a real JS object, or a Harmony proxy.
|
| + if (IsNumber()) {
|
| + uint32_t hash = ComputeLongHash(double_to_uint64(Number()));
|
| + return Smi::FromInt(hash & Smi::kMaxValue);
|
| + }
|
| + if (IsString()) {
|
| + uint32_t hash = String::cast(this)->Hash();
|
| + return Smi::FromInt(hash);
|
| + }
|
| + if (IsOddball()) {
|
| + uint32_t hash = Oddball::cast(this)->to_string()->Hash();
|
| + return Smi::FromInt(hash);
|
| + }
|
| + if (IsJSReceiver()) {
|
| + return JSReceiver::cast(this)->GetIdentityHash(flag);
|
| + }
|
| +
|
| + UNREACHABLE();
|
| + return Smi::FromInt(0);
|
| +}
|
| +
|
| +
|
| +bool Object::SameValue(Object* other) {
|
| + if (other == this) return true;
|
| + if (!IsHeapObject() || !other->IsHeapObject()) return false;
|
| +
|
| + // The object is either a number, a string, an odd-ball,
|
| + // a real JS object, or a Harmony proxy.
|
| + if (IsNumber() && other->IsNumber()) {
|
| + double this_value = Number();
|
| + double other_value = other->Number();
|
| + return (this_value == other_value) ||
|
| + (isnan(this_value) && isnan(other_value));
|
| + }
|
| + if (IsString() && other->IsString()) {
|
| + return String::cast(this)->Equals(String::cast(other));
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +
|
| void Object::ShortPrint(FILE* out) {
|
| HeapStringAllocator allocator;
|
| StringStream accumulator(&allocator);
|
| @@ -1074,6 +1188,27 @@
|
| }
|
|
|
|
|
| +void JSObject::PrintElementsTransition(
|
| + FILE* file, ElementsKind from_kind, FixedArrayBase* from_elements,
|
| + ElementsKind to_kind, FixedArrayBase* to_elements) {
|
| + if (from_kind != to_kind) {
|
| + PrintF(file, "elements transition [");
|
| + PrintElementsKind(file, from_kind);
|
| + PrintF(file, " -> ");
|
| + PrintElementsKind(file, to_kind);
|
| + PrintF(file, "] in ");
|
| + JavaScriptFrame::PrintTop(file, false, true);
|
| + PrintF(file, " for ");
|
| + ShortPrint(file);
|
| + PrintF(file, " from ");
|
| + from_elements->ShortPrint(file);
|
| + PrintF(file, " to ");
|
| + to_elements->ShortPrint(file);
|
| + PrintF(file, "\n");
|
| + }
|
| +}
|
| +
|
| +
|
| void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
|
| Heap* heap = GetHeap();
|
| if (!heap->Contains(this)) {
|
| @@ -1102,6 +1237,10 @@
|
| case FIXED_ARRAY_TYPE:
|
| accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
|
| break;
|
| + case FIXED_DOUBLE_ARRAY_TYPE:
|
| + accumulator->Add("<FixedDoubleArray[%u]>",
|
| + FixedDoubleArray::cast(this)->length());
|
| + break;
|
| case BYTE_ARRAY_TYPE:
|
| accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
|
| break;
|
| @@ -1247,6 +1386,8 @@
|
| case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
| case JS_VALUE_TYPE:
|
| case JS_ARRAY_TYPE:
|
| + case JS_SET_TYPE:
|
| + case JS_MAP_TYPE:
|
| case JS_WEAK_MAP_TYPE:
|
| case JS_REGEXP_TYPE:
|
| case JS_GLOBAL_PROXY_TYPE:
|
| @@ -1658,7 +1799,7 @@
|
| PropertyAttributes attributes,
|
| StrictModeFlag strict_mode) {
|
| // Check local property, ignore interceptor.
|
| - LookupResult result;
|
| + LookupResult result(GetIsolate());
|
| LocalLookupRealNamedProperty(name, &result);
|
| if (result.IsFound()) {
|
| // An existing property, a map transition or a null descriptor was
|
| @@ -1840,7 +1981,7 @@
|
| Object* value,
|
| PropertyAttributes attributes,
|
| StrictModeFlag strict_mode) {
|
| - LookupResult result;
|
| + LookupResult result(GetIsolate());
|
| LocalLookup(name, &result);
|
| return SetProperty(&result, name, value, attributes, strict_mode);
|
| }
|
| @@ -2006,9 +2147,9 @@
|
| PropertyAttributes attributes,
|
| bool* found,
|
| StrictModeFlag strict_mode) {
|
| - LookupResult result;
|
| + Heap* heap = GetHeap();
|
| + LookupResult result(heap->isolate());
|
| LookupCallbackSetterInPrototypes(name, &result);
|
| - Heap* heap = GetHeap();
|
| if (result.IsFound()) {
|
| *found = true;
|
| if (result.type() == CALLBACKS) {
|
| @@ -2020,7 +2161,7 @@
|
| } else if (result.type() == HANDLER) {
|
| // We could not find a local property so let's check whether there is an
|
| // accessor that wants to handle the property.
|
| - LookupResult accessor_result;
|
| + LookupResult accessor_result(heap->isolate());
|
| LookupCallbackSetterInPrototypes(name, &accessor_result);
|
| if (accessor_result.IsFound()) {
|
| if (accessor_result.type() == CALLBACKS) {
|
| @@ -2085,6 +2226,51 @@
|
| }
|
|
|
|
|
| +static bool ContainsMap(MapHandleList* maps, Handle<Map> map) {
|
| + ASSERT(!map.is_null());
|
| + for (int i = 0; i < maps->length(); ++i) {
|
| + if (!maps->at(i).is_null() && maps->at(i).is_identical_to(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 elms_kind = elements_kind();
|
| + if (elms_kind == FAST_DOUBLE_ELEMENTS) {
|
| + bool dummy = true;
|
| + Handle<Map> fast_map =
|
| + MaybeNull(LookupElementsTransitionMap(FAST_ELEMENTS, &dummy));
|
| + if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) {
|
| + return fast_map;
|
| + }
|
| + return Handle<Map>::null();
|
| + }
|
| + if (elms_kind == FAST_SMI_ONLY_ELEMENTS) {
|
| + bool dummy = true;
|
| + Handle<Map> double_map =
|
| + MaybeNull(LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, &dummy));
|
| + // In the current implementation, if the DOUBLE map doesn't exist, the
|
| + // FAST map can't exist either.
|
| + if (double_map.is_null()) return Handle<Map>::null();
|
| + Handle<Map> fast_map =
|
| + MaybeNull(double_map->LookupElementsTransitionMap(FAST_ELEMENTS,
|
| + &dummy));
|
| + if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) {
|
| + return fast_map;
|
| + }
|
| + if (ContainsMap(candidates, double_map)) return double_map;
|
| + }
|
| + return Handle<Map>::null();
|
| +}
|
| +
|
| static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents,
|
| ElementsKind elements_kind) {
|
| if (descriptor_contents->IsMap()) {
|
| @@ -2268,6 +2454,15 @@
|
| }
|
|
|
|
|
| +Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object,
|
| + ElementsKind to_kind) {
|
| + Isolate* isolate = object->GetIsolate();
|
| + CALL_HEAP_FUNCTION(isolate,
|
| + object->GetElementsTransitionMap(to_kind),
|
| + Map);
|
| +}
|
| +
|
| +
|
| MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind to_kind) {
|
| Map* current_map = map();
|
| ElementsKind from_kind = current_map->elements_kind();
|
| @@ -2423,7 +2618,7 @@
|
| case INTERCEPTOR: {
|
| // Try lookup real named properties. Note that only property can be
|
| // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
|
| - LookupResult r;
|
| + LookupResult r(GetIsolate());
|
| LookupRealNamedProperty(name, &r);
|
| if (r.IsProperty()) {
|
| return SetPropertyWithFailedAccessCheck(&r,
|
| @@ -2441,10 +2636,10 @@
|
| }
|
| }
|
|
|
| - Heap* heap = GetHeap();
|
| - HandleScope scope(heap->isolate());
|
| + Isolate* isolate = GetIsolate();
|
| + HandleScope scope(isolate);
|
| Handle<Object> value_handle(value);
|
| - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
|
| + isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
|
| return *value_handle;
|
| }
|
|
|
| @@ -2507,6 +2702,7 @@
|
| *found = true; // except where defined otherwise...
|
| Isolate* isolate = GetHeap()->isolate();
|
| Handle<JSProxy> proxy(this);
|
| + Handle<Object> handler(this->handler()); // Trap might morph proxy.
|
| Handle<String> name(name_raw);
|
| Handle<Object> value(value_raw);
|
| Handle<Object> args[] = { name };
|
| @@ -2530,7 +2726,9 @@
|
| Handle<Object> configurable(v8::internal::GetProperty(desc, conf_name));
|
| ASSERT(!isolate->has_pending_exception());
|
| if (configurable->IsFalse()) {
|
| - Handle<Object> args[] = { Handle<Object>(proxy->handler()), proxy, name };
|
| + Handle<String> trap =
|
| + isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor");
|
| + Handle<Object> args[] = { handler, trap, name };
|
| Handle<Object> error = isolate->factory()->NewTypeError(
|
| "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args)));
|
| return isolate->Throw(*error);
|
| @@ -2610,6 +2808,7 @@
|
| Isolate* isolate = GetIsolate();
|
| HandleScope scope(isolate);
|
| Handle<JSProxy> proxy(this);
|
| + Handle<Object> handler(this->handler()); // Trap might morph proxy.
|
| Handle<JSReceiver> receiver(receiver_raw);
|
| Handle<Object> name(name_raw);
|
|
|
| @@ -2639,7 +2838,9 @@
|
| if (isolate->has_pending_exception()) return NONE;
|
|
|
| if (configurable->IsFalse()) {
|
| - Handle<Object> args[] = { Handle<Object>(proxy->handler()), proxy, name };
|
| + Handle<String> trap =
|
| + isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor");
|
| + Handle<Object> args[] = { handler, trap, name };
|
| Handle<Object> error = isolate->factory()->NewTypeError(
|
| "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args)));
|
| isolate->Throw(*error);
|
| @@ -2859,12 +3060,12 @@
|
| // Make sure that the top context does not change when doing callbacks or
|
| // interceptor calls.
|
| AssertNoContextChange ncc;
|
| - LookupResult result;
|
| + Isolate* isolate = GetIsolate();
|
| + LookupResult result(isolate);
|
| LocalLookup(name, &result);
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded()) {
|
| - Heap* heap = GetHeap();
|
| - if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| + if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| return SetPropertyWithFailedAccessCheck(&result,
|
| name,
|
| value,
|
| @@ -2935,7 +3136,7 @@
|
| String* name,
|
| bool continue_search) {
|
| // Check local property, ignore interceptor.
|
| - LookupResult result;
|
| + LookupResult result(GetIsolate());
|
| LocalLookupRealNamedProperty(name, &result);
|
| if (result.IsProperty()) return result.GetAttributes();
|
|
|
| @@ -3011,7 +3212,7 @@
|
| ? NONE : ABSENT;
|
| }
|
| // Named property.
|
| - LookupResult result;
|
| + LookupResult result(GetIsolate());
|
| Lookup(key, &result);
|
| return GetPropertyAttribute(receiver, &result, key, true);
|
| }
|
| @@ -3060,7 +3261,7 @@
|
| return ABSENT;
|
| }
|
| // Named property.
|
| - LookupResult result;
|
| + LookupResult result(GetIsolate());
|
| LocalLookup(name, &result);
|
| return GetPropertyAttribute(this, &result, name, false);
|
| }
|
| @@ -3075,7 +3276,9 @@
|
| if (result->IsMap() &&
|
| Map::cast(result)->EquivalentToForNormalization(fast, mode)) {
|
| #ifdef DEBUG
|
| - Map::cast(result)->SharedMapVerify();
|
| + if (FLAG_verify_heap) {
|
| + Map::cast(result)->SharedMapVerify();
|
| + }
|
| if (FLAG_enable_slow_asserts) {
|
| // The cached map should match newly created normalized map bit-by-bit.
|
| Object* fresh;
|
| @@ -3111,6 +3314,15 @@
|
| }
|
|
|
|
|
| +void JSObject::UpdateMapCodeCache(Handle<JSObject> object,
|
| + Handle<String> name,
|
| + Handle<Code> code) {
|
| + Isolate* isolate = object->GetIsolate();
|
| + CALL_HEAP_FUNCTION_VOID(isolate,
|
| + object->UpdateMapCodeCache(*name, *code));
|
| +}
|
| +
|
| +
|
| MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) {
|
| if (map()->is_shared()) {
|
| // Fast case maps are never marked as shared.
|
| @@ -3356,7 +3568,7 @@
|
| do {
|
| // Generate a random 32-bit hash value but limit range to fit
|
| // within a smi.
|
| - hash_value = V8::Random(isolate) & Smi::kMaxValue;
|
| + hash_value = V8::RandomPrivate(isolate) & Smi::kMaxValue;
|
| attempts++;
|
| } while (hash_value == 0 && attempts < 30);
|
| hash_value = hash_value != 0 ? hash_value : 1; // never return 0
|
| @@ -3377,6 +3589,9 @@
|
| Object* stored_value = GetHiddenProperty(GetHeap()->identity_hash_symbol());
|
| if (stored_value->IsSmi()) return stored_value;
|
|
|
| + // Do not generate permanent identity hash code if not requested.
|
| + if (flag == OMIT_CREATION) return GetHeap()->undefined_value();
|
| +
|
| Smi* hash = GenerateIdentityHash();
|
| MaybeObject* result = SetHiddenProperty(GetHeap()->identity_hash_symbol(),
|
| hash);
|
| @@ -3567,7 +3782,7 @@
|
| MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
|
| DeleteMode mode) {
|
| // Check local property, ignore interceptor.
|
| - LookupResult result;
|
| + LookupResult result(GetIsolate());
|
| LocalLookupRealNamedProperty(name, &result);
|
| if (!result.IsProperty()) return GetHeap()->true_value();
|
|
|
| @@ -3716,7 +3931,7 @@
|
| if (name->AsArrayIndex(&index)) {
|
| return DeleteElement(index, mode);
|
| } else {
|
| - LookupResult result;
|
| + LookupResult result(isolate);
|
| LocalLookup(name, &result);
|
| if (!result.IsProperty()) return isolate->heap()->true_value();
|
| // Ignore attributes if forcing a deletion.
|
| @@ -3927,15 +4142,16 @@
|
|
|
|
|
| // Tests for the fast common case for property enumeration:
|
| -// - This object and all prototypes has an enum cache (which means that it has
|
| -// no interceptors and needs no access checks).
|
| +// - This object and all prototypes has an enum cache (which means that
|
| +// it is no proxy, has no interceptors and needs no access checks).
|
| // - This object has no elements.
|
| // - No prototype has enumerable properties/elements.
|
| -bool JSObject::IsSimpleEnum() {
|
| +bool JSReceiver::IsSimpleEnum() {
|
| Heap* heap = GetHeap();
|
| for (Object* o = this;
|
| o != heap->null_value();
|
| o = JSObject::cast(o)->GetPrototype()) {
|
| + if (!o->IsJSObject()) return false;
|
| JSObject* curr = JSObject::cast(o);
|
| if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
|
| ASSERT(!curr->HasNamedInterceptor());
|
| @@ -4065,19 +4281,27 @@
|
| }
|
|
|
|
|
| -// Search for a getter or setter in an elements dictionary. Returns either
|
| -// undefined if the element is read-only, or the getter/setter pair (fixed
|
| -// array) if there is an existing one, or the hole value if the element does
|
| -// not exist or is a normal non-getter/setter data element.
|
| -static Object* FindGetterSetterInDictionary(NumberDictionary* dictionary,
|
| - uint32_t index,
|
| - Heap* heap) {
|
| +// Search for a getter or setter in an elements dictionary and update its
|
| +// attributes. Returns either undefined if the element is read-only, or the
|
| +// getter/setter pair (fixed array) if there is an existing one, or the hole
|
| +// value if the element does not exist or is a normal non-getter/setter data
|
| +// element.
|
| +static Object* UpdateGetterSetterInDictionary(NumberDictionary* dictionary,
|
| + uint32_t index,
|
| + PropertyAttributes attributes,
|
| + Heap* heap) {
|
| int entry = dictionary->FindEntry(index);
|
| if (entry != NumberDictionary::kNotFound) {
|
| Object* result = dictionary->ValueAt(entry);
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| if (details.IsReadOnly()) return heap->undefined_value();
|
| - if (details.type() == CALLBACKS && result->IsFixedArray()) return result;
|
| + if (details.type() == CALLBACKS && result->IsFixedArray()) {
|
| + if (details.attributes() != attributes) {
|
| + dictionary->DetailsAtPut(entry,
|
| + PropertyDetails(attributes, CALLBACKS, index));
|
| + }
|
| + return result;
|
| + }
|
| }
|
| return heap->the_hole_value();
|
| }
|
| @@ -4119,8 +4343,10 @@
|
| // elements.
|
| return heap->undefined_value();
|
| case DICTIONARY_ELEMENTS: {
|
| - Object* probe =
|
| - FindGetterSetterInDictionary(element_dictionary(), index, heap);
|
| + Object* probe = UpdateGetterSetterInDictionary(element_dictionary(),
|
| + index,
|
| + attributes,
|
| + heap);
|
| if (!probe->IsTheHole()) return probe;
|
| // Otherwise allow to override it.
|
| break;
|
| @@ -4137,7 +4363,10 @@
|
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
|
| if (arguments->IsDictionary()) {
|
| NumberDictionary* dictionary = NumberDictionary::cast(arguments);
|
| - probe = FindGetterSetterInDictionary(dictionary, index, heap);
|
| + probe = UpdateGetterSetterInDictionary(dictionary,
|
| + index,
|
| + attributes,
|
| + heap);
|
| if (!probe->IsTheHole()) return probe;
|
| }
|
| }
|
| @@ -4146,7 +4375,7 @@
|
| }
|
| } else {
|
| // Lookup the name.
|
| - LookupResult result;
|
| + LookupResult result(heap->isolate());
|
| LocalLookup(name, &result);
|
| if (result.IsProperty()) {
|
| if (result.IsReadOnly()) return heap->undefined_value();
|
| @@ -4176,8 +4405,8 @@
|
|
|
|
|
| bool JSObject::CanSetCallback(String* name) {
|
| - ASSERT(!IsAccessCheckNeeded()
|
| - || Isolate::Current()->MayNamedAccess(this, name, v8::ACCESS_SET));
|
| + ASSERT(!IsAccessCheckNeeded() ||
|
| + GetIsolate()->MayNamedAccess(this, name, v8::ACCESS_SET));
|
|
|
| // Check if there is an API defined callback object which prohibits
|
| // callback overwriting in this object or it's prototype chain.
|
| @@ -4185,7 +4414,7 @@
|
| // certain accessors such as window.location should not be allowed
|
| // to be overwritten because allowing overwriting could potentially
|
| // cause security problems.
|
| - LookupResult callback_result;
|
| + LookupResult callback_result(GetIsolate());
|
| LookupCallback(name, &callback_result);
|
| if (callback_result.IsProperty()) {
|
| Object* obj = callback_result.GetCallbackObject();
|
| @@ -4382,7 +4611,7 @@
|
| }
|
| } else {
|
| // Lookup the name.
|
| - LookupResult result;
|
| + LookupResult result(isolate);
|
| LocalLookup(name, &result);
|
| // ES5 forbids turning a property into an accessor if it's not
|
| // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
|
| @@ -4440,7 +4669,7 @@
|
| for (Object* obj = this;
|
| obj != heap->null_value();
|
| obj = JSObject::cast(obj)->GetPrototype()) {
|
| - LookupResult result;
|
| + LookupResult result(heap->isolate());
|
| JSObject::cast(obj)->LocalLookup(name, &result);
|
| if (result.IsProperty()) {
|
| if (result.IsReadOnly()) return heap->undefined_value();
|
| @@ -4548,7 +4777,7 @@
|
| Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
|
|
|
| #ifdef DEBUG
|
| - if (Map::cast(result)->is_shared()) {
|
| + if (FLAG_verify_heap && Map::cast(result)->is_shared()) {
|
| Map::cast(result)->SharedMapVerify();
|
| }
|
| #endif
|
| @@ -4571,6 +4800,13 @@
|
| return new_map;
|
| }
|
|
|
| +void Map::UpdateCodeCache(Handle<Map> map,
|
| + Handle<String> name,
|
| + Handle<Code> code) {
|
| + Isolate* isolate = map->GetIsolate();
|
| + CALL_HEAP_FUNCTION_VOID(isolate,
|
| + map->UpdateCodeCache(*name, *code));
|
| +}
|
|
|
| MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
|
| // Allocate the code cache if not present.
|
| @@ -4960,7 +5196,16 @@
|
| }
|
|
|
|
|
| -MaybeObject* PolymorphicCodeCache::Update(MapList* maps,
|
| +void PolymorphicCodeCache::Update(Handle<PolymorphicCodeCache> cache,
|
| + MapHandleList* maps,
|
| + Code::Flags flags,
|
| + Handle<Code> code) {
|
| + Isolate* isolate = cache->GetIsolate();
|
| + CALL_HEAP_FUNCTION_VOID(isolate, cache->Update(maps, flags, *code));
|
| +}
|
| +
|
| +
|
| +MaybeObject* PolymorphicCodeCache::Update(MapHandleList* maps,
|
| Code::Flags flags,
|
| Code* code) {
|
| // Initialize cache if necessary.
|
| @@ -4988,13 +5233,14 @@
|
| }
|
|
|
|
|
| -Object* PolymorphicCodeCache::Lookup(MapList* maps, Code::Flags flags) {
|
| +Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps,
|
| + Code::Flags flags) {
|
| if (!cache()->IsUndefined()) {
|
| PolymorphicCodeCacheHashTable* hash_table =
|
| PolymorphicCodeCacheHashTable::cast(cache());
|
| - return hash_table->Lookup(maps, flags);
|
| + return Handle<Object>(hash_table->Lookup(maps, flags));
|
| } else {
|
| - return GetHeap()->undefined_value();
|
| + return GetIsolate()->factory()->undefined_value();
|
| }
|
| }
|
|
|
| @@ -5005,12 +5251,12 @@
|
| class PolymorphicCodeCacheHashTableKey : public HashTableKey {
|
| public:
|
| // Callers must ensure that |maps| outlives the newly constructed object.
|
| - PolymorphicCodeCacheHashTableKey(MapList* maps, int code_flags)
|
| + PolymorphicCodeCacheHashTableKey(MapHandleList* maps, int code_flags)
|
| : maps_(maps),
|
| code_flags_(code_flags) {}
|
|
|
| bool IsMatch(Object* other) {
|
| - MapList other_maps(kDefaultListAllocationSize);
|
| + MapHandleList other_maps(kDefaultListAllocationSize);
|
| int other_flags;
|
| FromObject(other, &other_flags, &other_maps);
|
| if (code_flags_ != other_flags) return false;
|
| @@ -5026,7 +5272,7 @@
|
| for (int i = 0; i < maps_->length(); ++i) {
|
| bool match_found = false;
|
| for (int j = 0; j < other_maps.length(); ++j) {
|
| - if (maps_->at(i)->EquivalentTo(other_maps.at(j))) {
|
| + if (maps_->at(i)->EquivalentTo(*other_maps.at(j))) {
|
| match_found = true;
|
| break;
|
| }
|
| @@ -5036,7 +5282,7 @@
|
| return true;
|
| }
|
|
|
| - static uint32_t MapsHashHelper(MapList* maps, int code_flags) {
|
| + static uint32_t MapsHashHelper(MapHandleList* maps, int code_flags) {
|
| uint32_t hash = code_flags;
|
| for (int i = 0; i < maps->length(); ++i) {
|
| hash ^= maps->at(i)->Hash();
|
| @@ -5049,7 +5295,7 @@
|
| }
|
|
|
| uint32_t HashForObject(Object* obj) {
|
| - MapList other_maps(kDefaultListAllocationSize);
|
| + MapHandleList other_maps(kDefaultListAllocationSize);
|
| int other_flags;
|
| FromObject(obj, &other_flags, &other_maps);
|
| return MapsHashHelper(&other_maps, other_flags);
|
| @@ -5067,29 +5313,32 @@
|
| FixedArray* list = FixedArray::cast(obj);
|
| list->set(0, Smi::FromInt(code_flags_));
|
| for (int i = 0; i < maps_->length(); ++i) {
|
| - list->set(i + 1, maps_->at(i));
|
| + list->set(i + 1, *maps_->at(i));
|
| }
|
| return list;
|
| }
|
|
|
| private:
|
| - static MapList* FromObject(Object* obj, int* code_flags, MapList* maps) {
|
| + static MapHandleList* FromObject(Object* obj,
|
| + int* code_flags,
|
| + MapHandleList* maps) {
|
| FixedArray* list = FixedArray::cast(obj);
|
| maps->Rewind(0);
|
| *code_flags = Smi::cast(list->get(0))->value();
|
| for (int i = 1; i < list->length(); ++i) {
|
| - maps->Add(Map::cast(list->get(i)));
|
| + maps->Add(Handle<Map>(Map::cast(list->get(i))));
|
| }
|
| return maps;
|
| }
|
|
|
| - MapList* maps_; // weak.
|
| + MapHandleList* maps_; // weak.
|
| int code_flags_;
|
| static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1;
|
| };
|
|
|
|
|
| -Object* PolymorphicCodeCacheHashTable::Lookup(MapList* maps, int code_flags) {
|
| +Object* PolymorphicCodeCacheHashTable::Lookup(MapHandleList* maps,
|
| + int code_flags) {
|
| PolymorphicCodeCacheHashTableKey key(maps, code_flags);
|
| int entry = FindEntry(&key);
|
| if (entry == kNotFound) return GetHeap()->undefined_value();
|
| @@ -5097,7 +5346,7 @@
|
| }
|
|
|
|
|
| -MaybeObject* PolymorphicCodeCacheHashTable::Put(MapList* maps,
|
| +MaybeObject* PolymorphicCodeCacheHashTable::Put(MapHandleList* maps,
|
| int code_flags,
|
| Code* code) {
|
| PolymorphicCodeCacheHashTableKey key(maps, code_flags);
|
| @@ -5232,9 +5481,9 @@
|
| if (IsEmpty()) return; // Do nothing for empty descriptor array.
|
| FixedArray::cast(bridge_storage)->
|
| set(kEnumCacheBridgeCacheIndex, new_cache);
|
| - fast_set(FixedArray::cast(bridge_storage),
|
| - kEnumCacheBridgeEnumIndex,
|
| - get(kEnumerationIndexIndex));
|
| + NoWriteBarrierSet(FixedArray::cast(bridge_storage),
|
| + kEnumCacheBridgeEnumIndex,
|
| + get(kEnumerationIndexIndex));
|
| set(kEnumerationIndexIndex, bridge_storage);
|
| }
|
| }
|
| @@ -5295,10 +5544,16 @@
|
| ++new_size;
|
| }
|
| }
|
| +
|
| + DescriptorArray* new_descriptors;
|
| { MaybeObject* maybe_result = Allocate(new_size);
|
| - if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + if (!maybe_result->To<DescriptorArray>(&new_descriptors)) {
|
| + return maybe_result;
|
| + }
|
| }
|
| - DescriptorArray* new_descriptors = DescriptorArray::cast(result);
|
| +
|
| + DescriptorArray::WhitenessWitness witness(new_descriptors);
|
| +
|
| // Set the enumeration index in the descriptors and set the enumeration index
|
| // in the result.
|
| int enumeration_index = NextEnumerationIndex();
|
| @@ -5326,16 +5581,16 @@
|
| }
|
| if (IsNullDescriptor(from_index)) continue;
|
| if (remove_transitions && IsTransition(from_index)) continue;
|
| - new_descriptors->CopyFrom(to_index++, this, from_index);
|
| + new_descriptors->CopyFrom(to_index++, this, from_index, witness);
|
| }
|
|
|
| - new_descriptors->Set(to_index++, descriptor);
|
| + new_descriptors->Set(to_index++, descriptor, witness);
|
| if (replacing) from_index++;
|
|
|
| for (; from_index < number_of_descriptors(); from_index++) {
|
| if (IsNullDescriptor(from_index)) continue;
|
| if (remove_transitions && IsTransition(from_index)) continue;
|
| - new_descriptors->CopyFrom(to_index++, this, from_index);
|
| + new_descriptors->CopyFrom(to_index++, this, from_index, witness);
|
| }
|
|
|
| ASSERT(to_index == new_descriptors->number_of_descriptors());
|
| @@ -5357,16 +5612,21 @@
|
| }
|
|
|
| // Allocate the new descriptor array.
|
| - Object* result;
|
| + DescriptorArray* new_descriptors;
|
| { MaybeObject* maybe_result = Allocate(number_of_descriptors() - num_removed);
|
| - if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + if (!maybe_result->To<DescriptorArray>(&new_descriptors)) {
|
| + return maybe_result;
|
| + }
|
| }
|
| - DescriptorArray* new_descriptors = DescriptorArray::cast(result);
|
|
|
| + DescriptorArray::WhitenessWitness witness(new_descriptors);
|
| +
|
| // Copy the content.
|
| int next_descriptor = 0;
|
| for (int i = 0; i < number_of_descriptors(); i++) {
|
| - if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
|
| + if (IsProperty(i)) {
|
| + new_descriptors->CopyFrom(next_descriptor++, this, i, witness);
|
| + }
|
| }
|
| ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
|
|
|
| @@ -5374,7 +5634,7 @@
|
| }
|
|
|
|
|
| -void DescriptorArray::SortUnchecked() {
|
| +void DescriptorArray::SortUnchecked(const WhitenessWitness& witness) {
|
| // In-place heap sort.
|
| int len = number_of_descriptors();
|
|
|
| @@ -5395,7 +5655,7 @@
|
| }
|
| }
|
| if (child_hash <= parent_hash) break;
|
| - Swap(parent_index, child_index);
|
| + NoWriteBarrierSwapDescriptors(parent_index, child_index);
|
| // Now element at child_index could be < its children.
|
| parent_index = child_index; // parent_hash remains correct.
|
| }
|
| @@ -5404,8 +5664,8 @@
|
| // Extract elements and create sorted array.
|
| for (int i = len - 1; i > 0; --i) {
|
| // Put max element at the back of the array.
|
| - Swap(0, i);
|
| - // Sift down the new top element.
|
| + NoWriteBarrierSwapDescriptors(0, i);
|
| + // Shift down the new top element.
|
| int parent_index = 0;
|
| const uint32_t parent_hash = GetKey(parent_index)->Hash();
|
| const int max_parent_index = (i / 2) - 1;
|
| @@ -5420,15 +5680,15 @@
|
| }
|
| }
|
| if (child_hash <= parent_hash) break;
|
| - Swap(parent_index, child_index);
|
| + NoWriteBarrierSwapDescriptors(parent_index, child_index);
|
| parent_index = child_index;
|
| }
|
| }
|
| }
|
|
|
|
|
| -void DescriptorArray::Sort() {
|
| - SortUnchecked();
|
| +void DescriptorArray::Sort(const WhitenessWitness& witness) {
|
| + SortUnchecked(witness);
|
| SLOW_ASSERT(IsSortedNoDuplicates());
|
| }
|
|
|
| @@ -5513,24 +5773,6 @@
|
| }
|
|
|
|
|
| -int String::Utf8Length() {
|
| - if (IsAsciiRepresentation()) return length();
|
| - // Attempt to flatten before accessing the string. It probably
|
| - // doesn't make Utf8Length faster, but it is very likely that
|
| - // the string will be accessed later (for example by WriteUtf8)
|
| - // so it's still a good idea.
|
| - Heap* heap = GetHeap();
|
| - TryFlatten();
|
| - Access<StringInputBuffer> buffer(
|
| - heap->isolate()->objects_string_input_buffer());
|
| - buffer->Reset(0, this);
|
| - int result = 0;
|
| - while (buffer->has_more())
|
| - result += unibrow::Utf8::Length(buffer->GetNext());
|
| - return result;
|
| -}
|
| -
|
| -
|
| String::FlatContent String::GetFlatContent() {
|
| int length = this->length();
|
| StringShape shape(this);
|
| @@ -5954,6 +6196,73 @@
|
| }
|
|
|
|
|
| +// This method determines the type of string involved and then gets the UTF8
|
| +// length of the string. It doesn't flatten the string and has log(n) recursion
|
| +// for a string of length n.
|
| +int String::Utf8Length(String* input, int from, int to) {
|
| + if (from == to) return 0;
|
| + int total = 0;
|
| + while (true) {
|
| + if (input->IsAsciiRepresentation()) return total + to - from;
|
| + switch (StringShape(input).representation_tag()) {
|
| + case kConsStringTag: {
|
| + ConsString* str = ConsString::cast(input);
|
| + String* first = str->first();
|
| + String* second = str->second();
|
| + int first_length = first->length();
|
| + if (first_length - from < to - first_length) {
|
| + if (first_length > from) {
|
| + // Left hand side is shorter.
|
| + total += Utf8Length(first, from, first_length);
|
| + input = second;
|
| + from = 0;
|
| + to -= first_length;
|
| + } else {
|
| + // We only need the right hand side.
|
| + input = second;
|
| + from -= first_length;
|
| + to -= first_length;
|
| + }
|
| + } else {
|
| + if (first_length <= to) {
|
| + // Right hand side is shorter.
|
| + total += Utf8Length(second, 0, to - first_length);
|
| + input = first;
|
| + to = first_length;
|
| + } else {
|
| + // We only need the left hand side.
|
| + input = first;
|
| + }
|
| + }
|
| + continue;
|
| + }
|
| + case kExternalStringTag:
|
| + case kSeqStringTag: {
|
| + Vector<const uc16> vector = input->GetFlatContent().ToUC16Vector();
|
| + const uc16* p = vector.start();
|
| + for (int i = from; i < to; i++) {
|
| + total += unibrow::Utf8::Length(p[i]);
|
| + }
|
| + return total;
|
| + }
|
| + case kSlicedStringTag: {
|
| + SlicedString* str = SlicedString::cast(input);
|
| + int offset = str->offset();
|
| + input = str->parent();
|
| + from += offset;
|
| + to += offset;
|
| + continue;
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| + UNREACHABLE();
|
| + return 0;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +
|
| void Relocatable::PostGarbageCollectionProcessing() {
|
| Isolate* isolate = Isolate::Current();
|
| Relocatable* current = isolate->relocatable_top();
|
| @@ -6851,6 +7160,57 @@
|
| }
|
|
|
|
|
| +bool SharedFunctionInfo::EnsureCompiled(Handle<SharedFunctionInfo> shared,
|
| + ClearExceptionFlag flag) {
|
| + return shared->is_compiled() || CompileLazy(shared, flag);
|
| +}
|
| +
|
| +
|
| +static bool CompileLazyHelper(CompilationInfo* info,
|
| + ClearExceptionFlag flag) {
|
| + // Compile the source information to a code object.
|
| + ASSERT(info->IsOptimizing() || !info->shared_info()->is_compiled());
|
| + ASSERT(!info->isolate()->has_pending_exception());
|
| + bool result = Compiler::CompileLazy(info);
|
| + ASSERT(result != Isolate::Current()->has_pending_exception());
|
| + if (!result && flag == CLEAR_EXCEPTION) {
|
| + info->isolate()->clear_pending_exception();
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +
|
| +bool SharedFunctionInfo::CompileLazy(Handle<SharedFunctionInfo> shared,
|
| + ClearExceptionFlag flag) {
|
| + CompilationInfo info(shared);
|
| + return CompileLazyHelper(&info, flag);
|
| +}
|
| +
|
| +
|
| +bool JSFunction::CompileLazy(Handle<JSFunction> function,
|
| + ClearExceptionFlag flag) {
|
| + bool result = true;
|
| + if (function->shared()->is_compiled()) {
|
| + function->ReplaceCode(function->shared()->code());
|
| + function->shared()->set_code_age(0);
|
| + } else {
|
| + CompilationInfo info(function);
|
| + result = CompileLazyHelper(&info, flag);
|
| + ASSERT(!result || function->is_compiled());
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +
|
| +bool JSFunction::CompileOptimized(Handle<JSFunction> function,
|
| + int osr_ast_id,
|
| + ClearExceptionFlag flag) {
|
| + CompilationInfo info(function);
|
| + info.SetOptimizing(osr_ast_id);
|
| + return CompileLazyHelper(&info, flag);
|
| +}
|
| +
|
| +
|
| bool JSFunction::IsInlineable() {
|
| if (IsBuiltin()) return false;
|
| SharedFunctionInfo* shared_info = shared();
|
| @@ -7033,7 +7393,7 @@
|
| obj = obj->GetPrototype()) {
|
| JSObject* js_object = JSObject::cast(obj);
|
| for (int i = 0; i < this_property_assignments_count(); i++) {
|
| - LookupResult result;
|
| + LookupResult result(heap->isolate());
|
| String* name = GetThisPropertyAssignmentName(i);
|
| js_object->LocalLookupRealNamedProperty(name, &result);
|
| if (result.IsProperty() && result.type() == CALLBACKS) {
|
| @@ -7391,6 +7751,8 @@
|
|
|
|
|
| void Code::CopyFrom(const CodeDesc& desc) {
|
| + ASSERT(Marking::Color(this) == Marking::WHITE_OBJECT);
|
| +
|
| // copy code
|
| memmove(instruction_start(), desc.buffer, desc.instr_size);
|
|
|
| @@ -7410,16 +7772,17 @@
|
| RelocInfo::Mode mode = it.rinfo()->rmode();
|
| if (mode == RelocInfo::EMBEDDED_OBJECT) {
|
| Handle<Object> p = it.rinfo()->target_object_handle(origin);
|
| - it.rinfo()->set_target_object(*p);
|
| + it.rinfo()->set_target_object(*p, SKIP_WRITE_BARRIER);
|
| } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
|
| Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle();
|
| - it.rinfo()->set_target_cell(*cell);
|
| + it.rinfo()->set_target_cell(*cell, SKIP_WRITE_BARRIER);
|
| } else if (RelocInfo::IsCodeTarget(mode)) {
|
| // rewrite code handles in inline cache targets to direct
|
| // pointers to the first instruction in the code object
|
| Handle<Object> p = it.rinfo()->target_object_handle(origin);
|
| Code* code = Code::cast(*p);
|
| - it.rinfo()->set_target_address(code->instruction_start());
|
| + it.rinfo()->set_target_address(code->instruction_start(),
|
| + SKIP_WRITE_BARRIER);
|
| } else {
|
| it.rinfo()->apply(delta);
|
| }
|
| @@ -7847,13 +8210,15 @@
|
| new_map = Map::cast(object);
|
| }
|
|
|
| + FixedArrayBase* old_elements_raw = elements();
|
| ElementsKind elements_kind = GetElementsKind();
|
| switch (elements_kind) {
|
| case FAST_SMI_ONLY_ELEMENTS:
|
| case FAST_ELEMENTS: {
|
| AssertNoAllocation no_gc;
|
| WriteBarrierMode mode(new_elements->GetWriteBarrierMode(no_gc));
|
| - CopyFastElementsToFast(FixedArray::cast(elements()), new_elements, mode);
|
| + CopyFastElementsToFast(FixedArray::cast(old_elements_raw),
|
| + new_elements, mode);
|
| set_map(new_map);
|
| set_elements(new_elements);
|
| break;
|
| @@ -7861,7 +8226,7 @@
|
| case DICTIONARY_ELEMENTS: {
|
| AssertNoAllocation no_gc;
|
| WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
|
| - CopySlowElementsToFast(NumberDictionary::cast(elements()),
|
| + CopySlowElementsToFast(NumberDictionary::cast(old_elements_raw),
|
| new_elements,
|
| mode);
|
| set_map(new_map);
|
| @@ -7873,7 +8238,7 @@
|
| WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
|
| // The object's map and the parameter map are unchanged, the unaliased
|
| // arguments are copied to the new backing store.
|
| - FixedArray* parameter_map = FixedArray::cast(elements());
|
| + FixedArray* parameter_map = FixedArray::cast(old_elements_raw);
|
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
|
| if (arguments->IsDictionary()) {
|
| CopySlowElementsToFast(NumberDictionary::cast(arguments),
|
| @@ -7886,7 +8251,7 @@
|
| break;
|
| }
|
| case FAST_DOUBLE_ELEMENTS: {
|
| - FixedDoubleArray* old_elements = FixedDoubleArray::cast(elements());
|
| + FixedDoubleArray* old_elements = FixedDoubleArray::cast(old_elements_raw);
|
| uint32_t old_length = static_cast<uint32_t>(old_elements->length());
|
| // Fill out the new array with this content and array holes.
|
| for (uint32_t i = 0; i < old_length; i++) {
|
| @@ -7924,6 +8289,11 @@
|
| break;
|
| }
|
|
|
| + if (FLAG_trace_elements_transitions) {
|
| + PrintElementsTransition(stdout, elements_kind, old_elements_raw,
|
| + FAST_ELEMENTS, new_elements);
|
| + }
|
| +
|
| // Update the length if necessary.
|
| if (IsJSArray()) {
|
| JSArray::cast(this)->set_length(Smi::FromInt(length));
|
| @@ -7953,19 +8323,21 @@
|
| }
|
| Map* new_map = Map::cast(obj);
|
|
|
| + FixedArrayBase* old_elements = elements();
|
| + ElementsKind elements_kind(GetElementsKind());
|
| AssertNoAllocation no_gc;
|
| - switch (GetElementsKind()) {
|
| + switch (elements_kind) {
|
| case FAST_SMI_ONLY_ELEMENTS:
|
| case FAST_ELEMENTS: {
|
| - elems->Initialize(FixedArray::cast(elements()));
|
| + elems->Initialize(FixedArray::cast(old_elements));
|
| break;
|
| }
|
| case FAST_DOUBLE_ELEMENTS: {
|
| - elems->Initialize(FixedDoubleArray::cast(elements()));
|
| + elems->Initialize(FixedDoubleArray::cast(old_elements));
|
| break;
|
| }
|
| case DICTIONARY_ELEMENTS: {
|
| - elems->Initialize(NumberDictionary::cast(elements()));
|
| + elems->Initialize(NumberDictionary::cast(old_elements));
|
| break;
|
| }
|
| default:
|
| @@ -7973,6 +8345,11 @@
|
| break;
|
| }
|
|
|
| + if (FLAG_trace_elements_transitions) {
|
| + PrintElementsTransition(stdout, elements_kind, old_elements,
|
| + FAST_DOUBLE_ELEMENTS, elems);
|
| + }
|
| +
|
| ASSERT(new_map->has_fast_double_elements());
|
| set_map(new_map);
|
| ASSERT(elems->IsFixedDoubleArray());
|
| @@ -7992,13 +8369,14 @@
|
|
|
| uint32_t new_length = static_cast<uint32_t>(len->Number());
|
|
|
| - switch (GetElementsKind()) {
|
| + FixedArrayBase* old_elements = elements();
|
| + ElementsKind elements_kind = GetElementsKind();
|
| + switch (elements_kind) {
|
| case FAST_SMI_ONLY_ELEMENTS:
|
| case FAST_ELEMENTS:
|
| case FAST_DOUBLE_ELEMENTS: {
|
| // Make sure we never try to shrink dense arrays into sparse arrays.
|
| - ASSERT(static_cast<uint32_t>(
|
| - FixedArrayBase::cast(elements())->length()) <= new_length);
|
| + ASSERT(static_cast<uint32_t>(old_elements->length()) <= new_length);
|
| MaybeObject* result = NormalizeElements();
|
| if (result->IsFailure()) return result;
|
|
|
| @@ -8030,6 +8408,12 @@
|
| UNREACHABLE();
|
| break;
|
| }
|
| +
|
| + if (FLAG_trace_elements_transitions) {
|
| + PrintElementsTransition(stdout, elements_kind, old_elements,
|
| + DICTIONARY_ELEMENTS, elements());
|
| + }
|
| +
|
| return this;
|
| }
|
|
|
| @@ -8957,6 +9341,10 @@
|
| Map* new_map;
|
| if (!maybe_new_map->To<Map>(&new_map)) return maybe_new_map;
|
| set_map(new_map);
|
| + if (FLAG_trace_elements_transitions) {
|
| + PrintElementsTransition(stdout, FAST_SMI_ONLY_ELEMENTS, elements(),
|
| + FAST_ELEMENTS, elements());
|
| + }
|
| }
|
| // Increase backing store capacity if that's been decided previously.
|
| if (new_capacity != capacity) {
|
| @@ -9313,6 +9701,51 @@
|
| }
|
|
|
|
|
| +MUST_USE_RESULT MaybeObject* JSObject::TransitionElementsKind(
|
| + ElementsKind to_kind) {
|
| + ElementsKind from_kind = map()->elements_kind();
|
| + FixedArrayBase* elms = FixedArrayBase::cast(elements());
|
| + uint32_t capacity = static_cast<uint32_t>(elms->length());
|
| + uint32_t length = capacity;
|
| + if (IsJSArray()) {
|
| + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
|
| + }
|
| + if (from_kind == FAST_SMI_ONLY_ELEMENTS) {
|
| + if (to_kind == FAST_DOUBLE_ELEMENTS) {
|
| + MaybeObject* maybe_result =
|
| + SetFastDoubleElementsCapacityAndLength(capacity, length);
|
| + if (maybe_result->IsFailure()) return maybe_result;
|
| + return this;
|
| + } else if (to_kind == FAST_ELEMENTS) {
|
| + MaybeObject* maybe_new_map = GetElementsTransitionMap(FAST_ELEMENTS);
|
| + Map* new_map;
|
| + if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
| + set_map(new_map);
|
| + return this;
|
| + }
|
| + } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) {
|
| + MaybeObject* maybe_result = SetFastElementsCapacityAndLength(
|
| + capacity, length, kDontAllowSmiOnlyElements);
|
| + if (maybe_result->IsFailure()) return maybe_result;
|
| + return this;
|
| + }
|
| + // This method should never be called for any other case than the ones
|
| + // handled above.
|
| + UNREACHABLE();
|
| + return GetIsolate()->heap()->null_value();
|
| +}
|
| +
|
| +
|
| +// static
|
| +bool Map::IsValidElementsTransition(ElementsKind from_kind,
|
| + ElementsKind to_kind) {
|
| + return
|
| + (from_kind == FAST_SMI_ONLY_ELEMENTS &&
|
| + (to_kind == FAST_DOUBLE_ELEMENTS || to_kind == FAST_ELEMENTS)) ||
|
| + (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS);
|
| +}
|
| +
|
| +
|
| MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
|
| Object* value) {
|
| uint32_t old_len = 0;
|
| @@ -9579,7 +10012,7 @@
|
| String* name,
|
| PropertyAttributes* attributes) {
|
| // Check local property in holder, ignore interceptor.
|
| - LookupResult result;
|
| + LookupResult result(GetIsolate());
|
| LocalLookupRealNamedProperty(name, &result);
|
| if (result.IsProperty()) {
|
| return GetProperty(receiver, &result, name, attributes);
|
| @@ -9597,7 +10030,7 @@
|
| String* name,
|
| PropertyAttributes* attributes) {
|
| // Check local property in holder, ignore interceptor.
|
| - LookupResult result;
|
| + LookupResult result(GetIsolate());
|
| LocalLookupRealNamedProperty(name, &result);
|
| if (result.IsProperty()) {
|
| return GetProperty(receiver, &result, name, attributes);
|
| @@ -9648,15 +10081,15 @@
|
|
|
| bool JSObject::HasRealNamedProperty(String* key) {
|
| // Check access rights if needed.
|
| + Isolate* isolate = GetIsolate();
|
| if (IsAccessCheckNeeded()) {
|
| - Heap* heap = GetHeap();
|
| - if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
|
| - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| + if (!isolate->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
|
| + isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| return false;
|
| }
|
| }
|
|
|
| - LookupResult result;
|
| + LookupResult result(isolate);
|
| LocalLookupRealNamedProperty(key, &result);
|
| return result.IsProperty() && (result.type() != INTERCEPTOR);
|
| }
|
| @@ -9725,15 +10158,15 @@
|
|
|
| bool JSObject::HasRealNamedCallbackProperty(String* key) {
|
| // Check access rights if needed.
|
| + Isolate* isolate = GetIsolate();
|
| if (IsAccessCheckNeeded()) {
|
| - Heap* heap = GetHeap();
|
| - if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
|
| - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| + if (!isolate->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
|
| + isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| return false;
|
| }
|
| }
|
|
|
| - LookupResult result;
|
| + LookupResult result(isolate);
|
| LocalLookupRealNamedProperty(key, &result);
|
| return result.IsProperty() && (result.type() == CALLBACKS);
|
| }
|
| @@ -10598,8 +11031,10 @@
|
|
|
| template class HashTable<MapCacheShape, HashTableKey*>;
|
|
|
| -template class HashTable<ObjectHashTableShape, JSReceiver*>;
|
| +template class HashTable<ObjectHashTableShape<1>, Object*>;
|
|
|
| +template class HashTable<ObjectHashTableShape<2>, Object*>;
|
| +
|
| template class Dictionary<StringDictionaryShape, String*>;
|
|
|
| template class Dictionary<NumberDictionaryShape, uint32_t>;
|
| @@ -11089,6 +11524,16 @@
|
| }
|
|
|
|
|
| +Handle<JSGlobalPropertyCell> GlobalObject::EnsurePropertyCell(
|
| + Handle<GlobalObject> global,
|
| + Handle<String> name) {
|
| + Isolate* isolate = global->GetIsolate();
|
| + CALL_HEAP_FUNCTION(isolate,
|
| + global->EnsurePropertyCell(*name),
|
| + JSGlobalPropertyCell);
|
| +}
|
| +
|
| +
|
| MaybeObject* GlobalObject::EnsurePropertyCell(String* name) {
|
| ASSERT(!HasFastProperties());
|
| int entry = property_dictionary()->FindEntry(name);
|
| @@ -11326,7 +11771,7 @@
|
| SharedFunctionInfo* value) {
|
| StringSharedKey key(src,
|
| context->closure()->shared(),
|
| - value->strict_mode() ? kStrictMode : kNonStrictMode);
|
| + value->strict_mode_flag());
|
| Object* obj;
|
| { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
|
| if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| @@ -11375,8 +11820,8 @@
|
| int entry_index = EntryToIndex(entry);
|
| int value_index = entry_index + 1;
|
| if (get(value_index) == value) {
|
| - fast_set(this, entry_index, null_value);
|
| - fast_set(this, value_index, null_value);
|
| + NoWriteBarrierSet(this, entry_index, null_value);
|
| + NoWriteBarrierSet(this, value_index, null_value);
|
| ElementRemoved();
|
| }
|
| }
|
| @@ -11848,15 +12293,16 @@
|
| }
|
|
|
| // Allocate the instance descriptor.
|
| - Object* descriptors_unchecked;
|
| - { MaybeObject* maybe_descriptors_unchecked =
|
| + DescriptorArray* descriptors;
|
| + { MaybeObject* maybe_descriptors =
|
| DescriptorArray::Allocate(instance_descriptor_length);
|
| - if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
|
| - return maybe_descriptors_unchecked;
|
| + if (!maybe_descriptors->To<DescriptorArray>(&descriptors)) {
|
| + return maybe_descriptors;
|
| }
|
| }
|
| - DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
|
|
|
| + DescriptorArray::WhitenessWitness witness(descriptors);
|
| +
|
| int inobject_props = obj->map()->inobject_properties();
|
| int number_of_allocated_fields =
|
| number_of_fields + unused_property_fields - inobject_props;
|
| @@ -11893,7 +12339,7 @@
|
| JSFunction::cast(value),
|
| details.attributes(),
|
| details.index());
|
| - descriptors->Set(next_descriptor++, &d);
|
| + descriptors->Set(next_descriptor++, &d, witness);
|
| } else if (type == NORMAL) {
|
| if (current_offset < inobject_props) {
|
| obj->InObjectPropertyAtPut(current_offset,
|
| @@ -11907,13 +12353,13 @@
|
| current_offset++,
|
| details.attributes(),
|
| details.index());
|
| - descriptors->Set(next_descriptor++, &d);
|
| + descriptors->Set(next_descriptor++, &d, witness);
|
| } else if (type == CALLBACKS) {
|
| CallbacksDescriptor d(String::cast(key),
|
| value,
|
| details.attributes(),
|
| details.index());
|
| - descriptors->Set(next_descriptor++, &d);
|
| + descriptors->Set(next_descriptor++, &d, witness);
|
| } else {
|
| UNREACHABLE();
|
| }
|
| @@ -11921,7 +12367,7 @@
|
| }
|
| ASSERT(current_offset == number_of_fields);
|
|
|
| - descriptors->Sort();
|
| + descriptors->Sort(witness);
|
| // Allocate new map.
|
| Object* new_map;
|
| { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
|
| @@ -11944,20 +12390,74 @@
|
| }
|
|
|
|
|
| -Object* ObjectHashTable::Lookup(JSReceiver* key) {
|
| +bool ObjectHashSet::Contains(Object* key) {
|
| // If the object does not have an identity hash, it was never used as a key.
|
| - MaybeObject* maybe_hash = key->GetIdentityHash(OMIT_CREATION);
|
| - if (maybe_hash->IsFailure()) return GetHeap()->undefined_value();
|
| + { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
|
| + if (maybe_hash->ToObjectUnchecked()->IsUndefined()) return false;
|
| + }
|
| + return (FindEntry(key) != kNotFound);
|
| +}
|
| +
|
| +
|
| +MaybeObject* ObjectHashSet::Add(Object* key) {
|
| + // Make sure the key object has an identity hash code.
|
| + int hash;
|
| + { MaybeObject* maybe_hash = key->GetHash(ALLOW_CREATION);
|
| + if (maybe_hash->IsFailure()) return maybe_hash;
|
| + hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value();
|
| + }
|
| int entry = FindEntry(key);
|
| +
|
| + // Check whether key is already present.
|
| + if (entry != kNotFound) return this;
|
| +
|
| + // Check whether the hash set should be extended and add entry.
|
| + Object* obj;
|
| + { MaybeObject* maybe_obj = EnsureCapacity(1, key);
|
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + }
|
| + ObjectHashSet* table = ObjectHashSet::cast(obj);
|
| + entry = table->FindInsertionEntry(hash);
|
| + table->set(EntryToIndex(entry), key);
|
| + table->ElementAdded();
|
| + return table;
|
| +}
|
| +
|
| +
|
| +MaybeObject* ObjectHashSet::Remove(Object* key) {
|
| + // If the object does not have an identity hash, it was never used as a key.
|
| + { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
|
| + if (maybe_hash->ToObjectUnchecked()->IsUndefined()) return this;
|
| + }
|
| + int entry = FindEntry(key);
|
| +
|
| + // Check whether key is actually present.
|
| + if (entry == kNotFound) return this;
|
| +
|
| + // Remove entry and try to shrink this hash set.
|
| + set_null(EntryToIndex(entry));
|
| + ElementRemoved();
|
| + return Shrink(key);
|
| +}
|
| +
|
| +
|
| +Object* ObjectHashTable::Lookup(Object* key) {
|
| + // If the object does not have an identity hash, it was never used as a key.
|
| + { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
|
| + if (maybe_hash->ToObjectUnchecked()->IsUndefined()) {
|
| + return GetHeap()->undefined_value();
|
| + }
|
| + }
|
| + int entry = FindEntry(key);
|
| if (entry == kNotFound) return GetHeap()->undefined_value();
|
| return get(EntryToIndex(entry) + 1);
|
| }
|
|
|
|
|
| -MaybeObject* ObjectHashTable::Put(JSReceiver* key, Object* value) {
|
| +MaybeObject* ObjectHashTable::Put(Object* key, Object* value) {
|
| // Make sure the key object has an identity hash code.
|
| int hash;
|
| - { MaybeObject* maybe_hash = key->GetIdentityHash(ALLOW_CREATION);
|
| + { MaybeObject* maybe_hash = key->GetHash(ALLOW_CREATION);
|
| if (maybe_hash->IsFailure()) return maybe_hash;
|
| hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value();
|
| }
|
| @@ -11987,7 +12487,7 @@
|
| }
|
|
|
|
|
| -void ObjectHashTable::AddEntry(int entry, JSReceiver* key, Object* value) {
|
| +void ObjectHashTable::AddEntry(int entry, Object* key, Object* value) {
|
| set(EntryToIndex(entry), key);
|
| set(EntryToIndex(entry) + 1, value);
|
| ElementAdded();
|
|
|