Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1 | // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5 | #include "src/v8.h" |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7 | #include "src/accessors.h" |
| 8 | #include "src/allocation-site-scopes.h" |
| 9 | #include "src/api.h" |
| 10 | #include "src/arguments.h" |
| 11 | #include "src/base/bits.h" |
| 12 | #include "src/bootstrapper.h" |
| 13 | #include "src/code-stubs.h" |
| 14 | #include "src/codegen.h" |
| 15 | #include "src/cpu-profiler.h" |
| 16 | #include "src/date.h" |
| 17 | #include "src/debug.h" |
| 18 | #include "src/deoptimizer.h" |
| 19 | #include "src/elements.h" |
| 20 | #include "src/execution.h" |
| 21 | #include "src/field-index-inl.h" |
| 22 | #include "src/field-index.h" |
| 23 | #include "src/full-codegen.h" |
| 24 | #include "src/heap/mark-compact.h" |
| 25 | #include "src/heap/objects-visiting-inl.h" |
| 26 | #include "src/hydrogen.h" |
| 27 | #include "src/ic/ic.h" |
| 28 | #include "src/isolate-inl.h" |
| 29 | #include "src/log.h" |
| 30 | #include "src/lookup.h" |
| 31 | #include "src/macro-assembler.h" |
| 32 | #include "src/objects-inl.h" |
| 33 | #include "src/prototype.h" |
| 34 | #include "src/safepoint-table.h" |
| 35 | #include "src/string-search.h" |
| 36 | #include "src/string-stream.h" |
| 37 | #include "src/utils.h" |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 38 | |
| 39 | #ifdef ENABLE_DISASSEMBLER |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 40 | #include "src/disasm.h" |
| 41 | #include "src/disassembler.h" |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 42 | #endif |
| 43 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 44 | namespace v8 { |
| 45 | namespace internal { |
| 46 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 47 | Handle<HeapType> Object::OptimalType(Isolate* isolate, |
| 48 | Representation representation) { |
| 49 | if (representation.IsNone()) return HeapType::None(isolate); |
| 50 | if (FLAG_track_field_types) { |
| 51 | if (representation.IsHeapObject() && IsHeapObject()) { |
| 52 | // We can track only JavaScript objects with stable maps. |
| 53 | Handle<Map> map(HeapObject::cast(this)->map(), isolate); |
| 54 | if (map->is_stable() && |
| 55 | map->instance_type() >= FIRST_NONCALLABLE_SPEC_OBJECT_TYPE && |
| 56 | map->instance_type() <= LAST_NONCALLABLE_SPEC_OBJECT_TYPE) { |
| 57 | return HeapType::Class(map, isolate); |
| 58 | } |
| 59 | } |
| 60 | } |
| 61 | return HeapType::Any(isolate); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 62 | } |
| 63 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 64 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 65 | MaybeHandle<JSReceiver> Object::ToObject(Isolate* isolate, |
| 66 | Handle<Object> object, |
| 67 | Handle<Context> native_context) { |
| 68 | if (object->IsJSReceiver()) return Handle<JSReceiver>::cast(object); |
| 69 | Handle<JSFunction> constructor; |
| 70 | if (object->IsNumber()) { |
| 71 | constructor = handle(native_context->number_function(), isolate); |
| 72 | } else if (object->IsBoolean()) { |
| 73 | constructor = handle(native_context->boolean_function(), isolate); |
| 74 | } else if (object->IsString()) { |
| 75 | constructor = handle(native_context->string_function(), isolate); |
| 76 | } else if (object->IsSymbol()) { |
| 77 | constructor = handle(native_context->symbol_function(), isolate); |
| 78 | } else { |
| 79 | return MaybeHandle<JSReceiver>(); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 80 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 81 | Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); |
| 82 | Handle<JSValue>::cast(result)->set_value(*object); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 83 | return result; |
| 84 | } |
| 85 | |
| 86 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 87 | bool Object::BooleanValue() { |
| 88 | if (IsBoolean()) return IsTrue(); |
| 89 | if (IsSmi()) return Smi::cast(this)->value() != 0; |
| 90 | if (IsUndefined() || IsNull()) return false; |
| 91 | if (IsUndetectableObject()) return false; // Undetectable object is false. |
| 92 | if (IsString()) return String::cast(this)->length() != 0; |
| 93 | if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue(); |
| 94 | return true; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 98 | bool Object::IsCallable() const { |
| 99 | const Object* fun = this; |
| 100 | while (fun->IsJSFunctionProxy()) { |
| 101 | fun = JSFunctionProxy::cast(fun)->call_trap(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 102 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 103 | return fun->IsJSFunction() || |
| 104 | (fun->IsHeapObject() && |
| 105 | HeapObject::cast(fun)->map()->has_instance_call_handler()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 106 | } |
| 107 | |
| 108 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 109 | MaybeHandle<Object> Object::GetProperty(LookupIterator* it) { |
| 110 | for (; it->IsFound(); it->Next()) { |
| 111 | switch (it->state()) { |
| 112 | case LookupIterator::NOT_FOUND: |
| 113 | case LookupIterator::TRANSITION: |
| 114 | UNREACHABLE(); |
| 115 | case LookupIterator::JSPROXY: |
| 116 | return JSProxy::GetPropertyWithHandler(it->GetHolder<JSProxy>(), |
| 117 | it->GetReceiver(), it->name()); |
| 118 | case LookupIterator::INTERCEPTOR: { |
| 119 | MaybeHandle<Object> maybe_result = JSObject::GetPropertyWithInterceptor( |
| 120 | it->GetHolder<JSObject>(), it->GetReceiver(), it->name()); |
| 121 | if (!maybe_result.is_null()) return maybe_result; |
| 122 | if (it->isolate()->has_pending_exception()) return maybe_result; |
| 123 | break; |
| 124 | } |
| 125 | case LookupIterator::ACCESS_CHECK: |
| 126 | if (it->HasAccess(v8::ACCESS_GET)) break; |
| 127 | return JSObject::GetPropertyWithFailedAccessCheck(it); |
| 128 | case LookupIterator::ACCESSOR: |
| 129 | return GetPropertyWithAccessor(it->GetReceiver(), it->name(), |
| 130 | it->GetHolder<JSObject>(), |
| 131 | it->GetAccessors()); |
| 132 | case LookupIterator::DATA: |
| 133 | return it->GetDataValue(); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 134 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 135 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 136 | return it->factory()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 140 | Handle<Object> JSObject::GetDataProperty(Handle<JSObject> object, |
| 141 | Handle<Name> key) { |
| 142 | LookupIterator it(object, key, |
| 143 | LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); |
| 144 | return GetDataProperty(&it); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 145 | } |
| 146 | |
| 147 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 148 | Handle<Object> JSObject::GetDataProperty(LookupIterator* it) { |
| 149 | for (; it->IsFound(); it->Next()) { |
| 150 | switch (it->state()) { |
| 151 | case LookupIterator::INTERCEPTOR: |
| 152 | case LookupIterator::NOT_FOUND: |
| 153 | case LookupIterator::TRANSITION: |
| 154 | UNREACHABLE(); |
| 155 | case LookupIterator::ACCESS_CHECK: |
| 156 | if (it->HasAccess(v8::ACCESS_GET)) continue; |
| 157 | // Fall through. |
| 158 | case LookupIterator::JSPROXY: |
| 159 | it->NotFound(); |
| 160 | return it->isolate()->factory()->undefined_value(); |
| 161 | case LookupIterator::ACCESSOR: |
| 162 | // TODO(verwaest): For now this doesn't call into |
| 163 | // ExecutableAccessorInfo, since clients don't need it. Update once |
| 164 | // relevant. |
| 165 | it->NotFound(); |
| 166 | return it->isolate()->factory()->undefined_value(); |
| 167 | case LookupIterator::DATA: |
| 168 | return it->GetDataValue(); |
| 169 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 170 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 171 | return it->isolate()->factory()->undefined_value(); |
| 172 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 173 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 174 | |
| 175 | bool Object::ToInt32(int32_t* value) { |
| 176 | if (IsSmi()) { |
| 177 | *value = Smi::cast(this)->value(); |
| 178 | return true; |
| 179 | } |
| 180 | if (IsHeapNumber()) { |
| 181 | double num = HeapNumber::cast(this)->value(); |
| 182 | if (FastI2D(FastD2I(num)) == num) { |
| 183 | *value = FastD2I(num); |
| 184 | return true; |
| 185 | } |
| 186 | } |
| 187 | return false; |
| 188 | } |
| 189 | |
| 190 | |
| 191 | bool Object::ToUint32(uint32_t* value) { |
| 192 | if (IsSmi()) { |
| 193 | int num = Smi::cast(this)->value(); |
| 194 | if (num >= 0) { |
| 195 | *value = static_cast<uint32_t>(num); |
| 196 | return true; |
| 197 | } |
| 198 | } |
| 199 | if (IsHeapNumber()) { |
| 200 | double num = HeapNumber::cast(this)->value(); |
| 201 | if (num >= 0 && FastUI2D(FastD2UI(num)) == num) { |
| 202 | *value = FastD2UI(num); |
| 203 | return true; |
| 204 | } |
| 205 | } |
| 206 | return false; |
| 207 | } |
| 208 | |
| 209 | |
| 210 | bool FunctionTemplateInfo::IsTemplateFor(Object* object) { |
| 211 | if (!object->IsHeapObject()) return false; |
| 212 | return IsTemplateFor(HeapObject::cast(object)->map()); |
| 213 | } |
| 214 | |
| 215 | |
| 216 | bool FunctionTemplateInfo::IsTemplateFor(Map* map) { |
| 217 | // There is a constraint on the object; check. |
| 218 | if (!map->IsJSObjectMap()) return false; |
| 219 | // Fetch the constructor function of the object. |
| 220 | Object* cons_obj = map->constructor(); |
| 221 | if (!cons_obj->IsJSFunction()) return false; |
| 222 | JSFunction* fun = JSFunction::cast(cons_obj); |
| 223 | // Iterate through the chain of inheriting function templates to |
| 224 | // see if the required one occurs. |
| 225 | for (Object* type = fun->shared()->function_data(); |
| 226 | type->IsFunctionTemplateInfo(); |
| 227 | type = FunctionTemplateInfo::cast(type)->parent_template()) { |
| 228 | if (type == this) return true; |
| 229 | } |
| 230 | // Didn't find the required type in the inheritance chain. |
| 231 | return false; |
| 232 | } |
| 233 | |
| 234 | |
| 235 | template<typename To> |
| 236 | static inline To* CheckedCast(void *from) { |
| 237 | uintptr_t temp = reinterpret_cast<uintptr_t>(from); |
| 238 | DCHECK(temp % sizeof(To) == 0); |
| 239 | return reinterpret_cast<To*>(temp); |
| 240 | } |
| 241 | |
| 242 | |
| 243 | static Handle<Object> PerformCompare(const BitmaskCompareDescriptor& descriptor, |
| 244 | char* ptr, |
| 245 | Isolate* isolate) { |
| 246 | uint32_t bitmask = descriptor.bitmask; |
| 247 | uint32_t compare_value = descriptor.compare_value; |
| 248 | uint32_t value; |
| 249 | switch (descriptor.size) { |
| 250 | case 1: |
| 251 | value = static_cast<uint32_t>(*CheckedCast<uint8_t>(ptr)); |
| 252 | compare_value &= 0xff; |
| 253 | bitmask &= 0xff; |
| 254 | break; |
| 255 | case 2: |
| 256 | value = static_cast<uint32_t>(*CheckedCast<uint16_t>(ptr)); |
| 257 | compare_value &= 0xffff; |
| 258 | bitmask &= 0xffff; |
| 259 | break; |
| 260 | case 4: |
| 261 | value = *CheckedCast<uint32_t>(ptr); |
| 262 | break; |
| 263 | default: |
| 264 | UNREACHABLE(); |
| 265 | return isolate->factory()->undefined_value(); |
| 266 | } |
| 267 | return isolate->factory()->ToBoolean( |
| 268 | (bitmask & value) == (bitmask & compare_value)); |
| 269 | } |
| 270 | |
| 271 | |
| 272 | static Handle<Object> PerformCompare(const PointerCompareDescriptor& descriptor, |
| 273 | char* ptr, |
| 274 | Isolate* isolate) { |
| 275 | uintptr_t compare_value = |
| 276 | reinterpret_cast<uintptr_t>(descriptor.compare_value); |
| 277 | uintptr_t value = *CheckedCast<uintptr_t>(ptr); |
| 278 | return isolate->factory()->ToBoolean(compare_value == value); |
| 279 | } |
| 280 | |
| 281 | |
| 282 | static Handle<Object> GetPrimitiveValue( |
| 283 | const PrimitiveValueDescriptor& descriptor, |
| 284 | char* ptr, |
| 285 | Isolate* isolate) { |
| 286 | int32_t int32_value = 0; |
| 287 | switch (descriptor.data_type) { |
| 288 | case kDescriptorInt8Type: |
| 289 | int32_value = *CheckedCast<int8_t>(ptr); |
| 290 | break; |
| 291 | case kDescriptorUint8Type: |
| 292 | int32_value = *CheckedCast<uint8_t>(ptr); |
| 293 | break; |
| 294 | case kDescriptorInt16Type: |
| 295 | int32_value = *CheckedCast<int16_t>(ptr); |
| 296 | break; |
| 297 | case kDescriptorUint16Type: |
| 298 | int32_value = *CheckedCast<uint16_t>(ptr); |
| 299 | break; |
| 300 | case kDescriptorInt32Type: |
| 301 | int32_value = *CheckedCast<int32_t>(ptr); |
| 302 | break; |
| 303 | case kDescriptorUint32Type: { |
| 304 | uint32_t value = *CheckedCast<uint32_t>(ptr); |
| 305 | AllowHeapAllocation allow_gc; |
| 306 | return isolate->factory()->NewNumberFromUint(value); |
| 307 | } |
| 308 | case kDescriptorBoolType: { |
| 309 | uint8_t byte = *CheckedCast<uint8_t>(ptr); |
| 310 | return isolate->factory()->ToBoolean( |
| 311 | byte & (0x1 << descriptor.bool_offset)); |
| 312 | } |
| 313 | case kDescriptorFloatType: { |
| 314 | float value = *CheckedCast<float>(ptr); |
| 315 | AllowHeapAllocation allow_gc; |
| 316 | return isolate->factory()->NewNumber(value); |
| 317 | } |
| 318 | case kDescriptorDoubleType: { |
| 319 | double value = *CheckedCast<double>(ptr); |
| 320 | AllowHeapAllocation allow_gc; |
| 321 | return isolate->factory()->NewNumber(value); |
| 322 | } |
| 323 | } |
| 324 | AllowHeapAllocation allow_gc; |
| 325 | return isolate->factory()->NewNumberFromInt(int32_value); |
| 326 | } |
| 327 | |
| 328 | |
| 329 | static Handle<Object> GetDeclaredAccessorProperty( |
| 330 | Handle<Object> receiver, |
| 331 | Handle<DeclaredAccessorInfo> info, |
| 332 | Isolate* isolate) { |
| 333 | DisallowHeapAllocation no_gc; |
| 334 | char* current = reinterpret_cast<char*>(*receiver); |
| 335 | DeclaredAccessorDescriptorIterator iterator(info->descriptor()); |
| 336 | while (true) { |
| 337 | const DeclaredAccessorDescriptorData* data = iterator.Next(); |
| 338 | switch (data->type) { |
| 339 | case kDescriptorReturnObject: { |
| 340 | DCHECK(iterator.Complete()); |
| 341 | current = *CheckedCast<char*>(current); |
| 342 | return handle(*CheckedCast<Object*>(current), isolate); |
| 343 | } |
| 344 | case kDescriptorPointerDereference: |
| 345 | DCHECK(!iterator.Complete()); |
| 346 | current = *reinterpret_cast<char**>(current); |
| 347 | break; |
| 348 | case kDescriptorPointerShift: |
| 349 | DCHECK(!iterator.Complete()); |
| 350 | current += data->pointer_shift_descriptor.byte_offset; |
| 351 | break; |
| 352 | case kDescriptorObjectDereference: { |
| 353 | DCHECK(!iterator.Complete()); |
| 354 | Object* object = CheckedCast<Object>(current); |
| 355 | int field = data->object_dereference_descriptor.internal_field; |
| 356 | Object* smi = JSObject::cast(object)->GetInternalField(field); |
| 357 | DCHECK(smi->IsSmi()); |
| 358 | current = reinterpret_cast<char*>(smi); |
| 359 | break; |
| 360 | } |
| 361 | case kDescriptorBitmaskCompare: |
| 362 | DCHECK(iterator.Complete()); |
| 363 | return PerformCompare(data->bitmask_compare_descriptor, |
| 364 | current, |
| 365 | isolate); |
| 366 | case kDescriptorPointerCompare: |
| 367 | DCHECK(iterator.Complete()); |
| 368 | return PerformCompare(data->pointer_compare_descriptor, |
| 369 | current, |
| 370 | isolate); |
| 371 | case kDescriptorPrimitiveValue: |
| 372 | DCHECK(iterator.Complete()); |
| 373 | return GetPrimitiveValue(data->primitive_value_descriptor, |
| 374 | current, |
| 375 | isolate); |
| 376 | } |
| 377 | } |
| 378 | UNREACHABLE(); |
| 379 | return isolate->factory()->undefined_value(); |
| 380 | } |
| 381 | |
| 382 | |
| 383 | Handle<FixedArray> JSObject::EnsureWritableFastElements( |
| 384 | Handle<JSObject> object) { |
| 385 | DCHECK(object->HasFastSmiOrObjectElements()); |
| 386 | Isolate* isolate = object->GetIsolate(); |
| 387 | Handle<FixedArray> elems(FixedArray::cast(object->elements()), isolate); |
| 388 | if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems; |
| 389 | Handle<FixedArray> writable_elems = isolate->factory()->CopyFixedArrayWithMap( |
| 390 | elems, isolate->factory()->fixed_array_map()); |
| 391 | object->set_elements(*writable_elems); |
| 392 | isolate->counters()->cow_arrays_converted()->Increment(); |
| 393 | return writable_elems; |
| 394 | } |
| 395 | |
| 396 | |
| 397 | MaybeHandle<Object> JSProxy::GetPropertyWithHandler(Handle<JSProxy> proxy, |
| 398 | Handle<Object> receiver, |
| 399 | Handle<Name> name) { |
| 400 | Isolate* isolate = proxy->GetIsolate(); |
| 401 | |
| 402 | // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
| 403 | if (name->IsSymbol()) return isolate->factory()->undefined_value(); |
| 404 | |
| 405 | Handle<Object> args[] = { receiver, name }; |
| 406 | return CallTrap( |
| 407 | proxy, "get", isolate->derived_get_trap(), arraysize(args), args); |
| 408 | } |
| 409 | |
| 410 | |
| 411 | MaybeHandle<Object> Object::GetPropertyWithAccessor(Handle<Object> receiver, |
| 412 | Handle<Name> name, |
| 413 | Handle<JSObject> holder, |
| 414 | Handle<Object> structure) { |
| 415 | Isolate* isolate = name->GetIsolate(); |
| 416 | DCHECK(!structure->IsForeign()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 417 | // api style callbacks. |
| 418 | if (structure->IsAccessorInfo()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 419 | Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure); |
| 420 | if (!info->IsCompatibleReceiver(*receiver)) { |
| 421 | Handle<Object> args[2] = { name, receiver }; |
| 422 | THROW_NEW_ERROR(isolate, |
| 423 | NewTypeError("incompatible_method_receiver", |
| 424 | HandleVector(args, arraysize(args))), |
| 425 | Object); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 426 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 427 | if (structure->IsDeclaredAccessorInfo()) { |
| 428 | return GetDeclaredAccessorProperty( |
| 429 | receiver, |
| 430 | Handle<DeclaredAccessorInfo>::cast(structure), |
| 431 | isolate); |
| 432 | } |
| 433 | |
| 434 | Handle<ExecutableAccessorInfo> data = |
| 435 | Handle<ExecutableAccessorInfo>::cast(structure); |
| 436 | v8::AccessorNameGetterCallback call_fun = |
| 437 | v8::ToCData<v8::AccessorNameGetterCallback>(data->getter()); |
| 438 | if (call_fun == NULL) return isolate->factory()->undefined_value(); |
| 439 | |
| 440 | LOG(isolate, ApiNamedPropertyAccess("load", *holder, *name)); |
| 441 | PropertyCallbackArguments args(isolate, data->data(), *receiver, *holder); |
| 442 | v8::Handle<v8::Value> result = |
| 443 | args.Call(call_fun, v8::Utils::ToLocal(name)); |
| 444 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 445 | if (result.IsEmpty()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 446 | return isolate->factory()->undefined_value(); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 447 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 448 | Handle<Object> return_value = v8::Utils::OpenHandle(*result); |
| 449 | return_value->VerifyApiCallResultType(); |
| 450 | // Rebox handle before return. |
| 451 | return handle(*return_value, isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 452 | } |
| 453 | |
| 454 | // __defineGetter__ callback |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 455 | Handle<Object> getter(Handle<AccessorPair>::cast(structure)->getter(), |
| 456 | isolate); |
| 457 | if (getter->IsSpecFunction()) { |
| 458 | // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| 459 | return Object::GetPropertyWithDefinedGetter( |
| 460 | receiver, Handle<JSReceiver>::cast(getter)); |
| 461 | } |
| 462 | // Getter is not a function. |
| 463 | return isolate->factory()->undefined_value(); |
| 464 | } |
| 465 | |
| 466 | |
| 467 | bool AccessorInfo::IsCompatibleReceiverType(Isolate* isolate, |
| 468 | Handle<AccessorInfo> info, |
| 469 | Handle<HeapType> type) { |
| 470 | if (!info->HasExpectedReceiverType()) return true; |
| 471 | Handle<Map> map = IC::TypeToMap(*type, isolate); |
| 472 | if (!map->IsJSObjectMap()) return false; |
| 473 | return FunctionTemplateInfo::cast(info->expected_receiver_type()) |
| 474 | ->IsTemplateFor(*map); |
| 475 | } |
| 476 | |
| 477 | |
| 478 | MaybeHandle<Object> Object::SetPropertyWithAccessor( |
| 479 | Handle<Object> receiver, Handle<Name> name, Handle<Object> value, |
| 480 | Handle<JSObject> holder, Handle<Object> structure, StrictMode strict_mode) { |
| 481 | Isolate* isolate = name->GetIsolate(); |
| 482 | |
| 483 | // We should never get here to initialize a const with the hole |
| 484 | // value since a const declaration would conflict with the setter. |
| 485 | DCHECK(!structure->IsForeign()); |
| 486 | if (structure->IsExecutableAccessorInfo()) { |
| 487 | // Don't call executable accessor setters with non-JSObject receivers. |
| 488 | if (!receiver->IsJSObject()) return value; |
| 489 | // api style callbacks |
| 490 | ExecutableAccessorInfo* info = ExecutableAccessorInfo::cast(*structure); |
| 491 | if (!info->IsCompatibleReceiver(*receiver)) { |
| 492 | Handle<Object> args[2] = { name, receiver }; |
| 493 | THROW_NEW_ERROR(isolate, |
| 494 | NewTypeError("incompatible_method_receiver", |
| 495 | HandleVector(args, arraysize(args))), |
| 496 | Object); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 497 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 498 | Object* call_obj = info->setter(); |
| 499 | v8::AccessorNameSetterCallback call_fun = |
| 500 | v8::ToCData<v8::AccessorNameSetterCallback>(call_obj); |
| 501 | if (call_fun == NULL) return value; |
| 502 | LOG(isolate, ApiNamedPropertyAccess("store", *holder, *name)); |
| 503 | PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder); |
| 504 | args.Call(call_fun, |
| 505 | v8::Utils::ToLocal(name), |
| 506 | v8::Utils::ToLocal(value)); |
| 507 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 508 | return value; |
| 509 | } |
| 510 | |
| 511 | if (structure->IsAccessorPair()) { |
| 512 | Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); |
| 513 | if (setter->IsSpecFunction()) { |
| 514 | // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| 515 | return SetPropertyWithDefinedSetter( |
| 516 | receiver, Handle<JSReceiver>::cast(setter), value); |
| 517 | } else { |
| 518 | if (strict_mode == SLOPPY) return value; |
| 519 | Handle<Object> args[2] = { name, holder }; |
| 520 | THROW_NEW_ERROR( |
| 521 | isolate, NewTypeError("no_setter_in_callback", HandleVector(args, 2)), |
| 522 | Object); |
| 523 | } |
| 524 | } |
| 525 | |
| 526 | // TODO(dcarney): Handle correctly. |
| 527 | if (structure->IsDeclaredAccessorInfo()) { |
| 528 | return value; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 529 | } |
| 530 | |
| 531 | UNREACHABLE(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 532 | return MaybeHandle<Object>(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 533 | } |
| 534 | |
| 535 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 536 | MaybeHandle<Object> Object::GetPropertyWithDefinedGetter( |
| 537 | Handle<Object> receiver, |
| 538 | Handle<JSReceiver> getter) { |
| 539 | Isolate* isolate = getter->GetIsolate(); |
| 540 | Debug* debug = isolate->debug(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 541 | // Handle stepping into a getter if step into is active. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 542 | // TODO(rossberg): should this apply to getters that are function proxies? |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 543 | if (debug->StepInActive() && getter->IsJSFunction()) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 544 | debug->HandleStepIn( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 545 | Handle<JSFunction>::cast(getter), Handle<Object>::null(), 0, false); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 546 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 547 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 548 | return Execution::Call(isolate, getter, receiver, 0, NULL, true); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 549 | } |
| 550 | |
| 551 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 552 | MaybeHandle<Object> Object::SetPropertyWithDefinedSetter( |
| 553 | Handle<Object> receiver, |
| 554 | Handle<JSReceiver> setter, |
| 555 | Handle<Object> value) { |
| 556 | Isolate* isolate = setter->GetIsolate(); |
| 557 | |
| 558 | Debug* debug = isolate->debug(); |
| 559 | // Handle stepping into a setter if step into is active. |
| 560 | // TODO(rossberg): should this apply to getters that are function proxies? |
| 561 | if (debug->StepInActive() && setter->IsJSFunction()) { |
| 562 | debug->HandleStepIn( |
| 563 | Handle<JSFunction>::cast(setter), Handle<Object>::null(), 0, false); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 564 | } |
| 565 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 566 | Handle<Object> argv[] = { value }; |
| 567 | RETURN_ON_EXCEPTION(isolate, Execution::Call(isolate, setter, receiver, |
| 568 | arraysize(argv), argv, true), |
| 569 | Object); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 570 | return value; |
| 571 | } |
| 572 | |
| 573 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 574 | static bool FindAllCanReadHolder(LookupIterator* it) { |
| 575 | for (; it->IsFound(); it->Next()) { |
| 576 | if (it->state() == LookupIterator::ACCESSOR) { |
| 577 | Handle<Object> accessors = it->GetAccessors(); |
| 578 | if (accessors->IsAccessorInfo()) { |
| 579 | if (AccessorInfo::cast(*accessors)->all_can_read()) return true; |
| 580 | } |
| 581 | } |
| 582 | } |
| 583 | return false; |
| 584 | } |
| 585 | |
| 586 | |
| 587 | MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck( |
| 588 | LookupIterator* it) { |
| 589 | Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| 590 | if (FindAllCanReadHolder(it)) { |
| 591 | return GetPropertyWithAccessor(it->GetReceiver(), it->name(), |
| 592 | it->GetHolder<JSObject>(), |
| 593 | it->GetAccessors()); |
| 594 | } |
| 595 | it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_GET); |
| 596 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object); |
| 597 | return it->factory()->undefined_value(); |
| 598 | } |
| 599 | |
| 600 | |
| 601 | Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck( |
| 602 | LookupIterator* it) { |
| 603 | Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| 604 | if (FindAllCanReadHolder(it)) |
| 605 | return maybe(it->property_details().attributes()); |
| 606 | it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_HAS); |
| 607 | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), |
| 608 | Maybe<PropertyAttributes>()); |
| 609 | return maybe(ABSENT); |
| 610 | } |
| 611 | |
| 612 | |
| 613 | static bool FindAllCanWriteHolder(LookupIterator* it) { |
| 614 | for (; it->IsFound(); it->Next()) { |
| 615 | if (it->state() == LookupIterator::ACCESSOR) { |
| 616 | Handle<Object> accessors = it->GetAccessors(); |
| 617 | if (accessors->IsAccessorInfo()) { |
| 618 | if (AccessorInfo::cast(*accessors)->all_can_write()) return true; |
| 619 | } |
| 620 | } |
| 621 | } |
| 622 | return false; |
| 623 | } |
| 624 | |
| 625 | |
| 626 | MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck( |
| 627 | LookupIterator* it, Handle<Object> value, StrictMode strict_mode) { |
| 628 | Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| 629 | if (FindAllCanWriteHolder(it)) { |
| 630 | return SetPropertyWithAccessor(it->GetReceiver(), it->name(), value, |
| 631 | it->GetHolder<JSObject>(), |
| 632 | it->GetAccessors(), strict_mode); |
| 633 | } |
| 634 | |
| 635 | it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_SET); |
| 636 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object); |
| 637 | return value; |
| 638 | } |
| 639 | |
| 640 | |
| 641 | void JSObject::SetNormalizedProperty(Handle<JSObject> object, |
| 642 | Handle<Name> name, |
| 643 | Handle<Object> value, |
| 644 | PropertyDetails details) { |
| 645 | DCHECK(!object->HasFastProperties()); |
| 646 | Handle<NameDictionary> property_dictionary(object->property_dictionary()); |
| 647 | |
| 648 | if (!name->IsUniqueName()) { |
| 649 | name = object->GetIsolate()->factory()->InternalizeString( |
| 650 | Handle<String>::cast(name)); |
| 651 | } |
| 652 | |
| 653 | int entry = property_dictionary->FindEntry(name); |
| 654 | if (entry == NameDictionary::kNotFound) { |
| 655 | Handle<Object> store_value = value; |
| 656 | if (object->IsGlobalObject()) { |
| 657 | store_value = object->GetIsolate()->factory()->NewPropertyCell(value); |
| 658 | } |
| 659 | |
| 660 | property_dictionary = NameDictionary::Add( |
| 661 | property_dictionary, name, store_value, details); |
| 662 | object->set_properties(*property_dictionary); |
| 663 | return; |
| 664 | } |
| 665 | |
| 666 | PropertyDetails original_details = property_dictionary->DetailsAt(entry); |
| 667 | int enumeration_index; |
| 668 | // Preserve the enumeration index unless the property was deleted. |
| 669 | if (original_details.IsDeleted()) { |
| 670 | enumeration_index = property_dictionary->NextEnumerationIndex(); |
| 671 | property_dictionary->SetNextEnumerationIndex(enumeration_index + 1); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 672 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 673 | enumeration_index = original_details.dictionary_index(); |
| 674 | DCHECK(enumeration_index > 0); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 675 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 676 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 677 | details = PropertyDetails( |
| 678 | details.attributes(), details.type(), enumeration_index); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 679 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 680 | if (object->IsGlobalObject()) { |
| 681 | Handle<PropertyCell> cell( |
| 682 | PropertyCell::cast(property_dictionary->ValueAt(entry))); |
| 683 | PropertyCell::SetValueInferType(cell, value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 684 | // Please note we have to update the property details. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 685 | property_dictionary->DetailsAtPut(entry, details); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 686 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 687 | property_dictionary->SetEntry(entry, name, value, details); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 688 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 689 | } |
| 690 | |
| 691 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 692 | Handle<Object> JSObject::DeleteNormalizedProperty(Handle<JSObject> object, |
| 693 | Handle<Name> name, |
| 694 | DeleteMode mode) { |
| 695 | DCHECK(!object->HasFastProperties()); |
| 696 | Isolate* isolate = object->GetIsolate(); |
| 697 | Handle<NameDictionary> dictionary(object->property_dictionary()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 698 | int entry = dictionary->FindEntry(name); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 699 | if (entry != NameDictionary::kNotFound) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 700 | // If we have a global object set the cell to the hole. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 701 | if (object->IsGlobalObject()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 702 | PropertyDetails details = dictionary->DetailsAt(entry); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 703 | if (!details.IsConfigurable()) { |
| 704 | if (mode != FORCE_DELETION) return isolate->factory()->false_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 705 | // When forced to delete global properties, we have to make a |
| 706 | // map change to invalidate any ICs that think they can load |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 707 | // from the non-configurable cell without checking if it contains |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 708 | // the hole value. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 709 | Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); |
| 710 | DCHECK(new_map->is_dictionary_map()); |
| 711 | JSObject::MigrateToMap(object, new_map); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 712 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 713 | Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); |
| 714 | Handle<Object> value = isolate->factory()->the_hole_value(); |
| 715 | PropertyCell::SetValueInferType(cell, value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 716 | dictionary->DetailsAtPut(entry, details.AsDeleted()); |
| 717 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 718 | Handle<Object> deleted( |
| 719 | NameDictionary::DeleteProperty(dictionary, entry, mode)); |
| 720 | if (*deleted == isolate->heap()->true_value()) { |
| 721 | Handle<NameDictionary> new_properties = |
| 722 | NameDictionary::Shrink(dictionary, name); |
| 723 | object->set_properties(*new_properties); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 724 | } |
| 725 | return deleted; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 726 | } |
| 727 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 728 | return isolate->factory()->true_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 729 | } |
| 730 | |
| 731 | |
| 732 | bool JSObject::IsDirty() { |
| 733 | Object* cons_obj = map()->constructor(); |
| 734 | if (!cons_obj->IsJSFunction()) |
| 735 | return true; |
| 736 | JSFunction* fun = JSFunction::cast(cons_obj); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 737 | if (!fun->shared()->IsApiFunction()) |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 738 | return true; |
| 739 | // If the object is fully fast case and has the same map it was |
| 740 | // created with then no changes can have been made to it. |
| 741 | return map() != fun->initial_map() |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 742 | || !HasFastObjectElements() |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 743 | || !HasFastProperties(); |
| 744 | } |
| 745 | |
| 746 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 747 | MaybeHandle<Object> Object::GetElementWithReceiver(Isolate* isolate, |
| 748 | Handle<Object> object, |
| 749 | Handle<Object> receiver, |
| 750 | uint32_t index) { |
| 751 | if (object->IsUndefined()) { |
| 752 | // TODO(verwaest): Why is this check here? |
| 753 | UNREACHABLE(); |
| 754 | return isolate->factory()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 755 | } |
| 756 | |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 757 | // Iterate up the prototype chain until an element is found or the null |
| 758 | // prototype is encountered. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 759 | for (PrototypeIterator iter(isolate, object, |
| 760 | object->IsJSProxy() || object->IsJSObject() |
| 761 | ? PrototypeIterator::START_AT_RECEIVER |
| 762 | : PrototypeIterator::START_AT_PROTOTYPE); |
| 763 | !iter.IsAtEnd(); iter.Advance()) { |
| 764 | if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { |
| 765 | return JSProxy::GetElementWithHandler( |
| 766 | Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), receiver, |
| 767 | index); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 768 | } |
| 769 | |
| 770 | // Inline the case for JSObjects. Doing so significantly improves the |
| 771 | // performance of fetching elements where checking the prototype chain is |
| 772 | // necessary. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 773 | Handle<JSObject> js_object = |
| 774 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 775 | |
| 776 | // Check access rights if needed. |
| 777 | if (js_object->IsAccessCheckNeeded()) { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 778 | if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) { |
| 779 | isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_GET); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 780 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 781 | return isolate->factory()->undefined_value(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 782 | } |
| 783 | } |
| 784 | |
| 785 | if (js_object->HasIndexedInterceptor()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 786 | return JSObject::GetElementWithInterceptor(js_object, receiver, index); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 787 | } |
| 788 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 789 | if (js_object->elements() != isolate->heap()->empty_fixed_array()) { |
| 790 | Handle<Object> result; |
| 791 | ASSIGN_RETURN_ON_EXCEPTION( |
| 792 | isolate, result, |
| 793 | js_object->GetElementsAccessor()->Get(receiver, js_object, index), |
| 794 | Object); |
| 795 | if (!result->IsTheHole()) return result; |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 796 | } |
Ben Murdoch | e0cee9b | 2011-05-25 10:26:03 +0100 | [diff] [blame] | 797 | } |
| 798 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 799 | return isolate->factory()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 800 | } |
| 801 | |
| 802 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 803 | Map* Object::GetRootMap(Isolate* isolate) { |
| 804 | DisallowHeapAllocation no_alloc; |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 805 | if (IsSmi()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 806 | Context* context = isolate->context()->native_context(); |
| 807 | return context->number_function()->initial_map(); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 808 | } |
| 809 | |
| 810 | HeapObject* heap_object = HeapObject::cast(this); |
| 811 | |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 812 | // The object is either a number, a string, a boolean, |
| 813 | // a real JS object, or a Harmony proxy. |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 814 | if (heap_object->IsJSReceiver()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 815 | return heap_object->map(); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 816 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 817 | Context* context = isolate->context()->native_context(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 818 | |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 819 | if (heap_object->IsHeapNumber()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 820 | return context->number_function()->initial_map(); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 821 | } |
| 822 | if (heap_object->IsString()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 823 | return context->string_function()->initial_map(); |
| 824 | } |
| 825 | if (heap_object->IsSymbol()) { |
| 826 | return context->symbol_function()->initial_map(); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 827 | } |
| 828 | if (heap_object->IsBoolean()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 829 | return context->boolean_function()->initial_map(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 830 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 831 | return isolate->heap()->null_value()->map(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 832 | } |
| 833 | |
| 834 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 835 | Object* Object::GetHash() { |
| 836 | // The object is either a number, a name, an odd-ball, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 837 | // a real JS object, or a Harmony proxy. |
| 838 | if (IsNumber()) { |
| 839 | uint32_t hash = ComputeLongHash(double_to_uint64(Number())); |
| 840 | return Smi::FromInt(hash & Smi::kMaxValue); |
| 841 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 842 | if (IsName()) { |
| 843 | uint32_t hash = Name::cast(this)->Hash(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 844 | return Smi::FromInt(hash); |
| 845 | } |
| 846 | if (IsOddball()) { |
| 847 | uint32_t hash = Oddball::cast(this)->to_string()->Hash(); |
| 848 | return Smi::FromInt(hash); |
| 849 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 850 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 851 | DCHECK(IsJSReceiver()); |
| 852 | return JSReceiver::cast(this)->GetIdentityHash(); |
| 853 | } |
| 854 | |
| 855 | |
| 856 | Handle<Smi> Object::GetOrCreateHash(Isolate* isolate, Handle<Object> object) { |
| 857 | Handle<Object> hash(object->GetHash(), isolate); |
| 858 | if (hash->IsSmi()) return Handle<Smi>::cast(hash); |
| 859 | |
| 860 | DCHECK(object->IsJSReceiver()); |
| 861 | return JSReceiver::GetOrCreateIdentityHash(Handle<JSReceiver>::cast(object)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 862 | } |
| 863 | |
| 864 | |
| 865 | bool Object::SameValue(Object* other) { |
| 866 | if (other == this) return true; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 867 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 868 | // The object is either a number, a name, an odd-ball, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 869 | // a real JS object, or a Harmony proxy. |
| 870 | if (IsNumber() && other->IsNumber()) { |
| 871 | double this_value = Number(); |
| 872 | double other_value = other->Number(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 873 | bool equal = this_value == other_value; |
| 874 | // SameValue(NaN, NaN) is true. |
| 875 | if (!equal) return std::isnan(this_value) && std::isnan(other_value); |
| 876 | // SameValue(0.0, -0.0) is false. |
| 877 | return (this_value != 0) || ((1 / this_value) == (1 / other_value)); |
| 878 | } |
| 879 | if (IsString() && other->IsString()) { |
| 880 | return String::cast(this)->Equals(String::cast(other)); |
| 881 | } |
| 882 | return false; |
| 883 | } |
| 884 | |
| 885 | |
| 886 | bool Object::SameValueZero(Object* other) { |
| 887 | if (other == this) return true; |
| 888 | |
| 889 | // The object is either a number, a name, an odd-ball, |
| 890 | // a real JS object, or a Harmony proxy. |
| 891 | if (IsNumber() && other->IsNumber()) { |
| 892 | double this_value = Number(); |
| 893 | double other_value = other->Number(); |
| 894 | // +0 == -0 is true |
| 895 | return this_value == other_value |
| 896 | || (std::isnan(this_value) && std::isnan(other_value)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 897 | } |
| 898 | if (IsString() && other->IsString()) { |
| 899 | return String::cast(this)->Equals(String::cast(other)); |
| 900 | } |
| 901 | return false; |
| 902 | } |
| 903 | |
| 904 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 905 | void Object::ShortPrint(FILE* out) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 906 | OFStream os(out); |
| 907 | os << Brief(this); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 908 | } |
| 909 | |
| 910 | |
| 911 | void Object::ShortPrint(StringStream* accumulator) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 912 | OStringStream os; |
| 913 | os << Brief(this); |
| 914 | accumulator->Add(os.c_str()); |
| 915 | } |
| 916 | |
| 917 | |
| 918 | OStream& operator<<(OStream& os, const Brief& v) { |
| 919 | if (v.value->IsSmi()) { |
| 920 | Smi::cast(v.value)->SmiPrint(os); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 921 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 922 | // TODO(svenpanne) Const-correct HeapObjectShortPrint! |
| 923 | HeapObject* obj = const_cast<HeapObject*>(HeapObject::cast(v.value)); |
| 924 | obj->HeapObjectShortPrint(os); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 925 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 926 | return os; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 927 | } |
| 928 | |
| 929 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 930 | void Smi::SmiPrint(OStream& os) const { // NOLINT |
| 931 | os << value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 932 | } |
| 933 | |
| 934 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 935 | // Should a word be prefixed by 'a' or 'an' in order to read naturally in |
| 936 | // English? Returns false for non-ASCII or words that don't start with |
| 937 | // a capital letter. The a/an rule follows pronunciation in English. |
| 938 | // We don't use the BBC's overcorrect "an historic occasion" though if |
| 939 | // you speak a dialect you may well say "an 'istoric occasion". |
| 940 | static bool AnWord(String* str) { |
| 941 | if (str->length() == 0) return false; // A nothing. |
| 942 | int c0 = str->Get(0); |
| 943 | int c1 = str->length() > 1 ? str->Get(1) : 0; |
| 944 | if (c0 == 'U') { |
| 945 | if (c1 > 'Z') { |
| 946 | return true; // An Umpire, but a UTF8String, a U. |
| 947 | } |
| 948 | } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') { |
| 949 | return true; // An Ape, an ABCBook. |
| 950 | } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) && |
| 951 | (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' || |
| 952 | c0 == 'S' || c0 == 'X')) { |
| 953 | return true; // An MP3File, an M. |
| 954 | } |
| 955 | return false; |
| 956 | } |
| 957 | |
| 958 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 959 | Handle<String> String::SlowFlatten(Handle<ConsString> cons, |
| 960 | PretenureFlag pretenure) { |
| 961 | DCHECK(AllowHeapAllocation::IsAllowed()); |
| 962 | DCHECK(cons->second()->length() != 0); |
| 963 | Isolate* isolate = cons->GetIsolate(); |
| 964 | int length = cons->length(); |
| 965 | PretenureFlag tenure = isolate->heap()->InNewSpace(*cons) ? pretenure |
| 966 | : TENURED; |
| 967 | Handle<SeqString> result; |
| 968 | if (cons->IsOneByteRepresentation()) { |
| 969 | Handle<SeqOneByteString> flat = isolate->factory()->NewRawOneByteString( |
| 970 | length, tenure).ToHandleChecked(); |
| 971 | DisallowHeapAllocation no_gc; |
| 972 | WriteToFlat(*cons, flat->GetChars(), 0, length); |
| 973 | result = flat; |
| 974 | } else { |
| 975 | Handle<SeqTwoByteString> flat = isolate->factory()->NewRawTwoByteString( |
| 976 | length, tenure).ToHandleChecked(); |
| 977 | DisallowHeapAllocation no_gc; |
| 978 | WriteToFlat(*cons, flat->GetChars(), 0, length); |
| 979 | result = flat; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 980 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 981 | cons->set_first(*result); |
| 982 | cons->set_second(isolate->heap()->empty_string()); |
| 983 | DCHECK(result->IsFlat()); |
| 984 | return result; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 985 | } |
| 986 | |
| 987 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 988 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 989 | bool String::MakeExternal(v8::String::ExternalStringResource* resource) { |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 990 | // Externalizing twice leaks the external resource, so it's |
Kristian Monsen | 9dcf7e2 | 2010-06-28 14:14:28 +0100 | [diff] [blame] | 991 | // prohibited by the API. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 992 | DCHECK(!this->IsExternalString()); |
| 993 | #ifdef ENABLE_SLOW_DCHECKS |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 994 | if (FLAG_enable_slow_asserts) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 995 | // Assert that the resource and the string are equivalent. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 996 | DCHECK(static_cast<size_t>(this->length()) == resource->length()); |
Kristian Monsen | 25f6136 | 2010-05-21 11:50:48 +0100 | [diff] [blame] | 997 | ScopedVector<uc16> smart_chars(this->length()); |
| 998 | String::WriteToFlat(this, smart_chars.start(), 0, this->length()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 999 | DCHECK(memcmp(smart_chars.start(), |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1000 | resource->data(), |
Kristian Monsen | 25f6136 | 2010-05-21 11:50:48 +0100 | [diff] [blame] | 1001 | resource->length() * sizeof(smart_chars[0])) == 0); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1002 | } |
| 1003 | #endif // DEBUG |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1004 | int size = this->Size(); // Byte size of the original string. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1005 | // Abort if size does not allow in-place conversion. |
| 1006 | if (size < ExternalString::kShortSize) return false; |
| 1007 | Heap* heap = GetHeap(); |
| 1008 | bool is_one_byte = this->IsOneByteRepresentation(); |
| 1009 | bool is_internalized = this->IsInternalizedString(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1010 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1011 | // Morph the string to an external string by replacing the map and |
| 1012 | // reinitializing the fields. This won't work if the space the existing |
| 1013 | // string occupies is too small for a regular external string. |
| 1014 | // Instead, we resort to a short external string instead, omitting |
| 1015 | // the field caching the address of the backing store. When we encounter |
| 1016 | // short external strings in generated code, we need to bailout to runtime. |
| 1017 | Map* new_map; |
| 1018 | if (size < ExternalString::kSize) { |
| 1019 | new_map = is_internalized |
| 1020 | ? (is_one_byte |
| 1021 | ? heap->short_external_internalized_string_with_one_byte_data_map() |
| 1022 | : heap->short_external_internalized_string_map()) |
| 1023 | : (is_one_byte ? heap->short_external_string_with_one_byte_data_map() |
| 1024 | : heap->short_external_string_map()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1025 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1026 | new_map = is_internalized |
| 1027 | ? (is_one_byte |
| 1028 | ? heap->external_internalized_string_with_one_byte_data_map() |
| 1029 | : heap->external_internalized_string_map()) |
| 1030 | : (is_one_byte ? heap->external_string_with_one_byte_data_map() |
| 1031 | : heap->external_string_map()); |
Ben Murdoch | 85b7179 | 2012-04-11 18:30:58 +0100 | [diff] [blame] | 1032 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1033 | |
| 1034 | // Byte size of the external String object. |
| 1035 | int new_size = this->SizeFromMap(new_map); |
| 1036 | heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); |
| 1037 | |
| 1038 | // We are storing the new map using release store after creating a filler for |
| 1039 | // the left-over space to avoid races with the sweeper thread. |
| 1040 | this->synchronized_set_map(new_map); |
| 1041 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1042 | ExternalTwoByteString* self = ExternalTwoByteString::cast(this); |
| 1043 | self->set_resource(resource); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1044 | if (is_internalized) self->Hash(); // Force regeneration of the hash value. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1045 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1046 | heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1047 | return true; |
| 1048 | } |
| 1049 | |
| 1050 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1051 | bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { |
| 1052 | // Externalizing twice leaks the external resource, so it's |
| 1053 | // prohibited by the API. |
| 1054 | DCHECK(!this->IsExternalString()); |
| 1055 | #ifdef ENABLE_SLOW_DCHECKS |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 1056 | if (FLAG_enable_slow_asserts) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1057 | // Assert that the resource and the string are equivalent. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1058 | DCHECK(static_cast<size_t>(this->length()) == resource->length()); |
| 1059 | if (this->IsTwoByteRepresentation()) { |
| 1060 | ScopedVector<uint16_t> smart_chars(this->length()); |
| 1061 | String::WriteToFlat(this, smart_chars.start(), 0, this->length()); |
| 1062 | DCHECK(String::IsOneByte(smart_chars.start(), this->length())); |
| 1063 | } |
Kristian Monsen | 25f6136 | 2010-05-21 11:50:48 +0100 | [diff] [blame] | 1064 | ScopedVector<char> smart_chars(this->length()); |
| 1065 | String::WriteToFlat(this, smart_chars.start(), 0, this->length()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1066 | DCHECK(memcmp(smart_chars.start(), |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1067 | resource->data(), |
Kristian Monsen | 25f6136 | 2010-05-21 11:50:48 +0100 | [diff] [blame] | 1068 | resource->length() * sizeof(smart_chars[0])) == 0); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1069 | } |
| 1070 | #endif // DEBUG |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1071 | int size = this->Size(); // Byte size of the original string. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1072 | // Abort if size does not allow in-place conversion. |
| 1073 | if (size < ExternalString::kShortSize) return false; |
| 1074 | Heap* heap = GetHeap(); |
| 1075 | bool is_internalized = this->IsInternalizedString(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1076 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1077 | // Morph the string to an external string by replacing the map and |
| 1078 | // reinitializing the fields. This won't work if the space the existing |
| 1079 | // string occupies is too small for a regular external string. |
| 1080 | // Instead, we resort to a short external string instead, omitting |
| 1081 | // the field caching the address of the backing store. When we encounter |
| 1082 | // short external strings in generated code, we need to bailout to runtime. |
| 1083 | Map* new_map; |
| 1084 | if (size < ExternalString::kSize) { |
| 1085 | new_map = is_internalized |
| 1086 | ? heap->short_external_one_byte_internalized_string_map() |
| 1087 | : heap->short_external_one_byte_string_map(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1088 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1089 | new_map = is_internalized |
| 1090 | ? heap->external_one_byte_internalized_string_map() |
| 1091 | : heap->external_one_byte_string_map(); |
Ben Murdoch | 85b7179 | 2012-04-11 18:30:58 +0100 | [diff] [blame] | 1092 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1093 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1094 | // Byte size of the external String object. |
| 1095 | int new_size = this->SizeFromMap(new_map); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1096 | heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1097 | |
| 1098 | // We are storing the new map using release store after creating a filler for |
| 1099 | // the left-over space to avoid races with the sweeper thread. |
| 1100 | this->synchronized_set_map(new_map); |
| 1101 | |
| 1102 | ExternalOneByteString* self = ExternalOneByteString::cast(this); |
| 1103 | self->set_resource(resource); |
| 1104 | if (is_internalized) self->Hash(); // Force regeneration of the hash value. |
| 1105 | |
| 1106 | heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1107 | return true; |
| 1108 | } |
| 1109 | |
| 1110 | |
| 1111 | void String::StringShortPrint(StringStream* accumulator) { |
| 1112 | int len = length(); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 1113 | if (len > kMaxShortPrintLength) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1114 | accumulator->Add("<Very long string[%u]>", len); |
| 1115 | return; |
| 1116 | } |
| 1117 | |
| 1118 | if (!LooksValid()) { |
| 1119 | accumulator->Add("<Invalid String>"); |
| 1120 | return; |
| 1121 | } |
| 1122 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1123 | ConsStringIteratorOp op; |
| 1124 | StringCharacterStream stream(this, &op); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1125 | |
| 1126 | bool truncated = false; |
| 1127 | if (len > kMaxShortPrintLength) { |
| 1128 | len = kMaxShortPrintLength; |
| 1129 | truncated = true; |
| 1130 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1131 | bool one_byte = true; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1132 | for (int i = 0; i < len; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1133 | uint16_t c = stream.GetNext(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1134 | |
| 1135 | if (c < 32 || c >= 127) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1136 | one_byte = false; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1137 | } |
| 1138 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1139 | stream.Reset(this); |
| 1140 | if (one_byte) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1141 | accumulator->Add("<String[%u]: ", length()); |
| 1142 | for (int i = 0; i < len; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1143 | accumulator->Put(static_cast<char>(stream.GetNext())); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1144 | } |
| 1145 | accumulator->Put('>'); |
| 1146 | } else { |
| 1147 | // Backslash indicates that the string contains control |
| 1148 | // characters and that backslashes are therefore escaped. |
| 1149 | accumulator->Add("<String[%u]\\: ", length()); |
| 1150 | for (int i = 0; i < len; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1151 | uint16_t c = stream.GetNext(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1152 | if (c == '\n') { |
| 1153 | accumulator->Add("\\n"); |
| 1154 | } else if (c == '\r') { |
| 1155 | accumulator->Add("\\r"); |
| 1156 | } else if (c == '\\') { |
| 1157 | accumulator->Add("\\\\"); |
| 1158 | } else if (c < 32 || c > 126) { |
| 1159 | accumulator->Add("\\x%02x", c); |
| 1160 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1161 | accumulator->Put(static_cast<char>(c)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1162 | } |
| 1163 | } |
| 1164 | if (truncated) { |
| 1165 | accumulator->Put('.'); |
| 1166 | accumulator->Put('.'); |
| 1167 | accumulator->Put('.'); |
| 1168 | } |
| 1169 | accumulator->Put('>'); |
| 1170 | } |
| 1171 | return; |
| 1172 | } |
| 1173 | |
| 1174 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1175 | void String::PrintUC16(OStream& os, int start, int end) { // NOLINT |
| 1176 | if (end < 0) end = length(); |
| 1177 | ConsStringIteratorOp op; |
| 1178 | StringCharacterStream stream(this, &op, start); |
| 1179 | for (int i = start; i < end && stream.HasMore(); i++) { |
| 1180 | os << AsUC16(stream.GetNext()); |
| 1181 | } |
| 1182 | } |
| 1183 | |
| 1184 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1185 | void JSObject::JSObjectShortPrint(StringStream* accumulator) { |
| 1186 | switch (map()->instance_type()) { |
| 1187 | case JS_ARRAY_TYPE: { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1188 | double length = JSArray::cast(this)->length()->IsUndefined() |
| 1189 | ? 0 |
| 1190 | : JSArray::cast(this)->length()->Number(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1191 | accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1192 | break; |
| 1193 | } |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 1194 | case JS_WEAK_MAP_TYPE: { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1195 | accumulator->Add("<JS WeakMap>"); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 1196 | break; |
| 1197 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1198 | case JS_WEAK_SET_TYPE: { |
| 1199 | accumulator->Add("<JS WeakSet>"); |
| 1200 | break; |
| 1201 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1202 | case JS_REGEXP_TYPE: { |
| 1203 | accumulator->Add("<JS RegExp>"); |
| 1204 | break; |
| 1205 | } |
| 1206 | case JS_FUNCTION_TYPE: { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1207 | JSFunction* function = JSFunction::cast(this); |
| 1208 | Object* fun_name = function->shared()->DebugName(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1209 | bool printed = false; |
| 1210 | if (fun_name->IsString()) { |
| 1211 | String* str = String::cast(fun_name); |
| 1212 | if (str->length() > 0) { |
| 1213 | accumulator->Add("<JS Function "); |
| 1214 | accumulator->Put(str); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1215 | printed = true; |
| 1216 | } |
| 1217 | } |
| 1218 | if (!printed) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1219 | accumulator->Add("<JS Function"); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1220 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1221 | accumulator->Add(" (SharedFunctionInfo %p)", |
| 1222 | reinterpret_cast<void*>(function->shared())); |
| 1223 | accumulator->Put('>'); |
| 1224 | break; |
| 1225 | } |
| 1226 | case JS_GENERATOR_OBJECT_TYPE: { |
| 1227 | accumulator->Add("<JS Generator>"); |
| 1228 | break; |
| 1229 | } |
| 1230 | case JS_MODULE_TYPE: { |
| 1231 | accumulator->Add("<JS Module>"); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1232 | break; |
| 1233 | } |
| 1234 | // All other JSObjects are rather similar to each other (JSObject, |
| 1235 | // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). |
| 1236 | default: { |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 1237 | Map* map_of_this = map(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1238 | Heap* heap = GetHeap(); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 1239 | Object* constructor = map_of_this->constructor(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1240 | bool printed = false; |
| 1241 | if (constructor->IsHeapObject() && |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1242 | !heap->Contains(HeapObject::cast(constructor))) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1243 | accumulator->Add("!!!INVALID CONSTRUCTOR!!!"); |
| 1244 | } else { |
| 1245 | bool global_object = IsJSGlobalProxy(); |
| 1246 | if (constructor->IsJSFunction()) { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1247 | if (!heap->Contains(JSFunction::cast(constructor)->shared())) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1248 | accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!"); |
| 1249 | } else { |
| 1250 | Object* constructor_name = |
| 1251 | JSFunction::cast(constructor)->shared()->name(); |
| 1252 | if (constructor_name->IsString()) { |
| 1253 | String* str = String::cast(constructor_name); |
| 1254 | if (str->length() > 0) { |
| 1255 | bool vowel = AnWord(str); |
| 1256 | accumulator->Add("<%sa%s ", |
| 1257 | global_object ? "Global Object: " : "", |
| 1258 | vowel ? "n" : ""); |
| 1259 | accumulator->Put(str); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1260 | accumulator->Add(" with %smap %p", |
| 1261 | map_of_this->is_deprecated() ? "deprecated " : "", |
| 1262 | map_of_this); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1263 | printed = true; |
| 1264 | } |
| 1265 | } |
| 1266 | } |
| 1267 | } |
| 1268 | if (!printed) { |
| 1269 | accumulator->Add("<JS %sObject", global_object ? "Global " : ""); |
| 1270 | } |
| 1271 | } |
| 1272 | if (IsJSValue()) { |
| 1273 | accumulator->Add(" value = "); |
| 1274 | JSValue::cast(this)->value()->ShortPrint(accumulator); |
| 1275 | } |
| 1276 | accumulator->Put('>'); |
| 1277 | break; |
| 1278 | } |
| 1279 | } |
| 1280 | } |
| 1281 | |
| 1282 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1283 | void JSObject::PrintElementsTransition( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1284 | FILE* file, Handle<JSObject> object, |
| 1285 | ElementsKind from_kind, Handle<FixedArrayBase> from_elements, |
| 1286 | ElementsKind to_kind, Handle<FixedArrayBase> to_elements) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1287 | if (from_kind != to_kind) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1288 | OFStream os(file); |
| 1289 | os << "elements transition [" << ElementsKindToString(from_kind) << " -> " |
| 1290 | << ElementsKindToString(to_kind) << "] in "; |
| 1291 | JavaScriptFrame::PrintTop(object->GetIsolate(), file, false, true); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1292 | PrintF(file, " for "); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1293 | object->ShortPrint(file); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1294 | PrintF(file, " from "); |
| 1295 | from_elements->ShortPrint(file); |
| 1296 | PrintF(file, " to "); |
| 1297 | to_elements->ShortPrint(file); |
| 1298 | PrintF(file, "\n"); |
| 1299 | } |
| 1300 | } |
| 1301 | |
| 1302 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1303 | void Map::PrintGeneralization(FILE* file, |
| 1304 | const char* reason, |
| 1305 | int modify_index, |
| 1306 | int split, |
| 1307 | int descriptors, |
| 1308 | bool constant_to_field, |
| 1309 | Representation old_representation, |
| 1310 | Representation new_representation, |
| 1311 | HeapType* old_field_type, |
| 1312 | HeapType* new_field_type) { |
| 1313 | OFStream os(file); |
| 1314 | os << "[generalizing "; |
| 1315 | constructor_name()->PrintOn(file); |
| 1316 | os << "] "; |
| 1317 | Name* name = instance_descriptors()->GetKey(modify_index); |
| 1318 | if (name->IsString()) { |
| 1319 | String::cast(name)->PrintOn(file); |
| 1320 | } else { |
| 1321 | os << "{symbol " << static_cast<void*>(name) << "}"; |
| 1322 | } |
| 1323 | os << ":"; |
| 1324 | if (constant_to_field) { |
| 1325 | os << "c"; |
| 1326 | } else { |
| 1327 | os << old_representation.Mnemonic() << "{"; |
| 1328 | old_field_type->PrintTo(os, HeapType::SEMANTIC_DIM); |
| 1329 | os << "}"; |
| 1330 | } |
| 1331 | os << "->" << new_representation.Mnemonic() << "{"; |
| 1332 | new_field_type->PrintTo(os, HeapType::SEMANTIC_DIM); |
| 1333 | os << "} ("; |
| 1334 | if (strlen(reason) > 0) { |
| 1335 | os << reason; |
| 1336 | } else { |
| 1337 | os << "+" << (descriptors - split) << " maps"; |
| 1338 | } |
| 1339 | os << ") ["; |
| 1340 | JavaScriptFrame::PrintTop(GetIsolate(), file, false, true); |
| 1341 | os << "]\n"; |
| 1342 | } |
| 1343 | |
| 1344 | |
| 1345 | void JSObject::PrintInstanceMigration(FILE* file, |
| 1346 | Map* original_map, |
| 1347 | Map* new_map) { |
| 1348 | PrintF(file, "[migrating "); |
| 1349 | map()->constructor_name()->PrintOn(file); |
| 1350 | PrintF(file, "] "); |
| 1351 | DescriptorArray* o = original_map->instance_descriptors(); |
| 1352 | DescriptorArray* n = new_map->instance_descriptors(); |
| 1353 | for (int i = 0; i < original_map->NumberOfOwnDescriptors(); i++) { |
| 1354 | Representation o_r = o->GetDetails(i).representation(); |
| 1355 | Representation n_r = n->GetDetails(i).representation(); |
| 1356 | if (!o_r.Equals(n_r)) { |
| 1357 | String::cast(o->GetKey(i))->PrintOn(file); |
| 1358 | PrintF(file, ":%s->%s ", o_r.Mnemonic(), n_r.Mnemonic()); |
| 1359 | } else if (o->GetDetails(i).type() == CONSTANT && |
| 1360 | n->GetDetails(i).type() == FIELD) { |
| 1361 | Name* name = o->GetKey(i); |
| 1362 | if (name->IsString()) { |
| 1363 | String::cast(name)->PrintOn(file); |
| 1364 | } else { |
| 1365 | PrintF(file, "{symbol %p}", static_cast<void*>(name)); |
| 1366 | } |
| 1367 | PrintF(file, " "); |
| 1368 | } |
| 1369 | } |
| 1370 | PrintF(file, "\n"); |
| 1371 | } |
| 1372 | |
| 1373 | |
| 1374 | void HeapObject::HeapObjectShortPrint(OStream& os) { // NOLINT |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1375 | Heap* heap = GetHeap(); |
| 1376 | if (!heap->Contains(this)) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1377 | os << "!!!INVALID POINTER!!!"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1378 | return; |
| 1379 | } |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1380 | if (!heap->Contains(map())) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1381 | os << "!!!INVALID MAP!!!"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1382 | return; |
| 1383 | } |
| 1384 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1385 | os << this << " "; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1386 | |
| 1387 | if (IsString()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1388 | HeapStringAllocator allocator; |
| 1389 | StringStream accumulator(&allocator); |
| 1390 | String::cast(this)->StringShortPrint(&accumulator); |
| 1391 | os << accumulator.ToCString().get(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1392 | return; |
| 1393 | } |
| 1394 | if (IsJSObject()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1395 | HeapStringAllocator allocator; |
| 1396 | StringStream accumulator(&allocator); |
| 1397 | JSObject::cast(this)->JSObjectShortPrint(&accumulator); |
| 1398 | os << accumulator.ToCString().get(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1399 | return; |
| 1400 | } |
| 1401 | switch (map()->instance_type()) { |
| 1402 | case MAP_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1403 | os << "<Map(elements=" << Map::cast(this)->elements_kind() << ")>"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1404 | break; |
| 1405 | case FIXED_ARRAY_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1406 | os << "<FixedArray[" << FixedArray::cast(this)->length() << "]>"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1407 | break; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1408 | case FIXED_DOUBLE_ARRAY_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1409 | os << "<FixedDoubleArray[" << FixedDoubleArray::cast(this)->length() |
| 1410 | << "]>"; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1411 | break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1412 | case BYTE_ARRAY_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1413 | os << "<ByteArray[" << ByteArray::cast(this)->length() << "]>"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1414 | break; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1415 | case FREE_SPACE_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1416 | os << "<FreeSpace[" << FreeSpace::cast(this)->Size() << "]>"; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1417 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1418 | #define TYPED_ARRAY_SHORT_PRINT(Type, type, TYPE, ctype, size) \ |
| 1419 | case EXTERNAL_##TYPE##_ARRAY_TYPE: \ |
| 1420 | os << "<External" #Type "Array[" \ |
| 1421 | << External##Type##Array::cast(this)->length() << "]>"; \ |
| 1422 | break; \ |
| 1423 | case FIXED_##TYPE##_ARRAY_TYPE: \ |
| 1424 | os << "<Fixed" #Type "Array[" << Fixed##Type##Array::cast(this)->length() \ |
| 1425 | << "]>"; \ |
| 1426 | break; |
| 1427 | |
| 1428 | TYPED_ARRAYS(TYPED_ARRAY_SHORT_PRINT) |
| 1429 | #undef TYPED_ARRAY_SHORT_PRINT |
| 1430 | |
| 1431 | case SHARED_FUNCTION_INFO_TYPE: { |
| 1432 | SharedFunctionInfo* shared = SharedFunctionInfo::cast(this); |
| 1433 | SmartArrayPointer<char> debug_name = |
| 1434 | shared->DebugName()->ToCString(); |
| 1435 | if (debug_name[0] != 0) { |
| 1436 | os << "<SharedFunctionInfo " << debug_name.get() << ">"; |
| 1437 | } else { |
| 1438 | os << "<SharedFunctionInfo>"; |
| 1439 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1440 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1441 | } |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 1442 | case JS_MESSAGE_OBJECT_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1443 | os << "<JSMessageObject>"; |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 1444 | break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1445 | #define MAKE_STRUCT_CASE(NAME, Name, name) \ |
| 1446 | case NAME##_TYPE: \ |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1447 | os << "<" #Name ">"; \ |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1448 | break; |
| 1449 | STRUCT_LIST(MAKE_STRUCT_CASE) |
| 1450 | #undef MAKE_STRUCT_CASE |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1451 | case CODE_TYPE: { |
| 1452 | Code* code = Code::cast(this); |
| 1453 | os << "<Code: " << Code::Kind2String(code->kind()) << ">"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1454 | break; |
| 1455 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1456 | case ODDBALL_TYPE: { |
| 1457 | if (IsUndefined()) { |
| 1458 | os << "<undefined>"; |
| 1459 | } else if (IsTheHole()) { |
| 1460 | os << "<the hole>"; |
| 1461 | } else if (IsNull()) { |
| 1462 | os << "<null>"; |
| 1463 | } else if (IsTrue()) { |
| 1464 | os << "<true>"; |
| 1465 | } else if (IsFalse()) { |
| 1466 | os << "<false>"; |
| 1467 | } else { |
| 1468 | os << "<Odd Oddball>"; |
| 1469 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1470 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1471 | } |
| 1472 | case SYMBOL_TYPE: { |
| 1473 | Symbol* symbol = Symbol::cast(this); |
| 1474 | os << "<Symbol: " << symbol->Hash(); |
| 1475 | if (!symbol->name()->IsUndefined()) { |
| 1476 | os << " "; |
| 1477 | HeapStringAllocator allocator; |
| 1478 | StringStream accumulator(&allocator); |
| 1479 | String::cast(symbol->name())->StringShortPrint(&accumulator); |
| 1480 | os << accumulator.ToCString().get(); |
| 1481 | } |
| 1482 | os << ">"; |
| 1483 | break; |
| 1484 | } |
| 1485 | case HEAP_NUMBER_TYPE: { |
| 1486 | os << "<Number: "; |
| 1487 | HeapNumber::cast(this)->HeapNumberPrint(os); |
| 1488 | os << ">"; |
| 1489 | break; |
| 1490 | } |
| 1491 | case MUTABLE_HEAP_NUMBER_TYPE: { |
| 1492 | os << "<MutableNumber: "; |
| 1493 | HeapNumber::cast(this)->HeapNumberPrint(os); |
| 1494 | os << '>'; |
| 1495 | break; |
| 1496 | } |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 1497 | case JS_PROXY_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1498 | os << "<JSProxy>"; |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 1499 | break; |
| 1500 | case JS_FUNCTION_PROXY_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1501 | os << "<JSFunctionProxy>"; |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 1502 | break; |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1503 | case FOREIGN_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1504 | os << "<Foreign>"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1505 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1506 | case CELL_TYPE: { |
| 1507 | os << "Cell for "; |
| 1508 | HeapStringAllocator allocator; |
| 1509 | StringStream accumulator(&allocator); |
| 1510 | Cell::cast(this)->value()->ShortPrint(&accumulator); |
| 1511 | os << accumulator.ToCString().get(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1512 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1513 | } |
| 1514 | case PROPERTY_CELL_TYPE: { |
| 1515 | os << "PropertyCell for "; |
| 1516 | HeapStringAllocator allocator; |
| 1517 | StringStream accumulator(&allocator); |
| 1518 | PropertyCell::cast(this)->value()->ShortPrint(&accumulator); |
| 1519 | os << accumulator.ToCString().get(); |
| 1520 | break; |
| 1521 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1522 | default: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1523 | os << "<Other heap object (" << map()->instance_type() << ")>"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1524 | break; |
| 1525 | } |
| 1526 | } |
| 1527 | |
| 1528 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1529 | void HeapObject::Iterate(ObjectVisitor* v) { |
| 1530 | // Handle header |
| 1531 | IteratePointer(v, kMapOffset); |
| 1532 | // Handle object body |
| 1533 | Map* m = map(); |
| 1534 | IterateBody(m->instance_type(), SizeFromMap(m), v); |
| 1535 | } |
| 1536 | |
| 1537 | |
| 1538 | void HeapObject::IterateBody(InstanceType type, int object_size, |
| 1539 | ObjectVisitor* v) { |
| 1540 | // Avoiding <Type>::cast(this) because it accesses the map pointer field. |
| 1541 | // During GC, the map pointer field is encoded. |
| 1542 | if (type < FIRST_NONSTRING_TYPE) { |
| 1543 | switch (type & kStringRepresentationMask) { |
| 1544 | case kSeqStringTag: |
| 1545 | break; |
| 1546 | case kConsStringTag: |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 1547 | ConsString::BodyDescriptor::IterateBody(this, v); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1548 | break; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 1549 | case kSlicedStringTag: |
| 1550 | SlicedString::BodyDescriptor::IterateBody(this, v); |
| 1551 | break; |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 1552 | case kExternalStringTag: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1553 | if ((type & kStringEncodingMask) == kOneByteStringTag) { |
| 1554 | reinterpret_cast<ExternalOneByteString*>(this) |
| 1555 | ->ExternalOneByteStringIterateBody(v); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 1556 | } else { |
| 1557 | reinterpret_cast<ExternalTwoByteString*>(this)-> |
| 1558 | ExternalTwoByteStringIterateBody(v); |
| 1559 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1560 | break; |
| 1561 | } |
| 1562 | return; |
| 1563 | } |
| 1564 | |
| 1565 | switch (type) { |
| 1566 | case FIXED_ARRAY_TYPE: |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 1567 | FixedArray::BodyDescriptor::IterateBody(this, object_size, v); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1568 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1569 | case CONSTANT_POOL_ARRAY_TYPE: |
| 1570 | reinterpret_cast<ConstantPoolArray*>(this)->ConstantPoolIterateBody(v); |
| 1571 | break; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1572 | case FIXED_DOUBLE_ARRAY_TYPE: |
| 1573 | break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1574 | case JS_OBJECT_TYPE: |
| 1575 | case JS_CONTEXT_EXTENSION_OBJECT_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1576 | case JS_GENERATOR_OBJECT_TYPE: |
| 1577 | case JS_MODULE_TYPE: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1578 | case JS_VALUE_TYPE: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1579 | case JS_DATE_TYPE: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1580 | case JS_ARRAY_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1581 | case JS_ARRAY_BUFFER_TYPE: |
| 1582 | case JS_TYPED_ARRAY_TYPE: |
| 1583 | case JS_DATA_VIEW_TYPE: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1584 | case JS_SET_TYPE: |
| 1585 | case JS_MAP_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1586 | case JS_SET_ITERATOR_TYPE: |
| 1587 | case JS_MAP_ITERATOR_TYPE: |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 1588 | case JS_WEAK_MAP_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1589 | case JS_WEAK_SET_TYPE: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1590 | case JS_REGEXP_TYPE: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1591 | case JS_GLOBAL_PROXY_TYPE: |
| 1592 | case JS_GLOBAL_OBJECT_TYPE: |
| 1593 | case JS_BUILTINS_OBJECT_TYPE: |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 1594 | case JS_MESSAGE_OBJECT_TYPE: |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 1595 | JSObject::BodyDescriptor::IterateBody(this, object_size, v); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1596 | break; |
Steve Block | 791712a | 2010-08-27 10:21:07 +0100 | [diff] [blame] | 1597 | case JS_FUNCTION_TYPE: |
| 1598 | reinterpret_cast<JSFunction*>(this) |
| 1599 | ->JSFunctionIterateBody(object_size, v); |
| 1600 | break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1601 | case ODDBALL_TYPE: |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 1602 | Oddball::BodyDescriptor::IterateBody(this, v); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1603 | break; |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1604 | case JS_PROXY_TYPE: |
| 1605 | JSProxy::BodyDescriptor::IterateBody(this, v); |
| 1606 | break; |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 1607 | case JS_FUNCTION_PROXY_TYPE: |
| 1608 | JSFunctionProxy::BodyDescriptor::IterateBody(this, v); |
| 1609 | break; |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1610 | case FOREIGN_TYPE: |
| 1611 | reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1612 | break; |
| 1613 | case MAP_TYPE: |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 1614 | Map::BodyDescriptor::IterateBody(this, v); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1615 | break; |
| 1616 | case CODE_TYPE: |
| 1617 | reinterpret_cast<Code*>(this)->CodeIterateBody(v); |
| 1618 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1619 | case CELL_TYPE: |
| 1620 | Cell::BodyDescriptor::IterateBody(this, v); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1621 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1622 | case PROPERTY_CELL_TYPE: |
| 1623 | PropertyCell::BodyDescriptor::IterateBody(this, v); |
| 1624 | break; |
| 1625 | case SYMBOL_TYPE: |
| 1626 | Symbol::BodyDescriptor::IterateBody(this, v); |
| 1627 | break; |
| 1628 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1629 | case HEAP_NUMBER_TYPE: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1630 | case MUTABLE_HEAP_NUMBER_TYPE: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1631 | case FILLER_TYPE: |
| 1632 | case BYTE_ARRAY_TYPE: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1633 | case FREE_SPACE_TYPE: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1634 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1635 | |
| 1636 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 1637 | case EXTERNAL_##TYPE##_ARRAY_TYPE: \ |
| 1638 | case FIXED_##TYPE##_ARRAY_TYPE: \ |
| 1639 | break; |
| 1640 | |
| 1641 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 1642 | #undef TYPED_ARRAY_CASE |
| 1643 | |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 1644 | case SHARED_FUNCTION_INFO_TYPE: { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1645 | SharedFunctionInfo::BodyDescriptor::IterateBody(this, v); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1646 | break; |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 1647 | } |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 1648 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1649 | #define MAKE_STRUCT_CASE(NAME, Name, name) \ |
| 1650 | case NAME##_TYPE: |
| 1651 | STRUCT_LIST(MAKE_STRUCT_CASE) |
| 1652 | #undef MAKE_STRUCT_CASE |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1653 | if (type == ALLOCATION_SITE_TYPE) { |
| 1654 | AllocationSite::BodyDescriptor::IterateBody(this, v); |
| 1655 | } else { |
| 1656 | StructBodyDescriptor::IterateBody(this, object_size, v); |
| 1657 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1658 | break; |
| 1659 | default: |
| 1660 | PrintF("Unknown type: %d\n", type); |
| 1661 | UNREACHABLE(); |
| 1662 | } |
| 1663 | } |
| 1664 | |
| 1665 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1666 | bool HeapNumber::HeapNumberBooleanValue() { |
| 1667 | return DoubleToBoolean(value()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1668 | } |
| 1669 | |
| 1670 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1671 | void HeapNumber::HeapNumberPrint(OStream& os) { // NOLINT |
| 1672 | os << value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1673 | } |
| 1674 | |
| 1675 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1676 | String* JSReceiver::class_name() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1677 | if (IsJSFunction() || IsJSFunctionProxy()) { |
| 1678 | return GetHeap()->Function_string(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1679 | } |
| 1680 | if (map()->constructor()->IsJSFunction()) { |
| 1681 | JSFunction* constructor = JSFunction::cast(map()->constructor()); |
| 1682 | return String::cast(constructor->shared()->instance_class_name()); |
| 1683 | } |
| 1684 | // If the constructor is not present, return "Object". |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1685 | return GetHeap()->Object_string(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1686 | } |
| 1687 | |
| 1688 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1689 | String* Map::constructor_name() { |
| 1690 | if (constructor()->IsJSFunction()) { |
| 1691 | JSFunction* constructor = JSFunction::cast(this->constructor()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1692 | String* name = String::cast(constructor->shared()->name()); |
Ben Murdoch | f87a203 | 2010-10-22 12:50:53 +0100 | [diff] [blame] | 1693 | if (name->length() > 0) return name; |
| 1694 | String* inferred_name = constructor->shared()->inferred_name(); |
| 1695 | if (inferred_name->length() > 0) return inferred_name; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1696 | Object* proto = prototype(); |
Ben Murdoch | f87a203 | 2010-10-22 12:50:53 +0100 | [diff] [blame] | 1697 | if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1698 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1699 | // TODO(rossberg): what about proxies? |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1700 | // If the constructor is not present, return "Object". |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1701 | return GetHeap()->Object_string(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1702 | } |
| 1703 | |
| 1704 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1705 | String* JSReceiver::constructor_name() { |
| 1706 | return map()->constructor_name(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1707 | } |
| 1708 | |
| 1709 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1710 | MaybeHandle<Map> Map::CopyWithField(Handle<Map> map, |
| 1711 | Handle<Name> name, |
| 1712 | Handle<HeapType> type, |
| 1713 | PropertyAttributes attributes, |
| 1714 | Representation representation, |
| 1715 | TransitionFlag flag) { |
| 1716 | DCHECK(DescriptorArray::kNotFound == |
| 1717 | map->instance_descriptors()->Search( |
| 1718 | *name, map->NumberOfOwnDescriptors())); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 1719 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1720 | // Ensure the descriptor array does not get too big. |
| 1721 | if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors) { |
| 1722 | return MaybeHandle<Map>(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1723 | } |
| 1724 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1725 | Isolate* isolate = map->GetIsolate(); |
| 1726 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1727 | // Compute the new index for new field. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1728 | int index = map->NextFreePropertyIndex(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1729 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1730 | if (map->instance_type() == JS_CONTEXT_EXTENSION_OBJECT_TYPE) { |
| 1731 | representation = Representation::Tagged(); |
| 1732 | type = HeapType::Any(isolate); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 1733 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1734 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1735 | FieldDescriptor new_field_desc(name, index, type, attributes, representation); |
| 1736 | Handle<Map> new_map = Map::CopyAddDescriptor(map, &new_field_desc, flag); |
| 1737 | int unused_property_fields = new_map->unused_property_fields() - 1; |
| 1738 | if (unused_property_fields < 0) { |
| 1739 | unused_property_fields += JSObject::kFieldsAdded; |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 1740 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1741 | new_map->set_unused_property_fields(unused_property_fields); |
| 1742 | return new_map; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1743 | } |
| 1744 | |
| 1745 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1746 | MaybeHandle<Map> Map::CopyWithConstant(Handle<Map> map, |
| 1747 | Handle<Name> name, |
| 1748 | Handle<Object> constant, |
| 1749 | PropertyAttributes attributes, |
| 1750 | TransitionFlag flag) { |
| 1751 | // Ensure the descriptor array does not get too big. |
| 1752 | if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors) { |
| 1753 | return MaybeHandle<Map>(); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 1754 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1755 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1756 | // Allocate new instance descriptors with (name, constant) added. |
| 1757 | ConstantDescriptor new_constant_desc(name, constant, attributes); |
| 1758 | return Map::CopyAddDescriptor(map, &new_constant_desc, flag); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1759 | } |
| 1760 | |
| 1761 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1762 | void JSObject::AddSlowProperty(Handle<JSObject> object, |
| 1763 | Handle<Name> name, |
| 1764 | Handle<Object> value, |
| 1765 | PropertyAttributes attributes) { |
| 1766 | DCHECK(!object->HasFastProperties()); |
| 1767 | Isolate* isolate = object->GetIsolate(); |
| 1768 | Handle<NameDictionary> dict(object->property_dictionary()); |
| 1769 | if (object->IsGlobalObject()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1770 | // In case name is an orphaned property reuse the cell. |
| 1771 | int entry = dict->FindEntry(name); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1772 | if (entry != NameDictionary::kNotFound) { |
| 1773 | Handle<PropertyCell> cell(PropertyCell::cast(dict->ValueAt(entry))); |
| 1774 | PropertyCell::SetValueInferType(cell, value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1775 | // Assign an enumeration index to the property and update |
| 1776 | // SetNextEnumerationIndex. |
| 1777 | int index = dict->NextEnumerationIndex(); |
| 1778 | PropertyDetails details = PropertyDetails(attributes, NORMAL, index); |
| 1779 | dict->SetNextEnumerationIndex(index + 1); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1780 | dict->SetEntry(entry, name, cell, details); |
| 1781 | return; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1782 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1783 | Handle<PropertyCell> cell = isolate->factory()->NewPropertyCell(value); |
| 1784 | PropertyCell::SetValueInferType(cell, value); |
| 1785 | value = cell; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1786 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1787 | PropertyDetails details = PropertyDetails(attributes, NORMAL, 0); |
| 1788 | Handle<NameDictionary> result = |
| 1789 | NameDictionary::Add(dict, name, value, details); |
| 1790 | if (*dict != *result) object->set_properties(*result); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1791 | } |
| 1792 | |
| 1793 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1794 | Context* JSObject::GetCreationContext() { |
| 1795 | Object* constructor = this->map()->constructor(); |
| 1796 | JSFunction* function; |
| 1797 | if (!constructor->IsJSFunction()) { |
| 1798 | // Functions have null as a constructor, |
| 1799 | // but any JSFunction knows its context immediately. |
| 1800 | function = JSFunction::cast(this); |
| 1801 | } else { |
| 1802 | function = JSFunction::cast(constructor); |
| 1803 | } |
| 1804 | |
| 1805 | return function->context()->native_context(); |
| 1806 | } |
| 1807 | |
| 1808 | |
| 1809 | void JSObject::EnqueueChangeRecord(Handle<JSObject> object, |
| 1810 | const char* type_str, |
| 1811 | Handle<Name> name, |
| 1812 | Handle<Object> old_value) { |
| 1813 | DCHECK(!object->IsJSGlobalProxy()); |
| 1814 | DCHECK(!object->IsJSGlobalObject()); |
| 1815 | Isolate* isolate = object->GetIsolate(); |
| 1816 | HandleScope scope(isolate); |
| 1817 | Handle<String> type = isolate->factory()->InternalizeUtf8String(type_str); |
| 1818 | Handle<Object> args[] = { type, object, name, old_value }; |
| 1819 | int argc = name.is_null() ? 2 : old_value->IsTheHole() ? 3 : 4; |
| 1820 | |
| 1821 | Execution::Call(isolate, |
| 1822 | Handle<JSFunction>(isolate->observers_notify_change()), |
| 1823 | isolate->factory()->undefined_value(), |
| 1824 | argc, args).Assert(); |
| 1825 | } |
| 1826 | |
| 1827 | |
| 1828 | const char* Representation::Mnemonic() const { |
| 1829 | switch (kind_) { |
| 1830 | case kNone: return "v"; |
| 1831 | case kTagged: return "t"; |
| 1832 | case kSmi: return "s"; |
| 1833 | case kDouble: return "d"; |
| 1834 | case kInteger32: return "i"; |
| 1835 | case kHeapObject: return "h"; |
| 1836 | case kExternal: return "x"; |
| 1837 | default: |
| 1838 | UNREACHABLE(); |
| 1839 | return NULL; |
| 1840 | } |
| 1841 | } |
| 1842 | |
| 1843 | |
| 1844 | bool Map::InstancesNeedRewriting(Map* target, int target_number_of_fields, |
| 1845 | int target_inobject, int target_unused, |
| 1846 | int* old_number_of_fields) { |
| 1847 | // If fields were added (or removed), rewrite the instance. |
| 1848 | *old_number_of_fields = NumberOfFields(); |
| 1849 | DCHECK(target_number_of_fields >= *old_number_of_fields); |
| 1850 | if (target_number_of_fields != *old_number_of_fields) return true; |
| 1851 | |
| 1852 | // If smi descriptors were replaced by double descriptors, rewrite. |
| 1853 | DescriptorArray* old_desc = instance_descriptors(); |
| 1854 | DescriptorArray* new_desc = target->instance_descriptors(); |
| 1855 | int limit = NumberOfOwnDescriptors(); |
| 1856 | for (int i = 0; i < limit; i++) { |
| 1857 | if (new_desc->GetDetails(i).representation().IsDouble() != |
| 1858 | old_desc->GetDetails(i).representation().IsDouble()) { |
| 1859 | return true; |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1860 | } |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 1861 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1862 | |
| 1863 | // If no fields were added, and no inobject properties were removed, setting |
| 1864 | // the map is sufficient. |
| 1865 | if (target_inobject == inobject_properties()) return false; |
| 1866 | // In-object slack tracking may have reduced the object size of the new map. |
| 1867 | // In that case, succeed if all existing fields were inobject, and they still |
| 1868 | // fit within the new inobject size. |
| 1869 | DCHECK(target_inobject < inobject_properties()); |
| 1870 | if (target_number_of_fields <= target_inobject) { |
| 1871 | DCHECK(target_number_of_fields + target_unused == target_inobject); |
| 1872 | return false; |
| 1873 | } |
| 1874 | // Otherwise, properties will need to be moved to the backing store. |
| 1875 | return true; |
| 1876 | } |
| 1877 | |
| 1878 | |
| 1879 | void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) { |
| 1880 | Isolate* isolate = parent->GetIsolate(); |
| 1881 | Handle<Name> name = isolate->factory()->elements_transition_symbol(); |
| 1882 | ConnectTransition(parent, child, name, FULL_TRANSITION); |
| 1883 | } |
| 1884 | |
| 1885 | |
| 1886 | void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) { |
| 1887 | if (object->map() == *new_map) return; |
| 1888 | if (object->HasFastProperties()) { |
| 1889 | if (!new_map->is_dictionary_map()) { |
| 1890 | Handle<Map> old_map(object->map()); |
| 1891 | MigrateFastToFast(object, new_map); |
| 1892 | if (old_map->is_prototype_map()) { |
| 1893 | // Clear out the old descriptor array to avoid problems to sharing |
| 1894 | // the descriptor array without using an explicit. |
| 1895 | old_map->InitializeDescriptors( |
| 1896 | old_map->GetHeap()->empty_descriptor_array()); |
| 1897 | // Ensure that no transition was inserted for prototype migrations. |
| 1898 | DCHECK(!old_map->HasTransitionArray()); |
| 1899 | DCHECK(new_map->GetBackPointer()->IsUndefined()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1900 | } |
| 1901 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1902 | MigrateFastToSlow(object, new_map, 0); |
| 1903 | } |
| 1904 | } else { |
| 1905 | // For slow-to-fast migrations JSObject::TransformToFastProperties() |
| 1906 | // must be used instead. |
| 1907 | CHECK(new_map->is_dictionary_map()); |
| 1908 | |
| 1909 | // Slow-to-slow migration is trivial. |
| 1910 | object->set_map(*new_map); |
| 1911 | } |
| 1912 | } |
| 1913 | |
| 1914 | |
| 1915 | // To migrate a fast instance to a fast map: |
| 1916 | // - First check whether the instance needs to be rewritten. If not, simply |
| 1917 | // change the map. |
| 1918 | // - Otherwise, allocate a fixed array large enough to hold all fields, in |
| 1919 | // addition to unused space. |
| 1920 | // - Copy all existing properties in, in the following order: backing store |
| 1921 | // properties, unused fields, inobject properties. |
| 1922 | // - If all allocation succeeded, commit the state atomically: |
| 1923 | // * Copy inobject properties from the backing store back into the object. |
| 1924 | // * Trim the difference in instance size of the object. This also cleanly |
| 1925 | // frees inobject properties that moved to the backing store. |
| 1926 | // * If there are properties left in the backing store, trim of the space used |
| 1927 | // to temporarily store the inobject properties. |
| 1928 | // * If there are properties left in the backing store, install the backing |
| 1929 | // store. |
| 1930 | void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { |
| 1931 | Isolate* isolate = object->GetIsolate(); |
| 1932 | Handle<Map> old_map(object->map()); |
| 1933 | int old_number_of_fields; |
| 1934 | int number_of_fields = new_map->NumberOfFields(); |
| 1935 | int inobject = new_map->inobject_properties(); |
| 1936 | int unused = new_map->unused_property_fields(); |
| 1937 | |
| 1938 | // Nothing to do if no functions were converted to fields and no smis were |
| 1939 | // converted to doubles. |
| 1940 | if (!old_map->InstancesNeedRewriting(*new_map, number_of_fields, inobject, |
| 1941 | unused, &old_number_of_fields)) { |
| 1942 | object->synchronized_set_map(*new_map); |
| 1943 | return; |
| 1944 | } |
| 1945 | |
| 1946 | int total_size = number_of_fields + unused; |
| 1947 | int external = total_size - inobject; |
| 1948 | |
| 1949 | if (number_of_fields != old_number_of_fields && |
| 1950 | new_map->GetBackPointer() == *old_map) { |
| 1951 | PropertyDetails details = new_map->GetLastDescriptorDetails(); |
| 1952 | |
| 1953 | if (old_map->unused_property_fields() > 0) { |
| 1954 | if (details.representation().IsDouble()) { |
| 1955 | Handle<Object> value = isolate->factory()->NewHeapNumber(0, MUTABLE); |
| 1956 | FieldIndex index = |
| 1957 | FieldIndex::ForDescriptor(*new_map, new_map->LastAdded()); |
| 1958 | object->FastPropertyAtPut(index, *value); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 1959 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1960 | object->synchronized_set_map(*new_map); |
| 1961 | return; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1962 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1963 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1964 | DCHECK(number_of_fields == old_number_of_fields + 1); |
| 1965 | // This migration is a transition from a map that has run out out property |
| 1966 | // space. Therefore it could be done by extending the backing store. |
| 1967 | Handle<FixedArray> old_storage = handle(object->properties(), isolate); |
| 1968 | Handle<FixedArray> new_storage = |
| 1969 | FixedArray::CopySize(old_storage, external); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1970 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1971 | // Properly initialize newly added property. |
| 1972 | Handle<Object> value; |
| 1973 | if (details.representation().IsDouble()) { |
| 1974 | value = isolate->factory()->NewHeapNumber(0, MUTABLE); |
| 1975 | } else { |
| 1976 | value = isolate->factory()->uninitialized_value(); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 1977 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1978 | DCHECK(details.type() == FIELD); |
| 1979 | int target_index = details.field_index() - inobject; |
| 1980 | DCHECK(target_index >= 0); // Must be a backing store index. |
| 1981 | new_storage->set(target_index, *value); |
| 1982 | |
| 1983 | // From here on we cannot fail and we shouldn't GC anymore. |
| 1984 | DisallowHeapAllocation no_allocation; |
| 1985 | |
| 1986 | // Set the new property value and do the map transition. |
| 1987 | object->set_properties(*new_storage); |
| 1988 | object->synchronized_set_map(*new_map); |
| 1989 | return; |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 1990 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1991 | Handle<FixedArray> array = isolate->factory()->NewFixedArray(total_size); |
| 1992 | |
| 1993 | Handle<DescriptorArray> old_descriptors(old_map->instance_descriptors()); |
| 1994 | Handle<DescriptorArray> new_descriptors(new_map->instance_descriptors()); |
| 1995 | int old_nof = old_map->NumberOfOwnDescriptors(); |
| 1996 | int new_nof = new_map->NumberOfOwnDescriptors(); |
| 1997 | |
| 1998 | // This method only supports generalizing instances to at least the same |
| 1999 | // number of properties. |
| 2000 | DCHECK(old_nof <= new_nof); |
| 2001 | |
| 2002 | for (int i = 0; i < old_nof; i++) { |
| 2003 | PropertyDetails details = new_descriptors->GetDetails(i); |
| 2004 | if (details.type() != FIELD) continue; |
| 2005 | PropertyDetails old_details = old_descriptors->GetDetails(i); |
| 2006 | if (old_details.type() == CALLBACKS) { |
| 2007 | DCHECK(details.representation().IsTagged()); |
| 2008 | continue; |
| 2009 | } |
| 2010 | DCHECK(old_details.type() == CONSTANT || |
| 2011 | old_details.type() == FIELD); |
| 2012 | Object* raw_value = old_details.type() == CONSTANT |
| 2013 | ? old_descriptors->GetValue(i) |
| 2014 | : object->RawFastPropertyAt(FieldIndex::ForDescriptor(*old_map, i)); |
| 2015 | Handle<Object> value(raw_value, isolate); |
| 2016 | if (!old_details.representation().IsDouble() && |
| 2017 | details.representation().IsDouble()) { |
| 2018 | if (old_details.representation().IsNone()) { |
| 2019 | value = handle(Smi::FromInt(0), isolate); |
| 2020 | } |
| 2021 | value = Object::NewStorageFor(isolate, value, details.representation()); |
| 2022 | } else if (old_details.representation().IsDouble() && |
| 2023 | !details.representation().IsDouble()) { |
| 2024 | value = Object::WrapForRead(isolate, value, old_details.representation()); |
| 2025 | } |
| 2026 | DCHECK(!(details.representation().IsDouble() && value->IsSmi())); |
| 2027 | int target_index = new_descriptors->GetFieldIndex(i) - inobject; |
| 2028 | if (target_index < 0) target_index += total_size; |
| 2029 | array->set(target_index, *value); |
| 2030 | } |
| 2031 | |
| 2032 | for (int i = old_nof; i < new_nof; i++) { |
| 2033 | PropertyDetails details = new_descriptors->GetDetails(i); |
| 2034 | if (details.type() != FIELD) continue; |
| 2035 | Handle<Object> value; |
| 2036 | if (details.representation().IsDouble()) { |
| 2037 | value = isolate->factory()->NewHeapNumber(0, MUTABLE); |
| 2038 | } else { |
| 2039 | value = isolate->factory()->uninitialized_value(); |
| 2040 | } |
| 2041 | int target_index = new_descriptors->GetFieldIndex(i) - inobject; |
| 2042 | if (target_index < 0) target_index += total_size; |
| 2043 | array->set(target_index, *value); |
| 2044 | } |
| 2045 | |
| 2046 | // From here on we cannot fail and we shouldn't GC anymore. |
| 2047 | DisallowHeapAllocation no_allocation; |
| 2048 | |
| 2049 | // Copy (real) inobject properties. If necessary, stop at number_of_fields to |
| 2050 | // avoid overwriting |one_pointer_filler_map|. |
| 2051 | int limit = Min(inobject, number_of_fields); |
| 2052 | for (int i = 0; i < limit; i++) { |
| 2053 | FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i); |
| 2054 | object->FastPropertyAtPut(index, array->get(external + i)); |
| 2055 | } |
| 2056 | |
| 2057 | Heap* heap = isolate->heap(); |
| 2058 | |
| 2059 | // If there are properties in the new backing store, trim it to the correct |
| 2060 | // size and install the backing store into the object. |
| 2061 | if (external > 0) { |
| 2062 | heap->RightTrimFixedArray<Heap::FROM_MUTATOR>(*array, inobject); |
| 2063 | object->set_properties(*array); |
| 2064 | } |
| 2065 | |
| 2066 | // Create filler object past the new instance size. |
| 2067 | int new_instance_size = new_map->instance_size(); |
| 2068 | int instance_size_delta = old_map->instance_size() - new_instance_size; |
| 2069 | DCHECK(instance_size_delta >= 0); |
| 2070 | |
| 2071 | if (instance_size_delta > 0) { |
| 2072 | Address address = object->address(); |
| 2073 | heap->CreateFillerObjectAt( |
| 2074 | address + new_instance_size, instance_size_delta); |
| 2075 | heap->AdjustLiveBytes(address, -instance_size_delta, Heap::FROM_MUTATOR); |
| 2076 | } |
| 2077 | |
| 2078 | // We are storing the new map using release store after creating a filler for |
| 2079 | // the left-over space to avoid races with the sweeper thread. |
| 2080 | object->synchronized_set_map(*new_map); |
| 2081 | } |
| 2082 | |
| 2083 | |
| 2084 | void JSObject::GeneralizeFieldRepresentation(Handle<JSObject> object, |
| 2085 | int modify_index, |
| 2086 | Representation new_representation, |
| 2087 | Handle<HeapType> new_field_type) { |
| 2088 | Handle<Map> new_map = Map::GeneralizeRepresentation( |
| 2089 | handle(object->map()), modify_index, new_representation, new_field_type, |
| 2090 | FORCE_FIELD); |
| 2091 | MigrateToMap(object, new_map); |
| 2092 | } |
| 2093 | |
| 2094 | |
| 2095 | int Map::NumberOfFields() { |
| 2096 | DescriptorArray* descriptors = instance_descriptors(); |
| 2097 | int result = 0; |
| 2098 | for (int i = 0; i < NumberOfOwnDescriptors(); i++) { |
| 2099 | if (descriptors->GetDetails(i).type() == FIELD) result++; |
| 2100 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2101 | return result; |
| 2102 | } |
| 2103 | |
| 2104 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2105 | Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map, |
| 2106 | int modify_index, |
| 2107 | StoreMode store_mode, |
| 2108 | PropertyAttributes attributes, |
| 2109 | const char* reason) { |
| 2110 | Isolate* isolate = map->GetIsolate(); |
| 2111 | Handle<Map> new_map = Copy(map); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2112 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2113 | DescriptorArray* descriptors = new_map->instance_descriptors(); |
| 2114 | int length = descriptors->number_of_descriptors(); |
| 2115 | for (int i = 0; i < length; i++) { |
| 2116 | descriptors->SetRepresentation(i, Representation::Tagged()); |
| 2117 | if (descriptors->GetDetails(i).type() == FIELD) { |
| 2118 | descriptors->SetValue(i, HeapType::Any()); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 2119 | } |
| 2120 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2121 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2122 | // Unless the instance is being migrated, ensure that modify_index is a field. |
| 2123 | PropertyDetails details = descriptors->GetDetails(modify_index); |
| 2124 | if (store_mode == FORCE_FIELD && |
| 2125 | (details.type() != FIELD || details.attributes() != attributes)) { |
| 2126 | int field_index = details.type() == FIELD ? details.field_index() |
| 2127 | : new_map->NumberOfFields(); |
| 2128 | FieldDescriptor d(handle(descriptors->GetKey(modify_index), isolate), |
| 2129 | field_index, attributes, Representation::Tagged()); |
| 2130 | descriptors->Replace(modify_index, &d); |
| 2131 | if (details.type() != FIELD) { |
| 2132 | int unused_property_fields = new_map->unused_property_fields() - 1; |
| 2133 | if (unused_property_fields < 0) { |
| 2134 | unused_property_fields += JSObject::kFieldsAdded; |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 2135 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2136 | new_map->set_unused_property_fields(unused_property_fields); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 2137 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2138 | } else { |
| 2139 | DCHECK(details.attributes() == attributes); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2140 | } |
| 2141 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2142 | if (FLAG_trace_generalization) { |
| 2143 | HeapType* field_type = (details.type() == FIELD) |
| 2144 | ? map->instance_descriptors()->GetFieldType(modify_index) |
| 2145 | : NULL; |
| 2146 | map->PrintGeneralization(stdout, reason, modify_index, |
| 2147 | new_map->NumberOfOwnDescriptors(), |
| 2148 | new_map->NumberOfOwnDescriptors(), |
| 2149 | details.type() == CONSTANT && store_mode == FORCE_FIELD, |
| 2150 | details.representation(), Representation::Tagged(), |
| 2151 | field_type, HeapType::Any()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2152 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2153 | return new_map; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2154 | } |
| 2155 | |
| 2156 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2157 | // static |
| 2158 | Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map, |
| 2159 | int modify_index, |
| 2160 | StoreMode store_mode, |
| 2161 | const char* reason) { |
| 2162 | PropertyDetails details = |
| 2163 | map->instance_descriptors()->GetDetails(modify_index); |
| 2164 | return CopyGeneralizeAllRepresentations(map, modify_index, store_mode, |
| 2165 | details.attributes(), reason); |
| 2166 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2167 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2168 | |
| 2169 | void Map::DeprecateTransitionTree() { |
| 2170 | if (is_deprecated()) return; |
| 2171 | if (HasTransitionArray()) { |
| 2172 | TransitionArray* transitions = this->transitions(); |
| 2173 | for (int i = 0; i < transitions->number_of_transitions(); i++) { |
| 2174 | transitions->GetTarget(i)->DeprecateTransitionTree(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2175 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2176 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2177 | deprecate(); |
| 2178 | dependent_code()->DeoptimizeDependentCodeGroup( |
| 2179 | GetIsolate(), DependentCode::kTransitionGroup); |
| 2180 | NotifyLeafMapLayoutChange(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2181 | } |
| 2182 | |
| 2183 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2184 | // Invalidates a transition target at |key|, and installs |new_descriptors| over |
| 2185 | // the current instance_descriptors to ensure proper sharing of descriptor |
| 2186 | // arrays. |
| 2187 | void Map::DeprecateTarget(Name* key, DescriptorArray* new_descriptors) { |
| 2188 | if (HasTransitionArray()) { |
| 2189 | TransitionArray* transitions = this->transitions(); |
| 2190 | int transition = transitions->Search(key); |
| 2191 | if (transition != TransitionArray::kNotFound) { |
| 2192 | transitions->GetTarget(transition)->DeprecateTransitionTree(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2193 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2194 | } |
| 2195 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2196 | // Don't overwrite the empty descriptor array. |
| 2197 | if (NumberOfOwnDescriptors() == 0) return; |
| 2198 | |
| 2199 | DescriptorArray* to_replace = instance_descriptors(); |
| 2200 | Map* current = this; |
| 2201 | GetHeap()->incremental_marking()->RecordWrites(to_replace); |
| 2202 | while (current->instance_descriptors() == to_replace) { |
| 2203 | current->SetEnumLength(kInvalidEnumCacheSentinel); |
| 2204 | current->set_instance_descriptors(new_descriptors); |
| 2205 | Object* next = current->GetBackPointer(); |
| 2206 | if (next->IsUndefined()) break; |
| 2207 | current = Map::cast(next); |
| 2208 | } |
| 2209 | |
| 2210 | set_owns_descriptors(false); |
| 2211 | } |
| 2212 | |
| 2213 | |
| 2214 | Map* Map::FindRootMap() { |
| 2215 | Map* result = this; |
| 2216 | while (true) { |
| 2217 | Object* back = result->GetBackPointer(); |
| 2218 | if (back->IsUndefined()) return result; |
| 2219 | result = Map::cast(back); |
| 2220 | } |
| 2221 | } |
| 2222 | |
| 2223 | |
| 2224 | Map* Map::FindLastMatchMap(int verbatim, |
| 2225 | int length, |
| 2226 | DescriptorArray* descriptors) { |
| 2227 | DisallowHeapAllocation no_allocation; |
| 2228 | |
| 2229 | // This can only be called on roots of transition trees. |
| 2230 | DCHECK(GetBackPointer()->IsUndefined()); |
| 2231 | |
| 2232 | Map* current = this; |
| 2233 | |
| 2234 | for (int i = verbatim; i < length; i++) { |
| 2235 | if (!current->HasTransitionArray()) break; |
| 2236 | Name* name = descriptors->GetKey(i); |
| 2237 | TransitionArray* transitions = current->transitions(); |
| 2238 | int transition = transitions->Search(name); |
| 2239 | if (transition == TransitionArray::kNotFound) break; |
| 2240 | |
| 2241 | Map* next = transitions->GetTarget(transition); |
| 2242 | DescriptorArray* next_descriptors = next->instance_descriptors(); |
| 2243 | |
| 2244 | PropertyDetails details = descriptors->GetDetails(i); |
| 2245 | PropertyDetails next_details = next_descriptors->GetDetails(i); |
| 2246 | if (details.type() != next_details.type()) break; |
| 2247 | if (details.attributes() != next_details.attributes()) break; |
| 2248 | if (!details.representation().Equals(next_details.representation())) break; |
| 2249 | if (next_details.type() == FIELD) { |
| 2250 | if (!descriptors->GetFieldType(i)->NowIs( |
| 2251 | next_descriptors->GetFieldType(i))) break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2252 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2253 | if (descriptors->GetValue(i) != next_descriptors->GetValue(i)) break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2254 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2255 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2256 | current = next; |
| 2257 | } |
| 2258 | return current; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2259 | } |
| 2260 | |
| 2261 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2262 | Map* Map::FindFieldOwner(int descriptor) { |
| 2263 | DisallowHeapAllocation no_allocation; |
| 2264 | DCHECK_EQ(FIELD, instance_descriptors()->GetDetails(descriptor).type()); |
| 2265 | Map* result = this; |
| 2266 | while (true) { |
| 2267 | Object* back = result->GetBackPointer(); |
| 2268 | if (back->IsUndefined()) break; |
| 2269 | Map* parent = Map::cast(back); |
| 2270 | if (parent->NumberOfOwnDescriptors() <= descriptor) break; |
| 2271 | result = parent; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2272 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2273 | return result; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2274 | } |
| 2275 | |
| 2276 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2277 | void Map::UpdateFieldType(int descriptor, Handle<Name> name, |
| 2278 | Handle<HeapType> new_type) { |
| 2279 | DisallowHeapAllocation no_allocation; |
| 2280 | PropertyDetails details = instance_descriptors()->GetDetails(descriptor); |
| 2281 | if (details.type() != FIELD) return; |
| 2282 | if (HasTransitionArray()) { |
| 2283 | TransitionArray* transitions = this->transitions(); |
| 2284 | for (int i = 0; i < transitions->number_of_transitions(); ++i) { |
| 2285 | transitions->GetTarget(i)->UpdateFieldType(descriptor, name, new_type); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 2286 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2287 | } |
| 2288 | // Skip if already updated the shared descriptor. |
| 2289 | if (instance_descriptors()->GetFieldType(descriptor) == *new_type) return; |
| 2290 | FieldDescriptor d(name, instance_descriptors()->GetFieldIndex(descriptor), |
| 2291 | new_type, details.attributes(), details.representation()); |
| 2292 | instance_descriptors()->Replace(descriptor, &d); |
| 2293 | } |
| 2294 | |
| 2295 | |
| 2296 | // static |
| 2297 | Handle<HeapType> Map::GeneralizeFieldType(Handle<HeapType> type1, |
| 2298 | Handle<HeapType> type2, |
| 2299 | Isolate* isolate) { |
| 2300 | static const int kMaxClassesPerFieldType = 5; |
| 2301 | if (type1->NowIs(type2)) return type2; |
| 2302 | if (type2->NowIs(type1)) return type1; |
| 2303 | if (type1->NowStable() && type2->NowStable()) { |
| 2304 | Handle<HeapType> type = HeapType::Union(type1, type2, isolate); |
| 2305 | if (type->NumClasses() <= kMaxClassesPerFieldType) { |
| 2306 | DCHECK(type->NowStable()); |
| 2307 | DCHECK(type1->NowIs(type)); |
| 2308 | DCHECK(type2->NowIs(type)); |
| 2309 | return type; |
| 2310 | } |
| 2311 | } |
| 2312 | return HeapType::Any(isolate); |
| 2313 | } |
| 2314 | |
| 2315 | |
| 2316 | // static |
| 2317 | void Map::GeneralizeFieldType(Handle<Map> map, |
| 2318 | int modify_index, |
| 2319 | Handle<HeapType> new_field_type) { |
| 2320 | Isolate* isolate = map->GetIsolate(); |
| 2321 | |
| 2322 | // Check if we actually need to generalize the field type at all. |
| 2323 | Handle<HeapType> old_field_type( |
| 2324 | map->instance_descriptors()->GetFieldType(modify_index), isolate); |
| 2325 | if (new_field_type->NowIs(old_field_type)) { |
| 2326 | DCHECK(Map::GeneralizeFieldType(old_field_type, |
| 2327 | new_field_type, |
| 2328 | isolate)->NowIs(old_field_type)); |
| 2329 | return; |
| 2330 | } |
| 2331 | |
| 2332 | // Determine the field owner. |
| 2333 | Handle<Map> field_owner(map->FindFieldOwner(modify_index), isolate); |
| 2334 | Handle<DescriptorArray> descriptors( |
| 2335 | field_owner->instance_descriptors(), isolate); |
| 2336 | DCHECK_EQ(*old_field_type, descriptors->GetFieldType(modify_index)); |
| 2337 | |
| 2338 | // Determine the generalized new field type. |
| 2339 | new_field_type = Map::GeneralizeFieldType( |
| 2340 | old_field_type, new_field_type, isolate); |
| 2341 | |
| 2342 | PropertyDetails details = descriptors->GetDetails(modify_index); |
| 2343 | Handle<Name> name(descriptors->GetKey(modify_index)); |
| 2344 | field_owner->UpdateFieldType(modify_index, name, new_field_type); |
| 2345 | field_owner->dependent_code()->DeoptimizeDependentCodeGroup( |
| 2346 | isolate, DependentCode::kFieldTypeGroup); |
| 2347 | |
| 2348 | if (FLAG_trace_generalization) { |
| 2349 | map->PrintGeneralization( |
| 2350 | stdout, "field type generalization", |
| 2351 | modify_index, map->NumberOfOwnDescriptors(), |
| 2352 | map->NumberOfOwnDescriptors(), false, |
| 2353 | details.representation(), details.representation(), |
| 2354 | *old_field_type, *new_field_type); |
| 2355 | } |
| 2356 | } |
| 2357 | |
| 2358 | |
| 2359 | // Generalize the representation of the descriptor at |modify_index|. |
| 2360 | // This method rewrites the transition tree to reflect the new change. To avoid |
| 2361 | // high degrees over polymorphism, and to stabilize quickly, on every rewrite |
| 2362 | // the new type is deduced by merging the current type with any potential new |
| 2363 | // (partial) version of the type in the transition tree. |
| 2364 | // To do this, on each rewrite: |
| 2365 | // - Search the root of the transition tree using FindRootMap. |
| 2366 | // - Find |target_map|, the newest matching version of this map using the keys |
| 2367 | // in the |old_map|'s descriptor array to walk the transition tree. |
| 2368 | // - Merge/generalize the descriptor array of the |old_map| and |target_map|. |
| 2369 | // - Generalize the |modify_index| descriptor using |new_representation| and |
| 2370 | // |new_field_type|. |
| 2371 | // - Walk the tree again starting from the root towards |target_map|. Stop at |
| 2372 | // |split_map|, the first map who's descriptor array does not match the merged |
| 2373 | // descriptor array. |
| 2374 | // - If |target_map| == |split_map|, |target_map| is in the expected state. |
| 2375 | // Return it. |
| 2376 | // - Otherwise, invalidate the outdated transition target from |target_map|, and |
| 2377 | // replace its transition tree with a new branch for the updated descriptors. |
| 2378 | Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map, |
| 2379 | int modify_index, |
| 2380 | Representation new_representation, |
| 2381 | Handle<HeapType> new_field_type, |
| 2382 | StoreMode store_mode) { |
| 2383 | Isolate* isolate = old_map->GetIsolate(); |
| 2384 | |
| 2385 | Handle<DescriptorArray> old_descriptors( |
| 2386 | old_map->instance_descriptors(), isolate); |
| 2387 | int old_nof = old_map->NumberOfOwnDescriptors(); |
| 2388 | PropertyDetails old_details = old_descriptors->GetDetails(modify_index); |
| 2389 | Representation old_representation = old_details.representation(); |
| 2390 | |
| 2391 | // It's fine to transition from None to anything but double without any |
| 2392 | // modification to the object, because the default uninitialized value for |
| 2393 | // representation None can be overwritten by both smi and tagged values. |
| 2394 | // Doubles, however, would require a box allocation. |
| 2395 | if (old_representation.IsNone() && |
| 2396 | !new_representation.IsNone() && |
| 2397 | !new_representation.IsDouble()) { |
| 2398 | DCHECK(old_details.type() == FIELD); |
| 2399 | DCHECK(old_descriptors->GetFieldType(modify_index)->NowIs( |
| 2400 | HeapType::None())); |
| 2401 | if (FLAG_trace_generalization) { |
| 2402 | old_map->PrintGeneralization( |
| 2403 | stdout, "uninitialized field", |
| 2404 | modify_index, old_map->NumberOfOwnDescriptors(), |
| 2405 | old_map->NumberOfOwnDescriptors(), false, |
| 2406 | old_representation, new_representation, |
| 2407 | old_descriptors->GetFieldType(modify_index), *new_field_type); |
| 2408 | } |
| 2409 | old_descriptors->SetRepresentation(modify_index, new_representation); |
| 2410 | old_descriptors->SetValue(modify_index, *new_field_type); |
| 2411 | return old_map; |
| 2412 | } |
| 2413 | |
| 2414 | // Check the state of the root map. |
| 2415 | Handle<Map> root_map(old_map->FindRootMap(), isolate); |
| 2416 | if (!old_map->EquivalentToForTransition(*root_map)) { |
| 2417 | return CopyGeneralizeAllRepresentations( |
| 2418 | old_map, modify_index, store_mode, "not equivalent"); |
| 2419 | } |
| 2420 | int root_nof = root_map->NumberOfOwnDescriptors(); |
| 2421 | if (modify_index < root_nof) { |
| 2422 | PropertyDetails old_details = old_descriptors->GetDetails(modify_index); |
| 2423 | if ((old_details.type() != FIELD && store_mode == FORCE_FIELD) || |
| 2424 | (old_details.type() == FIELD && |
| 2425 | (!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) || |
| 2426 | !new_representation.fits_into(old_details.representation())))) { |
| 2427 | return CopyGeneralizeAllRepresentations( |
| 2428 | old_map, modify_index, store_mode, "root modification"); |
| 2429 | } |
| 2430 | } |
| 2431 | |
| 2432 | Handle<Map> target_map = root_map; |
| 2433 | for (int i = root_nof; i < old_nof; ++i) { |
| 2434 | int j = target_map->SearchTransition(old_descriptors->GetKey(i)); |
| 2435 | if (j == TransitionArray::kNotFound) break; |
| 2436 | Handle<Map> tmp_map(target_map->GetTransition(j), isolate); |
| 2437 | Handle<DescriptorArray> tmp_descriptors = handle( |
| 2438 | tmp_map->instance_descriptors(), isolate); |
| 2439 | |
| 2440 | // Check if target map is incompatible. |
| 2441 | PropertyDetails old_details = old_descriptors->GetDetails(i); |
| 2442 | PropertyDetails tmp_details = tmp_descriptors->GetDetails(i); |
| 2443 | PropertyType old_type = old_details.type(); |
| 2444 | PropertyType tmp_type = tmp_details.type(); |
| 2445 | if (tmp_details.attributes() != old_details.attributes() || |
| 2446 | ((tmp_type == CALLBACKS || old_type == CALLBACKS) && |
| 2447 | (tmp_type != old_type || |
| 2448 | tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i)))) { |
| 2449 | return CopyGeneralizeAllRepresentations( |
| 2450 | old_map, modify_index, store_mode, "incompatible"); |
| 2451 | } |
| 2452 | Representation old_representation = old_details.representation(); |
| 2453 | Representation tmp_representation = tmp_details.representation(); |
| 2454 | if (!old_representation.fits_into(tmp_representation) || |
| 2455 | (!new_representation.fits_into(tmp_representation) && |
| 2456 | modify_index == i)) { |
Ben Murdoch | 7d3e7fc | 2011-07-12 16:37:06 +0100 | [diff] [blame] | 2457 | break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2458 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2459 | if (tmp_type == FIELD) { |
| 2460 | // Generalize the field type as necessary. |
| 2461 | Handle<HeapType> old_field_type = (old_type == FIELD) |
| 2462 | ? handle(old_descriptors->GetFieldType(i), isolate) |
| 2463 | : old_descriptors->GetValue(i)->OptimalType( |
| 2464 | isolate, tmp_representation); |
| 2465 | if (modify_index == i) { |
| 2466 | old_field_type = GeneralizeFieldType( |
| 2467 | new_field_type, old_field_type, isolate); |
| 2468 | } |
| 2469 | GeneralizeFieldType(tmp_map, i, old_field_type); |
| 2470 | } else if (tmp_type == CONSTANT) { |
| 2471 | if (old_type != CONSTANT || |
| 2472 | old_descriptors->GetConstant(i) != tmp_descriptors->GetConstant(i)) { |
| 2473 | break; |
| 2474 | } |
| 2475 | } else { |
| 2476 | DCHECK_EQ(tmp_type, old_type); |
| 2477 | DCHECK_EQ(tmp_descriptors->GetValue(i), old_descriptors->GetValue(i)); |
| 2478 | } |
| 2479 | target_map = tmp_map; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2480 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2481 | |
| 2482 | // Directly change the map if the target map is more general. |
| 2483 | Handle<DescriptorArray> target_descriptors( |
| 2484 | target_map->instance_descriptors(), isolate); |
| 2485 | int target_nof = target_map->NumberOfOwnDescriptors(); |
| 2486 | if (target_nof == old_nof && |
| 2487 | (store_mode != FORCE_FIELD || |
| 2488 | target_descriptors->GetDetails(modify_index).type() == FIELD)) { |
| 2489 | DCHECK(modify_index < target_nof); |
| 2490 | DCHECK(new_representation.fits_into( |
| 2491 | target_descriptors->GetDetails(modify_index).representation())); |
| 2492 | DCHECK(target_descriptors->GetDetails(modify_index).type() != FIELD || |
| 2493 | new_field_type->NowIs( |
| 2494 | target_descriptors->GetFieldType(modify_index))); |
| 2495 | return target_map; |
| 2496 | } |
| 2497 | |
| 2498 | // Find the last compatible target map in the transition tree. |
| 2499 | for (int i = target_nof; i < old_nof; ++i) { |
| 2500 | int j = target_map->SearchTransition(old_descriptors->GetKey(i)); |
| 2501 | if (j == TransitionArray::kNotFound) break; |
| 2502 | Handle<Map> tmp_map(target_map->GetTransition(j), isolate); |
| 2503 | Handle<DescriptorArray> tmp_descriptors( |
| 2504 | tmp_map->instance_descriptors(), isolate); |
| 2505 | |
| 2506 | // Check if target map is compatible. |
| 2507 | PropertyDetails old_details = old_descriptors->GetDetails(i); |
| 2508 | PropertyDetails tmp_details = tmp_descriptors->GetDetails(i); |
| 2509 | if (tmp_details.attributes() != old_details.attributes() || |
| 2510 | ((tmp_details.type() == CALLBACKS || old_details.type() == CALLBACKS) && |
| 2511 | (tmp_details.type() != old_details.type() || |
| 2512 | tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i)))) { |
| 2513 | return CopyGeneralizeAllRepresentations( |
| 2514 | old_map, modify_index, store_mode, "incompatible"); |
| 2515 | } |
| 2516 | target_map = tmp_map; |
| 2517 | } |
| 2518 | target_nof = target_map->NumberOfOwnDescriptors(); |
| 2519 | target_descriptors = handle(target_map->instance_descriptors(), isolate); |
| 2520 | |
| 2521 | // Allocate a new descriptor array large enough to hold the required |
| 2522 | // descriptors, with minimally the exact same size as the old descriptor |
| 2523 | // array. |
| 2524 | int new_slack = Max( |
| 2525 | old_nof, old_descriptors->number_of_descriptors()) - old_nof; |
| 2526 | Handle<DescriptorArray> new_descriptors = DescriptorArray::Allocate( |
| 2527 | isolate, old_nof, new_slack); |
| 2528 | DCHECK(new_descriptors->length() > target_descriptors->length() || |
| 2529 | new_descriptors->NumberOfSlackDescriptors() > 0 || |
| 2530 | new_descriptors->number_of_descriptors() == |
| 2531 | old_descriptors->number_of_descriptors()); |
| 2532 | DCHECK(new_descriptors->number_of_descriptors() == old_nof); |
| 2533 | |
| 2534 | // 0 -> |root_nof| |
| 2535 | int current_offset = 0; |
| 2536 | for (int i = 0; i < root_nof; ++i) { |
| 2537 | PropertyDetails old_details = old_descriptors->GetDetails(i); |
| 2538 | if (old_details.type() == FIELD) current_offset++; |
| 2539 | Descriptor d(handle(old_descriptors->GetKey(i), isolate), |
| 2540 | handle(old_descriptors->GetValue(i), isolate), |
| 2541 | old_details); |
| 2542 | new_descriptors->Set(i, &d); |
| 2543 | } |
| 2544 | |
| 2545 | // |root_nof| -> |target_nof| |
| 2546 | for (int i = root_nof; i < target_nof; ++i) { |
| 2547 | Handle<Name> target_key(target_descriptors->GetKey(i), isolate); |
| 2548 | PropertyDetails old_details = old_descriptors->GetDetails(i); |
| 2549 | PropertyDetails target_details = target_descriptors->GetDetails(i); |
| 2550 | target_details = target_details.CopyWithRepresentation( |
| 2551 | old_details.representation().generalize( |
| 2552 | target_details.representation())); |
| 2553 | if (modify_index == i) { |
| 2554 | target_details = target_details.CopyWithRepresentation( |
| 2555 | new_representation.generalize(target_details.representation())); |
| 2556 | } |
| 2557 | DCHECK_EQ(old_details.attributes(), target_details.attributes()); |
| 2558 | if (old_details.type() == FIELD || |
| 2559 | target_details.type() == FIELD || |
| 2560 | (modify_index == i && store_mode == FORCE_FIELD) || |
| 2561 | (target_descriptors->GetValue(i) != old_descriptors->GetValue(i))) { |
| 2562 | Handle<HeapType> old_field_type = (old_details.type() == FIELD) |
| 2563 | ? handle(old_descriptors->GetFieldType(i), isolate) |
| 2564 | : old_descriptors->GetValue(i)->OptimalType( |
| 2565 | isolate, target_details.representation()); |
| 2566 | Handle<HeapType> target_field_type = (target_details.type() == FIELD) |
| 2567 | ? handle(target_descriptors->GetFieldType(i), isolate) |
| 2568 | : target_descriptors->GetValue(i)->OptimalType( |
| 2569 | isolate, target_details.representation()); |
| 2570 | target_field_type = GeneralizeFieldType( |
| 2571 | target_field_type, old_field_type, isolate); |
| 2572 | if (modify_index == i) { |
| 2573 | target_field_type = GeneralizeFieldType( |
| 2574 | target_field_type, new_field_type, isolate); |
| 2575 | } |
| 2576 | FieldDescriptor d(target_key, |
| 2577 | current_offset++, |
| 2578 | target_field_type, |
| 2579 | target_details.attributes(), |
| 2580 | target_details.representation()); |
| 2581 | new_descriptors->Set(i, &d); |
| 2582 | } else { |
| 2583 | DCHECK_NE(FIELD, target_details.type()); |
| 2584 | Descriptor d(target_key, |
| 2585 | handle(target_descriptors->GetValue(i), isolate), |
| 2586 | target_details); |
| 2587 | new_descriptors->Set(i, &d); |
| 2588 | } |
| 2589 | } |
| 2590 | |
| 2591 | // |target_nof| -> |old_nof| |
| 2592 | for (int i = target_nof; i < old_nof; ++i) { |
| 2593 | PropertyDetails old_details = old_descriptors->GetDetails(i); |
| 2594 | Handle<Name> old_key(old_descriptors->GetKey(i), isolate); |
| 2595 | if (modify_index == i) { |
| 2596 | old_details = old_details.CopyWithRepresentation( |
| 2597 | new_representation.generalize(old_details.representation())); |
| 2598 | } |
| 2599 | if (old_details.type() == FIELD) { |
| 2600 | Handle<HeapType> old_field_type( |
| 2601 | old_descriptors->GetFieldType(i), isolate); |
| 2602 | if (modify_index == i) { |
| 2603 | old_field_type = GeneralizeFieldType( |
| 2604 | old_field_type, new_field_type, isolate); |
| 2605 | } |
| 2606 | FieldDescriptor d(old_key, |
| 2607 | current_offset++, |
| 2608 | old_field_type, |
| 2609 | old_details.attributes(), |
| 2610 | old_details.representation()); |
| 2611 | new_descriptors->Set(i, &d); |
| 2612 | } else { |
| 2613 | DCHECK(old_details.type() == CONSTANT || old_details.type() == CALLBACKS); |
| 2614 | if (modify_index == i && store_mode == FORCE_FIELD) { |
| 2615 | FieldDescriptor d(old_key, |
| 2616 | current_offset++, |
| 2617 | GeneralizeFieldType( |
| 2618 | old_descriptors->GetValue(i)->OptimalType( |
| 2619 | isolate, old_details.representation()), |
| 2620 | new_field_type, isolate), |
| 2621 | old_details.attributes(), |
| 2622 | old_details.representation()); |
| 2623 | new_descriptors->Set(i, &d); |
| 2624 | } else { |
| 2625 | DCHECK_NE(FIELD, old_details.type()); |
| 2626 | Descriptor d(old_key, |
| 2627 | handle(old_descriptors->GetValue(i), isolate), |
| 2628 | old_details); |
| 2629 | new_descriptors->Set(i, &d); |
| 2630 | } |
| 2631 | } |
| 2632 | } |
| 2633 | |
| 2634 | new_descriptors->Sort(); |
| 2635 | |
| 2636 | DCHECK(store_mode != FORCE_FIELD || |
| 2637 | new_descriptors->GetDetails(modify_index).type() == FIELD); |
| 2638 | |
| 2639 | Handle<Map> split_map(root_map->FindLastMatchMap( |
| 2640 | root_nof, old_nof, *new_descriptors), isolate); |
| 2641 | int split_nof = split_map->NumberOfOwnDescriptors(); |
| 2642 | DCHECK_NE(old_nof, split_nof); |
| 2643 | |
| 2644 | split_map->DeprecateTarget( |
| 2645 | old_descriptors->GetKey(split_nof), *new_descriptors); |
| 2646 | |
| 2647 | if (FLAG_trace_generalization) { |
| 2648 | PropertyDetails old_details = old_descriptors->GetDetails(modify_index); |
| 2649 | PropertyDetails new_details = new_descriptors->GetDetails(modify_index); |
| 2650 | Handle<HeapType> old_field_type = (old_details.type() == FIELD) |
| 2651 | ? handle(old_descriptors->GetFieldType(modify_index), isolate) |
| 2652 | : HeapType::Constant(handle(old_descriptors->GetValue(modify_index), |
| 2653 | isolate), isolate); |
| 2654 | Handle<HeapType> new_field_type = (new_details.type() == FIELD) |
| 2655 | ? handle(new_descriptors->GetFieldType(modify_index), isolate) |
| 2656 | : HeapType::Constant(handle(new_descriptors->GetValue(modify_index), |
| 2657 | isolate), isolate); |
| 2658 | old_map->PrintGeneralization( |
| 2659 | stdout, "", modify_index, split_nof, old_nof, |
| 2660 | old_details.type() == CONSTANT && store_mode == FORCE_FIELD, |
| 2661 | old_details.representation(), new_details.representation(), |
| 2662 | *old_field_type, *new_field_type); |
| 2663 | } |
| 2664 | |
| 2665 | // Add missing transitions. |
| 2666 | Handle<Map> new_map = split_map; |
| 2667 | for (int i = split_nof; i < old_nof; ++i) { |
| 2668 | new_map = CopyInstallDescriptors(new_map, i, new_descriptors); |
| 2669 | } |
| 2670 | new_map->set_owns_descriptors(true); |
| 2671 | return new_map; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 2672 | } |
| 2673 | |
| 2674 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2675 | // Generalize the representation of all FIELD descriptors. |
| 2676 | Handle<Map> Map::GeneralizeAllFieldRepresentations( |
| 2677 | Handle<Map> map) { |
| 2678 | Handle<DescriptorArray> descriptors(map->instance_descriptors()); |
| 2679 | for (int i = 0; i < map->NumberOfOwnDescriptors(); ++i) { |
| 2680 | if (descriptors->GetDetails(i).type() == FIELD) { |
| 2681 | map = GeneralizeRepresentation(map, i, Representation::Tagged(), |
| 2682 | HeapType::Any(map->GetIsolate()), |
| 2683 | FORCE_FIELD); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 2684 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 2685 | } |
| 2686 | return map; |
| 2687 | } |
| 2688 | |
| 2689 | |
| 2690 | // static |
| 2691 | MaybeHandle<Map> Map::TryUpdate(Handle<Map> map) { |
| 2692 | Handle<Map> proto_map(map); |
| 2693 | while (proto_map->prototype()->IsJSObject()) { |
| 2694 | Handle<JSObject> holder(JSObject::cast(proto_map->prototype())); |
| 2695 | proto_map = Handle<Map>(holder->map()); |
| 2696 | if (proto_map->is_deprecated() && JSObject::TryMigrateInstance(holder)) { |
| 2697 | proto_map = Handle<Map>(holder->map()); |
| 2698 | } |
| 2699 | } |
| 2700 | return TryUpdateInternal(map); |
| 2701 | } |
| 2702 | |
| 2703 | |
| 2704 | // static |
| 2705 | Handle<Map> Map::Update(Handle<Map> map) { |
| 2706 | if (!map->is_deprecated()) return map; |
| 2707 | return GeneralizeRepresentation(map, 0, Representation::None(), |
| 2708 | HeapType::None(map->GetIsolate()), |
| 2709 | ALLOW_AS_CONSTANT); |
| 2710 | } |
| 2711 | |
| 2712 | |
| 2713 | // static |
| 2714 | MaybeHandle<Map> Map::TryUpdateInternal(Handle<Map> old_map) { |
| 2715 | DisallowHeapAllocation no_allocation; |
| 2716 | DisallowDeoptimization no_deoptimization(old_map->GetIsolate()); |
| 2717 | |
| 2718 | if (!old_map->is_deprecated()) return old_map; |
| 2719 | |
| 2720 | // Check the state of the root map. |
| 2721 | Map* root_map = old_map->FindRootMap(); |
| 2722 | if (!old_map->EquivalentToForTransition(root_map)) return MaybeHandle<Map>(); |
| 2723 | int root_nof = root_map->NumberOfOwnDescriptors(); |
| 2724 | |
| 2725 | int old_nof = old_map->NumberOfOwnDescriptors(); |
| 2726 | DescriptorArray* old_descriptors = old_map->instance_descriptors(); |
| 2727 | |
| 2728 | Map* new_map = root_map; |
| 2729 | for (int i = root_nof; i < old_nof; ++i) { |
| 2730 | int j = new_map->SearchTransition(old_descriptors->GetKey(i)); |
| 2731 | if (j == TransitionArray::kNotFound) return MaybeHandle<Map>(); |
| 2732 | new_map = new_map->GetTransition(j); |
| 2733 | DescriptorArray* new_descriptors = new_map->instance_descriptors(); |
| 2734 | |
| 2735 | PropertyDetails new_details = new_descriptors->GetDetails(i); |
| 2736 | PropertyDetails old_details = old_descriptors->GetDetails(i); |
| 2737 | if (old_details.attributes() != new_details.attributes() || |
| 2738 | !old_details.representation().fits_into(new_details.representation())) { |
| 2739 | return MaybeHandle<Map>(); |
| 2740 | } |
| 2741 | PropertyType new_type = new_details.type(); |
| 2742 | PropertyType old_type = old_details.type(); |
| 2743 | Object* new_value = new_descriptors->GetValue(i); |
| 2744 | Object* old_value = old_descriptors->GetValue(i); |
| 2745 | switch (new_type) { |
| 2746 | case FIELD: |
| 2747 | if ((old_type == FIELD && |
| 2748 | !HeapType::cast(old_value)->NowIs(HeapType::cast(new_value))) || |
| 2749 | (old_type == CONSTANT && |
| 2750 | !HeapType::cast(new_value)->NowContains(old_value)) || |
| 2751 | (old_type == CALLBACKS && |
| 2752 | !HeapType::Any()->Is(HeapType::cast(new_value)))) { |
| 2753 | return MaybeHandle<Map>(); |
| 2754 | } |
| 2755 | break; |
| 2756 | |
| 2757 | case CONSTANT: |
| 2758 | case CALLBACKS: |
| 2759 | if (old_type != new_type || old_value != new_value) { |
| 2760 | return MaybeHandle<Map>(); |
| 2761 | } |
| 2762 | break; |
| 2763 | |
| 2764 | case NORMAL: |
| 2765 | UNREACHABLE(); |
| 2766 | } |
| 2767 | } |
| 2768 | if (new_map->NumberOfOwnDescriptors() != old_nof) return MaybeHandle<Map>(); |
| 2769 | return handle(new_map); |
| 2770 | } |
| 2771 | |
| 2772 | |
| 2773 | MaybeHandle<Object> JSObject::SetPropertyWithInterceptor(LookupIterator* it, |
| 2774 | Handle<Object> value) { |
| 2775 | // TODO(rossberg): Support symbols in the API. |
| 2776 | if (it->name()->IsSymbol()) return value; |
| 2777 | |
| 2778 | Handle<String> name_string = Handle<String>::cast(it->name()); |
| 2779 | Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| 2780 | Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor()); |
| 2781 | if (interceptor->setter()->IsUndefined()) return MaybeHandle<Object>(); |
| 2782 | |
| 2783 | LOG(it->isolate(), |
| 2784 | ApiNamedPropertyAccess("interceptor-named-set", *holder, *name_string)); |
| 2785 | PropertyCallbackArguments args(it->isolate(), interceptor->data(), *holder, |
| 2786 | *holder); |
| 2787 | v8::NamedPropertySetterCallback setter = |
| 2788 | v8::ToCData<v8::NamedPropertySetterCallback>(interceptor->setter()); |
| 2789 | v8::Handle<v8::Value> result = args.Call( |
| 2790 | setter, v8::Utils::ToLocal(name_string), v8::Utils::ToLocal(value)); |
| 2791 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object); |
| 2792 | if (!result.IsEmpty()) return value; |
| 2793 | |
| 2794 | return MaybeHandle<Object>(); |
| 2795 | } |
| 2796 | |
| 2797 | |
| 2798 | MaybeHandle<Object> Object::SetProperty(Handle<Object> object, |
| 2799 | Handle<Name> name, Handle<Object> value, |
| 2800 | StrictMode strict_mode, |
| 2801 | StoreFromKeyed store_mode) { |
| 2802 | LookupIterator it(object, name); |
| 2803 | return SetProperty(&it, value, strict_mode, store_mode); |
| 2804 | } |
| 2805 | |
| 2806 | |
| 2807 | MaybeHandle<Object> Object::SetProperty(LookupIterator* it, |
| 2808 | Handle<Object> value, |
| 2809 | StrictMode strict_mode, |
| 2810 | StoreFromKeyed store_mode) { |
| 2811 | // Make sure that the top context does not change when doing callbacks or |
| 2812 | // interceptor calls. |
| 2813 | AssertNoContextChange ncc(it->isolate()); |
| 2814 | |
| 2815 | bool done = false; |
| 2816 | for (; it->IsFound(); it->Next()) { |
| 2817 | switch (it->state()) { |
| 2818 | case LookupIterator::NOT_FOUND: |
| 2819 | UNREACHABLE(); |
| 2820 | |
| 2821 | case LookupIterator::ACCESS_CHECK: |
| 2822 | // TODO(verwaest): Remove the distinction. This is mostly bogus since we |
| 2823 | // don't know whether we'll want to fetch attributes or call a setter |
| 2824 | // until we find the property. |
| 2825 | if (it->HasAccess(v8::ACCESS_SET)) break; |
| 2826 | return JSObject::SetPropertyWithFailedAccessCheck(it, value, |
| 2827 | strict_mode); |
| 2828 | |
| 2829 | case LookupIterator::JSPROXY: |
| 2830 | if (it->HolderIsReceiverOrHiddenPrototype()) { |
| 2831 | return JSProxy::SetPropertyWithHandler(it->GetHolder<JSProxy>(), |
| 2832 | it->GetReceiver(), it->name(), |
| 2833 | value, strict_mode); |
| 2834 | } else { |
| 2835 | // TODO(verwaest): Use the MaybeHandle to indicate result. |
| 2836 | bool has_result = false; |
| 2837 | MaybeHandle<Object> maybe_result = |
| 2838 | JSProxy::SetPropertyViaPrototypesWithHandler( |
| 2839 | it->GetHolder<JSProxy>(), it->GetReceiver(), it->name(), |
| 2840 | value, strict_mode, &has_result); |
| 2841 | if (has_result) return maybe_result; |
| 2842 | done = true; |
| 2843 | } |
| 2844 | break; |
| 2845 | |
| 2846 | case LookupIterator::INTERCEPTOR: |
| 2847 | if (it->HolderIsReceiverOrHiddenPrototype()) { |
| 2848 | MaybeHandle<Object> maybe_result = |
| 2849 | JSObject::SetPropertyWithInterceptor(it, value); |
| 2850 | if (!maybe_result.is_null()) return maybe_result; |
| 2851 | if (it->isolate()->has_pending_exception()) return maybe_result; |
| 2852 | } else { |
| 2853 | Maybe<PropertyAttributes> maybe_attributes = |
| 2854 | JSObject::GetPropertyAttributesWithInterceptor( |
| 2855 | it->GetHolder<JSObject>(), it->GetReceiver(), it->name()); |
| 2856 | if (!maybe_attributes.has_value) return MaybeHandle<Object>(); |
| 2857 | done = maybe_attributes.value != ABSENT; |
| 2858 | if (done && (maybe_attributes.value & READ_ONLY) != 0) { |
| 2859 | return WriteToReadOnlyProperty(it, value, strict_mode); |
| 2860 | } |
| 2861 | } |
| 2862 | break; |
| 2863 | |
| 2864 | case LookupIterator::ACCESSOR: |
| 2865 | if (it->property_details().IsReadOnly()) { |
| 2866 | return WriteToReadOnlyProperty(it, value, strict_mode); |
| 2867 | } |
| 2868 | if (it->HolderIsReceiverOrHiddenPrototype() || |
| 2869 | !it->GetAccessors()->IsDeclaredAccessorInfo()) { |
| 2870 | return SetPropertyWithAccessor(it->GetReceiver(), it->name(), value, |
| 2871 | it->GetHolder<JSObject>(), |
| 2872 | it->GetAccessors(), strict_mode); |
| 2873 | } |
| 2874 | done = true; |
| 2875 | break; |
| 2876 | |
| 2877 | case LookupIterator::DATA: |
| 2878 | if (it->property_details().IsReadOnly()) { |
| 2879 | return WriteToReadOnlyProperty(it, value, strict_mode); |
| 2880 | } |
| 2881 | if (it->HolderIsReceiverOrHiddenPrototype()) { |
| 2882 | return SetDataProperty(it, value); |
| 2883 | } |
| 2884 | done = true; |
| 2885 | break; |
| 2886 | |
| 2887 | case LookupIterator::TRANSITION: |
| 2888 | done = true; |
| 2889 | break; |
| 2890 | } |
| 2891 | |
| 2892 | if (done) break; |
| 2893 | } |
| 2894 | |
| 2895 | // If the receiver is the JSGlobalObject, the store was contextual. In case |
| 2896 | // the property did not exist yet on the global object itself, we have to |
| 2897 | // throw a reference error in strict mode. |
| 2898 | if (it->GetReceiver()->IsJSGlobalObject() && strict_mode == STRICT) { |
| 2899 | Handle<Object> args[1] = {it->name()}; |
| 2900 | THROW_NEW_ERROR(it->isolate(), |
| 2901 | NewReferenceError("not_defined", HandleVector(args, 1)), |
| 2902 | Object); |
| 2903 | } |
| 2904 | |
| 2905 | return AddDataProperty(it, value, NONE, strict_mode, store_mode); |
| 2906 | } |
| 2907 | |
| 2908 | |
| 2909 | MaybeHandle<Object> Object::WriteToReadOnlyProperty(LookupIterator* it, |
| 2910 | Handle<Object> value, |
| 2911 | StrictMode strict_mode) { |
| 2912 | if (strict_mode != STRICT) return value; |
| 2913 | |
| 2914 | Handle<Object> args[] = {it->name(), it->GetReceiver()}; |
| 2915 | THROW_NEW_ERROR(it->isolate(), |
| 2916 | NewTypeError("strict_read_only_property", |
| 2917 | HandleVector(args, arraysize(args))), |
| 2918 | Object); |
| 2919 | } |
| 2920 | |
| 2921 | |
| 2922 | Handle<Object> Object::SetDataProperty(LookupIterator* it, |
| 2923 | Handle<Object> value) { |
| 2924 | // Proxies are handled on the WithHandler path. Other non-JSObjects cannot |
| 2925 | // have own properties. |
| 2926 | Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver()); |
| 2927 | |
| 2928 | // Store on the holder which may be hidden behind the receiver. |
| 2929 | DCHECK(it->HolderIsReceiverOrHiddenPrototype()); |
| 2930 | |
| 2931 | // Old value for the observation change record. |
| 2932 | // Fetch before transforming the object since the encoding may become |
| 2933 | // incompatible with what's cached in |it|. |
| 2934 | bool is_observed = |
| 2935 | receiver->map()->is_observed() && |
| 2936 | !it->name().is_identical_to(it->factory()->hidden_string()); |
| 2937 | MaybeHandle<Object> maybe_old; |
| 2938 | if (is_observed) maybe_old = it->GetDataValue(); |
| 2939 | |
| 2940 | // Possibly migrate to the most up-to-date map that will be able to store |
| 2941 | // |value| under it->name(). |
| 2942 | it->PrepareForDataProperty(value); |
| 2943 | |
| 2944 | // Write the property value. |
| 2945 | it->WriteDataValue(value); |
| 2946 | |
| 2947 | // Send the change record if there are observers. |
| 2948 | if (is_observed && !value->SameValue(*maybe_old.ToHandleChecked())) { |
| 2949 | JSObject::EnqueueChangeRecord(receiver, "update", it->name(), |
| 2950 | maybe_old.ToHandleChecked()); |
| 2951 | } |
| 2952 | |
| 2953 | return value; |
| 2954 | } |
| 2955 | |
| 2956 | |
| 2957 | MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it, |
| 2958 | Handle<Object> value, |
| 2959 | PropertyAttributes attributes, |
| 2960 | StrictMode strict_mode, |
| 2961 | StoreFromKeyed store_mode) { |
| 2962 | DCHECK(!it->GetReceiver()->IsJSProxy()); |
| 2963 | if (!it->GetReceiver()->IsJSObject()) { |
| 2964 | // TODO(verwaest): Throw a TypeError with a more specific message. |
| 2965 | return WriteToReadOnlyProperty(it, value, strict_mode); |
| 2966 | } |
| 2967 | |
| 2968 | Handle<JSObject> receiver = it->GetStoreTarget(); |
| 2969 | |
| 2970 | // If the receiver is a JSGlobalProxy, store on the prototype (JSGlobalObject) |
| 2971 | // instead. If the prototype is Null, the proxy is detached. |
| 2972 | if (receiver->IsJSGlobalProxy()) return value; |
| 2973 | |
| 2974 | // Possibly migrate to the most up-to-date map that will be able to store |
| 2975 | // |value| under it->name() with |attributes|. |
| 2976 | it->PrepareTransitionToDataProperty(value, attributes, store_mode); |
| 2977 | if (it->state() != LookupIterator::TRANSITION) { |
| 2978 | if (strict_mode == SLOPPY) return value; |
| 2979 | |
| 2980 | Handle<Object> args[1] = {it->name()}; |
| 2981 | THROW_NEW_ERROR(it->isolate(), |
| 2982 | NewTypeError("object_not_extensible", |
| 2983 | HandleVector(args, arraysize(args))), |
| 2984 | Object); |
| 2985 | } |
| 2986 | it->ApplyTransitionToDataProperty(); |
| 2987 | |
| 2988 | // TODO(verwaest): Encapsulate dictionary handling better. |
| 2989 | if (receiver->map()->is_dictionary_map()) { |
| 2990 | // TODO(verwaest): Probably should ensure this is done beforehand. |
| 2991 | it->InternalizeName(); |
| 2992 | JSObject::AddSlowProperty(receiver, it->name(), value, attributes); |
| 2993 | } else { |
| 2994 | // Write the property value. |
| 2995 | it->WriteDataValue(value); |
| 2996 | } |
| 2997 | |
| 2998 | // Send the change record if there are observers. |
| 2999 | if (receiver->map()->is_observed() && |
| 3000 | !it->name().is_identical_to(it->factory()->hidden_string())) { |
| 3001 | JSObject::EnqueueChangeRecord(receiver, "add", it->name(), |
| 3002 | it->factory()->the_hole_value()); |
| 3003 | } |
| 3004 | |
| 3005 | return value; |
| 3006 | } |
| 3007 | |
| 3008 | |
| 3009 | MaybeHandle<Object> JSObject::SetElementWithCallbackSetterInPrototypes( |
| 3010 | Handle<JSObject> object, |
| 3011 | uint32_t index, |
| 3012 | Handle<Object> value, |
| 3013 | bool* found, |
| 3014 | StrictMode strict_mode) { |
| 3015 | Isolate *isolate = object->GetIsolate(); |
| 3016 | for (PrototypeIterator iter(isolate, object); !iter.IsAtEnd(); |
| 3017 | iter.Advance()) { |
| 3018 | if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { |
| 3019 | return JSProxy::SetPropertyViaPrototypesWithHandler( |
| 3020 | Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), object, |
| 3021 | isolate->factory()->Uint32ToString(index), // name |
| 3022 | value, strict_mode, found); |
| 3023 | } |
| 3024 | Handle<JSObject> js_proto = |
| 3025 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); |
| 3026 | if (!js_proto->HasDictionaryElements()) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3027 | continue; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3028 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3029 | Handle<SeededNumberDictionary> dictionary(js_proto->element_dictionary()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3030 | int entry = dictionary->FindEntry(index); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 3031 | if (entry != SeededNumberDictionary::kNotFound) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3032 | PropertyDetails details = dictionary->DetailsAt(entry); |
| 3033 | if (details.type() == CALLBACKS) { |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 3034 | *found = true; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3035 | Handle<Object> structure(dictionary->ValueAt(entry), isolate); |
| 3036 | return SetElementWithCallback(object, structure, index, value, js_proto, |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3037 | strict_mode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3038 | } |
| 3039 | } |
| 3040 | } |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 3041 | *found = false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3042 | return isolate->factory()->the_hole_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3043 | } |
| 3044 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3045 | |
| 3046 | void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) { |
| 3047 | // Only supports adding slack to owned descriptors. |
| 3048 | DCHECK(map->owns_descriptors()); |
| 3049 | |
| 3050 | Handle<DescriptorArray> descriptors(map->instance_descriptors()); |
| 3051 | int old_size = map->NumberOfOwnDescriptors(); |
| 3052 | if (slack <= descriptors->NumberOfSlackDescriptors()) return; |
| 3053 | |
| 3054 | Handle<DescriptorArray> new_descriptors = DescriptorArray::CopyUpTo( |
| 3055 | descriptors, old_size, slack); |
| 3056 | |
| 3057 | if (old_size == 0) { |
| 3058 | map->set_instance_descriptors(*new_descriptors); |
| 3059 | return; |
| 3060 | } |
| 3061 | |
| 3062 | // If the source descriptors had an enum cache we copy it. This ensures |
| 3063 | // that the maps to which we push the new descriptor array back can rely |
| 3064 | // on a cache always being available once it is set. If the map has more |
| 3065 | // enumerated descriptors than available in the original cache, the cache |
| 3066 | // will be lazily replaced by the extended cache when needed. |
| 3067 | if (descriptors->HasEnumCache()) { |
| 3068 | new_descriptors->CopyEnumCacheFrom(*descriptors); |
| 3069 | } |
| 3070 | |
| 3071 | // Replace descriptors by new_descriptors in all maps that share it. |
| 3072 | map->GetHeap()->incremental_marking()->RecordWrites(*descriptors); |
| 3073 | |
| 3074 | Map* walk_map; |
| 3075 | for (Object* current = map->GetBackPointer(); |
| 3076 | !current->IsUndefined(); |
| 3077 | current = walk_map->GetBackPointer()) { |
| 3078 | walk_map = Map::cast(current); |
| 3079 | if (walk_map->instance_descriptors() != *descriptors) break; |
| 3080 | walk_map->set_instance_descriptors(*new_descriptors); |
| 3081 | } |
| 3082 | |
| 3083 | map->set_instance_descriptors(*new_descriptors); |
| 3084 | } |
| 3085 | |
| 3086 | |
| 3087 | template<class T> |
| 3088 | static int AppendUniqueCallbacks(NeanderArray* callbacks, |
| 3089 | Handle<typename T::Array> array, |
| 3090 | int valid_descriptors) { |
| 3091 | int nof_callbacks = callbacks->length(); |
| 3092 | |
| 3093 | Isolate* isolate = array->GetIsolate(); |
| 3094 | // Ensure the keys are unique names before writing them into the |
| 3095 | // instance descriptor. Since it may cause a GC, it has to be done before we |
| 3096 | // temporarily put the heap in an invalid state while appending descriptors. |
| 3097 | for (int i = 0; i < nof_callbacks; ++i) { |
| 3098 | Handle<AccessorInfo> entry(AccessorInfo::cast(callbacks->get(i))); |
| 3099 | if (entry->name()->IsUniqueName()) continue; |
| 3100 | Handle<String> key = |
| 3101 | isolate->factory()->InternalizeString( |
| 3102 | Handle<String>(String::cast(entry->name()))); |
| 3103 | entry->set_name(*key); |
| 3104 | } |
| 3105 | |
| 3106 | // Fill in new callback descriptors. Process the callbacks from |
| 3107 | // back to front so that the last callback with a given name takes |
| 3108 | // precedence over previously added callbacks with that name. |
| 3109 | for (int i = nof_callbacks - 1; i >= 0; i--) { |
| 3110 | Handle<AccessorInfo> entry(AccessorInfo::cast(callbacks->get(i))); |
| 3111 | Handle<Name> key(Name::cast(entry->name())); |
| 3112 | // Check if a descriptor with this name already exists before writing. |
| 3113 | if (!T::Contains(key, entry, valid_descriptors, array)) { |
| 3114 | T::Insert(key, entry, valid_descriptors, array); |
| 3115 | valid_descriptors++; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3116 | } |
| 3117 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3118 | |
| 3119 | return valid_descriptors; |
| 3120 | } |
| 3121 | |
| 3122 | struct DescriptorArrayAppender { |
| 3123 | typedef DescriptorArray Array; |
| 3124 | static bool Contains(Handle<Name> key, |
| 3125 | Handle<AccessorInfo> entry, |
| 3126 | int valid_descriptors, |
| 3127 | Handle<DescriptorArray> array) { |
| 3128 | DisallowHeapAllocation no_gc; |
| 3129 | return array->Search(*key, valid_descriptors) != DescriptorArray::kNotFound; |
| 3130 | } |
| 3131 | static void Insert(Handle<Name> key, |
| 3132 | Handle<AccessorInfo> entry, |
| 3133 | int valid_descriptors, |
| 3134 | Handle<DescriptorArray> array) { |
| 3135 | DisallowHeapAllocation no_gc; |
| 3136 | CallbacksDescriptor desc(key, entry, entry->property_attributes()); |
| 3137 | array->Append(&desc); |
| 3138 | } |
| 3139 | }; |
| 3140 | |
| 3141 | |
| 3142 | struct FixedArrayAppender { |
| 3143 | typedef FixedArray Array; |
| 3144 | static bool Contains(Handle<Name> key, |
| 3145 | Handle<AccessorInfo> entry, |
| 3146 | int valid_descriptors, |
| 3147 | Handle<FixedArray> array) { |
| 3148 | for (int i = 0; i < valid_descriptors; i++) { |
| 3149 | if (*key == AccessorInfo::cast(array->get(i))->name()) return true; |
| 3150 | } |
| 3151 | return false; |
| 3152 | } |
| 3153 | static void Insert(Handle<Name> key, |
| 3154 | Handle<AccessorInfo> entry, |
| 3155 | int valid_descriptors, |
| 3156 | Handle<FixedArray> array) { |
| 3157 | DisallowHeapAllocation no_gc; |
| 3158 | array->set(valid_descriptors, *entry); |
| 3159 | } |
| 3160 | }; |
| 3161 | |
| 3162 | |
| 3163 | void Map::AppendCallbackDescriptors(Handle<Map> map, |
| 3164 | Handle<Object> descriptors) { |
| 3165 | int nof = map->NumberOfOwnDescriptors(); |
| 3166 | Handle<DescriptorArray> array(map->instance_descriptors()); |
| 3167 | NeanderArray callbacks(descriptors); |
| 3168 | DCHECK(array->NumberOfSlackDescriptors() >= callbacks.length()); |
| 3169 | nof = AppendUniqueCallbacks<DescriptorArrayAppender>(&callbacks, array, nof); |
| 3170 | map->SetNumberOfOwnDescriptors(nof); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3171 | } |
| 3172 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3173 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3174 | int AccessorInfo::AppendUnique(Handle<Object> descriptors, |
| 3175 | Handle<FixedArray> array, |
| 3176 | int valid_descriptors) { |
| 3177 | NeanderArray callbacks(descriptors); |
| 3178 | DCHECK(array->length() >= callbacks.length() + valid_descriptors); |
| 3179 | return AppendUniqueCallbacks<FixedArrayAppender>(&callbacks, |
| 3180 | array, |
| 3181 | valid_descriptors); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 3182 | } |
| 3183 | |
| 3184 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3185 | static bool ContainsMap(MapHandleList* maps, Handle<Map> map) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3186 | DCHECK(!map.is_null()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3187 | for (int i = 0; i < maps->length(); ++i) { |
| 3188 | if (!maps->at(i).is_null() && maps->at(i).is_identical_to(map)) return true; |
| 3189 | } |
| 3190 | return false; |
| 3191 | } |
Ben Murdoch | 592a9fc | 2012-03-05 11:04:45 +0000 | [diff] [blame] | 3192 | |
Ben Murdoch | 85b7179 | 2012-04-11 18:30:58 +0100 | [diff] [blame] | 3193 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3194 | template <class T> |
| 3195 | static Handle<T> MaybeNull(T* p) { |
| 3196 | if (p == NULL) return Handle<T>::null(); |
| 3197 | return Handle<T>(p); |
| 3198 | } |
| 3199 | |
| 3200 | |
| 3201 | Handle<Map> Map::FindTransitionedMap(MapHandleList* candidates) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3202 | ElementsKind kind = elements_kind(); |
| 3203 | Handle<Map> transitioned_map = Handle<Map>::null(); |
| 3204 | Handle<Map> current_map(this); |
| 3205 | bool packed = IsFastPackedElementsKind(kind); |
| 3206 | if (IsTransitionableFastElementsKind(kind)) { |
| 3207 | while (CanTransitionToMoreGeneralFastElementsKind(kind, false)) { |
| 3208 | kind = GetNextMoreGeneralFastElementsKind(kind, false); |
| 3209 | Handle<Map> maybe_transitioned_map = |
| 3210 | MaybeNull(current_map->LookupElementsTransitionMap(kind)); |
| 3211 | if (maybe_transitioned_map.is_null()) break; |
| 3212 | if (ContainsMap(candidates, maybe_transitioned_map) && |
| 3213 | (packed || !IsFastPackedElementsKind(kind))) { |
| 3214 | transitioned_map = maybe_transitioned_map; |
| 3215 | if (!IsFastPackedElementsKind(kind)) packed = false; |
| 3216 | } |
| 3217 | current_map = maybe_transitioned_map; |
Ben Murdoch | 85b7179 | 2012-04-11 18:30:58 +0100 | [diff] [blame] | 3218 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3219 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3220 | return transitioned_map; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3221 | } |
Ben Murdoch | 85b7179 | 2012-04-11 18:30:58 +0100 | [diff] [blame] | 3222 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3223 | |
| 3224 | static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) { |
| 3225 | Map* current_map = map; |
| 3226 | int target_kind = |
| 3227 | IsFastElementsKind(to_kind) || IsExternalArrayElementsKind(to_kind) |
| 3228 | ? to_kind |
| 3229 | : TERMINAL_FAST_ELEMENTS_KIND; |
| 3230 | |
| 3231 | // Support for legacy API: SetIndexedPropertiesTo{External,Pixel}Data |
| 3232 | // allows to change elements from arbitrary kind to any ExternalArray |
| 3233 | // elements kind. Satisfy its requirements, checking whether we already |
| 3234 | // have the cached transition. |
| 3235 | if (IsExternalArrayElementsKind(to_kind) && |
| 3236 | !IsFixedTypedArrayElementsKind(map->elements_kind())) { |
| 3237 | if (map->HasElementsTransition()) { |
| 3238 | Map* next_map = map->elements_transition_map(); |
| 3239 | if (next_map->elements_kind() == to_kind) return next_map; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3240 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3241 | return map; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3242 | } |
| 3243 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3244 | ElementsKind kind = map->elements_kind(); |
| 3245 | while (kind != target_kind) { |
| 3246 | kind = GetNextTransitionElementsKind(kind); |
| 3247 | if (!current_map->HasElementsTransition()) return current_map; |
| 3248 | current_map = current_map->elements_transition_map(); |
| 3249 | } |
| 3250 | |
| 3251 | if (to_kind != kind && current_map->HasElementsTransition()) { |
| 3252 | DCHECK(to_kind == DICTIONARY_ELEMENTS); |
| 3253 | Map* next_map = current_map->elements_transition_map(); |
| 3254 | if (next_map->elements_kind() == to_kind) return next_map; |
| 3255 | } |
| 3256 | |
| 3257 | DCHECK(current_map->elements_kind() == target_kind); |
| 3258 | return current_map; |
| 3259 | } |
| 3260 | |
| 3261 | |
| 3262 | Map* Map::LookupElementsTransitionMap(ElementsKind to_kind) { |
| 3263 | Map* to_map = FindClosestElementsTransition(this, to_kind); |
| 3264 | if (to_map->elements_kind() == to_kind) return to_map; |
| 3265 | return NULL; |
| 3266 | } |
| 3267 | |
| 3268 | |
| 3269 | bool Map::IsMapInArrayPrototypeChain() { |
| 3270 | Isolate* isolate = GetIsolate(); |
| 3271 | if (isolate->initial_array_prototype()->map() == this) { |
| 3272 | return true; |
| 3273 | } |
| 3274 | |
| 3275 | if (isolate->initial_object_prototype()->map() == this) { |
| 3276 | return true; |
| 3277 | } |
| 3278 | |
| 3279 | return false; |
| 3280 | } |
| 3281 | |
| 3282 | |
| 3283 | static Handle<Map> AddMissingElementsTransitions(Handle<Map> map, |
| 3284 | ElementsKind to_kind) { |
| 3285 | DCHECK(IsTransitionElementsKind(map->elements_kind())); |
| 3286 | |
| 3287 | Handle<Map> current_map = map; |
| 3288 | |
| 3289 | ElementsKind kind = map->elements_kind(); |
| 3290 | if (!map->is_prototype_map()) { |
| 3291 | while (kind != to_kind && !IsTerminalElementsKind(kind)) { |
| 3292 | kind = GetNextTransitionElementsKind(kind); |
| 3293 | current_map = |
| 3294 | Map::CopyAsElementsKind(current_map, kind, INSERT_TRANSITION); |
| 3295 | } |
| 3296 | } |
| 3297 | |
| 3298 | // In case we are exiting the fast elements kind system, just add the map in |
| 3299 | // the end. |
| 3300 | if (kind != to_kind) { |
| 3301 | current_map = Map::CopyAsElementsKind( |
| 3302 | current_map, to_kind, INSERT_TRANSITION); |
| 3303 | } |
| 3304 | |
| 3305 | DCHECK(current_map->elements_kind() == to_kind); |
| 3306 | return current_map; |
| 3307 | } |
| 3308 | |
| 3309 | |
| 3310 | Handle<Map> Map::TransitionElementsTo(Handle<Map> map, |
| 3311 | ElementsKind to_kind) { |
| 3312 | ElementsKind from_kind = map->elements_kind(); |
| 3313 | if (from_kind == to_kind) return map; |
| 3314 | |
| 3315 | Isolate* isolate = map->GetIsolate(); |
| 3316 | Context* native_context = isolate->context()->native_context(); |
| 3317 | Object* maybe_array_maps = native_context->js_array_maps(); |
| 3318 | if (maybe_array_maps->IsFixedArray()) { |
| 3319 | DisallowHeapAllocation no_gc; |
| 3320 | FixedArray* array_maps = FixedArray::cast(maybe_array_maps); |
| 3321 | if (array_maps->get(from_kind) == *map) { |
| 3322 | Object* maybe_transitioned_map = array_maps->get(to_kind); |
| 3323 | if (maybe_transitioned_map->IsMap()) { |
| 3324 | return handle(Map::cast(maybe_transitioned_map)); |
Ben Murdoch | 85b7179 | 2012-04-11 18:30:58 +0100 | [diff] [blame] | 3325 | } |
Ben Murdoch | 592a9fc | 2012-03-05 11:04:45 +0000 | [diff] [blame] | 3326 | } |
| 3327 | } |
| 3328 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3329 | return TransitionElementsToSlow(map, to_kind); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3330 | } |
| 3331 | |
| 3332 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3333 | Handle<Map> Map::TransitionElementsToSlow(Handle<Map> map, |
| 3334 | ElementsKind to_kind) { |
| 3335 | ElementsKind from_kind = map->elements_kind(); |
| 3336 | |
| 3337 | if (from_kind == to_kind) { |
| 3338 | return map; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3339 | } |
| 3340 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3341 | bool allow_store_transition = |
| 3342 | // Only remember the map transition if there is not an already existing |
| 3343 | // non-matching element transition. |
| 3344 | !map->IsUndefined() && !map->is_dictionary_map() && |
| 3345 | IsTransitionElementsKind(from_kind); |
| 3346 | |
| 3347 | // Only store fast element maps in ascending generality. |
| 3348 | if (IsFastElementsKind(to_kind)) { |
| 3349 | allow_store_transition &= |
| 3350 | IsTransitionableFastElementsKind(from_kind) && |
| 3351 | IsMoreGeneralElementsKindTransition(from_kind, to_kind); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3352 | } |
| 3353 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3354 | if (!allow_store_transition) { |
| 3355 | return Map::CopyAsElementsKind(map, to_kind, OMIT_TRANSITION); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3356 | } |
| 3357 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3358 | return Map::AsElementsKind(map, to_kind); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3359 | } |
| 3360 | |
| 3361 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3362 | // static |
| 3363 | Handle<Map> Map::AsElementsKind(Handle<Map> map, ElementsKind kind) { |
| 3364 | Handle<Map> closest_map(FindClosestElementsTransition(*map, kind)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3365 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3366 | if (closest_map->elements_kind() == kind) { |
| 3367 | return closest_map; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3368 | } |
| 3369 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3370 | return AddMissingElementsTransitions(closest_map, kind); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3371 | } |
| 3372 | |
| 3373 | |
| 3374 | Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object, |
| 3375 | ElementsKind to_kind) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3376 | Handle<Map> map(object->map()); |
| 3377 | return Map::TransitionElementsTo(map, to_kind); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3378 | } |
| 3379 | |
| 3380 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3381 | Maybe<bool> JSProxy::HasPropertyWithHandler(Handle<JSProxy> proxy, |
| 3382 | Handle<Name> name) { |
| 3383 | Isolate* isolate = proxy->GetIsolate(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3384 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3385 | // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
| 3386 | if (name->IsSymbol()) return maybe(false); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3387 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3388 | Handle<Object> args[] = { name }; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3389 | Handle<Object> result; |
| 3390 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 3391 | isolate, result, CallTrap(proxy, "has", isolate->derived_has_trap(), |
| 3392 | arraysize(args), args), |
| 3393 | Maybe<bool>()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3394 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3395 | return maybe(result->BooleanValue()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3396 | } |
| 3397 | |
| 3398 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3399 | MaybeHandle<Object> JSProxy::SetPropertyWithHandler(Handle<JSProxy> proxy, |
| 3400 | Handle<Object> receiver, |
| 3401 | Handle<Name> name, |
| 3402 | Handle<Object> value, |
| 3403 | StrictMode strict_mode) { |
| 3404 | Isolate* isolate = proxy->GetIsolate(); |
| 3405 | |
| 3406 | // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
| 3407 | if (name->IsSymbol()) return value; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3408 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3409 | Handle<Object> args[] = { receiver, name, value }; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3410 | RETURN_ON_EXCEPTION( |
| 3411 | isolate, |
| 3412 | CallTrap(proxy, |
| 3413 | "set", |
| 3414 | isolate->derived_set_trap(), |
| 3415 | arraysize(args), |
| 3416 | args), |
| 3417 | Object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3418 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3419 | return value; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3420 | } |
| 3421 | |
| 3422 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3423 | MaybeHandle<Object> JSProxy::SetPropertyViaPrototypesWithHandler( |
| 3424 | Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name, |
| 3425 | Handle<Object> value, StrictMode strict_mode, bool* done) { |
| 3426 | Isolate* isolate = proxy->GetIsolate(); |
| 3427 | Handle<Object> handler(proxy->handler(), isolate); // Trap might morph proxy. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3428 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3429 | // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
| 3430 | if (name->IsSymbol()) { |
| 3431 | *done = false; |
| 3432 | return isolate->factory()->the_hole_value(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3433 | } |
| 3434 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3435 | *done = true; // except where redefined... |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3436 | Handle<Object> args[] = { name }; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3437 | Handle<Object> result; |
| 3438 | ASSIGN_RETURN_ON_EXCEPTION( |
| 3439 | isolate, result, |
| 3440 | CallTrap(proxy, |
| 3441 | "getPropertyDescriptor", |
| 3442 | Handle<Object>(), |
| 3443 | arraysize(args), |
| 3444 | args), |
| 3445 | Object); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3446 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3447 | if (result->IsUndefined()) { |
| 3448 | *done = false; |
| 3449 | return isolate->factory()->the_hole_value(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3450 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3451 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3452 | // Emulate [[GetProperty]] semantics for proxies. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3453 | Handle<Object> argv[] = { result }; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3454 | Handle<Object> desc; |
| 3455 | ASSIGN_RETURN_ON_EXCEPTION( |
| 3456 | isolate, desc, |
| 3457 | Execution::Call(isolate, |
| 3458 | isolate->to_complete_property_descriptor(), |
| 3459 | result, |
| 3460 | arraysize(argv), |
| 3461 | argv), |
| 3462 | Object); |
| 3463 | |
| 3464 | // [[GetProperty]] requires to check that all properties are configurable. |
| 3465 | Handle<String> configurable_name = |
| 3466 | isolate->factory()->InternalizeOneByteString( |
| 3467 | STATIC_CHAR_VECTOR("configurable_")); |
| 3468 | Handle<Object> configurable = |
| 3469 | Object::GetProperty(desc, configurable_name).ToHandleChecked(); |
| 3470 | DCHECK(configurable->IsBoolean()); |
| 3471 | if (configurable->IsFalse()) { |
| 3472 | Handle<String> trap = isolate->factory()->InternalizeOneByteString( |
| 3473 | STATIC_CHAR_VECTOR("getPropertyDescriptor")); |
| 3474 | Handle<Object> args[] = { handler, trap, name }; |
| 3475 | THROW_NEW_ERROR(isolate, NewTypeError("proxy_prop_not_configurable", |
| 3476 | HandleVector(args, arraysize(args))), |
| 3477 | Object); |
| 3478 | } |
| 3479 | DCHECK(configurable->IsTrue()); |
| 3480 | |
| 3481 | // Check for DataDescriptor. |
| 3482 | Handle<String> hasWritable_name = |
| 3483 | isolate->factory()->InternalizeOneByteString( |
| 3484 | STATIC_CHAR_VECTOR("hasWritable_")); |
| 3485 | Handle<Object> hasWritable = |
| 3486 | Object::GetProperty(desc, hasWritable_name).ToHandleChecked(); |
| 3487 | DCHECK(hasWritable->IsBoolean()); |
| 3488 | if (hasWritable->IsTrue()) { |
| 3489 | Handle<String> writable_name = isolate->factory()->InternalizeOneByteString( |
| 3490 | STATIC_CHAR_VECTOR("writable_")); |
| 3491 | Handle<Object> writable = |
| 3492 | Object::GetProperty(desc, writable_name).ToHandleChecked(); |
| 3493 | DCHECK(writable->IsBoolean()); |
| 3494 | *done = writable->IsFalse(); |
| 3495 | if (!*done) return isolate->factory()->the_hole_value(); |
| 3496 | if (strict_mode == SLOPPY) return value; |
| 3497 | Handle<Object> args[] = { name, receiver }; |
| 3498 | THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property", |
| 3499 | HandleVector(args, arraysize(args))), |
| 3500 | Object); |
| 3501 | } |
| 3502 | |
| 3503 | // We have an AccessorDescriptor. |
| 3504 | Handle<String> set_name = |
| 3505 | isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("set_")); |
| 3506 | Handle<Object> setter = Object::GetProperty(desc, set_name).ToHandleChecked(); |
| 3507 | if (!setter->IsUndefined()) { |
| 3508 | // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| 3509 | return SetPropertyWithDefinedSetter( |
| 3510 | receiver, Handle<JSReceiver>::cast(setter), value); |
| 3511 | } |
| 3512 | |
| 3513 | if (strict_mode == SLOPPY) return value; |
| 3514 | Handle<Object> args2[] = { name, proxy }; |
| 3515 | THROW_NEW_ERROR(isolate, NewTypeError("no_setter_in_callback", |
| 3516 | HandleVector(args2, arraysize(args2))), |
| 3517 | Object); |
| 3518 | } |
| 3519 | |
| 3520 | |
| 3521 | MaybeHandle<Object> JSProxy::DeletePropertyWithHandler( |
| 3522 | Handle<JSProxy> proxy, Handle<Name> name, DeleteMode mode) { |
| 3523 | Isolate* isolate = proxy->GetIsolate(); |
| 3524 | |
| 3525 | // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
| 3526 | if (name->IsSymbol()) return isolate->factory()->false_value(); |
| 3527 | |
| 3528 | Handle<Object> args[] = { name }; |
| 3529 | Handle<Object> result; |
| 3530 | ASSIGN_RETURN_ON_EXCEPTION( |
| 3531 | isolate, result, |
| 3532 | CallTrap(proxy, |
| 3533 | "delete", |
| 3534 | Handle<Object>(), |
| 3535 | arraysize(args), |
| 3536 | args), |
| 3537 | Object); |
| 3538 | |
| 3539 | bool result_bool = result->BooleanValue(); |
| 3540 | if (mode == STRICT_DELETION && !result_bool) { |
| 3541 | Handle<Object> handler(proxy->handler(), isolate); |
| 3542 | Handle<String> trap_name = isolate->factory()->InternalizeOneByteString( |
| 3543 | STATIC_CHAR_VECTOR("delete")); |
| 3544 | Handle<Object> args[] = { handler, trap_name }; |
| 3545 | THROW_NEW_ERROR(isolate, NewTypeError("handler_failed", |
| 3546 | HandleVector(args, arraysize(args))), |
| 3547 | Object); |
| 3548 | } |
| 3549 | return isolate->factory()->ToBoolean(result_bool); |
| 3550 | } |
| 3551 | |
| 3552 | |
| 3553 | MaybeHandle<Object> JSProxy::DeleteElementWithHandler( |
| 3554 | Handle<JSProxy> proxy, uint32_t index, DeleteMode mode) { |
| 3555 | Isolate* isolate = proxy->GetIsolate(); |
| 3556 | Handle<String> name = isolate->factory()->Uint32ToString(index); |
| 3557 | return JSProxy::DeletePropertyWithHandler(proxy, name, mode); |
| 3558 | } |
| 3559 | |
| 3560 | |
| 3561 | Maybe<PropertyAttributes> JSProxy::GetPropertyAttributesWithHandler( |
| 3562 | Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name) { |
| 3563 | Isolate* isolate = proxy->GetIsolate(); |
| 3564 | HandleScope scope(isolate); |
| 3565 | |
| 3566 | // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
| 3567 | if (name->IsSymbol()) return maybe(ABSENT); |
| 3568 | |
| 3569 | Handle<Object> args[] = { name }; |
| 3570 | Handle<Object> result; |
| 3571 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 3572 | isolate, result, |
| 3573 | proxy->CallTrap(proxy, "getPropertyDescriptor", Handle<Object>(), |
| 3574 | arraysize(args), args), |
| 3575 | Maybe<PropertyAttributes>()); |
| 3576 | |
| 3577 | if (result->IsUndefined()) return maybe(ABSENT); |
| 3578 | |
| 3579 | Handle<Object> argv[] = { result }; |
| 3580 | Handle<Object> desc; |
| 3581 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 3582 | isolate, desc, |
| 3583 | Execution::Call(isolate, isolate->to_complete_property_descriptor(), |
| 3584 | result, arraysize(argv), argv), |
| 3585 | Maybe<PropertyAttributes>()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3586 | |
| 3587 | // Convert result to PropertyAttributes. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3588 | Handle<String> enum_n = isolate->factory()->InternalizeOneByteString( |
| 3589 | STATIC_CHAR_VECTOR("enumerable_")); |
| 3590 | Handle<Object> enumerable; |
| 3591 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, enumerable, |
| 3592 | Object::GetProperty(desc, enum_n), |
| 3593 | Maybe<PropertyAttributes>()); |
| 3594 | Handle<String> conf_n = isolate->factory()->InternalizeOneByteString( |
| 3595 | STATIC_CHAR_VECTOR("configurable_")); |
| 3596 | Handle<Object> configurable; |
| 3597 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, configurable, |
| 3598 | Object::GetProperty(desc, conf_n), |
| 3599 | Maybe<PropertyAttributes>()); |
| 3600 | Handle<String> writ_n = isolate->factory()->InternalizeOneByteString( |
| 3601 | STATIC_CHAR_VECTOR("writable_")); |
| 3602 | Handle<Object> writable; |
| 3603 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, writable, |
| 3604 | Object::GetProperty(desc, writ_n), |
| 3605 | Maybe<PropertyAttributes>()); |
| 3606 | if (!writable->BooleanValue()) { |
| 3607 | Handle<String> set_n = isolate->factory()->InternalizeOneByteString( |
| 3608 | STATIC_CHAR_VECTOR("set_")); |
| 3609 | Handle<Object> setter; |
| 3610 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, setter, |
| 3611 | Object::GetProperty(desc, set_n), |
| 3612 | Maybe<PropertyAttributes>()); |
| 3613 | writable = isolate->factory()->ToBoolean(!setter->IsUndefined()); |
| 3614 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3615 | |
| 3616 | if (configurable->IsFalse()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3617 | Handle<Object> handler(proxy->handler(), isolate); |
| 3618 | Handle<String> trap = isolate->factory()->InternalizeOneByteString( |
| 3619 | STATIC_CHAR_VECTOR("getPropertyDescriptor")); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3620 | Handle<Object> args[] = { handler, trap, name }; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3621 | Handle<Object> error; |
| 3622 | MaybeHandle<Object> maybe_error = isolate->factory()->NewTypeError( |
| 3623 | "proxy_prop_not_configurable", HandleVector(args, arraysize(args))); |
| 3624 | if (maybe_error.ToHandle(&error)) isolate->Throw(*error); |
| 3625 | return maybe(NONE); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3626 | } |
| 3627 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3628 | int attributes = NONE; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3629 | if (!enumerable->BooleanValue()) attributes |= DONT_ENUM; |
| 3630 | if (!configurable->BooleanValue()) attributes |= DONT_DELETE; |
| 3631 | if (!writable->BooleanValue()) attributes |= READ_ONLY; |
| 3632 | return maybe(static_cast<PropertyAttributes>(attributes)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3633 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3634 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3635 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3636 | Maybe<PropertyAttributes> JSProxy::GetElementAttributeWithHandler( |
| 3637 | Handle<JSProxy> proxy, Handle<JSReceiver> receiver, uint32_t index) { |
| 3638 | Isolate* isolate = proxy->GetIsolate(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3639 | Handle<String> name = isolate->factory()->Uint32ToString(index); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3640 | return GetPropertyAttributesWithHandler(proxy, receiver, name); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3641 | } |
| 3642 | |
| 3643 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3644 | void JSProxy::Fix(Handle<JSProxy> proxy) { |
| 3645 | Isolate* isolate = proxy->GetIsolate(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3646 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3647 | // Save identity hash. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3648 | Handle<Object> hash(proxy->GetIdentityHash(), isolate); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3649 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3650 | if (proxy->IsJSFunctionProxy()) { |
| 3651 | isolate->factory()->BecomeJSFunction(proxy); |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 3652 | // Code will be set on the JavaScript side. |
| 3653 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3654 | isolate->factory()->BecomeJSObject(proxy); |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 3655 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3656 | DCHECK(proxy->IsJSObject()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3657 | |
| 3658 | // Inherit identity, if it was present. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3659 | if (hash->IsSmi()) { |
| 3660 | JSObject::SetIdentityHash(Handle<JSObject>::cast(proxy), |
| 3661 | Handle<Smi>::cast(hash)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3662 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3663 | } |
| 3664 | |
| 3665 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3666 | MaybeHandle<Object> JSProxy::CallTrap(Handle<JSProxy> proxy, |
| 3667 | const char* name, |
| 3668 | Handle<Object> derived, |
| 3669 | int argc, |
| 3670 | Handle<Object> argv[]) { |
| 3671 | Isolate* isolate = proxy->GetIsolate(); |
| 3672 | Handle<Object> handler(proxy->handler(), isolate); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3673 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3674 | Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name); |
| 3675 | Handle<Object> trap; |
| 3676 | ASSIGN_RETURN_ON_EXCEPTION( |
| 3677 | isolate, trap, |
| 3678 | Object::GetPropertyOrElement(handler, trap_name), |
| 3679 | Object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3680 | |
| 3681 | if (trap->IsUndefined()) { |
| 3682 | if (derived.is_null()) { |
| 3683 | Handle<Object> args[] = { handler, trap_name }; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3684 | THROW_NEW_ERROR(isolate, |
| 3685 | NewTypeError("handler_trap_missing", |
| 3686 | HandleVector(args, arraysize(args))), |
| 3687 | Object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3688 | } |
| 3689 | trap = Handle<Object>(derived); |
| 3690 | } |
| 3691 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3692 | return Execution::Call(isolate, trap, handler, argc, argv); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3693 | } |
| 3694 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 3695 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3696 | void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) { |
| 3697 | DCHECK(object->map()->inobject_properties() == map->inobject_properties()); |
| 3698 | ElementsKind obj_kind = object->map()->elements_kind(); |
| 3699 | ElementsKind map_kind = map->elements_kind(); |
| 3700 | if (map_kind != obj_kind) { |
| 3701 | ElementsKind to_kind = map_kind; |
| 3702 | if (IsMoreGeneralElementsKindTransition(map_kind, obj_kind) || |
| 3703 | IsDictionaryElementsKind(obj_kind)) { |
| 3704 | to_kind = obj_kind; |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 3705 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3706 | if (IsDictionaryElementsKind(to_kind)) { |
| 3707 | NormalizeElements(object); |
Ben Murdoch | e0cee9b | 2011-05-25 10:26:03 +0100 | [diff] [blame] | 3708 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3709 | TransitionElementsKind(object, to_kind); |
Ben Murdoch | e0cee9b | 2011-05-25 10:26:03 +0100 | [diff] [blame] | 3710 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3711 | map = Map::AsElementsKind(map, to_kind); |
Ben Murdoch | e0cee9b | 2011-05-25 10:26:03 +0100 | [diff] [blame] | 3712 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3713 | JSObject::MigrateToMap(object, map); |
| 3714 | } |
| 3715 | |
| 3716 | |
| 3717 | void JSObject::MigrateInstance(Handle<JSObject> object) { |
| 3718 | Handle<Map> original_map(object->map()); |
| 3719 | Handle<Map> map = Map::Update(original_map); |
| 3720 | map->set_migration_target(true); |
| 3721 | MigrateToMap(object, map); |
| 3722 | if (FLAG_trace_migration) { |
| 3723 | object->PrintInstanceMigration(stdout, *original_map, *map); |
| 3724 | } |
| 3725 | } |
| 3726 | |
| 3727 | |
| 3728 | // static |
| 3729 | bool JSObject::TryMigrateInstance(Handle<JSObject> object) { |
| 3730 | Isolate* isolate = object->GetIsolate(); |
| 3731 | DisallowDeoptimization no_deoptimization(isolate); |
| 3732 | Handle<Map> original_map(object->map(), isolate); |
| 3733 | Handle<Map> new_map; |
| 3734 | if (!Map::TryUpdate(original_map).ToHandle(&new_map)) { |
| 3735 | return false; |
| 3736 | } |
| 3737 | JSObject::MigrateToMap(object, new_map); |
| 3738 | if (FLAG_trace_migration) { |
| 3739 | object->PrintInstanceMigration(stdout, *original_map, object->map()); |
| 3740 | } |
| 3741 | return true; |
| 3742 | } |
| 3743 | |
| 3744 | |
| 3745 | void JSObject::MigrateToNewProperty(Handle<JSObject> object, |
| 3746 | Handle<Map> map, |
| 3747 | Handle<Object> value) { |
| 3748 | JSObject::MigrateToMap(object, map); |
| 3749 | if (map->GetLastDescriptorDetails().type() != FIELD) return; |
| 3750 | object->WriteToField(map->LastAdded(), *value); |
| 3751 | } |
| 3752 | |
| 3753 | |
| 3754 | void JSObject::WriteToField(int descriptor, Object* value) { |
| 3755 | DisallowHeapAllocation no_gc; |
| 3756 | |
| 3757 | DescriptorArray* desc = map()->instance_descriptors(); |
| 3758 | PropertyDetails details = desc->GetDetails(descriptor); |
| 3759 | |
| 3760 | DCHECK(details.type() == FIELD); |
| 3761 | |
| 3762 | FieldIndex index = FieldIndex::ForDescriptor(map(), descriptor); |
| 3763 | if (details.representation().IsDouble()) { |
| 3764 | // Nothing more to be done. |
| 3765 | if (value->IsUninitialized()) return; |
| 3766 | HeapNumber* box = HeapNumber::cast(RawFastPropertyAt(index)); |
| 3767 | DCHECK(box->IsMutableHeapNumber()); |
| 3768 | box->set_value(value->Number()); |
| 3769 | } else { |
| 3770 | FastPropertyAtPut(index, value); |
| 3771 | } |
| 3772 | } |
| 3773 | |
| 3774 | |
| 3775 | void JSObject::AddProperty(Handle<JSObject> object, Handle<Name> name, |
| 3776 | Handle<Object> value, |
| 3777 | PropertyAttributes attributes) { |
| 3778 | LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 3779 | CHECK_NE(LookupIterator::ACCESS_CHECK, it.state()); |
| 3780 | #ifdef DEBUG |
| 3781 | uint32_t index; |
| 3782 | DCHECK(!object->IsJSProxy()); |
| 3783 | DCHECK(!name->AsArrayIndex(&index)); |
| 3784 | Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it); |
| 3785 | DCHECK(maybe.has_value); |
| 3786 | DCHECK(!it.IsFound()); |
| 3787 | DCHECK(object->map()->is_extensible() || |
| 3788 | name.is_identical_to(it.isolate()->factory()->hidden_string())); |
| 3789 | #endif |
| 3790 | AddDataProperty(&it, value, attributes, STRICT, |
| 3791 | CERTAINLY_NOT_STORE_FROM_KEYED).Check(); |
| 3792 | } |
| 3793 | |
| 3794 | |
| 3795 | // Reconfigures a property to a data property with attributes, even if it is not |
| 3796 | // reconfigurable. |
| 3797 | MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes( |
| 3798 | Handle<JSObject> object, |
| 3799 | Handle<Name> name, |
| 3800 | Handle<Object> value, |
| 3801 | PropertyAttributes attributes, |
| 3802 | ExecutableAccessorInfoHandling handling) { |
| 3803 | DCHECK(!value->IsTheHole()); |
| 3804 | LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 3805 | bool is_observed = object->map()->is_observed() && |
| 3806 | *name != it.isolate()->heap()->hidden_string(); |
| 3807 | for (; it.IsFound(); it.Next()) { |
| 3808 | switch (it.state()) { |
| 3809 | case LookupIterator::INTERCEPTOR: |
| 3810 | case LookupIterator::JSPROXY: |
| 3811 | case LookupIterator::NOT_FOUND: |
| 3812 | case LookupIterator::TRANSITION: |
| 3813 | UNREACHABLE(); |
| 3814 | |
| 3815 | case LookupIterator::ACCESS_CHECK: |
| 3816 | if (!it.isolate()->MayNamedAccess(object, name, v8::ACCESS_SET)) { |
| 3817 | return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY); |
| 3818 | } |
| 3819 | break; |
| 3820 | |
| 3821 | case LookupIterator::ACCESSOR: { |
| 3822 | PropertyDetails details = it.property_details(); |
| 3823 | Handle<Object> old_value = it.isolate()->factory()->the_hole_value(); |
| 3824 | // Ensure the context isn't changed after calling into accessors. |
| 3825 | AssertNoContextChange ncc(it.isolate()); |
| 3826 | |
| 3827 | Handle<Object> accessors = it.GetAccessors(); |
| 3828 | |
| 3829 | if (is_observed && accessors->IsAccessorInfo()) { |
| 3830 | ASSIGN_RETURN_ON_EXCEPTION( |
| 3831 | it.isolate(), old_value, |
| 3832 | GetPropertyWithAccessor(it.GetReceiver(), it.name(), |
| 3833 | it.GetHolder<JSObject>(), accessors), |
| 3834 | Object); |
| 3835 | } |
| 3836 | |
| 3837 | // Special handling for ExecutableAccessorInfo, which behaves like a |
| 3838 | // data property. |
| 3839 | if (handling == DONT_FORCE_FIELD && |
| 3840 | accessors->IsExecutableAccessorInfo()) { |
| 3841 | Handle<Object> result; |
| 3842 | ASSIGN_RETURN_ON_EXCEPTION( |
| 3843 | it.isolate(), result, |
| 3844 | JSObject::SetPropertyWithAccessor(it.GetReceiver(), it.name(), |
| 3845 | value, it.GetHolder<JSObject>(), |
| 3846 | accessors, STRICT), |
| 3847 | Object); |
| 3848 | DCHECK(result->SameValue(*value)); |
| 3849 | |
| 3850 | if (details.attributes() == attributes) { |
| 3851 | // Regular property update if the attributes match. |
| 3852 | if (is_observed && !old_value->SameValue(*value)) { |
| 3853 | // If we are setting the prototype of a function and are |
| 3854 | // observed, don't send change records because the prototype |
| 3855 | // handles that itself. |
| 3856 | if (!object->IsJSFunction() || |
| 3857 | !Name::Equals(it.isolate()->factory()->prototype_string(), |
| 3858 | name) || |
| 3859 | !Handle<JSFunction>::cast(object)->should_have_prototype()) { |
| 3860 | EnqueueChangeRecord(object, "update", name, old_value); |
| 3861 | } |
| 3862 | } |
| 3863 | return value; |
| 3864 | } |
| 3865 | |
| 3866 | // Reconfigure the accessor if attributes mismatch. |
| 3867 | Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor( |
| 3868 | it.isolate(), Handle<ExecutableAccessorInfo>::cast(accessors)); |
| 3869 | new_data->set_property_attributes(attributes); |
| 3870 | // By clearing the setter we don't have to introduce a lookup to |
| 3871 | // the setter, simply make it unavailable to reflect the |
| 3872 | // attributes. |
| 3873 | if (attributes & READ_ONLY) new_data->clear_setter(); |
| 3874 | SetPropertyCallback(object, name, new_data, attributes); |
| 3875 | if (is_observed) { |
| 3876 | if (old_value->SameValue(*value)) { |
| 3877 | old_value = it.isolate()->factory()->the_hole_value(); |
| 3878 | } |
| 3879 | EnqueueChangeRecord(object, "reconfigure", name, old_value); |
| 3880 | } |
| 3881 | return value; |
| 3882 | } |
| 3883 | |
| 3884 | it.ReconfigureDataProperty(value, attributes); |
| 3885 | it.PrepareForDataProperty(value); |
| 3886 | it.WriteDataValue(value); |
| 3887 | |
| 3888 | if (is_observed) { |
| 3889 | if (old_value->SameValue(*value)) { |
| 3890 | old_value = it.isolate()->factory()->the_hole_value(); |
| 3891 | } |
| 3892 | EnqueueChangeRecord(object, "reconfigure", name, old_value); |
| 3893 | } |
| 3894 | |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 3895 | return value; |
| 3896 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3897 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3898 | case LookupIterator::DATA: { |
| 3899 | PropertyDetails details = it.property_details(); |
| 3900 | Handle<Object> old_value = it.isolate()->factory()->the_hole_value(); |
| 3901 | // Regular property update if the attributes match. |
| 3902 | if (details.attributes() == attributes) { |
| 3903 | return SetDataProperty(&it, value); |
| 3904 | } |
| 3905 | // Reconfigure the data property if the attributes mismatch. |
| 3906 | if (is_observed) old_value = it.GetDataValue(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3907 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3908 | it.ReconfigureDataProperty(value, attributes); |
| 3909 | it.PrepareForDataProperty(value); |
| 3910 | it.WriteDataValue(value); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3911 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3912 | if (is_observed) { |
| 3913 | if (old_value->SameValue(*value)) { |
| 3914 | old_value = it.isolate()->factory()->the_hole_value(); |
| 3915 | } |
| 3916 | EnqueueChangeRecord(object, "reconfigure", name, old_value); |
| 3917 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 3918 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3919 | return value; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3920 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3921 | } |
| 3922 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3923 | |
| 3924 | return AddDataProperty(&it, value, attributes, STRICT, |
| 3925 | CERTAINLY_NOT_STORE_FROM_KEYED); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3926 | } |
| 3927 | |
| 3928 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3929 | Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( |
| 3930 | Handle<JSObject> holder, |
| 3931 | Handle<Object> receiver, |
| 3932 | Handle<Name> name) { |
| 3933 | // TODO(rossberg): Support symbols in the API. |
| 3934 | if (name->IsSymbol()) return maybe(ABSENT); |
| 3935 | |
| 3936 | Isolate* isolate = holder->GetIsolate(); |
| 3937 | HandleScope scope(isolate); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3938 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3939 | // Make sure that the top context does not change when doing |
| 3940 | // callbacks or interceptor calls. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3941 | AssertNoContextChange ncc(isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3942 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3943 | Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor()); |
| 3944 | PropertyCallbackArguments args( |
| 3945 | isolate, interceptor->data(), *receiver, *holder); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3946 | if (!interceptor->query()->IsUndefined()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3947 | v8::NamedPropertyQueryCallback query = |
| 3948 | v8::ToCData<v8::NamedPropertyQueryCallback>(interceptor->query()); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3949 | LOG(isolate, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3950 | ApiNamedPropertyAccess("interceptor-named-has", *holder, *name)); |
| 3951 | v8::Handle<v8::Integer> result = |
| 3952 | args.Call(query, v8::Utils::ToLocal(Handle<String>::cast(name))); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3953 | if (!result.IsEmpty()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3954 | DCHECK(result->IsInt32()); |
| 3955 | return maybe(static_cast<PropertyAttributes>(result->Int32Value())); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3956 | } |
| 3957 | } else if (!interceptor->getter()->IsUndefined()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3958 | v8::NamedPropertyGetterCallback getter = |
| 3959 | v8::ToCData<v8::NamedPropertyGetterCallback>(interceptor->getter()); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3960 | LOG(isolate, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3961 | ApiNamedPropertyAccess("interceptor-named-get-has", *holder, *name)); |
| 3962 | v8::Handle<v8::Value> result = |
| 3963 | args.Call(getter, v8::Utils::ToLocal(Handle<String>::cast(name))); |
| 3964 | if (!result.IsEmpty()) return maybe(DONT_ENUM); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3965 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3966 | |
| 3967 | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe<PropertyAttributes>()); |
| 3968 | return maybe(ABSENT); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3969 | } |
| 3970 | |
| 3971 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3972 | Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes( |
| 3973 | Handle<JSReceiver> object, Handle<Name> name) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3974 | // Check whether the name is an array index. |
| 3975 | uint32_t index = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3976 | if (object->IsJSObject() && name->AsArrayIndex(&index)) { |
| 3977 | return GetOwnElementAttribute(object, index); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3978 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3979 | LookupIterator it(object, name, LookupIterator::HIDDEN); |
| 3980 | return GetPropertyAttributes(&it); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 3981 | } |
| 3982 | |
| 3983 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 3984 | Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes( |
| 3985 | LookupIterator* it) { |
| 3986 | for (; it->IsFound(); it->Next()) { |
| 3987 | switch (it->state()) { |
| 3988 | case LookupIterator::NOT_FOUND: |
| 3989 | case LookupIterator::TRANSITION: |
| 3990 | UNREACHABLE(); |
| 3991 | case LookupIterator::JSPROXY: |
| 3992 | return JSProxy::GetPropertyAttributesWithHandler( |
| 3993 | it->GetHolder<JSProxy>(), it->GetReceiver(), it->name()); |
| 3994 | case LookupIterator::INTERCEPTOR: { |
| 3995 | Maybe<PropertyAttributes> result = |
| 3996 | JSObject::GetPropertyAttributesWithInterceptor( |
| 3997 | it->GetHolder<JSObject>(), it->GetReceiver(), it->name()); |
| 3998 | if (!result.has_value) return result; |
| 3999 | if (result.value != ABSENT) return result; |
| 4000 | break; |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4001 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4002 | case LookupIterator::ACCESS_CHECK: |
| 4003 | if (it->HasAccess(v8::ACCESS_HAS)) break; |
| 4004 | return JSObject::GetPropertyAttributesWithFailedAccessCheck(it); |
| 4005 | case LookupIterator::ACCESSOR: |
| 4006 | case LookupIterator::DATA: |
| 4007 | return maybe(it->property_details().attributes()); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4008 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4009 | } |
| 4010 | return maybe(ABSENT); |
| 4011 | } |
| 4012 | |
| 4013 | |
| 4014 | Maybe<PropertyAttributes> JSObject::GetElementAttributeWithReceiver( |
| 4015 | Handle<JSObject> object, Handle<JSReceiver> receiver, uint32_t index, |
| 4016 | bool check_prototype) { |
| 4017 | Isolate* isolate = object->GetIsolate(); |
| 4018 | |
| 4019 | // Check access rights if needed. |
| 4020 | if (object->IsAccessCheckNeeded()) { |
| 4021 | if (!isolate->MayIndexedAccess(object, index, v8::ACCESS_HAS)) { |
| 4022 | isolate->ReportFailedAccessCheck(object, v8::ACCESS_HAS); |
| 4023 | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe<PropertyAttributes>()); |
| 4024 | return maybe(ABSENT); |
| 4025 | } |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4026 | } |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 4027 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4028 | if (object->IsJSGlobalProxy()) { |
| 4029 | PrototypeIterator iter(isolate, object); |
| 4030 | if (iter.IsAtEnd()) return maybe(ABSENT); |
| 4031 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 4032 | return JSObject::GetElementAttributeWithReceiver( |
| 4033 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), receiver, |
| 4034 | index, check_prototype); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 4035 | } |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4036 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4037 | // Check for lookup interceptor except when bootstrapping. |
| 4038 | if (object->HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) { |
| 4039 | return JSObject::GetElementAttributeWithInterceptor( |
| 4040 | object, receiver, index, check_prototype); |
| 4041 | } |
| 4042 | |
| 4043 | return GetElementAttributeWithoutInterceptor( |
| 4044 | object, receiver, index, check_prototype); |
| 4045 | } |
| 4046 | |
| 4047 | |
| 4048 | Maybe<PropertyAttributes> JSObject::GetElementAttributeWithInterceptor( |
| 4049 | Handle<JSObject> object, Handle<JSReceiver> receiver, uint32_t index, |
| 4050 | bool check_prototype) { |
| 4051 | Isolate* isolate = object->GetIsolate(); |
| 4052 | HandleScope scope(isolate); |
| 4053 | |
| 4054 | // Make sure that the top context does not change when doing |
| 4055 | // callbacks or interceptor calls. |
| 4056 | AssertNoContextChange ncc(isolate); |
| 4057 | |
| 4058 | Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor()); |
| 4059 | PropertyCallbackArguments args( |
| 4060 | isolate, interceptor->data(), *receiver, *object); |
| 4061 | if (!interceptor->query()->IsUndefined()) { |
| 4062 | v8::IndexedPropertyQueryCallback query = |
| 4063 | v8::ToCData<v8::IndexedPropertyQueryCallback>(interceptor->query()); |
| 4064 | LOG(isolate, |
| 4065 | ApiIndexedPropertyAccess("interceptor-indexed-has", *object, index)); |
| 4066 | v8::Handle<v8::Integer> result = args.Call(query, index); |
| 4067 | if (!result.IsEmpty()) |
| 4068 | return maybe(static_cast<PropertyAttributes>(result->Int32Value())); |
| 4069 | } else if (!interceptor->getter()->IsUndefined()) { |
| 4070 | v8::IndexedPropertyGetterCallback getter = |
| 4071 | v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter()); |
| 4072 | LOG(isolate, |
| 4073 | ApiIndexedPropertyAccess( |
| 4074 | "interceptor-indexed-get-has", *object, index)); |
| 4075 | v8::Handle<v8::Value> result = args.Call(getter, index); |
| 4076 | if (!result.IsEmpty()) return maybe(NONE); |
| 4077 | } |
| 4078 | |
| 4079 | return GetElementAttributeWithoutInterceptor( |
| 4080 | object, receiver, index, check_prototype); |
| 4081 | } |
| 4082 | |
| 4083 | |
| 4084 | Maybe<PropertyAttributes> JSObject::GetElementAttributeWithoutInterceptor( |
| 4085 | Handle<JSObject> object, Handle<JSReceiver> receiver, uint32_t index, |
| 4086 | bool check_prototype) { |
| 4087 | PropertyAttributes attr = object->GetElementsAccessor()->GetAttributes( |
| 4088 | receiver, object, index); |
| 4089 | if (attr != ABSENT) return maybe(attr); |
| 4090 | |
| 4091 | // Handle [] on String objects. |
| 4092 | if (object->IsStringObjectWithCharacterAt(index)) { |
| 4093 | return maybe(static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE)); |
| 4094 | } |
| 4095 | |
| 4096 | if (!check_prototype) return maybe(ABSENT); |
| 4097 | |
| 4098 | PrototypeIterator iter(object->GetIsolate(), object); |
| 4099 | if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { |
| 4100 | // We need to follow the spec and simulate a call to [[GetOwnProperty]]. |
| 4101 | return JSProxy::GetElementAttributeWithHandler( |
| 4102 | Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), receiver, |
| 4103 | index); |
| 4104 | } |
| 4105 | if (iter.IsAtEnd()) return maybe(ABSENT); |
| 4106 | return GetElementAttributeWithReceiver( |
| 4107 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), receiver, |
| 4108 | index, true); |
| 4109 | } |
| 4110 | |
| 4111 | |
| 4112 | Handle<NormalizedMapCache> NormalizedMapCache::New(Isolate* isolate) { |
| 4113 | Handle<FixedArray> array( |
| 4114 | isolate->factory()->NewFixedArray(kEntries, TENURED)); |
| 4115 | return Handle<NormalizedMapCache>::cast(array); |
| 4116 | } |
| 4117 | |
| 4118 | |
| 4119 | MaybeHandle<Map> NormalizedMapCache::Get(Handle<Map> fast_map, |
| 4120 | PropertyNormalizationMode mode) { |
| 4121 | DisallowHeapAllocation no_gc; |
| 4122 | Object* value = FixedArray::get(GetIndex(fast_map)); |
| 4123 | if (!value->IsMap() || |
| 4124 | !Map::cast(value)->EquivalentToForNormalization(*fast_map, mode)) { |
| 4125 | return MaybeHandle<Map>(); |
| 4126 | } |
| 4127 | return handle(Map::cast(value)); |
| 4128 | } |
| 4129 | |
| 4130 | |
| 4131 | void NormalizedMapCache::Set(Handle<Map> fast_map, |
| 4132 | Handle<Map> normalized_map) { |
| 4133 | DisallowHeapAllocation no_gc; |
| 4134 | DCHECK(normalized_map->is_dictionary_map()); |
| 4135 | FixedArray::set(GetIndex(fast_map), *normalized_map); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4136 | } |
| 4137 | |
| 4138 | |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4139 | void NormalizedMapCache::Clear() { |
| 4140 | int entries = length(); |
| 4141 | for (int i = 0; i != entries; i++) { |
| 4142 | set_undefined(i); |
| 4143 | } |
| 4144 | } |
| 4145 | |
| 4146 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4147 | void HeapObject::UpdateMapCodeCache(Handle<HeapObject> object, |
| 4148 | Handle<Name> name, |
| 4149 | Handle<Code> code) { |
| 4150 | Handle<Map> map(object->map()); |
| 4151 | Map::UpdateCodeCache(map, name, code); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4152 | } |
| 4153 | |
| 4154 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4155 | void JSObject::NormalizeProperties(Handle<JSObject> object, |
| 4156 | PropertyNormalizationMode mode, |
| 4157 | int expected_additional_properties) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4158 | if (!object->HasFastProperties()) return; |
| 4159 | |
| 4160 | Handle<Map> map(object->map()); |
| 4161 | Handle<Map> new_map = Map::Normalize(map, mode); |
| 4162 | |
| 4163 | MigrateFastToSlow(object, new_map, expected_additional_properties); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4164 | } |
| 4165 | |
| 4166 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4167 | void JSObject::MigrateFastToSlow(Handle<JSObject> object, |
| 4168 | Handle<Map> new_map, |
| 4169 | int expected_additional_properties) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4170 | // The global object is always normalized. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4171 | DCHECK(!object->IsGlobalObject()); |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 4172 | // JSGlobalProxy must never be normalized |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4173 | DCHECK(!object->IsJSGlobalProxy()); |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 4174 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4175 | Isolate* isolate = object->GetIsolate(); |
| 4176 | HandleScope scope(isolate); |
| 4177 | Handle<Map> map(object->map()); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4178 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4179 | // Allocate new content. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4180 | int real_size = map->NumberOfOwnDescriptors(); |
| 4181 | int property_count = real_size; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4182 | if (expected_additional_properties > 0) { |
| 4183 | property_count += expected_additional_properties; |
| 4184 | } else { |
| 4185 | property_count += 2; // Make space for two more properties. |
| 4186 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4187 | Handle<NameDictionary> dictionary = |
| 4188 | NameDictionary::New(isolate, property_count); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4189 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4190 | Handle<DescriptorArray> descs(map->instance_descriptors()); |
| 4191 | for (int i = 0; i < real_size; i++) { |
| 4192 | PropertyDetails details = descs->GetDetails(i); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4193 | switch (details.type()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4194 | case CONSTANT: { |
| 4195 | Handle<Name> key(descs->GetKey(i)); |
| 4196 | Handle<Object> value(descs->GetConstant(i), isolate); |
| 4197 | PropertyDetails d = PropertyDetails( |
| 4198 | details.attributes(), NORMAL, i + 1); |
| 4199 | dictionary = NameDictionary::Add(dictionary, key, value, d); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4200 | break; |
| 4201 | } |
| 4202 | case FIELD: { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4203 | Handle<Name> key(descs->GetKey(i)); |
| 4204 | FieldIndex index = FieldIndex::ForDescriptor(*map, i); |
| 4205 | Handle<Object> value( |
| 4206 | object->RawFastPropertyAt(index), isolate); |
| 4207 | if (details.representation().IsDouble()) { |
| 4208 | DCHECK(value->IsMutableHeapNumber()); |
| 4209 | Handle<HeapNumber> old = Handle<HeapNumber>::cast(value); |
| 4210 | value = isolate->factory()->NewHeapNumber(old->value()); |
| 4211 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4212 | PropertyDetails d = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4213 | PropertyDetails(details.attributes(), NORMAL, i + 1); |
| 4214 | dictionary = NameDictionary::Add(dictionary, key, value, d); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4215 | break; |
| 4216 | } |
| 4217 | case CALLBACKS: { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4218 | Handle<Name> key(descs->GetKey(i)); |
| 4219 | Handle<Object> value(descs->GetCallbacksObject(i), isolate); |
| 4220 | PropertyDetails d = PropertyDetails( |
| 4221 | details.attributes(), CALLBACKS, i + 1); |
| 4222 | dictionary = NameDictionary::Add(dictionary, key, value, d); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4223 | break; |
| 4224 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4225 | case NORMAL: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4226 | UNREACHABLE(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4227 | break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4228 | } |
| 4229 | } |
| 4230 | |
| 4231 | // Copy the next enumeration index from instance descriptor. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4232 | dictionary->SetNextEnumerationIndex(real_size + 1); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4233 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4234 | // From here on we cannot fail and we shouldn't GC anymore. |
| 4235 | DisallowHeapAllocation no_allocation; |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4236 | |
| 4237 | // Resize the object in the heap if necessary. |
| 4238 | int new_instance_size = new_map->instance_size(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4239 | int instance_size_delta = map->instance_size() - new_instance_size; |
| 4240 | DCHECK(instance_size_delta >= 0); |
| 4241 | |
| 4242 | if (instance_size_delta > 0) { |
| 4243 | Heap* heap = isolate->heap(); |
| 4244 | heap->CreateFillerObjectAt(object->address() + new_instance_size, |
| 4245 | instance_size_delta); |
| 4246 | heap->AdjustLiveBytes(object->address(), -instance_size_delta, |
| 4247 | Heap::FROM_MUTATOR); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4248 | } |
| 4249 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4250 | // We are storing the new map using release store after creating a filler for |
| 4251 | // the left-over space to avoid races with the sweeper thread. |
| 4252 | object->synchronized_set_map(*new_map); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 4253 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4254 | object->set_properties(*dictionary); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4255 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4256 | isolate->counters()->props_to_dictionary()->Increment(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4257 | |
| 4258 | #ifdef DEBUG |
| 4259 | if (FLAG_trace_normalization) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4260 | OFStream os(stdout); |
| 4261 | os << "Object properties have been normalized:\n"; |
| 4262 | object->Print(os); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4263 | } |
| 4264 | #endif |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4265 | } |
| 4266 | |
| 4267 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4268 | void JSObject::MigrateSlowToFast(Handle<JSObject> object, |
| 4269 | int unused_property_fields) { |
| 4270 | if (object->HasFastProperties()) return; |
| 4271 | DCHECK(!object->IsGlobalObject()); |
| 4272 | Isolate* isolate = object->GetIsolate(); |
| 4273 | Factory* factory = isolate->factory(); |
| 4274 | Handle<NameDictionary> dictionary(object->property_dictionary()); |
| 4275 | |
| 4276 | // Make sure we preserve dictionary representation if there are too many |
| 4277 | // descriptors. |
| 4278 | int number_of_elements = dictionary->NumberOfElements(); |
| 4279 | if (number_of_elements > kMaxNumberOfDescriptors) return; |
| 4280 | |
| 4281 | if (number_of_elements != dictionary->NextEnumerationIndex()) { |
| 4282 | NameDictionary::DoGenerateNewEnumerationIndices(dictionary); |
| 4283 | } |
| 4284 | |
| 4285 | int instance_descriptor_length = 0; |
| 4286 | int number_of_fields = 0; |
| 4287 | |
| 4288 | // Compute the length of the instance descriptor. |
| 4289 | int capacity = dictionary->Capacity(); |
| 4290 | for (int i = 0; i < capacity; i++) { |
| 4291 | Object* k = dictionary->KeyAt(i); |
| 4292 | if (dictionary->IsKey(k)) { |
| 4293 | Object* value = dictionary->ValueAt(i); |
| 4294 | PropertyType type = dictionary->DetailsAt(i).type(); |
| 4295 | DCHECK(type != FIELD); |
| 4296 | instance_descriptor_length++; |
| 4297 | if (type == NORMAL && !value->IsJSFunction()) { |
| 4298 | number_of_fields += 1; |
| 4299 | } |
| 4300 | } |
| 4301 | } |
| 4302 | |
| 4303 | int inobject_props = object->map()->inobject_properties(); |
| 4304 | |
| 4305 | // Allocate new map. |
| 4306 | Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); |
| 4307 | new_map->set_dictionary_map(false); |
| 4308 | |
| 4309 | if (instance_descriptor_length == 0) { |
| 4310 | DisallowHeapAllocation no_gc; |
| 4311 | DCHECK_LE(unused_property_fields, inobject_props); |
| 4312 | // Transform the object. |
| 4313 | new_map->set_unused_property_fields(inobject_props); |
| 4314 | object->synchronized_set_map(*new_map); |
| 4315 | object->set_properties(isolate->heap()->empty_fixed_array()); |
| 4316 | // Check that it really works. |
| 4317 | DCHECK(object->HasFastProperties()); |
| 4318 | return; |
| 4319 | } |
| 4320 | |
| 4321 | // Allocate the instance descriptor. |
| 4322 | Handle<DescriptorArray> descriptors = DescriptorArray::Allocate( |
| 4323 | isolate, instance_descriptor_length); |
| 4324 | |
| 4325 | int number_of_allocated_fields = |
| 4326 | number_of_fields + unused_property_fields - inobject_props; |
| 4327 | if (number_of_allocated_fields < 0) { |
| 4328 | // There is enough inobject space for all fields (including unused). |
| 4329 | number_of_allocated_fields = 0; |
| 4330 | unused_property_fields = inobject_props - number_of_fields; |
| 4331 | } |
| 4332 | |
| 4333 | // Allocate the fixed array for the fields. |
| 4334 | Handle<FixedArray> fields = factory->NewFixedArray( |
| 4335 | number_of_allocated_fields); |
| 4336 | |
| 4337 | // Fill in the instance descriptor and the fields. |
| 4338 | int current_offset = 0; |
| 4339 | for (int i = 0; i < capacity; i++) { |
| 4340 | Object* k = dictionary->KeyAt(i); |
| 4341 | if (dictionary->IsKey(k)) { |
| 4342 | Object* value = dictionary->ValueAt(i); |
| 4343 | Handle<Name> key; |
| 4344 | if (k->IsSymbol()) { |
| 4345 | key = handle(Symbol::cast(k)); |
| 4346 | } else { |
| 4347 | // Ensure the key is a unique name before writing into the |
| 4348 | // instance descriptor. |
| 4349 | key = factory->InternalizeString(handle(String::cast(k))); |
| 4350 | } |
| 4351 | |
| 4352 | PropertyDetails details = dictionary->DetailsAt(i); |
| 4353 | int enumeration_index = details.dictionary_index(); |
| 4354 | PropertyType type = details.type(); |
| 4355 | |
| 4356 | if (value->IsJSFunction()) { |
| 4357 | ConstantDescriptor d(key, |
| 4358 | handle(value, isolate), |
| 4359 | details.attributes()); |
| 4360 | descriptors->Set(enumeration_index - 1, &d); |
| 4361 | } else if (type == NORMAL) { |
| 4362 | if (current_offset < inobject_props) { |
| 4363 | object->InObjectPropertyAtPut(current_offset, |
| 4364 | value, |
| 4365 | UPDATE_WRITE_BARRIER); |
| 4366 | } else { |
| 4367 | int offset = current_offset - inobject_props; |
| 4368 | fields->set(offset, value); |
| 4369 | } |
| 4370 | FieldDescriptor d(key, |
| 4371 | current_offset++, |
| 4372 | details.attributes(), |
| 4373 | // TODO(verwaest): value->OptimalRepresentation(); |
| 4374 | Representation::Tagged()); |
| 4375 | descriptors->Set(enumeration_index - 1, &d); |
| 4376 | } else if (type == CALLBACKS) { |
| 4377 | CallbacksDescriptor d(key, |
| 4378 | handle(value, isolate), |
| 4379 | details.attributes()); |
| 4380 | descriptors->Set(enumeration_index - 1, &d); |
| 4381 | } else { |
| 4382 | UNREACHABLE(); |
| 4383 | } |
| 4384 | } |
| 4385 | } |
| 4386 | DCHECK(current_offset == number_of_fields); |
| 4387 | |
| 4388 | descriptors->Sort(); |
| 4389 | |
| 4390 | DisallowHeapAllocation no_gc; |
| 4391 | new_map->InitializeDescriptors(*descriptors); |
| 4392 | new_map->set_unused_property_fields(unused_property_fields); |
| 4393 | |
| 4394 | // Transform the object. |
| 4395 | object->synchronized_set_map(*new_map); |
| 4396 | |
| 4397 | object->set_properties(*fields); |
| 4398 | DCHECK(object->IsJSObject()); |
| 4399 | |
| 4400 | // Check that it really works. |
| 4401 | DCHECK(object->HasFastProperties()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4402 | } |
| 4403 | |
| 4404 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4405 | void JSObject::ResetElements(Handle<JSObject> object) { |
| 4406 | Isolate* isolate = object->GetIsolate(); |
| 4407 | CHECK(object->map() != isolate->heap()->sloppy_arguments_elements_map()); |
| 4408 | if (object->map()->has_dictionary_elements()) { |
| 4409 | Handle<SeededNumberDictionary> new_elements = |
| 4410 | SeededNumberDictionary::New(isolate, 0); |
| 4411 | object->set_elements(*new_elements); |
| 4412 | } else { |
| 4413 | object->set_elements(object->map()->GetInitialElements()); |
| 4414 | } |
| 4415 | } |
| 4416 | |
| 4417 | |
| 4418 | static Handle<SeededNumberDictionary> CopyFastElementsToDictionary( |
| 4419 | Handle<FixedArrayBase> array, |
| 4420 | int length, |
| 4421 | Handle<SeededNumberDictionary> dictionary) { |
| 4422 | Isolate* isolate = array->GetIsolate(); |
| 4423 | Factory* factory = isolate->factory(); |
| 4424 | bool has_double_elements = array->IsFixedDoubleArray(); |
| 4425 | for (int i = 0; i < length; i++) { |
| 4426 | Handle<Object> value; |
| 4427 | if (has_double_elements) { |
| 4428 | Handle<FixedDoubleArray> double_array = |
| 4429 | Handle<FixedDoubleArray>::cast(array); |
| 4430 | if (double_array->is_the_hole(i)) { |
| 4431 | value = factory->the_hole_value(); |
| 4432 | } else { |
| 4433 | value = factory->NewHeapNumber(double_array->get_scalar(i)); |
| 4434 | } |
| 4435 | } else { |
| 4436 | value = handle(Handle<FixedArray>::cast(array)->get(i), isolate); |
| 4437 | } |
| 4438 | if (!value->IsTheHole()) { |
| 4439 | PropertyDetails details = PropertyDetails(NONE, NORMAL, 0); |
| 4440 | dictionary = |
| 4441 | SeededNumberDictionary::AddNumberEntry(dictionary, i, value, details); |
| 4442 | } |
| 4443 | } |
| 4444 | return dictionary; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4445 | } |
| 4446 | |
| 4447 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4448 | Handle<SeededNumberDictionary> JSObject::NormalizeElements( |
| 4449 | Handle<JSObject> object) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4450 | DCHECK(!object->HasExternalArrayElements() && |
| 4451 | !object->HasFixedTypedArrayElements()); |
| 4452 | Isolate* isolate = object->GetIsolate(); |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 4453 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4454 | // Find the backing store. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4455 | Handle<FixedArrayBase> array(FixedArrayBase::cast(object->elements())); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4456 | bool is_arguments = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4457 | (array->map() == isolate->heap()->sloppy_arguments_elements_map()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4458 | if (is_arguments) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4459 | array = handle(FixedArrayBase::cast( |
| 4460 | Handle<FixedArray>::cast(array)->get(1))); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 4461 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4462 | if (array->IsDictionary()) return Handle<SeededNumberDictionary>::cast(array); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4463 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4464 | DCHECK(object->HasFastSmiOrObjectElements() || |
| 4465 | object->HasFastDoubleElements() || |
| 4466 | object->HasFastArgumentsElements()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4467 | // Compute the effective length and allocate a new backing store. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4468 | int length = object->IsJSArray() |
| 4469 | ? Smi::cast(Handle<JSArray>::cast(object)->length())->value() |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4470 | : array->length(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 4471 | int old_capacity = 0; |
| 4472 | int used_elements = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4473 | object->GetElementsCapacityAndUsage(&old_capacity, &used_elements); |
| 4474 | Handle<SeededNumberDictionary> dictionary = |
| 4475 | SeededNumberDictionary::New(isolate, used_elements); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4476 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4477 | dictionary = CopyFastElementsToDictionary(array, length, dictionary); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4478 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4479 | // Switch to using the dictionary as the backing storage for elements. |
| 4480 | if (is_arguments) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4481 | FixedArray::cast(object->elements())->set(1, *dictionary); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4482 | } else { |
| 4483 | // Set the new map first to satify the elements type assert in |
| 4484 | // set_elements(). |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4485 | Handle<Map> new_map = |
| 4486 | JSObject::GetElementsTransitionMap(object, DICTIONARY_ELEMENTS); |
| 4487 | |
| 4488 | JSObject::MigrateToMap(object, new_map); |
| 4489 | object->set_elements(*dictionary); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4490 | } |
| 4491 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4492 | isolate->counters()->elements_to_dictionary()->Increment(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4493 | |
| 4494 | #ifdef DEBUG |
| 4495 | if (FLAG_trace_normalization) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4496 | OFStream os(stdout); |
| 4497 | os << "Object elements have been normalized:\n"; |
| 4498 | object->Print(os); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4499 | } |
| 4500 | #endif |
| 4501 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4502 | DCHECK(object->HasDictionaryElements() || |
| 4503 | object->HasDictionaryArgumentsElements()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 4504 | return dictionary; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4505 | } |
| 4506 | |
| 4507 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4508 | static Smi* GenerateIdentityHash(Isolate* isolate) { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 4509 | int hash_value; |
| 4510 | int attempts = 0; |
| 4511 | do { |
| 4512 | // Generate a random 32-bit hash value but limit range to fit |
| 4513 | // within a smi. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4514 | hash_value = isolate->random_number_generator()->NextInt() & Smi::kMaxValue; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 4515 | attempts++; |
| 4516 | } while (hash_value == 0 && attempts < 30); |
| 4517 | hash_value = hash_value != 0 ? hash_value : 1; // never return 0 |
| 4518 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4519 | return Smi::FromInt(hash_value); |
| 4520 | } |
| 4521 | |
| 4522 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4523 | void JSObject::SetIdentityHash(Handle<JSObject> object, Handle<Smi> hash) { |
| 4524 | DCHECK(!object->IsJSGlobalProxy()); |
| 4525 | Isolate* isolate = object->GetIsolate(); |
| 4526 | SetHiddenProperty(object, isolate->factory()->identity_hash_string(), hash); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4527 | } |
| 4528 | |
| 4529 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4530 | template<typename ProxyType> |
| 4531 | static Handle<Smi> GetOrCreateIdentityHashHelper(Handle<ProxyType> proxy) { |
| 4532 | Isolate* isolate = proxy->GetIsolate(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4533 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4534 | Handle<Object> maybe_hash(proxy->hash(), isolate); |
| 4535 | if (maybe_hash->IsSmi()) return Handle<Smi>::cast(maybe_hash); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4536 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4537 | Handle<Smi> hash(GenerateIdentityHash(isolate), isolate); |
| 4538 | proxy->set_hash(*hash); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 4539 | return hash; |
| 4540 | } |
| 4541 | |
| 4542 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4543 | Object* JSObject::GetIdentityHash() { |
| 4544 | DisallowHeapAllocation no_gc; |
| 4545 | Isolate* isolate = GetIsolate(); |
| 4546 | if (IsJSGlobalProxy()) { |
| 4547 | return JSGlobalProxy::cast(this)->hash(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4548 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4549 | Object* stored_value = |
| 4550 | GetHiddenProperty(isolate->factory()->identity_hash_string()); |
| 4551 | return stored_value->IsSmi() |
| 4552 | ? stored_value |
| 4553 | : isolate->heap()->undefined_value(); |
| 4554 | } |
| 4555 | |
| 4556 | |
| 4557 | Handle<Smi> JSObject::GetOrCreateIdentityHash(Handle<JSObject> object) { |
| 4558 | if (object->IsJSGlobalProxy()) { |
| 4559 | return GetOrCreateIdentityHashHelper(Handle<JSGlobalProxy>::cast(object)); |
| 4560 | } |
| 4561 | |
| 4562 | Isolate* isolate = object->GetIsolate(); |
| 4563 | |
| 4564 | Handle<Object> maybe_hash(object->GetIdentityHash(), isolate); |
| 4565 | if (maybe_hash->IsSmi()) return Handle<Smi>::cast(maybe_hash); |
| 4566 | |
| 4567 | Handle<Smi> hash(GenerateIdentityHash(isolate), isolate); |
| 4568 | SetHiddenProperty(object, isolate->factory()->identity_hash_string(), hash); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4569 | return hash; |
| 4570 | } |
| 4571 | |
| 4572 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4573 | Object* JSProxy::GetIdentityHash() { |
| 4574 | return this->hash(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4575 | } |
| 4576 | |
| 4577 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4578 | Handle<Smi> JSProxy::GetOrCreateIdentityHash(Handle<JSProxy> proxy) { |
| 4579 | return GetOrCreateIdentityHashHelper(proxy); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4580 | } |
| 4581 | |
| 4582 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4583 | Object* JSObject::GetHiddenProperty(Handle<Name> key) { |
| 4584 | DisallowHeapAllocation no_gc; |
| 4585 | DCHECK(key->IsUniqueName()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4586 | if (IsJSGlobalProxy()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4587 | // JSGlobalProxies store their hash internally. |
| 4588 | DCHECK(*key != GetHeap()->identity_hash_string()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4589 | // For a proxy, use the prototype as target object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4590 | PrototypeIterator iter(GetIsolate(), this); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4591 | // If the proxy is detached, return undefined. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4592 | if (iter.IsAtEnd()) return GetHeap()->the_hole_value(); |
| 4593 | DCHECK(iter.GetCurrent()->IsJSGlobalObject()); |
| 4594 | return JSObject::cast(iter.GetCurrent())->GetHiddenProperty(key); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4595 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4596 | DCHECK(!IsJSGlobalProxy()); |
| 4597 | Object* inline_value = GetHiddenPropertiesHashTable(); |
| 4598 | |
| 4599 | if (inline_value->IsSmi()) { |
| 4600 | // Handle inline-stored identity hash. |
| 4601 | if (*key == GetHeap()->identity_hash_string()) { |
| 4602 | return inline_value; |
| 4603 | } else { |
| 4604 | return GetHeap()->the_hole_value(); |
| 4605 | } |
| 4606 | } |
| 4607 | |
| 4608 | if (inline_value->IsUndefined()) return GetHeap()->the_hole_value(); |
| 4609 | |
| 4610 | ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value); |
| 4611 | Object* entry = hashtable->Lookup(key); |
| 4612 | return entry; |
| 4613 | } |
| 4614 | |
| 4615 | |
| 4616 | Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> object, |
| 4617 | Handle<Name> key, |
| 4618 | Handle<Object> value) { |
| 4619 | Isolate* isolate = object->GetIsolate(); |
| 4620 | |
| 4621 | DCHECK(key->IsUniqueName()); |
| 4622 | if (object->IsJSGlobalProxy()) { |
| 4623 | // JSGlobalProxies store their hash internally. |
| 4624 | DCHECK(*key != *isolate->factory()->identity_hash_string()); |
| 4625 | // For a proxy, use the prototype as target object. |
| 4626 | PrototypeIterator iter(isolate, object); |
| 4627 | // If the proxy is detached, return undefined. |
| 4628 | if (iter.IsAtEnd()) return isolate->factory()->undefined_value(); |
| 4629 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 4630 | return SetHiddenProperty( |
| 4631 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), key, |
| 4632 | value); |
| 4633 | } |
| 4634 | DCHECK(!object->IsJSGlobalProxy()); |
| 4635 | |
| 4636 | Handle<Object> inline_value(object->GetHiddenPropertiesHashTable(), isolate); |
| 4637 | |
| 4638 | // If there is no backing store yet, store the identity hash inline. |
| 4639 | if (value->IsSmi() && |
| 4640 | *key == *isolate->factory()->identity_hash_string() && |
| 4641 | (inline_value->IsUndefined() || inline_value->IsSmi())) { |
| 4642 | return JSObject::SetHiddenPropertiesHashTable(object, value); |
| 4643 | } |
| 4644 | |
| 4645 | Handle<ObjectHashTable> hashtable = |
| 4646 | GetOrCreateHiddenPropertiesHashtable(object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4647 | |
| 4648 | // If it was found, check if the key is already in the dictionary. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4649 | Handle<ObjectHashTable> new_table = ObjectHashTable::Put(hashtable, key, |
| 4650 | value); |
| 4651 | if (*new_table != *hashtable) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4652 | // If adding the key expanded the dictionary (i.e., Add returned a new |
| 4653 | // dictionary), store it back to the object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4654 | SetHiddenPropertiesHashTable(object, new_table); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4655 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4656 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4657 | // Return this to mark success. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4658 | return object; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4659 | } |
| 4660 | |
| 4661 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4662 | void JSObject::DeleteHiddenProperty(Handle<JSObject> object, Handle<Name> key) { |
| 4663 | Isolate* isolate = object->GetIsolate(); |
| 4664 | DCHECK(key->IsUniqueName()); |
| 4665 | |
| 4666 | if (object->IsJSGlobalProxy()) { |
| 4667 | PrototypeIterator iter(isolate, object); |
| 4668 | if (iter.IsAtEnd()) return; |
| 4669 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 4670 | return DeleteHiddenProperty( |
| 4671 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), key); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4672 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4673 | |
| 4674 | Object* inline_value = object->GetHiddenPropertiesHashTable(); |
| 4675 | |
| 4676 | // We never delete (inline-stored) identity hashes. |
| 4677 | DCHECK(*key != *isolate->factory()->identity_hash_string()); |
| 4678 | if (inline_value->IsUndefined() || inline_value->IsSmi()) return; |
| 4679 | |
| 4680 | Handle<ObjectHashTable> hashtable(ObjectHashTable::cast(inline_value)); |
| 4681 | bool was_present = false; |
| 4682 | ObjectHashTable::Remove(hashtable, key, &was_present); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4683 | } |
| 4684 | |
| 4685 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4686 | bool JSObject::HasHiddenProperties(Handle<JSObject> object) { |
| 4687 | Handle<Name> hidden = object->GetIsolate()->factory()->hidden_string(); |
| 4688 | LookupIterator it(object, hidden, LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 4689 | Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it); |
| 4690 | // Cannot get an exception since the hidden_string isn't accessible to JS. |
| 4691 | DCHECK(maybe.has_value); |
| 4692 | return maybe.value != ABSENT; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4693 | } |
| 4694 | |
| 4695 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4696 | Object* JSObject::GetHiddenPropertiesHashTable() { |
| 4697 | DCHECK(!IsJSGlobalProxy()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4698 | if (HasFastProperties()) { |
| 4699 | // If the object has fast properties, check whether the first slot |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4700 | // in the descriptor array matches the hidden string. Since the |
| 4701 | // hidden strings hash code is zero (and no other name has hash |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4702 | // code zero) it will always occupy the first entry if present. |
| 4703 | DescriptorArray* descriptors = this->map()->instance_descriptors(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4704 | if (descriptors->number_of_descriptors() > 0) { |
| 4705 | int sorted_index = descriptors->GetSortedKeyIndex(0); |
| 4706 | if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_string() && |
| 4707 | sorted_index < map()->NumberOfOwnDescriptors()) { |
| 4708 | DCHECK(descriptors->GetType(sorted_index) == FIELD); |
| 4709 | DCHECK(descriptors->GetDetails(sorted_index).representation(). |
| 4710 | IsCompatibleForLoad(Representation::Tagged())); |
| 4711 | FieldIndex index = FieldIndex::ForDescriptor(this->map(), |
| 4712 | sorted_index); |
| 4713 | return this->RawFastPropertyAt(index); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4714 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4715 | return GetHeap()->undefined_value(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4716 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4717 | } else { |
| 4718 | return GetHeap()->undefined_value(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4719 | } |
| 4720 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4721 | Isolate* isolate = GetIsolate(); |
| 4722 | LookupIterator it(handle(this), isolate->factory()->hidden_string(), |
| 4723 | LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 4724 | // Access check is always skipped for the hidden string anyways. |
| 4725 | return *GetDataProperty(&it); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4726 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4727 | } |
| 4728 | |
| 4729 | Handle<ObjectHashTable> JSObject::GetOrCreateHiddenPropertiesHashtable( |
| 4730 | Handle<JSObject> object) { |
| 4731 | Isolate* isolate = object->GetIsolate(); |
| 4732 | |
| 4733 | static const int kInitialCapacity = 4; |
| 4734 | Handle<Object> inline_value(object->GetHiddenPropertiesHashTable(), isolate); |
| 4735 | if (inline_value->IsHashTable()) { |
| 4736 | return Handle<ObjectHashTable>::cast(inline_value); |
| 4737 | } |
| 4738 | |
| 4739 | Handle<ObjectHashTable> hashtable = ObjectHashTable::New( |
| 4740 | isolate, kInitialCapacity, USE_CUSTOM_MINIMUM_CAPACITY); |
| 4741 | |
| 4742 | if (inline_value->IsSmi()) { |
| 4743 | // We were storing the identity hash inline and now allocated an actual |
| 4744 | // dictionary. Put the identity hash into the new dictionary. |
| 4745 | hashtable = ObjectHashTable::Put(hashtable, |
| 4746 | isolate->factory()->identity_hash_string(), |
| 4747 | inline_value); |
| 4748 | } |
| 4749 | |
| 4750 | SetHiddenPropertiesHashTable(object, hashtable); |
| 4751 | return hashtable; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4752 | } |
| 4753 | |
| 4754 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4755 | Handle<Object> JSObject::SetHiddenPropertiesHashTable(Handle<JSObject> object, |
| 4756 | Handle<Object> value) { |
| 4757 | DCHECK(!object->IsJSGlobalProxy()); |
| 4758 | Isolate* isolate = object->GetIsolate(); |
| 4759 | Handle<Name> name = isolate->factory()->hidden_string(); |
| 4760 | SetOwnPropertyIgnoreAttributes(object, name, value, DONT_ENUM).Assert(); |
| 4761 | return object; |
| 4762 | } |
| 4763 | |
| 4764 | |
| 4765 | MaybeHandle<Object> JSObject::DeletePropertyWithInterceptor( |
| 4766 | Handle<JSObject> holder, Handle<JSObject> receiver, Handle<Name> name) { |
| 4767 | Isolate* isolate = holder->GetIsolate(); |
| 4768 | |
| 4769 | // TODO(rossberg): Support symbols in the API. |
| 4770 | if (name->IsSymbol()) return MaybeHandle<Object>(); |
| 4771 | |
| 4772 | Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor()); |
| 4773 | if (interceptor->deleter()->IsUndefined()) return MaybeHandle<Object>(); |
| 4774 | |
| 4775 | v8::NamedPropertyDeleterCallback deleter = |
| 4776 | v8::ToCData<v8::NamedPropertyDeleterCallback>(interceptor->deleter()); |
| 4777 | LOG(isolate, |
| 4778 | ApiNamedPropertyAccess("interceptor-named-delete", *holder, *name)); |
| 4779 | PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, |
| 4780 | *holder); |
| 4781 | v8::Handle<v8::Boolean> result = |
| 4782 | args.Call(deleter, v8::Utils::ToLocal(Handle<String>::cast(name))); |
| 4783 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 4784 | if (result.IsEmpty()) return MaybeHandle<Object>(); |
| 4785 | |
| 4786 | DCHECK(result->IsBoolean()); |
| 4787 | Handle<Object> result_internal = v8::Utils::OpenHandle(*result); |
| 4788 | result_internal->VerifyApiCallResultType(); |
| 4789 | // Rebox CustomArguments::kReturnValueOffset before returning. |
| 4790 | return handle(*result_internal, isolate); |
| 4791 | } |
| 4792 | |
| 4793 | |
| 4794 | MaybeHandle<Object> JSObject::DeleteElementWithInterceptor( |
| 4795 | Handle<JSObject> object, |
| 4796 | uint32_t index) { |
| 4797 | Isolate* isolate = object->GetIsolate(); |
| 4798 | Factory* factory = isolate->factory(); |
| 4799 | |
| 4800 | // Make sure that the top context does not change when doing |
| 4801 | // callbacks or interceptor calls. |
| 4802 | AssertNoContextChange ncc(isolate); |
| 4803 | |
| 4804 | Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor()); |
| 4805 | if (interceptor->deleter()->IsUndefined()) return factory->false_value(); |
| 4806 | v8::IndexedPropertyDeleterCallback deleter = |
| 4807 | v8::ToCData<v8::IndexedPropertyDeleterCallback>(interceptor->deleter()); |
| 4808 | LOG(isolate, |
| 4809 | ApiIndexedPropertyAccess("interceptor-indexed-delete", *object, index)); |
| 4810 | PropertyCallbackArguments args( |
| 4811 | isolate, interceptor->data(), *object, *object); |
| 4812 | v8::Handle<v8::Boolean> result = args.Call(deleter, index); |
| 4813 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 4814 | if (!result.IsEmpty()) { |
| 4815 | DCHECK(result->IsBoolean()); |
| 4816 | Handle<Object> result_internal = v8::Utils::OpenHandle(*result); |
| 4817 | result_internal->VerifyApiCallResultType(); |
| 4818 | // Rebox CustomArguments::kReturnValueOffset before returning. |
| 4819 | return handle(*result_internal, isolate); |
| 4820 | } |
| 4821 | MaybeHandle<Object> delete_result = object->GetElementsAccessor()->Delete( |
| 4822 | object, index, NORMAL_DELETION); |
| 4823 | return delete_result; |
| 4824 | } |
| 4825 | |
| 4826 | |
| 4827 | MaybeHandle<Object> JSObject::DeleteElement(Handle<JSObject> object, |
| 4828 | uint32_t index, |
| 4829 | DeleteMode mode) { |
| 4830 | Isolate* isolate = object->GetIsolate(); |
| 4831 | Factory* factory = isolate->factory(); |
| 4832 | |
| 4833 | // Check access rights if needed. |
| 4834 | if (object->IsAccessCheckNeeded() && |
| 4835 | !isolate->MayIndexedAccess(object, index, v8::ACCESS_DELETE)) { |
| 4836 | isolate->ReportFailedAccessCheck(object, v8::ACCESS_DELETE); |
| 4837 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 4838 | return factory->false_value(); |
| 4839 | } |
| 4840 | |
| 4841 | if (object->IsStringObjectWithCharacterAt(index)) { |
| 4842 | if (mode == STRICT_DELETION) { |
| 4843 | // Deleting a non-configurable property in strict mode. |
| 4844 | Handle<Object> name = factory->NewNumberFromUint(index); |
| 4845 | Handle<Object> args[2] = { name, object }; |
| 4846 | THROW_NEW_ERROR(isolate, NewTypeError("strict_delete_property", |
| 4847 | HandleVector(args, 2)), |
| 4848 | Object); |
| 4849 | } |
| 4850 | return factory->false_value(); |
| 4851 | } |
| 4852 | |
| 4853 | if (object->IsJSGlobalProxy()) { |
| 4854 | PrototypeIterator iter(isolate, object); |
| 4855 | if (iter.IsAtEnd()) return factory->false_value(); |
| 4856 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 4857 | return DeleteElement( |
| 4858 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index, |
| 4859 | mode); |
| 4860 | } |
| 4861 | |
| 4862 | Handle<Object> old_value; |
| 4863 | bool should_enqueue_change_record = false; |
| 4864 | if (object->map()->is_observed()) { |
| 4865 | Maybe<bool> maybe = HasOwnElement(object, index); |
| 4866 | if (!maybe.has_value) return MaybeHandle<Object>(); |
| 4867 | should_enqueue_change_record = maybe.value; |
| 4868 | if (should_enqueue_change_record) { |
| 4869 | if (!GetOwnElementAccessorPair(object, index).is_null()) { |
| 4870 | old_value = Handle<Object>::cast(factory->the_hole_value()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4871 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4872 | old_value = Object::GetElement( |
| 4873 | isolate, object, index).ToHandleChecked(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4874 | } |
| 4875 | } |
| 4876 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4877 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4878 | // Skip interceptor if forcing deletion. |
| 4879 | MaybeHandle<Object> maybe_result; |
| 4880 | if (object->HasIndexedInterceptor() && mode != FORCE_DELETION) { |
| 4881 | maybe_result = DeleteElementWithInterceptor(object, index); |
| 4882 | } else { |
| 4883 | maybe_result = object->GetElementsAccessor()->Delete(object, index, mode); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 4884 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4885 | Handle<Object> result; |
| 4886 | ASSIGN_RETURN_ON_EXCEPTION(isolate, result, maybe_result, Object); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4887 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4888 | if (should_enqueue_change_record) { |
| 4889 | Maybe<bool> maybe = HasOwnElement(object, index); |
| 4890 | if (!maybe.has_value) return MaybeHandle<Object>(); |
| 4891 | if (!maybe.value) { |
| 4892 | Handle<String> name = factory->Uint32ToString(index); |
| 4893 | EnqueueChangeRecord(object, "delete", name, old_value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4894 | } |
| 4895 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4896 | |
| 4897 | return result; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4898 | } |
| 4899 | |
| 4900 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4901 | MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object, |
| 4902 | Handle<Name> name, |
| 4903 | DeleteMode delete_mode) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4904 | // ECMA-262, 3rd, 8.6.2.5 |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4905 | DCHECK(name->IsName()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4906 | |
| 4907 | uint32_t index = 0; |
| 4908 | if (name->AsArrayIndex(&index)) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4909 | return DeleteElement(object, index, delete_mode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4910 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4911 | |
| 4912 | // Skip interceptors on FORCE_DELETION. |
| 4913 | LookupIterator::Configuration config = |
| 4914 | delete_mode == FORCE_DELETION ? LookupIterator::HIDDEN_SKIP_INTERCEPTOR |
| 4915 | : LookupIterator::HIDDEN; |
| 4916 | |
| 4917 | LookupIterator it(object, name, config); |
| 4918 | |
| 4919 | bool is_observed = object->map()->is_observed() && |
| 4920 | *name != it.isolate()->heap()->hidden_string(); |
| 4921 | Handle<Object> old_value = it.isolate()->factory()->the_hole_value(); |
| 4922 | |
| 4923 | for (; it.IsFound(); it.Next()) { |
| 4924 | switch (it.state()) { |
| 4925 | case LookupIterator::JSPROXY: |
| 4926 | case LookupIterator::NOT_FOUND: |
| 4927 | case LookupIterator::TRANSITION: |
| 4928 | UNREACHABLE(); |
| 4929 | case LookupIterator::ACCESS_CHECK: |
| 4930 | if (it.HasAccess(v8::ACCESS_DELETE)) break; |
| 4931 | it.isolate()->ReportFailedAccessCheck(it.GetHolder<JSObject>(), |
| 4932 | v8::ACCESS_DELETE); |
| 4933 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it.isolate(), Object); |
| 4934 | return it.isolate()->factory()->false_value(); |
| 4935 | case LookupIterator::INTERCEPTOR: { |
| 4936 | MaybeHandle<Object> maybe_result = |
| 4937 | JSObject::DeletePropertyWithInterceptor(it.GetHolder<JSObject>(), |
| 4938 | object, it.name()); |
| 4939 | // Delete with interceptor succeeded. Return result. |
| 4940 | if (!maybe_result.is_null()) return maybe_result; |
| 4941 | // An exception was thrown in the interceptor. Propagate. |
| 4942 | if (it.isolate()->has_pending_exception()) return maybe_result; |
| 4943 | break; |
| 4944 | } |
| 4945 | case LookupIterator::DATA: |
| 4946 | if (is_observed) { |
| 4947 | old_value = it.GetDataValue(); |
| 4948 | } |
| 4949 | // Fall through. |
| 4950 | case LookupIterator::ACCESSOR: { |
| 4951 | if (delete_mode != FORCE_DELETION && !it.IsConfigurable()) { |
| 4952 | // Fail if the property is not configurable. |
| 4953 | if (delete_mode == STRICT_DELETION) { |
| 4954 | Handle<Object> args[2] = {name, object}; |
| 4955 | THROW_NEW_ERROR(it.isolate(), |
| 4956 | NewTypeError("strict_delete_property", |
| 4957 | HandleVector(args, arraysize(args))), |
| 4958 | Object); |
| 4959 | } |
| 4960 | return it.isolate()->factory()->false_value(); |
| 4961 | } |
| 4962 | |
| 4963 | PropertyNormalizationMode mode = object->map()->is_prototype_map() |
| 4964 | ? KEEP_INOBJECT_PROPERTIES |
| 4965 | : CLEAR_INOBJECT_PROPERTIES; |
| 4966 | Handle<JSObject> holder = it.GetHolder<JSObject>(); |
| 4967 | // TODO(verwaest): Remove this temporary compatibility hack when blink |
| 4968 | // tests are updated. |
| 4969 | if (!holder.is_identical_to(object) && |
| 4970 | !(object->IsJSGlobalProxy() && holder->IsJSGlobalObject())) { |
| 4971 | return it.isolate()->factory()->true_value(); |
| 4972 | } |
| 4973 | NormalizeProperties(holder, mode, 0); |
| 4974 | Handle<Object> result = |
| 4975 | DeleteNormalizedProperty(holder, name, delete_mode); |
| 4976 | ReoptimizeIfPrototype(holder); |
| 4977 | |
| 4978 | if (is_observed) { |
| 4979 | EnqueueChangeRecord(object, "delete", name, old_value); |
| 4980 | } |
| 4981 | |
| 4982 | return result; |
| 4983 | } |
| 4984 | } |
| 4985 | } |
| 4986 | |
| 4987 | return it.isolate()->factory()->true_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 4988 | } |
| 4989 | |
| 4990 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4991 | MaybeHandle<Object> JSReceiver::DeleteElement(Handle<JSReceiver> object, |
| 4992 | uint32_t index, |
| 4993 | DeleteMode mode) { |
| 4994 | if (object->IsJSProxy()) { |
| 4995 | return JSProxy::DeleteElementWithHandler( |
| 4996 | Handle<JSProxy>::cast(object), index, mode); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4997 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 4998 | return JSObject::DeleteElement(Handle<JSObject>::cast(object), index, mode); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4999 | } |
| 5000 | |
| 5001 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5002 | MaybeHandle<Object> JSReceiver::DeleteProperty(Handle<JSReceiver> object, |
| 5003 | Handle<Name> name, |
| 5004 | DeleteMode mode) { |
| 5005 | if (object->IsJSProxy()) { |
| 5006 | return JSProxy::DeletePropertyWithHandler( |
| 5007 | Handle<JSProxy>::cast(object), name, mode); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5008 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5009 | return JSObject::DeleteProperty(Handle<JSObject>::cast(object), name, mode); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5010 | } |
| 5011 | |
| 5012 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5013 | bool JSObject::ReferencesObjectFromElements(FixedArray* elements, |
| 5014 | ElementsKind kind, |
| 5015 | Object* object) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5016 | DCHECK(IsFastObjectElementsKind(kind) || |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5017 | kind == DICTIONARY_ELEMENTS); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5018 | if (IsFastObjectElementsKind(kind)) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5019 | int length = IsJSArray() |
| 5020 | ? Smi::cast(JSArray::cast(this)->length())->value() |
| 5021 | : elements->length(); |
| 5022 | for (int i = 0; i < length; ++i) { |
| 5023 | Object* element = elements->get(i); |
| 5024 | if (!element->IsTheHole() && element == object) return true; |
| 5025 | } |
| 5026 | } else { |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 5027 | Object* key = |
| 5028 | SeededNumberDictionary::cast(elements)->SlowReverseLookup(object); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5029 | if (!key->IsUndefined()) return true; |
| 5030 | } |
| 5031 | return false; |
| 5032 | } |
| 5033 | |
| 5034 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5035 | // Check whether this object references another object. |
| 5036 | bool JSObject::ReferencesObject(Object* obj) { |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 5037 | Map* map_of_this = map(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5038 | Heap* heap = GetHeap(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5039 | DisallowHeapAllocation no_allocation; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5040 | |
| 5041 | // Is the object the constructor for this object? |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 5042 | if (map_of_this->constructor() == obj) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5043 | return true; |
| 5044 | } |
| 5045 | |
| 5046 | // Is the object the prototype for this object? |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 5047 | if (map_of_this->prototype() == obj) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5048 | return true; |
| 5049 | } |
| 5050 | |
| 5051 | // Check if the object is among the named properties. |
| 5052 | Object* key = SlowReverseLookup(obj); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 5053 | if (!key->IsUndefined()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5054 | return true; |
| 5055 | } |
| 5056 | |
| 5057 | // Check if the object is among the indexed properties. |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5058 | ElementsKind kind = GetElementsKind(); |
| 5059 | switch (kind) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5060 | // Raw pixels and external arrays do not reference other |
| 5061 | // objects. |
| 5062 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 5063 | case EXTERNAL_##TYPE##_ELEMENTS: \ |
| 5064 | case TYPE##_ELEMENTS: \ |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5065 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5066 | |
| 5067 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 5068 | #undef TYPED_ARRAY_CASE |
| 5069 | |
| 5070 | case FAST_DOUBLE_ELEMENTS: |
| 5071 | case FAST_HOLEY_DOUBLE_ELEMENTS: |
| 5072 | break; |
| 5073 | case FAST_SMI_ELEMENTS: |
| 5074 | case FAST_HOLEY_SMI_ELEMENTS: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5075 | break; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5076 | case FAST_ELEMENTS: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5077 | case FAST_HOLEY_ELEMENTS: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5078 | case DICTIONARY_ELEMENTS: { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5079 | FixedArray* elements = FixedArray::cast(this->elements()); |
| 5080 | if (ReferencesObjectFromElements(elements, kind, obj)) return true; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5081 | break; |
| 5082 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5083 | case SLOPPY_ARGUMENTS_ELEMENTS: { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5084 | FixedArray* parameter_map = FixedArray::cast(elements()); |
| 5085 | // Check the mapped parameters. |
| 5086 | int length = parameter_map->length(); |
| 5087 | for (int i = 2; i < length; ++i) { |
| 5088 | Object* value = parameter_map->get(i); |
| 5089 | if (!value->IsTheHole() && value == obj) return true; |
| 5090 | } |
| 5091 | // Check the arguments. |
| 5092 | FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5093 | kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : |
| 5094 | FAST_HOLEY_ELEMENTS; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5095 | if (ReferencesObjectFromElements(arguments, kind, obj)) return true; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5096 | break; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5097 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5098 | } |
| 5099 | |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 5100 | // For functions check the context. |
| 5101 | if (IsJSFunction()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5102 | // Get the constructor function for arguments array. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5103 | Map* arguments_map = |
| 5104 | heap->isolate()->context()->native_context()->sloppy_arguments_map(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5105 | JSFunction* arguments_function = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5106 | JSFunction::cast(arguments_map->constructor()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5107 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5108 | // Get the context and don't check if it is the native context. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5109 | JSFunction* f = JSFunction::cast(this); |
| 5110 | Context* context = f->context(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5111 | if (context->IsNativeContext()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5112 | return false; |
| 5113 | } |
| 5114 | |
| 5115 | // Check the non-special context slots. |
| 5116 | for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) { |
| 5117 | // Only check JS objects. |
| 5118 | if (context->get(i)->IsJSObject()) { |
| 5119 | JSObject* ctxobj = JSObject::cast(context->get(i)); |
| 5120 | // If it is an arguments array check the content. |
| 5121 | if (ctxobj->map()->constructor() == arguments_function) { |
| 5122 | if (ctxobj->ReferencesObject(obj)) { |
| 5123 | return true; |
| 5124 | } |
| 5125 | } else if (ctxobj == obj) { |
| 5126 | return true; |
| 5127 | } |
| 5128 | } |
| 5129 | } |
| 5130 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5131 | // Check the context extension (if any) if it can have references. |
| 5132 | if (context->has_extension() && !context->IsCatchContext()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5133 | // With harmony scoping, a JSFunction may have a global context. |
| 5134 | // TODO(mvstanton): walk into the ScopeInfo. |
| 5135 | if (FLAG_harmony_scoping && context->IsGlobalContext()) { |
| 5136 | return false; |
| 5137 | } |
| 5138 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5139 | return JSObject::cast(context->extension())->ReferencesObject(obj); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5140 | } |
| 5141 | } |
| 5142 | |
| 5143 | // No references to object. |
| 5144 | return false; |
| 5145 | } |
| 5146 | |
| 5147 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5148 | MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { |
| 5149 | Isolate* isolate = object->GetIsolate(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5150 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5151 | if (!object->map()->is_extensible()) return object; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5152 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5153 | if (object->IsAccessCheckNeeded() && |
| 5154 | !isolate->MayNamedAccess( |
| 5155 | object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) { |
| 5156 | isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS); |
| 5157 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 5158 | return isolate->factory()->false_value(); |
Ben Murdoch | e0cee9b | 2011-05-25 10:26:03 +0100 | [diff] [blame] | 5159 | } |
| 5160 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5161 | if (object->IsJSGlobalProxy()) { |
| 5162 | PrototypeIterator iter(isolate, object); |
| 5163 | if (iter.IsAtEnd()) return object; |
| 5164 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 5165 | return PreventExtensions( |
| 5166 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter))); |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 5167 | } |
| 5168 | |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 5169 | // It's not possible to seal objects with external array elements |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5170 | if (object->HasExternalArrayElements() || |
| 5171 | object->HasFixedTypedArrayElements()) { |
| 5172 | THROW_NEW_ERROR(isolate, |
| 5173 | NewTypeError("cant_prevent_ext_external_array_elements", |
| 5174 | HandleVector(&object, 1)), |
| 5175 | Object); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 5176 | } |
| 5177 | |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 5178 | // If there are fast elements we normalize. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5179 | Handle<SeededNumberDictionary> dictionary = NormalizeElements(object); |
| 5180 | DCHECK(object->HasDictionaryElements() || |
| 5181 | object->HasDictionaryArgumentsElements()); |
| 5182 | |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 5183 | // Make sure that we never go back to fast case. |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5184 | dictionary->set_requires_slow_elements(); |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 5185 | |
| 5186 | // Do a map transition, other objects with this map may still |
| 5187 | // be extensible. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5188 | // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. |
| 5189 | Handle<Map> new_map = Map::Copy(handle(object->map())); |
| 5190 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5191 | new_map->set_is_extensible(false); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5192 | JSObject::MigrateToMap(object, new_map); |
| 5193 | DCHECK(!object->map()->is_extensible()); |
| 5194 | |
| 5195 | if (object->map()->is_observed()) { |
| 5196 | EnqueueChangeRecord(object, "preventExtensions", Handle<Name>(), |
| 5197 | isolate->factory()->the_hole_value()); |
| 5198 | } |
| 5199 | return object; |
| 5200 | } |
| 5201 | |
| 5202 | |
| 5203 | template<typename Dictionary> |
| 5204 | static void FreezeDictionary(Dictionary* dictionary) { |
| 5205 | int capacity = dictionary->Capacity(); |
| 5206 | for (int i = 0; i < capacity; i++) { |
| 5207 | Object* k = dictionary->KeyAt(i); |
| 5208 | if (dictionary->IsKey(k) && |
| 5209 | !(k->IsSymbol() && Symbol::cast(k)->is_private())) { |
| 5210 | PropertyDetails details = dictionary->DetailsAt(i); |
| 5211 | int attrs = DONT_DELETE; |
| 5212 | // READ_ONLY is an invalid attribute for JS setters/getters. |
| 5213 | if (details.type() == CALLBACKS) { |
| 5214 | Object* v = dictionary->ValueAt(i); |
| 5215 | if (v->IsPropertyCell()) v = PropertyCell::cast(v)->value(); |
| 5216 | if (!v->IsAccessorPair()) attrs |= READ_ONLY; |
| 5217 | } else { |
| 5218 | attrs |= READ_ONLY; |
| 5219 | } |
| 5220 | details = details.CopyAddAttributes( |
| 5221 | static_cast<PropertyAttributes>(attrs)); |
| 5222 | dictionary->DetailsAtPut(i, details); |
| 5223 | } |
| 5224 | } |
| 5225 | } |
| 5226 | |
| 5227 | |
| 5228 | MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) { |
| 5229 | // Freezing sloppy arguments should be handled elsewhere. |
| 5230 | DCHECK(!object->HasSloppyArgumentsElements()); |
| 5231 | DCHECK(!object->map()->is_observed()); |
| 5232 | |
| 5233 | if (object->map()->is_frozen()) return object; |
| 5234 | |
| 5235 | Isolate* isolate = object->GetIsolate(); |
| 5236 | if (object->IsAccessCheckNeeded() && |
| 5237 | !isolate->MayNamedAccess( |
| 5238 | object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) { |
| 5239 | isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS); |
| 5240 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 5241 | return isolate->factory()->false_value(); |
| 5242 | } |
| 5243 | |
| 5244 | if (object->IsJSGlobalProxy()) { |
| 5245 | PrototypeIterator iter(isolate, object); |
| 5246 | if (iter.IsAtEnd()) return object; |
| 5247 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 5248 | return Freeze(Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter))); |
| 5249 | } |
| 5250 | |
| 5251 | // It's not possible to freeze objects with external array elements |
| 5252 | if (object->HasExternalArrayElements() || |
| 5253 | object->HasFixedTypedArrayElements()) { |
| 5254 | THROW_NEW_ERROR(isolate, |
| 5255 | NewTypeError("cant_prevent_ext_external_array_elements", |
| 5256 | HandleVector(&object, 1)), |
| 5257 | Object); |
| 5258 | } |
| 5259 | |
| 5260 | Handle<SeededNumberDictionary> new_element_dictionary; |
| 5261 | if (!object->elements()->IsDictionary()) { |
| 5262 | int length = object->IsJSArray() |
| 5263 | ? Smi::cast(Handle<JSArray>::cast(object)->length())->value() |
| 5264 | : object->elements()->length(); |
| 5265 | if (length > 0) { |
| 5266 | int capacity = 0; |
| 5267 | int used = 0; |
| 5268 | object->GetElementsCapacityAndUsage(&capacity, &used); |
| 5269 | new_element_dictionary = SeededNumberDictionary::New(isolate, used); |
| 5270 | |
| 5271 | // Move elements to a dictionary; avoid calling NormalizeElements to avoid |
| 5272 | // unnecessary transitions. |
| 5273 | new_element_dictionary = CopyFastElementsToDictionary( |
| 5274 | handle(object->elements()), length, new_element_dictionary); |
| 5275 | } else { |
| 5276 | // No existing elements, use a pre-allocated empty backing store |
| 5277 | new_element_dictionary = |
| 5278 | isolate->factory()->empty_slow_element_dictionary(); |
| 5279 | } |
| 5280 | } |
| 5281 | |
| 5282 | Handle<Map> old_map(object->map(), isolate); |
| 5283 | int transition_index = old_map->SearchTransition( |
| 5284 | isolate->heap()->frozen_symbol()); |
| 5285 | if (transition_index != TransitionArray::kNotFound) { |
| 5286 | Handle<Map> transition_map(old_map->GetTransition(transition_index)); |
| 5287 | DCHECK(transition_map->has_dictionary_elements()); |
| 5288 | DCHECK(transition_map->is_frozen()); |
| 5289 | DCHECK(!transition_map->is_extensible()); |
| 5290 | JSObject::MigrateToMap(object, transition_map); |
| 5291 | } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) { |
| 5292 | // Create a new descriptor array with fully-frozen properties |
| 5293 | Handle<Map> new_map = Map::CopyForFreeze(old_map); |
| 5294 | JSObject::MigrateToMap(object, new_map); |
| 5295 | } else { |
| 5296 | DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map()); |
| 5297 | // Slow path: need to normalize properties for safety |
| 5298 | NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0); |
| 5299 | |
| 5300 | // Create a new map, since other objects with this map may be extensible. |
| 5301 | // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. |
| 5302 | Handle<Map> new_map = Map::Copy(handle(object->map())); |
| 5303 | new_map->freeze(); |
| 5304 | new_map->set_is_extensible(false); |
| 5305 | new_map->set_elements_kind(DICTIONARY_ELEMENTS); |
| 5306 | JSObject::MigrateToMap(object, new_map); |
| 5307 | |
| 5308 | // Freeze dictionary-mode properties |
| 5309 | FreezeDictionary(object->property_dictionary()); |
| 5310 | } |
| 5311 | |
| 5312 | DCHECK(object->map()->has_dictionary_elements()); |
| 5313 | if (!new_element_dictionary.is_null()) { |
| 5314 | object->set_elements(*new_element_dictionary); |
| 5315 | } |
| 5316 | |
| 5317 | if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) { |
| 5318 | SeededNumberDictionary* dictionary = object->element_dictionary(); |
| 5319 | // Make sure we never go back to the fast case |
| 5320 | dictionary->set_requires_slow_elements(); |
| 5321 | // Freeze all elements in the dictionary |
| 5322 | FreezeDictionary(dictionary); |
| 5323 | } |
| 5324 | |
| 5325 | return object; |
| 5326 | } |
| 5327 | |
| 5328 | |
| 5329 | void JSObject::SetObserved(Handle<JSObject> object) { |
| 5330 | DCHECK(!object->IsJSGlobalProxy()); |
| 5331 | DCHECK(!object->IsJSGlobalObject()); |
| 5332 | Isolate* isolate = object->GetIsolate(); |
| 5333 | Handle<Map> new_map; |
| 5334 | Handle<Map> old_map(object->map(), isolate); |
| 5335 | DCHECK(!old_map->is_observed()); |
| 5336 | int transition_index = old_map->SearchTransition( |
| 5337 | isolate->heap()->observed_symbol()); |
| 5338 | if (transition_index != TransitionArray::kNotFound) { |
| 5339 | new_map = handle(old_map->GetTransition(transition_index), isolate); |
| 5340 | DCHECK(new_map->is_observed()); |
| 5341 | } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) { |
| 5342 | new_map = Map::CopyForObserved(old_map); |
| 5343 | } else { |
| 5344 | new_map = Map::Copy(old_map); |
| 5345 | new_map->set_is_observed(); |
| 5346 | } |
| 5347 | JSObject::MigrateToMap(object, new_map); |
| 5348 | } |
| 5349 | |
| 5350 | |
| 5351 | Handle<Object> JSObject::FastPropertyAt(Handle<JSObject> object, |
| 5352 | Representation representation, |
| 5353 | FieldIndex index) { |
| 5354 | Isolate* isolate = object->GetIsolate(); |
| 5355 | Handle<Object> raw_value(object->RawFastPropertyAt(index), isolate); |
| 5356 | return Object::WrapForRead(isolate, raw_value, representation); |
| 5357 | } |
| 5358 | |
| 5359 | |
| 5360 | template<class ContextObject> |
| 5361 | class JSObjectWalkVisitor { |
| 5362 | public: |
| 5363 | JSObjectWalkVisitor(ContextObject* site_context, bool copying, |
| 5364 | JSObject::DeepCopyHints hints) |
| 5365 | : site_context_(site_context), |
| 5366 | copying_(copying), |
| 5367 | hints_(hints) {} |
| 5368 | |
| 5369 | MUST_USE_RESULT MaybeHandle<JSObject> StructureWalk(Handle<JSObject> object); |
| 5370 | |
| 5371 | protected: |
| 5372 | MUST_USE_RESULT inline MaybeHandle<JSObject> VisitElementOrProperty( |
| 5373 | Handle<JSObject> object, |
| 5374 | Handle<JSObject> value) { |
| 5375 | Handle<AllocationSite> current_site = site_context()->EnterNewScope(); |
| 5376 | MaybeHandle<JSObject> copy_of_value = StructureWalk(value); |
| 5377 | site_context()->ExitScope(current_site, value); |
| 5378 | return copy_of_value; |
| 5379 | } |
| 5380 | |
| 5381 | inline ContextObject* site_context() { return site_context_; } |
| 5382 | inline Isolate* isolate() { return site_context()->isolate(); } |
| 5383 | |
| 5384 | inline bool copying() const { return copying_; } |
| 5385 | |
| 5386 | private: |
| 5387 | ContextObject* site_context_; |
| 5388 | const bool copying_; |
| 5389 | const JSObject::DeepCopyHints hints_; |
| 5390 | }; |
| 5391 | |
| 5392 | |
| 5393 | template <class ContextObject> |
| 5394 | MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk( |
| 5395 | Handle<JSObject> object) { |
| 5396 | Isolate* isolate = this->isolate(); |
| 5397 | bool copying = this->copying(); |
| 5398 | bool shallow = hints_ == JSObject::kObjectIsShallow; |
| 5399 | |
| 5400 | if (!shallow) { |
| 5401 | StackLimitCheck check(isolate); |
| 5402 | |
| 5403 | if (check.HasOverflowed()) { |
| 5404 | isolate->StackOverflow(); |
| 5405 | return MaybeHandle<JSObject>(); |
| 5406 | } |
| 5407 | } |
| 5408 | |
| 5409 | if (object->map()->is_deprecated()) { |
| 5410 | JSObject::MigrateInstance(object); |
| 5411 | } |
| 5412 | |
| 5413 | Handle<JSObject> copy; |
| 5414 | if (copying) { |
| 5415 | Handle<AllocationSite> site_to_pass; |
| 5416 | if (site_context()->ShouldCreateMemento(object)) { |
| 5417 | site_to_pass = site_context()->current(); |
| 5418 | } |
| 5419 | copy = isolate->factory()->CopyJSObjectWithAllocationSite( |
| 5420 | object, site_to_pass); |
| 5421 | } else { |
| 5422 | copy = object; |
| 5423 | } |
| 5424 | |
| 5425 | DCHECK(copying || copy.is_identical_to(object)); |
| 5426 | |
| 5427 | ElementsKind kind = copy->GetElementsKind(); |
| 5428 | if (copying && IsFastSmiOrObjectElementsKind(kind) && |
| 5429 | FixedArray::cast(copy->elements())->map() == |
| 5430 | isolate->heap()->fixed_cow_array_map()) { |
| 5431 | isolate->counters()->cow_arrays_created_runtime()->Increment(); |
| 5432 | } |
| 5433 | |
| 5434 | if (!shallow) { |
| 5435 | HandleScope scope(isolate); |
| 5436 | |
| 5437 | // Deep copy own properties. |
| 5438 | if (copy->HasFastProperties()) { |
| 5439 | Handle<DescriptorArray> descriptors(copy->map()->instance_descriptors()); |
| 5440 | int limit = copy->map()->NumberOfOwnDescriptors(); |
| 5441 | for (int i = 0; i < limit; i++) { |
| 5442 | PropertyDetails details = descriptors->GetDetails(i); |
| 5443 | if (details.type() != FIELD) continue; |
| 5444 | FieldIndex index = FieldIndex::ForDescriptor(copy->map(), i); |
| 5445 | Handle<Object> value(object->RawFastPropertyAt(index), isolate); |
| 5446 | if (value->IsJSObject()) { |
| 5447 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5448 | isolate, value, |
| 5449 | VisitElementOrProperty(copy, Handle<JSObject>::cast(value)), |
| 5450 | JSObject); |
| 5451 | } else { |
| 5452 | Representation representation = details.representation(); |
| 5453 | value = Object::NewStorageFor(isolate, value, representation); |
| 5454 | } |
| 5455 | if (copying) { |
| 5456 | copy->FastPropertyAtPut(index, *value); |
| 5457 | } |
| 5458 | } |
| 5459 | } else { |
| 5460 | Handle<FixedArray> names = |
| 5461 | isolate->factory()->NewFixedArray(copy->NumberOfOwnProperties()); |
| 5462 | copy->GetOwnPropertyNames(*names, 0); |
| 5463 | for (int i = 0; i < names->length(); i++) { |
| 5464 | DCHECK(names->get(i)->IsString()); |
| 5465 | Handle<String> key_string(String::cast(names->get(i))); |
| 5466 | Maybe<PropertyAttributes> maybe = |
| 5467 | JSReceiver::GetOwnPropertyAttributes(copy, key_string); |
| 5468 | DCHECK(maybe.has_value); |
| 5469 | PropertyAttributes attributes = maybe.value; |
| 5470 | // Only deep copy fields from the object literal expression. |
| 5471 | // In particular, don't try to copy the length attribute of |
| 5472 | // an array. |
| 5473 | if (attributes != NONE) continue; |
| 5474 | Handle<Object> value = |
| 5475 | Object::GetProperty(copy, key_string).ToHandleChecked(); |
| 5476 | if (value->IsJSObject()) { |
| 5477 | Handle<JSObject> result; |
| 5478 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5479 | isolate, result, |
| 5480 | VisitElementOrProperty(copy, Handle<JSObject>::cast(value)), |
| 5481 | JSObject); |
| 5482 | if (copying) { |
| 5483 | // Creating object copy for literals. No strict mode needed. |
| 5484 | JSObject::SetProperty(copy, key_string, result, SLOPPY).Assert(); |
| 5485 | } |
| 5486 | } |
| 5487 | } |
| 5488 | } |
| 5489 | |
| 5490 | // Deep copy own elements. |
| 5491 | // Pixel elements cannot be created using an object literal. |
| 5492 | DCHECK(!copy->HasExternalArrayElements()); |
| 5493 | switch (kind) { |
| 5494 | case FAST_SMI_ELEMENTS: |
| 5495 | case FAST_ELEMENTS: |
| 5496 | case FAST_HOLEY_SMI_ELEMENTS: |
| 5497 | case FAST_HOLEY_ELEMENTS: { |
| 5498 | Handle<FixedArray> elements(FixedArray::cast(copy->elements())); |
| 5499 | if (elements->map() == isolate->heap()->fixed_cow_array_map()) { |
| 5500 | #ifdef DEBUG |
| 5501 | for (int i = 0; i < elements->length(); i++) { |
| 5502 | DCHECK(!elements->get(i)->IsJSObject()); |
| 5503 | } |
| 5504 | #endif |
| 5505 | } else { |
| 5506 | for (int i = 0; i < elements->length(); i++) { |
| 5507 | Handle<Object> value(elements->get(i), isolate); |
| 5508 | DCHECK(value->IsSmi() || |
| 5509 | value->IsTheHole() || |
| 5510 | (IsFastObjectElementsKind(copy->GetElementsKind()))); |
| 5511 | if (value->IsJSObject()) { |
| 5512 | Handle<JSObject> result; |
| 5513 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5514 | isolate, result, |
| 5515 | VisitElementOrProperty(copy, Handle<JSObject>::cast(value)), |
| 5516 | JSObject); |
| 5517 | if (copying) { |
| 5518 | elements->set(i, *result); |
| 5519 | } |
| 5520 | } |
| 5521 | } |
| 5522 | } |
| 5523 | break; |
| 5524 | } |
| 5525 | case DICTIONARY_ELEMENTS: { |
| 5526 | Handle<SeededNumberDictionary> element_dictionary( |
| 5527 | copy->element_dictionary()); |
| 5528 | int capacity = element_dictionary->Capacity(); |
| 5529 | for (int i = 0; i < capacity; i++) { |
| 5530 | Object* k = element_dictionary->KeyAt(i); |
| 5531 | if (element_dictionary->IsKey(k)) { |
| 5532 | Handle<Object> value(element_dictionary->ValueAt(i), isolate); |
| 5533 | if (value->IsJSObject()) { |
| 5534 | Handle<JSObject> result; |
| 5535 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5536 | isolate, result, |
| 5537 | VisitElementOrProperty(copy, Handle<JSObject>::cast(value)), |
| 5538 | JSObject); |
| 5539 | if (copying) { |
| 5540 | element_dictionary->ValueAtPut(i, *result); |
| 5541 | } |
| 5542 | } |
| 5543 | } |
| 5544 | } |
| 5545 | break; |
| 5546 | } |
| 5547 | case SLOPPY_ARGUMENTS_ELEMENTS: |
| 5548 | UNIMPLEMENTED(); |
| 5549 | break; |
| 5550 | |
| 5551 | |
| 5552 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 5553 | case EXTERNAL_##TYPE##_ELEMENTS: \ |
| 5554 | case TYPE##_ELEMENTS: \ |
| 5555 | |
| 5556 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 5557 | #undef TYPED_ARRAY_CASE |
| 5558 | |
| 5559 | case FAST_DOUBLE_ELEMENTS: |
| 5560 | case FAST_HOLEY_DOUBLE_ELEMENTS: |
| 5561 | // No contained objects, nothing to do. |
| 5562 | break; |
| 5563 | } |
| 5564 | } |
| 5565 | |
| 5566 | return copy; |
| 5567 | } |
| 5568 | |
| 5569 | |
| 5570 | MaybeHandle<JSObject> JSObject::DeepWalk( |
| 5571 | Handle<JSObject> object, |
| 5572 | AllocationSiteCreationContext* site_context) { |
| 5573 | JSObjectWalkVisitor<AllocationSiteCreationContext> v(site_context, false, |
| 5574 | kNoHints); |
| 5575 | MaybeHandle<JSObject> result = v.StructureWalk(object); |
| 5576 | Handle<JSObject> for_assert; |
| 5577 | DCHECK(!result.ToHandle(&for_assert) || for_assert.is_identical_to(object)); |
| 5578 | return result; |
| 5579 | } |
| 5580 | |
| 5581 | |
| 5582 | MaybeHandle<JSObject> JSObject::DeepCopy( |
| 5583 | Handle<JSObject> object, |
| 5584 | AllocationSiteUsageContext* site_context, |
| 5585 | DeepCopyHints hints) { |
| 5586 | JSObjectWalkVisitor<AllocationSiteUsageContext> v(site_context, true, hints); |
| 5587 | MaybeHandle<JSObject> copy = v.StructureWalk(object); |
| 5588 | Handle<JSObject> for_assert; |
| 5589 | DCHECK(!copy.ToHandle(&for_assert) || !for_assert.is_identical_to(object)); |
| 5590 | return copy; |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 5591 | } |
| 5592 | |
| 5593 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5594 | // Tests for the fast common case for property enumeration: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5595 | // - This object and all prototypes has an enum cache (which means that |
| 5596 | // it is no proxy, has no interceptors and needs no access checks). |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 5597 | // - This object has no elements. |
| 5598 | // - No prototype has enumerable properties/elements. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5599 | bool JSReceiver::IsSimpleEnum() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5600 | for (PrototypeIterator iter(GetIsolate(), this, |
| 5601 | PrototypeIterator::START_AT_RECEIVER); |
| 5602 | !iter.IsAtEnd(); iter.Advance()) { |
| 5603 | if (!iter.GetCurrent()->IsJSObject()) return false; |
| 5604 | JSObject* curr = JSObject::cast(iter.GetCurrent()); |
| 5605 | int enum_length = curr->map()->EnumLength(); |
| 5606 | if (enum_length == kInvalidEnumCacheSentinel) return false; |
| 5607 | if (curr->IsAccessCheckNeeded()) return false; |
| 5608 | DCHECK(!curr->HasNamedInterceptor()); |
| 5609 | DCHECK(!curr->HasIndexedInterceptor()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5610 | if (curr->NumberOfEnumElements() > 0) return false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5611 | if (curr != this && enum_length != 0) return false; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5612 | } |
| 5613 | return true; |
| 5614 | } |
| 5615 | |
| 5616 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5617 | static bool FilterKey(Object* key, PropertyAttributes filter) { |
| 5618 | if ((filter & SYMBOLIC) && key->IsSymbol()) { |
| 5619 | return true; |
| 5620 | } |
| 5621 | |
| 5622 | if ((filter & PRIVATE_SYMBOL) && |
| 5623 | key->IsSymbol() && Symbol::cast(key)->is_private()) { |
| 5624 | return true; |
| 5625 | } |
| 5626 | |
| 5627 | if ((filter & STRING) && !key->IsSymbol()) { |
| 5628 | return true; |
| 5629 | } |
| 5630 | |
| 5631 | return false; |
| 5632 | } |
| 5633 | |
| 5634 | |
| 5635 | int Map::NumberOfDescribedProperties(DescriptorFlag which, |
| 5636 | PropertyAttributes filter) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5637 | int result = 0; |
| 5638 | DescriptorArray* descs = instance_descriptors(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5639 | int limit = which == ALL_DESCRIPTORS |
| 5640 | ? descs->number_of_descriptors() |
| 5641 | : NumberOfOwnDescriptors(); |
| 5642 | for (int i = 0; i < limit; i++) { |
| 5643 | if ((descs->GetDetails(i).attributes() & filter) == 0 && |
| 5644 | !FilterKey(descs->GetKey(i), filter)) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5645 | result++; |
| 5646 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5647 | } |
| 5648 | return result; |
| 5649 | } |
| 5650 | |
| 5651 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5652 | int Map::NextFreePropertyIndex() { |
| 5653 | int max_index = -1; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5654 | int number_of_own_descriptors = NumberOfOwnDescriptors(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5655 | DescriptorArray* descs = instance_descriptors(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5656 | for (int i = 0; i < number_of_own_descriptors; i++) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5657 | if (descs->GetType(i) == FIELD) { |
| 5658 | int current_index = descs->GetFieldIndex(i); |
| 5659 | if (current_index > max_index) max_index = current_index; |
| 5660 | } |
| 5661 | } |
| 5662 | return max_index + 1; |
| 5663 | } |
| 5664 | |
| 5665 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5666 | static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { |
| 5667 | int len = array->length(); |
| 5668 | for (int i = 0; i < len; i++) { |
| 5669 | Object* e = array->get(i); |
| 5670 | if (!(e->IsString() || e->IsNumber())) return false; |
| 5671 | } |
| 5672 | return true; |
| 5673 | } |
| 5674 | |
| 5675 | |
| 5676 | static Handle<FixedArray> ReduceFixedArrayTo( |
| 5677 | Handle<FixedArray> array, int length) { |
| 5678 | DCHECK(array->length() >= length); |
| 5679 | if (array->length() == length) return array; |
| 5680 | |
| 5681 | Handle<FixedArray> new_array = |
| 5682 | array->GetIsolate()->factory()->NewFixedArray(length); |
| 5683 | for (int i = 0; i < length; ++i) new_array->set(i, array->get(i)); |
| 5684 | return new_array; |
| 5685 | } |
| 5686 | |
| 5687 | |
| 5688 | static Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object, |
| 5689 | bool cache_result) { |
| 5690 | Isolate* isolate = object->GetIsolate(); |
| 5691 | if (object->HasFastProperties()) { |
| 5692 | int own_property_count = object->map()->EnumLength(); |
| 5693 | // If the enum length of the given map is set to kInvalidEnumCache, this |
| 5694 | // means that the map itself has never used the present enum cache. The |
| 5695 | // first step to using the cache is to set the enum length of the map by |
| 5696 | // counting the number of own descriptors that are not DONT_ENUM or |
| 5697 | // SYMBOLIC. |
| 5698 | if (own_property_count == kInvalidEnumCacheSentinel) { |
| 5699 | own_property_count = object->map()->NumberOfDescribedProperties( |
| 5700 | OWN_DESCRIPTORS, DONT_SHOW); |
| 5701 | } else { |
| 5702 | DCHECK(own_property_count == object->map()->NumberOfDescribedProperties( |
| 5703 | OWN_DESCRIPTORS, DONT_SHOW)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5704 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5705 | |
| 5706 | if (object->map()->instance_descriptors()->HasEnumCache()) { |
| 5707 | DescriptorArray* desc = object->map()->instance_descriptors(); |
| 5708 | Handle<FixedArray> keys(desc->GetEnumCache(), isolate); |
| 5709 | |
| 5710 | // In case the number of properties required in the enum are actually |
| 5711 | // present, we can reuse the enum cache. Otherwise, this means that the |
| 5712 | // enum cache was generated for a previous (smaller) version of the |
| 5713 | // Descriptor Array. In that case we regenerate the enum cache. |
| 5714 | if (own_property_count <= keys->length()) { |
| 5715 | if (cache_result) object->map()->SetEnumLength(own_property_count); |
| 5716 | isolate->counters()->enum_cache_hits()->Increment(); |
| 5717 | return ReduceFixedArrayTo(keys, own_property_count); |
| 5718 | } |
| 5719 | } |
| 5720 | |
| 5721 | Handle<Map> map(object->map()); |
| 5722 | |
| 5723 | if (map->instance_descriptors()->IsEmpty()) { |
| 5724 | isolate->counters()->enum_cache_hits()->Increment(); |
| 5725 | if (cache_result) map->SetEnumLength(0); |
| 5726 | return isolate->factory()->empty_fixed_array(); |
| 5727 | } |
| 5728 | |
| 5729 | isolate->counters()->enum_cache_misses()->Increment(); |
| 5730 | |
| 5731 | Handle<FixedArray> storage = isolate->factory()->NewFixedArray( |
| 5732 | own_property_count); |
| 5733 | Handle<FixedArray> indices = isolate->factory()->NewFixedArray( |
| 5734 | own_property_count); |
| 5735 | |
| 5736 | Handle<DescriptorArray> descs = |
| 5737 | Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate); |
| 5738 | |
| 5739 | int size = map->NumberOfOwnDescriptors(); |
| 5740 | int index = 0; |
| 5741 | |
| 5742 | for (int i = 0; i < size; i++) { |
| 5743 | PropertyDetails details = descs->GetDetails(i); |
| 5744 | Object* key = descs->GetKey(i); |
| 5745 | if (!(details.IsDontEnum() || key->IsSymbol())) { |
| 5746 | storage->set(index, key); |
| 5747 | if (!indices.is_null()) { |
| 5748 | if (details.type() != FIELD) { |
| 5749 | indices = Handle<FixedArray>(); |
| 5750 | } else { |
| 5751 | FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); |
| 5752 | int load_by_field_index = field_index.GetLoadByFieldIndex(); |
| 5753 | indices->set(index, Smi::FromInt(load_by_field_index)); |
| 5754 | } |
| 5755 | } |
| 5756 | index++; |
| 5757 | } |
| 5758 | } |
| 5759 | DCHECK(index == storage->length()); |
| 5760 | |
| 5761 | Handle<FixedArray> bridge_storage = |
| 5762 | isolate->factory()->NewFixedArray( |
| 5763 | DescriptorArray::kEnumCacheBridgeLength); |
| 5764 | DescriptorArray* desc = object->map()->instance_descriptors(); |
| 5765 | desc->SetEnumCache(*bridge_storage, |
| 5766 | *storage, |
| 5767 | indices.is_null() ? Object::cast(Smi::FromInt(0)) |
| 5768 | : Object::cast(*indices)); |
| 5769 | if (cache_result) { |
| 5770 | object->map()->SetEnumLength(own_property_count); |
| 5771 | } |
| 5772 | return storage; |
| 5773 | } else { |
| 5774 | Handle<NameDictionary> dictionary(object->property_dictionary()); |
| 5775 | int length = dictionary->NumberOfEnumElements(); |
| 5776 | if (length == 0) { |
| 5777 | return Handle<FixedArray>(isolate->heap()->empty_fixed_array()); |
| 5778 | } |
| 5779 | Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length); |
| 5780 | dictionary->CopyEnumKeysTo(*storage); |
| 5781 | return storage; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5782 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5783 | } |
| 5784 | |
| 5785 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5786 | MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, |
| 5787 | KeyCollectionType type) { |
| 5788 | USE(ContainsOnlyValidKeys); |
| 5789 | Isolate* isolate = object->GetIsolate(); |
| 5790 | Handle<FixedArray> content = isolate->factory()->empty_fixed_array(); |
| 5791 | Handle<JSFunction> arguments_function( |
| 5792 | JSFunction::cast(isolate->sloppy_arguments_map()->constructor())); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5793 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5794 | // Only collect keys if access is permitted. |
| 5795 | for (PrototypeIterator iter(isolate, object, |
| 5796 | PrototypeIterator::START_AT_RECEIVER); |
| 5797 | !iter.IsAtEnd(); iter.Advance()) { |
| 5798 | if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { |
| 5799 | Handle<JSProxy> proxy(JSProxy::cast(*PrototypeIterator::GetCurrent(iter)), |
| 5800 | isolate); |
| 5801 | Handle<Object> args[] = { proxy }; |
| 5802 | Handle<Object> names; |
| 5803 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5804 | isolate, names, |
| 5805 | Execution::Call(isolate, |
| 5806 | isolate->proxy_enumerate(), |
| 5807 | object, |
| 5808 | arraysize(args), |
| 5809 | args), |
| 5810 | FixedArray); |
| 5811 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5812 | isolate, content, |
| 5813 | FixedArray::AddKeysFromArrayLike( |
| 5814 | content, Handle<JSObject>::cast(names)), |
| 5815 | FixedArray); |
| 5816 | break; |
| 5817 | } |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 5818 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5819 | Handle<JSObject> current = |
| 5820 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); |
| 5821 | |
| 5822 | // Check access rights if required. |
| 5823 | if (current->IsAccessCheckNeeded() && |
| 5824 | !isolate->MayNamedAccess( |
| 5825 | current, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) { |
| 5826 | isolate->ReportFailedAccessCheck(current, v8::ACCESS_KEYS); |
| 5827 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, FixedArray); |
| 5828 | break; |
| 5829 | } |
| 5830 | |
| 5831 | // Compute the element keys. |
| 5832 | Handle<FixedArray> element_keys = |
| 5833 | isolate->factory()->NewFixedArray(current->NumberOfEnumElements()); |
| 5834 | current->GetEnumElementKeys(*element_keys); |
| 5835 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5836 | isolate, content, |
| 5837 | FixedArray::UnionOfKeys(content, element_keys), |
| 5838 | FixedArray); |
| 5839 | DCHECK(ContainsOnlyValidKeys(content)); |
| 5840 | |
| 5841 | // Add the element keys from the interceptor. |
| 5842 | if (current->HasIndexedInterceptor()) { |
| 5843 | Handle<JSObject> result; |
| 5844 | if (JSObject::GetKeysForIndexedInterceptor( |
| 5845 | current, object).ToHandle(&result)) { |
| 5846 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5847 | isolate, content, |
| 5848 | FixedArray::AddKeysFromArrayLike(content, result), |
| 5849 | FixedArray); |
| 5850 | } |
| 5851 | DCHECK(ContainsOnlyValidKeys(content)); |
| 5852 | } |
| 5853 | |
| 5854 | // We can cache the computed property keys if access checks are |
| 5855 | // not needed and no interceptors are involved. |
| 5856 | // |
| 5857 | // We do not use the cache if the object has elements and |
| 5858 | // therefore it does not make sense to cache the property names |
| 5859 | // for arguments objects. Arguments objects will always have |
| 5860 | // elements. |
| 5861 | // Wrapped strings have elements, but don't have an elements |
| 5862 | // array or dictionary. So the fast inline test for whether to |
| 5863 | // use the cache says yes, so we should not create a cache. |
| 5864 | bool cache_enum_keys = |
| 5865 | ((current->map()->constructor() != *arguments_function) && |
| 5866 | !current->IsJSValue() && |
| 5867 | !current->IsAccessCheckNeeded() && |
| 5868 | !current->HasNamedInterceptor() && |
| 5869 | !current->HasIndexedInterceptor()); |
| 5870 | // Compute the property keys and cache them if possible. |
| 5871 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5872 | isolate, content, |
| 5873 | FixedArray::UnionOfKeys( |
| 5874 | content, GetEnumPropertyKeys(current, cache_enum_keys)), |
| 5875 | FixedArray); |
| 5876 | DCHECK(ContainsOnlyValidKeys(content)); |
| 5877 | |
| 5878 | // Add the property keys from the interceptor. |
| 5879 | if (current->HasNamedInterceptor()) { |
| 5880 | Handle<JSObject> result; |
| 5881 | if (JSObject::GetKeysForNamedInterceptor( |
| 5882 | current, object).ToHandle(&result)) { |
| 5883 | ASSIGN_RETURN_ON_EXCEPTION( |
| 5884 | isolate, content, |
| 5885 | FixedArray::AddKeysFromArrayLike(content, result), |
| 5886 | FixedArray); |
| 5887 | } |
| 5888 | DCHECK(ContainsOnlyValidKeys(content)); |
| 5889 | } |
| 5890 | |
| 5891 | // If we only want own properties we bail out after the first |
| 5892 | // iteration. |
| 5893 | if (type == OWN_ONLY) break; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5894 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5895 | return content; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5896 | } |
| 5897 | |
| 5898 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5899 | // Try to update an accessor in an elements dictionary. Return true if the |
| 5900 | // update succeeded, and false otherwise. |
| 5901 | static bool UpdateGetterSetterInDictionary( |
| 5902 | SeededNumberDictionary* dictionary, |
| 5903 | uint32_t index, |
| 5904 | Object* getter, |
| 5905 | Object* setter, |
| 5906 | PropertyAttributes attributes) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5907 | int entry = dictionary->FindEntry(index); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 5908 | if (entry != SeededNumberDictionary::kNotFound) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5909 | Object* result = dictionary->ValueAt(entry); |
| 5910 | PropertyDetails details = dictionary->DetailsAt(entry); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5911 | if (details.type() == CALLBACKS && result->IsAccessorPair()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5912 | DCHECK(details.IsConfigurable()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5913 | if (details.attributes() != attributes) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5914 | dictionary->DetailsAtPut( |
| 5915 | entry, |
| 5916 | PropertyDetails(attributes, CALLBACKS, index)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5917 | } |
| 5918 | AccessorPair::cast(result)->SetComponents(getter, setter); |
| 5919 | return true; |
| 5920 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5921 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5922 | return false; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5923 | } |
| 5924 | |
| 5925 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5926 | void JSObject::DefineElementAccessor(Handle<JSObject> object, |
| 5927 | uint32_t index, |
| 5928 | Handle<Object> getter, |
| 5929 | Handle<Object> setter, |
| 5930 | PropertyAttributes attributes) { |
| 5931 | switch (object->GetElementsKind()) { |
| 5932 | case FAST_SMI_ELEMENTS: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5933 | case FAST_ELEMENTS: |
| 5934 | case FAST_DOUBLE_ELEMENTS: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5935 | case FAST_HOLEY_SMI_ELEMENTS: |
| 5936 | case FAST_HOLEY_ELEMENTS: |
| 5937 | case FAST_HOLEY_DOUBLE_ELEMENTS: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5938 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5939 | |
| 5940 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 5941 | case EXTERNAL_##TYPE##_ELEMENTS: \ |
| 5942 | case TYPE##_ELEMENTS: \ |
| 5943 | |
| 5944 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 5945 | #undef TYPED_ARRAY_CASE |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5946 | // Ignore getters and setters on pixel and external array elements. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5947 | return; |
| 5948 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5949 | case DICTIONARY_ELEMENTS: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5950 | if (UpdateGetterSetterInDictionary(object->element_dictionary(), |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5951 | index, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5952 | *getter, |
| 5953 | *setter, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5954 | attributes)) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5955 | return; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 5956 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5957 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5958 | case SLOPPY_ARGUMENTS_ELEMENTS: { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5959 | // Ascertain whether we have read-only properties or an existing |
| 5960 | // getter/setter pair in an arguments elements dictionary backing |
| 5961 | // store. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5962 | FixedArray* parameter_map = FixedArray::cast(object->elements()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5963 | uint32_t length = parameter_map->length(); |
| 5964 | Object* probe = |
| 5965 | index < (length - 2) ? parameter_map->get(index + 2) : NULL; |
| 5966 | if (probe == NULL || probe->IsTheHole()) { |
| 5967 | FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| 5968 | if (arguments->IsDictionary()) { |
| 5969 | SeededNumberDictionary* dictionary = |
| 5970 | SeededNumberDictionary::cast(arguments); |
| 5971 | if (UpdateGetterSetterInDictionary(dictionary, |
| 5972 | index, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5973 | *getter, |
| 5974 | *setter, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5975 | attributes)) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5976 | return; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5977 | } |
| 5978 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5979 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5980 | break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 5981 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5982 | } |
| 5983 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5984 | Isolate* isolate = object->GetIsolate(); |
| 5985 | Handle<AccessorPair> accessors = isolate->factory()->NewAccessorPair(); |
| 5986 | accessors->SetComponents(*getter, *setter); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5987 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5988 | SetElementCallback(object, index, accessors, attributes); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 5989 | } |
| 5990 | |
| 5991 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 5992 | bool Map::DictionaryElementsInPrototypeChainOnly() { |
| 5993 | if (IsDictionaryElementsKind(elements_kind())) { |
| 5994 | return false; |
| 5995 | } |
| 5996 | |
| 5997 | for (PrototypeIterator iter(this); !iter.IsAtEnd(); iter.Advance()) { |
| 5998 | if (iter.GetCurrent()->IsJSProxy()) { |
| 5999 | // Be conservative, don't walk into proxies. |
| 6000 | return true; |
| 6001 | } |
| 6002 | |
| 6003 | if (IsDictionaryElementsKind( |
| 6004 | JSObject::cast(iter.GetCurrent())->map()->elements_kind())) { |
| 6005 | return true; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6006 | } |
| 6007 | } |
| 6008 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6009 | return false; |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6010 | } |
| 6011 | |
| 6012 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6013 | void JSObject::SetElementCallback(Handle<JSObject> object, |
| 6014 | uint32_t index, |
| 6015 | Handle<Object> structure, |
| 6016 | PropertyAttributes attributes) { |
| 6017 | Heap* heap = object->GetHeap(); |
| 6018 | PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6019 | |
| 6020 | // Normalize elements to make this operation simple. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6021 | bool had_dictionary_elements = object->HasDictionaryElements(); |
| 6022 | Handle<SeededNumberDictionary> dictionary = NormalizeElements(object); |
| 6023 | DCHECK(object->HasDictionaryElements() || |
| 6024 | object->HasDictionaryArgumentsElements()); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6025 | // Update the dictionary with the new CALLBACKS property. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6026 | dictionary = SeededNumberDictionary::Set(dictionary, index, structure, |
| 6027 | details); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 6028 | dictionary->set_requires_slow_elements(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6029 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 6030 | // Update the dictionary backing store on the object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6031 | if (object->elements()->map() == heap->sloppy_arguments_elements_map()) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 6032 | // Also delete any parameter alias. |
| 6033 | // |
| 6034 | // TODO(kmillikin): when deleting the last parameter alias we could |
| 6035 | // switch to a direct backing store without the parameter map. This |
| 6036 | // would allow GC of the context. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6037 | FixedArray* parameter_map = FixedArray::cast(object->elements()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6038 | if (index < static_cast<uint32_t>(parameter_map->length()) - 2) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6039 | parameter_map->set(index + 2, heap->the_hole_value()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 6040 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6041 | parameter_map->set(1, *dictionary); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 6042 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6043 | object->set_elements(*dictionary); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6044 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6045 | if (!had_dictionary_elements) { |
| 6046 | // KeyedStoreICs (at least the non-generic ones) need a reset. |
| 6047 | heap->ClearAllICsByKind(Code::KEYED_STORE_IC); |
| 6048 | } |
| 6049 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6050 | } |
| 6051 | |
| 6052 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6053 | void JSObject::SetPropertyCallback(Handle<JSObject> object, |
| 6054 | Handle<Name> name, |
| 6055 | Handle<Object> structure, |
| 6056 | PropertyAttributes attributes) { |
| 6057 | PropertyNormalizationMode mode = object->map()->is_prototype_map() |
| 6058 | ? KEEP_INOBJECT_PROPERTIES |
| 6059 | : CLEAR_INOBJECT_PROPERTIES; |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6060 | // Normalize object to make this operation simple. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6061 | NormalizeProperties(object, mode, 0); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6062 | |
| 6063 | // For the global object allocate a new map to invalidate the global inline |
| 6064 | // caches which have a global property cell reference directly in the code. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6065 | if (object->IsGlobalObject()) { |
| 6066 | Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); |
| 6067 | DCHECK(new_map->is_dictionary_map()); |
| 6068 | JSObject::MigrateToMap(object, new_map); |
| 6069 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 6070 | // When running crankshaft, changing the map is not enough. We |
| 6071 | // need to deoptimize all functions that rely on this global |
| 6072 | // object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6073 | Deoptimizer::DeoptimizeGlobalObject(*object); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6074 | } |
| 6075 | |
| 6076 | // Update the dictionary with the new CALLBACKS property. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6077 | PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0); |
| 6078 | SetNormalizedProperty(object, name, structure, details); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6079 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6080 | ReoptimizeIfPrototype(object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6081 | } |
| 6082 | |
| 6083 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6084 | MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object, |
| 6085 | Handle<Name> name, |
| 6086 | Handle<Object> getter, |
| 6087 | Handle<Object> setter, |
| 6088 | PropertyAttributes attributes) { |
| 6089 | Isolate* isolate = object->GetIsolate(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6090 | // Check access rights if needed. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6091 | if (object->IsAccessCheckNeeded() && |
| 6092 | !isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) { |
| 6093 | isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET); |
| 6094 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 6095 | return isolate->factory()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6096 | } |
| 6097 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6098 | if (object->IsJSGlobalProxy()) { |
| 6099 | PrototypeIterator iter(isolate, object); |
| 6100 | if (iter.IsAtEnd()) return isolate->factory()->undefined_value(); |
| 6101 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 6102 | DefineAccessor(Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), |
| 6103 | name, getter, setter, attributes); |
| 6104 | return isolate->factory()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6105 | } |
| 6106 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6107 | // Make sure that the top context does not change when doing callbacks or |
| 6108 | // interceptor calls. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6109 | AssertNoContextChange ncc(isolate); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6110 | |
| 6111 | // Try to flatten before operating on the string. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6112 | if (name->IsString()) name = String::Flatten(Handle<String>::cast(name)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6113 | |
| 6114 | uint32_t index = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6115 | bool is_element = name->AsArrayIndex(&index); |
| 6116 | |
| 6117 | Handle<Object> old_value = isolate->factory()->the_hole_value(); |
| 6118 | bool is_observed = object->map()->is_observed() && |
| 6119 | *name != isolate->heap()->hidden_string(); |
| 6120 | bool preexists = false; |
| 6121 | if (is_observed) { |
| 6122 | if (is_element) { |
| 6123 | Maybe<bool> maybe = HasOwnElement(object, index); |
| 6124 | // Workaround for a GCC 4.4.3 bug which leads to "‘preexists’ may be used |
| 6125 | // uninitialized in this function". |
| 6126 | if (!maybe.has_value) { |
| 6127 | DCHECK(false); |
| 6128 | return isolate->factory()->undefined_value(); |
| 6129 | } |
| 6130 | preexists = maybe.value; |
| 6131 | if (preexists && GetOwnElementAccessorPair(object, index).is_null()) { |
| 6132 | old_value = |
| 6133 | Object::GetElement(isolate, object, index).ToHandleChecked(); |
| 6134 | } |
| 6135 | } else { |
| 6136 | LookupIterator it(object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR); |
| 6137 | CHECK(GetPropertyAttributes(&it).has_value); |
| 6138 | preexists = it.IsFound(); |
| 6139 | if (preexists && (it.state() == LookupIterator::DATA || |
| 6140 | it.GetAccessors()->IsAccessorInfo())) { |
| 6141 | old_value = GetProperty(&it).ToHandleChecked(); |
| 6142 | } |
| 6143 | } |
| 6144 | } |
| 6145 | |
| 6146 | if (is_element) { |
| 6147 | DefineElementAccessor(object, index, getter, setter, attributes); |
| 6148 | } else { |
| 6149 | DCHECK(getter->IsSpecFunction() || getter->IsUndefined() || |
| 6150 | getter->IsNull()); |
| 6151 | DCHECK(setter->IsSpecFunction() || setter->IsUndefined() || |
| 6152 | setter->IsNull()); |
| 6153 | // At least one of the accessors needs to be a new value. |
| 6154 | DCHECK(!getter->IsNull() || !setter->IsNull()); |
| 6155 | LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 6156 | if (it.state() == LookupIterator::ACCESS_CHECK) { |
| 6157 | // We already did an access check before. We do have access. |
| 6158 | it.Next(); |
| 6159 | } |
| 6160 | if (!getter->IsNull()) { |
| 6161 | it.TransitionToAccessorProperty(ACCESSOR_GETTER, getter, attributes); |
| 6162 | } |
| 6163 | if (!setter->IsNull()) { |
| 6164 | it.TransitionToAccessorProperty(ACCESSOR_SETTER, setter, attributes); |
| 6165 | } |
| 6166 | } |
| 6167 | |
| 6168 | if (is_observed) { |
| 6169 | const char* type = preexists ? "reconfigure" : "add"; |
| 6170 | EnqueueChangeRecord(object, type, name, old_value); |
| 6171 | } |
| 6172 | |
| 6173 | return isolate->factory()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6174 | } |
| 6175 | |
| 6176 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6177 | MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object, |
| 6178 | Handle<AccessorInfo> info) { |
| 6179 | Isolate* isolate = object->GetIsolate(); |
| 6180 | Factory* factory = isolate->factory(); |
| 6181 | Handle<Name> name(Name::cast(info->name())); |
| 6182 | |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6183 | // Check access rights if needed. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6184 | if (object->IsAccessCheckNeeded() && |
| 6185 | !isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) { |
| 6186 | isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET); |
| 6187 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 6188 | return factory->undefined_value(); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6189 | } |
| 6190 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6191 | if (object->IsJSGlobalProxy()) { |
| 6192 | PrototypeIterator iter(isolate, object); |
| 6193 | if (iter.IsAtEnd()) return object; |
| 6194 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 6195 | return SetAccessor( |
| 6196 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), info); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6197 | } |
| 6198 | |
| 6199 | // Make sure that the top context does not change when doing callbacks or |
| 6200 | // interceptor calls. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6201 | AssertNoContextChange ncc(isolate); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6202 | |
| 6203 | // Try to flatten before operating on the string. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6204 | if (name->IsString()) name = String::Flatten(Handle<String>::cast(name)); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6205 | |
| 6206 | uint32_t index = 0; |
| 6207 | bool is_element = name->AsArrayIndex(&index); |
| 6208 | |
| 6209 | if (is_element) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6210 | if (object->IsJSArray()) return factory->undefined_value(); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6211 | |
| 6212 | // Accessors overwrite previous callbacks (cf. with getters/setters). |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6213 | switch (object->GetElementsKind()) { |
| 6214 | case FAST_SMI_ELEMENTS: |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6215 | case FAST_ELEMENTS: |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 6216 | case FAST_DOUBLE_ELEMENTS: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6217 | case FAST_HOLEY_SMI_ELEMENTS: |
| 6218 | case FAST_HOLEY_ELEMENTS: |
| 6219 | case FAST_HOLEY_DOUBLE_ELEMENTS: |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6220 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6221 | |
| 6222 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 6223 | case EXTERNAL_##TYPE##_ELEMENTS: \ |
| 6224 | case TYPE##_ELEMENTS: \ |
| 6225 | |
| 6226 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 6227 | #undef TYPED_ARRAY_CASE |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6228 | // Ignore getters and setters on pixel and external array |
| 6229 | // elements. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6230 | return factory->undefined_value(); |
| 6231 | |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6232 | case DICTIONARY_ELEMENTS: |
| 6233 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6234 | case SLOPPY_ARGUMENTS_ELEMENTS: |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 6235 | UNIMPLEMENTED(); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6236 | break; |
| 6237 | } |
| 6238 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6239 | SetElementCallback(object, index, info, info->property_attributes()); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6240 | } else { |
| 6241 | // Lookup the name. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6242 | LookupIterator it(object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR); |
| 6243 | CHECK(GetPropertyAttributes(&it).has_value); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6244 | // ES5 forbids turning a property into an accessor if it's not |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6245 | // configurable. See 8.6.1 (Table 5). |
| 6246 | if (it.IsFound() && (it.IsReadOnly() || !it.IsConfigurable())) { |
| 6247 | return factory->undefined_value(); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6248 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6249 | |
| 6250 | SetPropertyCallback(object, name, info, info->property_attributes()); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6251 | } |
| 6252 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6253 | return object; |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 6254 | } |
| 6255 | |
| 6256 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6257 | MaybeHandle<Object> JSObject::GetAccessor(Handle<JSObject> object, |
| 6258 | Handle<Name> name, |
| 6259 | AccessorComponent component) { |
| 6260 | Isolate* isolate = object->GetIsolate(); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 6261 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6262 | // Make sure that the top context does not change when doing callbacks or |
| 6263 | // interceptor calls. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6264 | AssertNoContextChange ncc(isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6265 | |
| 6266 | // Make the lookup and include prototypes. |
Ben Murdoch | 7f4d5bd | 2010-06-15 11:15:29 +0100 | [diff] [blame] | 6267 | uint32_t index = 0; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6268 | if (name->AsArrayIndex(&index)) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6269 | for (PrototypeIterator iter(isolate, object, |
| 6270 | PrototypeIterator::START_AT_RECEIVER); |
| 6271 | !iter.IsAtEnd(); iter.Advance()) { |
| 6272 | Handle<Object> current = PrototypeIterator::GetCurrent(iter); |
| 6273 | // Check access rights if needed. |
| 6274 | if (current->IsAccessCheckNeeded() && |
| 6275 | !isolate->MayNamedAccess(Handle<JSObject>::cast(current), name, |
| 6276 | v8::ACCESS_HAS)) { |
| 6277 | isolate->ReportFailedAccessCheck(Handle<JSObject>::cast(current), |
| 6278 | v8::ACCESS_HAS); |
| 6279 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 6280 | return isolate->factory()->undefined_value(); |
| 6281 | } |
| 6282 | |
| 6283 | if (current->IsJSObject() && |
| 6284 | Handle<JSObject>::cast(current)->HasDictionaryElements()) { |
| 6285 | JSObject* js_object = JSObject::cast(*current); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 6286 | SeededNumberDictionary* dictionary = js_object->element_dictionary(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6287 | int entry = dictionary->FindEntry(index); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 6288 | if (entry != SeededNumberDictionary::kNotFound) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6289 | Object* element = dictionary->ValueAt(entry); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6290 | if (dictionary->DetailsAt(entry).type() == CALLBACKS && |
| 6291 | element->IsAccessorPair()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6292 | return handle(AccessorPair::cast(element)->GetComponent(component), |
| 6293 | isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6294 | } |
| 6295 | } |
| 6296 | } |
| 6297 | } |
| 6298 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6299 | LookupIterator it(object, name, |
| 6300 | LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); |
| 6301 | for (; it.IsFound(); it.Next()) { |
| 6302 | switch (it.state()) { |
| 6303 | case LookupIterator::INTERCEPTOR: |
| 6304 | case LookupIterator::NOT_FOUND: |
| 6305 | case LookupIterator::TRANSITION: |
| 6306 | UNREACHABLE(); |
| 6307 | |
| 6308 | case LookupIterator::ACCESS_CHECK: |
| 6309 | if (it.HasAccess(v8::ACCESS_HAS)) continue; |
| 6310 | isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>(), |
| 6311 | v8::ACCESS_HAS); |
| 6312 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 6313 | return isolate->factory()->undefined_value(); |
| 6314 | |
| 6315 | case LookupIterator::JSPROXY: |
| 6316 | return isolate->factory()->undefined_value(); |
| 6317 | |
| 6318 | case LookupIterator::DATA: |
| 6319 | continue; |
| 6320 | case LookupIterator::ACCESSOR: { |
| 6321 | Handle<Object> maybe_pair = it.GetAccessors(); |
| 6322 | if (maybe_pair->IsAccessorPair()) { |
| 6323 | return handle( |
| 6324 | AccessorPair::cast(*maybe_pair)->GetComponent(component), |
| 6325 | isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6326 | } |
| 6327 | } |
| 6328 | } |
| 6329 | } |
| 6330 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6331 | return isolate->factory()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6332 | } |
| 6333 | |
| 6334 | |
| 6335 | Object* JSObject::SlowReverseLookup(Object* value) { |
| 6336 | if (HasFastProperties()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6337 | int number_of_own_descriptors = map()->NumberOfOwnDescriptors(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6338 | DescriptorArray* descs = map()->instance_descriptors(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6339 | for (int i = 0; i < number_of_own_descriptors; i++) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6340 | if (descs->GetType(i) == FIELD) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6341 | Object* property = |
| 6342 | RawFastPropertyAt(FieldIndex::ForDescriptor(map(), i)); |
| 6343 | if (descs->GetDetails(i).representation().IsDouble()) { |
| 6344 | DCHECK(property->IsMutableHeapNumber()); |
| 6345 | if (value->IsNumber() && property->Number() == value->Number()) { |
| 6346 | return descs->GetKey(i); |
| 6347 | } |
| 6348 | } else if (property == value) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6349 | return descs->GetKey(i); |
| 6350 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6351 | } else if (descs->GetType(i) == CONSTANT) { |
| 6352 | if (descs->GetConstant(i) == value) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6353 | return descs->GetKey(i); |
| 6354 | } |
| 6355 | } |
| 6356 | } |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 6357 | return GetHeap()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6358 | } else { |
| 6359 | return property_dictionary()->SlowReverseLookup(value); |
| 6360 | } |
| 6361 | } |
| 6362 | |
| 6363 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6364 | Handle<Map> Map::RawCopy(Handle<Map> map, int instance_size) { |
| 6365 | Handle<Map> result = map->GetIsolate()->factory()->NewMap( |
| 6366 | map->instance_type(), instance_size); |
| 6367 | result->set_prototype(map->prototype()); |
| 6368 | result->set_constructor(map->constructor()); |
| 6369 | result->set_bit_field(map->bit_field()); |
| 6370 | result->set_bit_field2(map->bit_field2()); |
| 6371 | int new_bit_field3 = map->bit_field3(); |
| 6372 | new_bit_field3 = OwnsDescriptors::update(new_bit_field3, true); |
| 6373 | new_bit_field3 = NumberOfOwnDescriptorsBits::update(new_bit_field3, 0); |
| 6374 | new_bit_field3 = EnumLengthBits::update(new_bit_field3, |
| 6375 | kInvalidEnumCacheSentinel); |
| 6376 | new_bit_field3 = Deprecated::update(new_bit_field3, false); |
| 6377 | if (!map->is_dictionary_map()) { |
| 6378 | new_bit_field3 = IsUnstable::update(new_bit_field3, false); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 6379 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6380 | new_bit_field3 = ConstructionCount::update(new_bit_field3, |
| 6381 | JSFunction::kNoSlackTracking); |
| 6382 | result->set_bit_field3(new_bit_field3); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6383 | return result; |
| 6384 | } |
| 6385 | |
| 6386 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6387 | Handle<Map> Map::Normalize(Handle<Map> fast_map, |
| 6388 | PropertyNormalizationMode mode) { |
| 6389 | DCHECK(!fast_map->is_dictionary_map()); |
| 6390 | |
| 6391 | Isolate* isolate = fast_map->GetIsolate(); |
| 6392 | Handle<Object> maybe_cache(isolate->native_context()->normalized_map_cache(), |
| 6393 | isolate); |
| 6394 | bool use_cache = !maybe_cache->IsUndefined(); |
| 6395 | Handle<NormalizedMapCache> cache; |
| 6396 | if (use_cache) cache = Handle<NormalizedMapCache>::cast(maybe_cache); |
| 6397 | |
| 6398 | Handle<Map> new_map; |
| 6399 | if (use_cache && cache->Get(fast_map, mode).ToHandle(&new_map)) { |
| 6400 | #ifdef VERIFY_HEAP |
| 6401 | if (FLAG_verify_heap) new_map->DictionaryMapVerify(); |
| 6402 | #endif |
| 6403 | #ifdef ENABLE_SLOW_DCHECKS |
| 6404 | if (FLAG_enable_slow_asserts) { |
| 6405 | // The cached map should match newly created normalized map bit-by-bit, |
| 6406 | // except for the code cache, which can contain some ics which can be |
| 6407 | // applied to the shared map. |
| 6408 | Handle<Map> fresh = Map::CopyNormalized(fast_map, mode); |
| 6409 | |
| 6410 | DCHECK(memcmp(fresh->address(), |
| 6411 | new_map->address(), |
| 6412 | Map::kCodeCacheOffset) == 0); |
| 6413 | STATIC_ASSERT(Map::kDependentCodeOffset == |
| 6414 | Map::kCodeCacheOffset + kPointerSize); |
| 6415 | int offset = Map::kDependentCodeOffset + kPointerSize; |
| 6416 | DCHECK(memcmp(fresh->address() + offset, |
| 6417 | new_map->address() + offset, |
| 6418 | Map::kSize - offset) == 0); |
| 6419 | } |
| 6420 | #endif |
| 6421 | } else { |
| 6422 | new_map = Map::CopyNormalized(fast_map, mode); |
| 6423 | if (use_cache) { |
| 6424 | cache->Set(fast_map, new_map); |
| 6425 | isolate->counters()->normalized_maps()->Increment(); |
| 6426 | } |
| 6427 | } |
| 6428 | fast_map->NotifyLeafMapLayoutChange(); |
| 6429 | return new_map; |
| 6430 | } |
| 6431 | |
| 6432 | |
| 6433 | Handle<Map> Map::CopyNormalized(Handle<Map> map, |
| 6434 | PropertyNormalizationMode mode) { |
| 6435 | int new_instance_size = map->instance_size(); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 6436 | if (mode == CLEAR_INOBJECT_PROPERTIES) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6437 | new_instance_size -= map->inobject_properties() * kPointerSize; |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 6438 | } |
| 6439 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6440 | Handle<Map> result = RawCopy(map, new_instance_size); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 6441 | |
| 6442 | if (mode != CLEAR_INOBJECT_PROPERTIES) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6443 | result->set_inobject_properties(map->inobject_properties()); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 6444 | } |
| 6445 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6446 | result->set_dictionary_map(true); |
| 6447 | result->set_migration_target(false); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 6448 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6449 | #ifdef VERIFY_HEAP |
| 6450 | if (FLAG_verify_heap) result->DictionaryMapVerify(); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 6451 | #endif |
| 6452 | |
| 6453 | return result; |
| 6454 | } |
| 6455 | |
| 6456 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6457 | Handle<Map> Map::CopyDropDescriptors(Handle<Map> map) { |
| 6458 | Handle<Map> result = RawCopy(map, map->instance_size()); |
| 6459 | |
| 6460 | // Please note instance_type and instance_size are set when allocated. |
| 6461 | result->set_inobject_properties(map->inobject_properties()); |
| 6462 | result->set_unused_property_fields(map->unused_property_fields()); |
| 6463 | |
| 6464 | result->set_pre_allocated_property_fields( |
| 6465 | map->pre_allocated_property_fields()); |
| 6466 | result->ClearCodeCache(map->GetHeap()); |
| 6467 | map->NotifyLeafMapLayoutChange(); |
| 6468 | return result; |
| 6469 | } |
| 6470 | |
| 6471 | |
| 6472 | Handle<Map> Map::ShareDescriptor(Handle<Map> map, |
| 6473 | Handle<DescriptorArray> descriptors, |
| 6474 | Descriptor* descriptor) { |
| 6475 | // Sanity check. This path is only to be taken if the map owns its descriptor |
| 6476 | // array, implying that its NumberOfOwnDescriptors equals the number of |
| 6477 | // descriptors in the descriptor array. |
| 6478 | DCHECK(map->NumberOfOwnDescriptors() == |
| 6479 | map->instance_descriptors()->number_of_descriptors()); |
| 6480 | |
| 6481 | Handle<Map> result = CopyDropDescriptors(map); |
| 6482 | Handle<Name> name = descriptor->GetKey(); |
| 6483 | |
| 6484 | // Ensure there's space for the new descriptor in the shared descriptor array. |
| 6485 | if (descriptors->NumberOfSlackDescriptors() == 0) { |
| 6486 | int old_size = descriptors->number_of_descriptors(); |
| 6487 | if (old_size == 0) { |
| 6488 | descriptors = DescriptorArray::Allocate(map->GetIsolate(), 0, 1); |
| 6489 | } else { |
| 6490 | EnsureDescriptorSlack(map, old_size < 4 ? 1 : old_size / 2); |
| 6491 | descriptors = handle(map->instance_descriptors()); |
| 6492 | } |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 6493 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6494 | |
| 6495 | { |
| 6496 | DisallowHeapAllocation no_gc; |
| 6497 | descriptors->Append(descriptor); |
| 6498 | result->InitializeDescriptors(*descriptors); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 6499 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6500 | |
| 6501 | DCHECK(result->NumberOfOwnDescriptors() == map->NumberOfOwnDescriptors() + 1); |
| 6502 | ConnectTransition(map, result, name, SIMPLE_TRANSITION); |
| 6503 | |
| 6504 | return result; |
| 6505 | } |
| 6506 | |
| 6507 | |
| 6508 | void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child, |
| 6509 | Handle<Name> name, SimpleTransitionFlag flag) { |
| 6510 | parent->set_owns_descriptors(false); |
| 6511 | if (parent->is_prototype_map()) { |
| 6512 | DCHECK(child->is_prototype_map()); |
| 6513 | } else { |
| 6514 | Handle<TransitionArray> transitions = |
| 6515 | TransitionArray::CopyInsert(parent, name, child, flag); |
| 6516 | parent->set_transitions(*transitions); |
| 6517 | child->SetBackPointer(*parent); |
| 6518 | } |
| 6519 | } |
| 6520 | |
| 6521 | |
| 6522 | Handle<Map> Map::CopyReplaceDescriptors(Handle<Map> map, |
| 6523 | Handle<DescriptorArray> descriptors, |
| 6524 | TransitionFlag flag, |
| 6525 | MaybeHandle<Name> maybe_name, |
| 6526 | SimpleTransitionFlag simple_flag) { |
| 6527 | DCHECK(descriptors->IsSortedNoDuplicates()); |
| 6528 | |
| 6529 | Handle<Map> result = CopyDropDescriptors(map); |
| 6530 | result->InitializeDescriptors(*descriptors); |
| 6531 | |
| 6532 | if (!map->is_prototype_map()) { |
| 6533 | if (flag == INSERT_TRANSITION && map->CanHaveMoreTransitions()) { |
| 6534 | Handle<Name> name; |
| 6535 | CHECK(maybe_name.ToHandle(&name)); |
| 6536 | ConnectTransition(map, result, name, simple_flag); |
| 6537 | } else { |
| 6538 | int length = descriptors->number_of_descriptors(); |
| 6539 | for (int i = 0; i < length; i++) { |
| 6540 | descriptors->SetRepresentation(i, Representation::Tagged()); |
| 6541 | if (descriptors->GetDetails(i).type() == FIELD) { |
| 6542 | descriptors->SetValue(i, HeapType::Any()); |
| 6543 | } |
| 6544 | } |
| 6545 | } |
| 6546 | } |
| 6547 | |
| 6548 | return result; |
| 6549 | } |
| 6550 | |
| 6551 | |
| 6552 | // Since this method is used to rewrite an existing transition tree, it can |
| 6553 | // always insert transitions without checking. |
| 6554 | Handle<Map> Map::CopyInstallDescriptors(Handle<Map> map, |
| 6555 | int new_descriptor, |
| 6556 | Handle<DescriptorArray> descriptors) { |
| 6557 | DCHECK(descriptors->IsSortedNoDuplicates()); |
| 6558 | |
| 6559 | Handle<Map> result = CopyDropDescriptors(map); |
| 6560 | |
| 6561 | result->InitializeDescriptors(*descriptors); |
| 6562 | result->SetNumberOfOwnDescriptors(new_descriptor + 1); |
| 6563 | |
| 6564 | int unused_property_fields = map->unused_property_fields(); |
| 6565 | if (descriptors->GetDetails(new_descriptor).type() == FIELD) { |
| 6566 | unused_property_fields = map->unused_property_fields() - 1; |
| 6567 | if (unused_property_fields < 0) { |
| 6568 | unused_property_fields += JSObject::kFieldsAdded; |
| 6569 | } |
| 6570 | } |
| 6571 | |
| 6572 | result->set_unused_property_fields(unused_property_fields); |
| 6573 | |
| 6574 | Handle<Name> name = handle(descriptors->GetKey(new_descriptor)); |
| 6575 | ConnectTransition(map, result, name, SIMPLE_TRANSITION); |
| 6576 | |
| 6577 | return result; |
| 6578 | } |
| 6579 | |
| 6580 | |
| 6581 | Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind, |
| 6582 | TransitionFlag flag) { |
| 6583 | if (flag == INSERT_TRANSITION) { |
| 6584 | DCHECK(!map->HasElementsTransition() || |
| 6585 | ((map->elements_transition_map()->elements_kind() == |
| 6586 | DICTIONARY_ELEMENTS || |
| 6587 | IsExternalArrayElementsKind( |
| 6588 | map->elements_transition_map()->elements_kind())) && |
| 6589 | (kind == DICTIONARY_ELEMENTS || |
| 6590 | IsExternalArrayElementsKind(kind)))); |
| 6591 | DCHECK(!IsFastElementsKind(kind) || |
| 6592 | IsMoreGeneralElementsKindTransition(map->elements_kind(), kind)); |
| 6593 | DCHECK(kind != map->elements_kind()); |
| 6594 | } |
| 6595 | |
| 6596 | bool insert_transition = |
| 6597 | flag == INSERT_TRANSITION && !map->HasElementsTransition(); |
| 6598 | |
| 6599 | if (insert_transition && map->owns_descriptors()) { |
| 6600 | // In case the map owned its own descriptors, share the descriptors and |
| 6601 | // transfer ownership to the new map. |
| 6602 | Handle<Map> new_map = CopyDropDescriptors(map); |
| 6603 | |
| 6604 | ConnectElementsTransition(map, new_map); |
| 6605 | |
| 6606 | new_map->set_elements_kind(kind); |
| 6607 | new_map->InitializeDescriptors(map->instance_descriptors()); |
| 6608 | return new_map; |
| 6609 | } |
| 6610 | |
| 6611 | // In case the map did not own its own descriptors, a split is forced by |
| 6612 | // copying the map; creating a new descriptor array cell. |
| 6613 | // Create a new free-floating map only if we are not allowed to store it. |
| 6614 | Handle<Map> new_map = Copy(map); |
| 6615 | |
| 6616 | new_map->set_elements_kind(kind); |
| 6617 | |
| 6618 | if (insert_transition) { |
| 6619 | ConnectElementsTransition(map, new_map); |
| 6620 | } |
| 6621 | |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 6622 | return new_map; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6623 | } |
| 6624 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6625 | |
| 6626 | Handle<Map> Map::CopyForObserved(Handle<Map> map) { |
| 6627 | DCHECK(!map->is_observed()); |
| 6628 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6629 | Isolate* isolate = map->GetIsolate(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6630 | |
| 6631 | // In case the map owned its own descriptors, share the descriptors and |
| 6632 | // transfer ownership to the new map. |
| 6633 | Handle<Map> new_map; |
| 6634 | if (map->owns_descriptors()) { |
| 6635 | new_map = CopyDropDescriptors(map); |
| 6636 | } else { |
| 6637 | DCHECK(!map->is_prototype_map()); |
| 6638 | new_map = Copy(map); |
| 6639 | } |
| 6640 | |
| 6641 | new_map->set_is_observed(); |
| 6642 | if (map->owns_descriptors()) { |
| 6643 | new_map->InitializeDescriptors(map->instance_descriptors()); |
| 6644 | } |
| 6645 | |
| 6646 | Handle<Name> name = isolate->factory()->observed_symbol(); |
| 6647 | ConnectTransition(map, new_map, name, FULL_TRANSITION); |
| 6648 | |
| 6649 | return new_map; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6650 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 6651 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6652 | |
| 6653 | Handle<Map> Map::Copy(Handle<Map> map) { |
| 6654 | Handle<DescriptorArray> descriptors(map->instance_descriptors()); |
| 6655 | int number_of_own_descriptors = map->NumberOfOwnDescriptors(); |
| 6656 | Handle<DescriptorArray> new_descriptors = |
| 6657 | DescriptorArray::CopyUpTo(descriptors, number_of_own_descriptors); |
| 6658 | return CopyReplaceDescriptors( |
| 6659 | map, new_descriptors, OMIT_TRANSITION, MaybeHandle<Name>()); |
| 6660 | } |
| 6661 | |
| 6662 | |
| 6663 | Handle<Map> Map::Create(Isolate* isolate, int inobject_properties) { |
| 6664 | Handle<Map> copy = Copy(handle(isolate->object_function()->initial_map())); |
| 6665 | |
| 6666 | // Check that we do not overflow the instance size when adding the extra |
| 6667 | // inobject properties. If the instance size overflows, we allocate as many |
| 6668 | // properties as we can as inobject properties. |
| 6669 | int max_extra_properties = |
| 6670 | (JSObject::kMaxInstanceSize - JSObject::kHeaderSize) >> kPointerSizeLog2; |
| 6671 | |
| 6672 | if (inobject_properties > max_extra_properties) { |
| 6673 | inobject_properties = max_extra_properties; |
| 6674 | } |
| 6675 | |
| 6676 | int new_instance_size = |
| 6677 | JSObject::kHeaderSize + kPointerSize * inobject_properties; |
| 6678 | |
| 6679 | // Adjust the map with the extra inobject properties. |
| 6680 | copy->set_inobject_properties(inobject_properties); |
| 6681 | copy->set_unused_property_fields(inobject_properties); |
| 6682 | copy->set_instance_size(new_instance_size); |
| 6683 | copy->set_visitor_id(StaticVisitorBase::GetVisitorId(*copy)); |
| 6684 | return copy; |
| 6685 | } |
| 6686 | |
| 6687 | |
| 6688 | Handle<Map> Map::CopyForFreeze(Handle<Map> map) { |
| 6689 | int num_descriptors = map->NumberOfOwnDescriptors(); |
| 6690 | Isolate* isolate = map->GetIsolate(); |
| 6691 | Handle<DescriptorArray> new_desc = DescriptorArray::CopyUpToAddAttributes( |
| 6692 | handle(map->instance_descriptors(), isolate), num_descriptors, FROZEN); |
| 6693 | Handle<Map> new_map = CopyReplaceDescriptors( |
| 6694 | map, new_desc, INSERT_TRANSITION, isolate->factory()->frozen_symbol()); |
| 6695 | new_map->freeze(); |
| 6696 | new_map->set_is_extensible(false); |
| 6697 | new_map->set_elements_kind(DICTIONARY_ELEMENTS); |
| 6698 | return new_map; |
| 6699 | } |
| 6700 | |
| 6701 | |
| 6702 | bool DescriptorArray::CanHoldValue(int descriptor, Object* value) { |
| 6703 | PropertyDetails details = GetDetails(descriptor); |
| 6704 | switch (details.type()) { |
| 6705 | case FIELD: |
| 6706 | return value->FitsRepresentation(details.representation()) && |
| 6707 | GetFieldType(descriptor)->NowContains(value); |
| 6708 | |
| 6709 | case CONSTANT: |
| 6710 | DCHECK(GetConstant(descriptor) != value || |
| 6711 | value->FitsRepresentation(details.representation())); |
| 6712 | return GetConstant(descriptor) == value; |
| 6713 | |
| 6714 | case CALLBACKS: |
| 6715 | return false; |
| 6716 | |
| 6717 | case NORMAL: |
| 6718 | UNREACHABLE(); |
| 6719 | break; |
| 6720 | } |
| 6721 | |
| 6722 | UNREACHABLE(); |
| 6723 | return false; |
| 6724 | } |
| 6725 | |
| 6726 | |
| 6727 | Handle<Map> Map::PrepareForDataProperty(Handle<Map> map, int descriptor, |
| 6728 | Handle<Object> value) { |
| 6729 | // Dictionaries can store any property value. |
| 6730 | if (map->is_dictionary_map()) return map; |
| 6731 | |
| 6732 | // Migrate to the newest map before storing the property. |
| 6733 | map = Update(map); |
| 6734 | |
| 6735 | Handle<DescriptorArray> descriptors(map->instance_descriptors()); |
| 6736 | |
| 6737 | if (descriptors->CanHoldValue(descriptor, *value)) return map; |
| 6738 | |
| 6739 | Isolate* isolate = map->GetIsolate(); |
| 6740 | Representation representation = value->OptimalRepresentation(); |
| 6741 | Handle<HeapType> type = value->OptimalType(isolate, representation); |
| 6742 | |
| 6743 | return GeneralizeRepresentation(map, descriptor, representation, type, |
| 6744 | FORCE_FIELD); |
| 6745 | } |
| 6746 | |
| 6747 | |
| 6748 | Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name, |
| 6749 | Handle<Object> value, |
| 6750 | PropertyAttributes attributes, |
| 6751 | StoreFromKeyed store_mode) { |
| 6752 | // Dictionary maps can always have additional data properties. |
| 6753 | if (map->is_dictionary_map()) return map; |
| 6754 | |
| 6755 | // Migrate to the newest map before storing the property. |
| 6756 | map = Update(map); |
| 6757 | |
| 6758 | int index = map->SearchTransition(*name); |
| 6759 | if (index != TransitionArray::kNotFound) { |
| 6760 | Handle<Map> transition(map->GetTransition(index)); |
| 6761 | int descriptor = transition->LastAdded(); |
| 6762 | |
| 6763 | // TODO(verwaest): Handle attributes better. |
| 6764 | DescriptorArray* descriptors = transition->instance_descriptors(); |
| 6765 | if (descriptors->GetDetails(descriptor).attributes() != attributes) { |
| 6766 | return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 6767 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 6768 | |
| 6769 | return Map::PrepareForDataProperty(transition, descriptor, value); |
| 6770 | } |
| 6771 | |
| 6772 | TransitionFlag flag = INSERT_TRANSITION; |
| 6773 | MaybeHandle<Map> maybe_map; |
| 6774 | if (value->IsJSFunction()) { |
| 6775 | maybe_map = Map::CopyWithConstant(map, name, value, attributes, flag); |
| 6776 | } else if (!map->TooManyFastProperties(store_mode)) { |
| 6777 | Isolate* isolate = name->GetIsolate(); |
| 6778 | Representation representation = value->OptimalRepresentation(); |
| 6779 | Handle<HeapType> type = value->OptimalType(isolate, representation); |
| 6780 | maybe_map = |
| 6781 | Map::CopyWithField(map, name, type, attributes, representation, flag); |
| 6782 | } |
| 6783 | |
| 6784 | Handle<Map> result; |
| 6785 | if (!maybe_map.ToHandle(&result)) { |
| 6786 | return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); |
| 6787 | } |
| 6788 | |
| 6789 | return result; |
| 6790 | } |
| 6791 | |
| 6792 | |
| 6793 | Handle<Map> Map::ReconfigureDataProperty(Handle<Map> map, int descriptor, |
| 6794 | PropertyAttributes attributes) { |
| 6795 | // Dictionaries have to be reconfigured in-place. |
| 6796 | DCHECK(!map->is_dictionary_map()); |
| 6797 | |
| 6798 | // For now, give up on transitioning and just create a unique map. |
| 6799 | // TODO(verwaest/ishell): Cache transitions with different attributes. |
| 6800 | return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_FIELD, |
| 6801 | attributes, "attributes mismatch"); |
| 6802 | } |
| 6803 | |
| 6804 | |
| 6805 | Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, |
| 6806 | Handle<Name> name, |
| 6807 | AccessorComponent component, |
| 6808 | Handle<Object> accessor, |
| 6809 | PropertyAttributes attributes) { |
| 6810 | Isolate* isolate = name->GetIsolate(); |
| 6811 | |
| 6812 | // Dictionary maps can always have additional data properties. |
| 6813 | if (map->is_dictionary_map()) { |
| 6814 | // For global objects, property cells are inlined. We need to change the |
| 6815 | // map. |
| 6816 | if (map->IsGlobalObjectMap()) return Copy(map); |
| 6817 | return map; |
| 6818 | } |
| 6819 | |
| 6820 | // Migrate to the newest map before transitioning to the new property. |
| 6821 | map = Update(map); |
| 6822 | |
| 6823 | PropertyNormalizationMode mode = map->is_prototype_map() |
| 6824 | ? KEEP_INOBJECT_PROPERTIES |
| 6825 | : CLEAR_INOBJECT_PROPERTIES; |
| 6826 | |
| 6827 | int index = map->SearchTransition(*name); |
| 6828 | if (index != TransitionArray::kNotFound) { |
| 6829 | Handle<Map> transition(map->GetTransition(index)); |
| 6830 | DescriptorArray* descriptors = transition->instance_descriptors(); |
| 6831 | // Fast path, assume that we're modifying the last added descriptor. |
| 6832 | int descriptor = transition->LastAdded(); |
| 6833 | if (descriptors->GetKey(descriptor) != *name) { |
| 6834 | // If not, search for the descriptor. |
| 6835 | descriptor = descriptors->SearchWithCache(*name, *transition); |
| 6836 | } |
| 6837 | |
| 6838 | if (descriptors->GetDetails(descriptor).type() != CALLBACKS) { |
| 6839 | return Map::Normalize(map, mode); |
| 6840 | } |
| 6841 | |
| 6842 | // TODO(verwaest): Handle attributes better. |
| 6843 | if (descriptors->GetDetails(descriptor).attributes() != attributes) { |
| 6844 | return Map::Normalize(map, mode); |
| 6845 | } |
| 6846 | |
| 6847 | Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate); |
| 6848 | if (!maybe_pair->IsAccessorPair()) { |
| 6849 | return Map::Normalize(map, mode); |
| 6850 | } |
| 6851 | |
| 6852 | Handle<AccessorPair> pair = Handle<AccessorPair>::cast(maybe_pair); |
| 6853 | if (pair->get(component) != *accessor) { |
| 6854 | return Map::Normalize(map, mode); |
| 6855 | } |
| 6856 | |
| 6857 | return transition; |
| 6858 | } |
| 6859 | |
| 6860 | Handle<AccessorPair> pair; |
| 6861 | DescriptorArray* old_descriptors = map->instance_descriptors(); |
| 6862 | int descriptor = old_descriptors->SearchWithCache(*name, *map); |
| 6863 | if (descriptor != DescriptorArray::kNotFound) { |
| 6864 | PropertyDetails old_details = old_descriptors->GetDetails(descriptor); |
| 6865 | if (old_details.type() != CALLBACKS) { |
| 6866 | return Map::Normalize(map, mode); |
| 6867 | } |
| 6868 | |
| 6869 | if (old_details.attributes() != attributes) { |
| 6870 | return Map::Normalize(map, mode); |
| 6871 | } |
| 6872 | |
| 6873 | Handle<Object> maybe_pair(old_descriptors->GetValue(descriptor), isolate); |
| 6874 | if (!maybe_pair->IsAccessorPair()) { |
| 6875 | return Map::Normalize(map, mode); |
| 6876 | } |
| 6877 | |
| 6878 | Object* current = Handle<AccessorPair>::cast(maybe_pair)->get(component); |
| 6879 | if (current == *accessor) return map; |
| 6880 | |
| 6881 | if (!current->IsTheHole()) { |
| 6882 | return Map::Normalize(map, mode); |
| 6883 | } |
| 6884 | |
| 6885 | pair = AccessorPair::Copy(Handle<AccessorPair>::cast(maybe_pair)); |
| 6886 | } else if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors || |
| 6887 | map->TooManyFastProperties(CERTAINLY_NOT_STORE_FROM_KEYED)) { |
| 6888 | return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); |
| 6889 | } else { |
| 6890 | pair = isolate->factory()->NewAccessorPair(); |
| 6891 | } |
| 6892 | |
| 6893 | pair->set(component, *accessor); |
| 6894 | TransitionFlag flag = INSERT_TRANSITION; |
| 6895 | CallbacksDescriptor new_desc(name, pair, attributes); |
| 6896 | return Map::CopyInsertDescriptor(map, &new_desc, flag); |
| 6897 | } |
| 6898 | |
| 6899 | |
| 6900 | Handle<Map> Map::CopyAddDescriptor(Handle<Map> map, |
| 6901 | Descriptor* descriptor, |
| 6902 | TransitionFlag flag) { |
| 6903 | Handle<DescriptorArray> descriptors(map->instance_descriptors()); |
| 6904 | |
| 6905 | // Ensure the key is unique. |
| 6906 | descriptor->KeyToUniqueName(); |
| 6907 | |
| 6908 | if (flag == INSERT_TRANSITION && |
| 6909 | map->owns_descriptors() && |
| 6910 | map->CanHaveMoreTransitions()) { |
| 6911 | return ShareDescriptor(map, descriptors, descriptor); |
| 6912 | } |
| 6913 | |
| 6914 | Handle<DescriptorArray> new_descriptors = DescriptorArray::CopyUpTo( |
| 6915 | descriptors, map->NumberOfOwnDescriptors(), 1); |
| 6916 | new_descriptors->Append(descriptor); |
| 6917 | |
| 6918 | return CopyReplaceDescriptors( |
| 6919 | map, new_descriptors, flag, descriptor->GetKey(), SIMPLE_TRANSITION); |
| 6920 | } |
| 6921 | |
| 6922 | |
| 6923 | Handle<Map> Map::CopyInsertDescriptor(Handle<Map> map, |
| 6924 | Descriptor* descriptor, |
| 6925 | TransitionFlag flag) { |
| 6926 | Handle<DescriptorArray> old_descriptors(map->instance_descriptors()); |
| 6927 | |
| 6928 | // Ensure the key is unique. |
| 6929 | descriptor->KeyToUniqueName(); |
| 6930 | |
| 6931 | // We replace the key if it is already present. |
| 6932 | int index = old_descriptors->SearchWithCache(*descriptor->GetKey(), *map); |
| 6933 | if (index != DescriptorArray::kNotFound) { |
| 6934 | return CopyReplaceDescriptor(map, old_descriptors, descriptor, index, flag); |
| 6935 | } |
| 6936 | return CopyAddDescriptor(map, descriptor, flag); |
| 6937 | } |
| 6938 | |
| 6939 | |
| 6940 | Handle<DescriptorArray> DescriptorArray::CopyUpTo( |
| 6941 | Handle<DescriptorArray> desc, |
| 6942 | int enumeration_index, |
| 6943 | int slack) { |
| 6944 | return DescriptorArray::CopyUpToAddAttributes( |
| 6945 | desc, enumeration_index, NONE, slack); |
| 6946 | } |
| 6947 | |
| 6948 | |
| 6949 | Handle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes( |
| 6950 | Handle<DescriptorArray> desc, |
| 6951 | int enumeration_index, |
| 6952 | PropertyAttributes attributes, |
| 6953 | int slack) { |
| 6954 | if (enumeration_index + slack == 0) { |
| 6955 | return desc->GetIsolate()->factory()->empty_descriptor_array(); |
| 6956 | } |
| 6957 | |
| 6958 | int size = enumeration_index; |
| 6959 | |
| 6960 | Handle<DescriptorArray> descriptors = |
| 6961 | DescriptorArray::Allocate(desc->GetIsolate(), size, slack); |
| 6962 | DescriptorArray::WhitenessWitness witness(*descriptors); |
| 6963 | |
| 6964 | if (attributes != NONE) { |
| 6965 | for (int i = 0; i < size; ++i) { |
| 6966 | Object* value = desc->GetValue(i); |
| 6967 | Name* key = desc->GetKey(i); |
| 6968 | PropertyDetails details = desc->GetDetails(i); |
| 6969 | // Bulk attribute changes never affect private properties. |
| 6970 | if (!key->IsSymbol() || !Symbol::cast(key)->is_private()) { |
| 6971 | int mask = DONT_DELETE | DONT_ENUM; |
| 6972 | // READ_ONLY is an invalid attribute for JS setters/getters. |
| 6973 | if (details.type() != CALLBACKS || !value->IsAccessorPair()) { |
| 6974 | mask |= READ_ONLY; |
| 6975 | } |
| 6976 | details = details.CopyAddAttributes( |
| 6977 | static_cast<PropertyAttributes>(attributes & mask)); |
| 6978 | } |
| 6979 | Descriptor inner_desc( |
| 6980 | handle(key), handle(value, desc->GetIsolate()), details); |
| 6981 | descriptors->Set(i, &inner_desc, witness); |
| 6982 | } |
| 6983 | } else { |
| 6984 | for (int i = 0; i < size; ++i) { |
| 6985 | descriptors->CopyFrom(i, *desc, witness); |
| 6986 | } |
| 6987 | } |
| 6988 | |
| 6989 | if (desc->number_of_descriptors() != enumeration_index) descriptors->Sort(); |
| 6990 | |
| 6991 | return descriptors; |
| 6992 | } |
| 6993 | |
| 6994 | |
| 6995 | Handle<Map> Map::CopyReplaceDescriptor(Handle<Map> map, |
| 6996 | Handle<DescriptorArray> descriptors, |
| 6997 | Descriptor* descriptor, |
| 6998 | int insertion_index, |
| 6999 | TransitionFlag flag) { |
| 7000 | // Ensure the key is unique. |
| 7001 | descriptor->KeyToUniqueName(); |
| 7002 | |
| 7003 | Handle<Name> key = descriptor->GetKey(); |
| 7004 | DCHECK(*key == descriptors->GetKey(insertion_index)); |
| 7005 | |
| 7006 | Handle<DescriptorArray> new_descriptors = DescriptorArray::CopyUpTo( |
| 7007 | descriptors, map->NumberOfOwnDescriptors()); |
| 7008 | |
| 7009 | new_descriptors->Replace(insertion_index, descriptor); |
| 7010 | |
| 7011 | SimpleTransitionFlag simple_flag = |
| 7012 | (insertion_index == descriptors->number_of_descriptors() - 1) |
| 7013 | ? SIMPLE_TRANSITION |
| 7014 | : FULL_TRANSITION; |
| 7015 | return CopyReplaceDescriptors(map, new_descriptors, flag, key, simple_flag); |
| 7016 | } |
| 7017 | |
| 7018 | |
| 7019 | void Map::UpdateCodeCache(Handle<Map> map, |
| 7020 | Handle<Name> name, |
| 7021 | Handle<Code> code) { |
| 7022 | Isolate* isolate = map->GetIsolate(); |
| 7023 | HandleScope scope(isolate); |
| 7024 | // Allocate the code cache if not present. |
| 7025 | if (map->code_cache()->IsFixedArray()) { |
| 7026 | Handle<Object> result = isolate->factory()->NewCodeCache(); |
| 7027 | map->set_code_cache(*result); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7028 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7029 | |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7030 | // Update the code cache. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7031 | Handle<CodeCache> code_cache(CodeCache::cast(map->code_cache()), isolate); |
| 7032 | CodeCache::Update(code_cache, name, code); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7033 | } |
| 7034 | |
| 7035 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7036 | Object* Map::FindInCodeCache(Name* name, Code::Flags flags) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7037 | // Do a lookup if a code cache exists. |
| 7038 | if (!code_cache()->IsFixedArray()) { |
| 7039 | return CodeCache::cast(code_cache())->Lookup(name, flags); |
| 7040 | } else { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 7041 | return GetHeap()->undefined_value(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7042 | } |
| 7043 | } |
| 7044 | |
| 7045 | |
| 7046 | int Map::IndexInCodeCache(Object* name, Code* code) { |
| 7047 | // Get the internal index if a code cache exists. |
| 7048 | if (!code_cache()->IsFixedArray()) { |
| 7049 | return CodeCache::cast(code_cache())->GetIndex(name, code); |
| 7050 | } |
| 7051 | return -1; |
| 7052 | } |
| 7053 | |
| 7054 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7055 | void Map::RemoveFromCodeCache(Name* name, Code* code, int index) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7056 | // No GC is supposed to happen between a call to IndexInCodeCache and |
| 7057 | // RemoveFromCodeCache so the code cache must be there. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7058 | DCHECK(!code_cache()->IsFixedArray()); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7059 | CodeCache::cast(code_cache())->RemoveByIndex(name, code, index); |
| 7060 | } |
| 7061 | |
| 7062 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7063 | // An iterator over all map transitions in an descriptor array, reusing the |
| 7064 | // constructor field of the map while it is running. Negative values in |
| 7065 | // the constructor field indicate an active map transition iteration. The |
| 7066 | // original constructor is restored after iterating over all entries. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7067 | class IntrusiveMapTransitionIterator { |
| 7068 | public: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7069 | IntrusiveMapTransitionIterator( |
| 7070 | Map* map, TransitionArray* transition_array, Object* constructor) |
| 7071 | : map_(map), |
| 7072 | transition_array_(transition_array), |
| 7073 | constructor_(constructor) { } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7074 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7075 | void StartIfNotStarted() { |
| 7076 | DCHECK(!(*IteratorField())->IsSmi() || IsIterating()); |
| 7077 | if (!(*IteratorField())->IsSmi()) { |
| 7078 | DCHECK(*IteratorField() == constructor_); |
| 7079 | *IteratorField() = Smi::FromInt(-1); |
| 7080 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7081 | } |
| 7082 | |
| 7083 | bool IsIterating() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7084 | return (*IteratorField())->IsSmi() && |
| 7085 | Smi::cast(*IteratorField())->value() < 0; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7086 | } |
| 7087 | |
| 7088 | Map* Next() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7089 | DCHECK(IsIterating()); |
| 7090 | int value = Smi::cast(*IteratorField())->value(); |
| 7091 | int index = -value - 1; |
| 7092 | int number_of_transitions = transition_array_->number_of_transitions(); |
| 7093 | while (index < number_of_transitions) { |
| 7094 | *IteratorField() = Smi::FromInt(value - 1); |
| 7095 | return transition_array_->GetTarget(index); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 7096 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7097 | |
| 7098 | *IteratorField() = constructor_; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7099 | return NULL; |
| 7100 | } |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 7101 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7102 | private: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7103 | Object** IteratorField() { |
| 7104 | return HeapObject::RawField(map_, Map::kConstructorOffset); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7105 | } |
| 7106 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7107 | Map* map_; |
| 7108 | TransitionArray* transition_array_; |
| 7109 | Object* constructor_; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7110 | }; |
| 7111 | |
| 7112 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7113 | // An iterator over all prototype transitions, reusing the constructor field |
| 7114 | // of the map while it is running. Positive values in the constructor field |
| 7115 | // indicate an active prototype transition iteration. The original constructor |
| 7116 | // is restored after iterating over all entries. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7117 | class IntrusivePrototypeTransitionIterator { |
| 7118 | public: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7119 | IntrusivePrototypeTransitionIterator( |
| 7120 | Map* map, HeapObject* proto_trans, Object* constructor) |
| 7121 | : map_(map), proto_trans_(proto_trans), constructor_(constructor) { } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7122 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7123 | void StartIfNotStarted() { |
| 7124 | if (!(*IteratorField())->IsSmi()) { |
| 7125 | DCHECK(*IteratorField() == constructor_); |
| 7126 | *IteratorField() = Smi::FromInt(0); |
| 7127 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7128 | } |
| 7129 | |
| 7130 | bool IsIterating() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7131 | return (*IteratorField())->IsSmi() && |
| 7132 | Smi::cast(*IteratorField())->value() >= 0; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7133 | } |
| 7134 | |
| 7135 | Map* Next() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7136 | DCHECK(IsIterating()); |
| 7137 | int transitionNumber = Smi::cast(*IteratorField())->value(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7138 | if (transitionNumber < NumberOfTransitions()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7139 | *IteratorField() = Smi::FromInt(transitionNumber + 1); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7140 | return GetTransition(transitionNumber); |
| 7141 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7142 | *IteratorField() = constructor_; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7143 | return NULL; |
| 7144 | } |
| 7145 | |
| 7146 | private: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7147 | Object** IteratorField() { |
| 7148 | return HeapObject::RawField(map_, Map::kConstructorOffset); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7149 | } |
| 7150 | |
| 7151 | int NumberOfTransitions() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7152 | FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_); |
| 7153 | Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7154 | return Smi::cast(num)->value(); |
| 7155 | } |
| 7156 | |
| 7157 | Map* GetTransition(int transitionNumber) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7158 | FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_); |
| 7159 | return Map::cast(proto_trans->get(IndexFor(transitionNumber))); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7160 | } |
| 7161 | |
| 7162 | int IndexFor(int transitionNumber) { |
| 7163 | return Map::kProtoTransitionHeaderSize + |
| 7164 | Map::kProtoTransitionMapOffset + |
| 7165 | transitionNumber * Map::kProtoTransitionElementsPerEntry; |
| 7166 | } |
| 7167 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7168 | Map* map_; |
| 7169 | HeapObject* proto_trans_; |
| 7170 | Object* constructor_; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7171 | }; |
| 7172 | |
| 7173 | |
| 7174 | // To traverse the transition tree iteratively, we have to store two kinds of |
| 7175 | // information in a map: The parent map in the traversal and which children of a |
| 7176 | // node have already been visited. To do this without additional memory, we |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7177 | // temporarily reuse two fields with known values: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7178 | // |
| 7179 | // (1) The map of the map temporarily holds the parent, and is restored to the |
| 7180 | // meta map afterwards. |
| 7181 | // |
| 7182 | // (2) The info which children have already been visited depends on which part |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7183 | // of the map we currently iterate. We use the constructor field of the |
| 7184 | // map to store the current index. We can do that because the constructor |
| 7185 | // is the same for all involved maps. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7186 | // |
| 7187 | // (a) If we currently follow normal map transitions, we temporarily store |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7188 | // the current index in the constructor field, and restore it to the |
| 7189 | // original constructor afterwards. Note that a single descriptor can |
| 7190 | // have 0, 1, or 2 transitions. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7191 | // |
| 7192 | // (b) If we currently follow prototype transitions, we temporarily store |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7193 | // the current index in the constructor field, and restore it to the |
| 7194 | // original constructor afterwards. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7195 | // |
| 7196 | // Note that the child iterator is just a concatenation of two iterators: One |
| 7197 | // iterating over map transitions and one iterating over prototype transisitons. |
| 7198 | class TraversableMap : public Map { |
| 7199 | public: |
| 7200 | // Record the parent in the traversal within this map. Note that this destroys |
| 7201 | // this map's map! |
| 7202 | void SetParent(TraversableMap* parent) { set_map_no_write_barrier(parent); } |
| 7203 | |
| 7204 | // Reset the current map's map, returning the parent previously stored in it. |
| 7205 | TraversableMap* GetAndResetParent() { |
| 7206 | TraversableMap* old_parent = static_cast<TraversableMap*>(map()); |
| 7207 | set_map_no_write_barrier(GetHeap()->meta_map()); |
| 7208 | return old_parent; |
| 7209 | } |
| 7210 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7211 | // If we have an unvisited child map, return that one and advance. If we have |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7212 | // none, return NULL and restore the overwritten constructor field. |
| 7213 | TraversableMap* ChildIteratorNext(Object* constructor) { |
| 7214 | if (!HasTransitionArray()) return NULL; |
| 7215 | |
| 7216 | TransitionArray* transition_array = transitions(); |
| 7217 | if (transition_array->HasPrototypeTransitions()) { |
| 7218 | HeapObject* proto_transitions = |
| 7219 | transition_array->GetPrototypeTransitions(); |
| 7220 | IntrusivePrototypeTransitionIterator proto_iterator(this, |
| 7221 | proto_transitions, |
| 7222 | constructor); |
| 7223 | proto_iterator.StartIfNotStarted(); |
| 7224 | if (proto_iterator.IsIterating()) { |
| 7225 | Map* next = proto_iterator.Next(); |
| 7226 | if (next != NULL) return static_cast<TraversableMap*>(next); |
| 7227 | } |
| 7228 | } |
| 7229 | |
| 7230 | IntrusiveMapTransitionIterator transition_iterator(this, |
| 7231 | transition_array, |
| 7232 | constructor); |
| 7233 | transition_iterator.StartIfNotStarted(); |
| 7234 | if (transition_iterator.IsIterating()) { |
| 7235 | Map* next = transition_iterator.Next(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7236 | if (next != NULL) return static_cast<TraversableMap*>(next); |
| 7237 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7238 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7239 | return NULL; |
| 7240 | } |
| 7241 | }; |
| 7242 | |
| 7243 | |
| 7244 | // Traverse the transition tree in postorder without using the C++ stack by |
| 7245 | // doing pointer reversal. |
| 7246 | void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7247 | // Make sure that we do not allocate in the callback. |
| 7248 | DisallowHeapAllocation no_allocation; |
| 7249 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7250 | TraversableMap* current = static_cast<TraversableMap*>(this); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7251 | // Get the root constructor here to restore it later when finished iterating |
| 7252 | // over maps. |
| 7253 | Object* root_constructor = constructor(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7254 | while (true) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7255 | TraversableMap* child = current->ChildIteratorNext(root_constructor); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7256 | if (child != NULL) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7257 | child->SetParent(current); |
| 7258 | current = child; |
| 7259 | } else { |
| 7260 | TraversableMap* parent = current->GetAndResetParent(); |
| 7261 | callback(current, data); |
| 7262 | if (current == this) break; |
| 7263 | current = parent; |
| 7264 | } |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 7265 | } |
| 7266 | } |
| 7267 | |
| 7268 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7269 | void CodeCache::Update( |
| 7270 | Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7271 | // The number of monomorphic stubs for normal load/store/call IC's can grow to |
| 7272 | // a large number and therefore they need to go into a hash table. They are |
| 7273 | // used to load global properties from cells. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7274 | if (code->type() == Code::NORMAL) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7275 | // Make sure that a hash table is allocated for the normal load code cache. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7276 | if (code_cache->normal_type_cache()->IsUndefined()) { |
| 7277 | Handle<Object> result = |
| 7278 | CodeCacheHashTable::New(code_cache->GetIsolate(), |
| 7279 | CodeCacheHashTable::kInitialSize); |
| 7280 | code_cache->set_normal_type_cache(*result); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7281 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7282 | UpdateNormalTypeCache(code_cache, name, code); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7283 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7284 | DCHECK(code_cache->default_cache()->IsFixedArray()); |
| 7285 | UpdateDefaultCache(code_cache, name, code); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7286 | } |
| 7287 | } |
| 7288 | |
| 7289 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7290 | void CodeCache::UpdateDefaultCache( |
| 7291 | Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7292 | // When updating the default code cache we disregard the type encoded in the |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7293 | // flags. This allows call constant stubs to overwrite call field |
| 7294 | // stubs, etc. |
| 7295 | Code::Flags flags = Code::RemoveTypeFromFlags(code->flags()); |
| 7296 | |
| 7297 | // First check whether we can update existing code cache without |
| 7298 | // extending it. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7299 | Handle<FixedArray> cache = handle(code_cache->default_cache()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7300 | int length = cache->length(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7301 | { |
| 7302 | DisallowHeapAllocation no_alloc; |
| 7303 | int deleted_index = -1; |
| 7304 | for (int i = 0; i < length; i += kCodeCacheEntrySize) { |
| 7305 | Object* key = cache->get(i); |
| 7306 | if (key->IsNull()) { |
| 7307 | if (deleted_index < 0) deleted_index = i; |
| 7308 | continue; |
| 7309 | } |
| 7310 | if (key->IsUndefined()) { |
| 7311 | if (deleted_index >= 0) i = deleted_index; |
| 7312 | cache->set(i + kCodeCacheEntryNameOffset, *name); |
| 7313 | cache->set(i + kCodeCacheEntryCodeOffset, *code); |
| 7314 | return; |
| 7315 | } |
| 7316 | if (name->Equals(Name::cast(key))) { |
| 7317 | Code::Flags found = |
| 7318 | Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags(); |
| 7319 | if (Code::RemoveTypeFromFlags(found) == flags) { |
| 7320 | cache->set(i + kCodeCacheEntryCodeOffset, *code); |
| 7321 | return; |
| 7322 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7323 | } |
| 7324 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7325 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7326 | // Reached the end of the code cache. If there were deleted |
| 7327 | // elements, reuse the space for the first of them. |
| 7328 | if (deleted_index >= 0) { |
| 7329 | cache->set(deleted_index + kCodeCacheEntryNameOffset, *name); |
| 7330 | cache->set(deleted_index + kCodeCacheEntryCodeOffset, *code); |
| 7331 | return; |
| 7332 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7333 | } |
| 7334 | |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7335 | // Extend the code cache with some new entries (at least one). Must be a |
| 7336 | // multiple of the entry size. |
| 7337 | int new_length = length + ((length >> 1)) + kCodeCacheEntrySize; |
| 7338 | new_length = new_length - new_length % kCodeCacheEntrySize; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7339 | DCHECK((new_length % kCodeCacheEntrySize) == 0); |
| 7340 | cache = FixedArray::CopySize(cache, new_length); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7341 | |
| 7342 | // Add the (name, code) pair to the new cache. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7343 | cache->set(length + kCodeCacheEntryNameOffset, *name); |
| 7344 | cache->set(length + kCodeCacheEntryCodeOffset, *code); |
| 7345 | code_cache->set_default_cache(*cache); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7346 | } |
| 7347 | |
| 7348 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7349 | void CodeCache::UpdateNormalTypeCache( |
| 7350 | Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7351 | // Adding a new entry can cause a new cache to be allocated. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7352 | Handle<CodeCacheHashTable> cache( |
| 7353 | CodeCacheHashTable::cast(code_cache->normal_type_cache())); |
| 7354 | Handle<Object> new_cache = CodeCacheHashTable::Put(cache, name, code); |
| 7355 | code_cache->set_normal_type_cache(*new_cache); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7356 | } |
| 7357 | |
| 7358 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7359 | Object* CodeCache::Lookup(Name* name, Code::Flags flags) { |
| 7360 | Object* result = LookupDefaultCache(name, Code::RemoveTypeFromFlags(flags)); |
| 7361 | if (result->IsCode()) { |
| 7362 | if (Code::cast(result)->flags() == flags) return result; |
| 7363 | return GetHeap()->undefined_value(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7364 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7365 | return LookupNormalTypeCache(name, flags); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7366 | } |
| 7367 | |
| 7368 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7369 | Object* CodeCache::LookupDefaultCache(Name* name, Code::Flags flags) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7370 | FixedArray* cache = default_cache(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7371 | int length = cache->length(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7372 | for (int i = 0; i < length; i += kCodeCacheEntrySize) { |
| 7373 | Object* key = cache->get(i + kCodeCacheEntryNameOffset); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7374 | // Skip deleted elements. |
| 7375 | if (key->IsNull()) continue; |
| 7376 | if (key->IsUndefined()) return key; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7377 | if (name->Equals(Name::cast(key))) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7378 | Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7379 | if (Code::RemoveTypeFromFlags(code->flags()) == flags) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7380 | return code; |
| 7381 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7382 | } |
| 7383 | } |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 7384 | return GetHeap()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7385 | } |
| 7386 | |
| 7387 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7388 | Object* CodeCache::LookupNormalTypeCache(Name* name, Code::Flags flags) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7389 | if (!normal_type_cache()->IsUndefined()) { |
| 7390 | CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); |
| 7391 | return cache->Lookup(name, flags); |
| 7392 | } else { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 7393 | return GetHeap()->undefined_value(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7394 | } |
| 7395 | } |
| 7396 | |
| 7397 | |
| 7398 | int CodeCache::GetIndex(Object* name, Code* code) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7399 | if (code->type() == Code::NORMAL) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7400 | if (normal_type_cache()->IsUndefined()) return -1; |
| 7401 | CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7402 | return cache->GetIndex(Name::cast(name), code->flags()); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7403 | } |
| 7404 | |
| 7405 | FixedArray* array = default_cache(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7406 | int len = array->length(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7407 | for (int i = 0; i < len; i += kCodeCacheEntrySize) { |
| 7408 | if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7409 | } |
| 7410 | return -1; |
| 7411 | } |
| 7412 | |
| 7413 | |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7414 | void CodeCache::RemoveByIndex(Object* name, Code* code, int index) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7415 | if (code->type() == Code::NORMAL) { |
| 7416 | DCHECK(!normal_type_cache()->IsUndefined()); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7417 | CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7418 | DCHECK(cache->GetIndex(Name::cast(name), code->flags()) == index); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7419 | cache->RemoveByIndex(index); |
| 7420 | } else { |
| 7421 | FixedArray* array = default_cache(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7422 | DCHECK(array->length() >= index && array->get(index)->IsCode()); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7423 | // Use null instead of undefined for deleted elements to distinguish |
| 7424 | // deleted elements from unused elements. This distinction is used |
| 7425 | // when looking up in the cache and when updating the cache. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7426 | DCHECK_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7427 | array->set_null(index - 1); // Name. |
| 7428 | array->set_null(index); // Code. |
| 7429 | } |
| 7430 | } |
| 7431 | |
| 7432 | |
| 7433 | // The key in the code cache hash table consists of the property name and the |
| 7434 | // code object. The actual match is on the name and the code flags. If a key |
| 7435 | // is created using the flags and not a code object it can only be used for |
| 7436 | // lookup not to create a new entry. |
| 7437 | class CodeCacheHashTableKey : public HashTableKey { |
| 7438 | public: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7439 | CodeCacheHashTableKey(Handle<Name> name, Code::Flags flags) |
| 7440 | : name_(name), flags_(flags), code_() { } |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7441 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7442 | CodeCacheHashTableKey(Handle<Name> name, Handle<Code> code) |
| 7443 | : name_(name), flags_(code->flags()), code_(code) { } |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7444 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7445 | bool IsMatch(Object* other) OVERRIDE { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7446 | if (!other->IsFixedArray()) return false; |
| 7447 | FixedArray* pair = FixedArray::cast(other); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7448 | Name* name = Name::cast(pair->get(0)); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7449 | Code::Flags flags = Code::cast(pair->get(1))->flags(); |
| 7450 | if (flags != flags_) { |
| 7451 | return false; |
| 7452 | } |
| 7453 | return name_->Equals(name); |
| 7454 | } |
| 7455 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7456 | static uint32_t NameFlagsHashHelper(Name* name, Code::Flags flags) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7457 | return name->Hash() ^ flags; |
| 7458 | } |
| 7459 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7460 | uint32_t Hash() OVERRIDE { return NameFlagsHashHelper(*name_, flags_); } |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7461 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7462 | uint32_t HashForObject(Object* obj) OVERRIDE { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7463 | FixedArray* pair = FixedArray::cast(obj); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7464 | Name* name = Name::cast(pair->get(0)); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7465 | Code* code = Code::cast(pair->get(1)); |
| 7466 | return NameFlagsHashHelper(name, code->flags()); |
| 7467 | } |
| 7468 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7469 | MUST_USE_RESULT Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { |
| 7470 | Handle<Code> code = code_.ToHandleChecked(); |
| 7471 | Handle<FixedArray> pair = isolate->factory()->NewFixedArray(2); |
| 7472 | pair->set(0, *name_); |
| 7473 | pair->set(1, *code); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7474 | return pair; |
| 7475 | } |
| 7476 | |
| 7477 | private: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7478 | Handle<Name> name_; |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7479 | Code::Flags flags_; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7480 | // TODO(jkummerow): We should be able to get by without this. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7481 | MaybeHandle<Code> code_; |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7482 | }; |
| 7483 | |
| 7484 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7485 | Object* CodeCacheHashTable::Lookup(Name* name, Code::Flags flags) { |
| 7486 | DisallowHeapAllocation no_alloc; |
| 7487 | CodeCacheHashTableKey key(handle(name), flags); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7488 | int entry = FindEntry(&key); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 7489 | if (entry == kNotFound) return GetHeap()->undefined_value(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7490 | return get(EntryToIndex(entry) + 1); |
| 7491 | } |
| 7492 | |
| 7493 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7494 | Handle<CodeCacheHashTable> CodeCacheHashTable::Put( |
| 7495 | Handle<CodeCacheHashTable> cache, Handle<Name> name, Handle<Code> code) { |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7496 | CodeCacheHashTableKey key(name, code); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7497 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7498 | Handle<CodeCacheHashTable> new_cache = EnsureCapacity(cache, 1, &key); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7499 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7500 | int entry = new_cache->FindInsertionEntry(key.Hash()); |
| 7501 | Handle<Object> k = key.AsHandle(cache->GetIsolate()); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7502 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7503 | new_cache->set(EntryToIndex(entry), *k); |
| 7504 | new_cache->set(EntryToIndex(entry) + 1, *code); |
| 7505 | new_cache->ElementAdded(); |
| 7506 | return new_cache; |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7507 | } |
| 7508 | |
| 7509 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7510 | int CodeCacheHashTable::GetIndex(Name* name, Code::Flags flags) { |
| 7511 | DisallowHeapAllocation no_alloc; |
| 7512 | CodeCacheHashTableKey key(handle(name), flags); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7513 | int entry = FindEntry(&key); |
| 7514 | return (entry == kNotFound) ? -1 : entry; |
| 7515 | } |
| 7516 | |
| 7517 | |
| 7518 | void CodeCacheHashTable::RemoveByIndex(int index) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7519 | DCHECK(index >= 0); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 7520 | Heap* heap = GetHeap(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7521 | set(EntryToIndex(index), heap->the_hole_value()); |
| 7522 | set(EntryToIndex(index) + 1, heap->the_hole_value()); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7523 | ElementRemoved(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7524 | } |
| 7525 | |
| 7526 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7527 | void PolymorphicCodeCache::Update(Handle<PolymorphicCodeCache> code_cache, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7528 | MapHandleList* maps, |
| 7529 | Code::Flags flags, |
| 7530 | Handle<Code> code) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7531 | Isolate* isolate = code_cache->GetIsolate(); |
| 7532 | if (code_cache->cache()->IsUndefined()) { |
| 7533 | Handle<PolymorphicCodeCacheHashTable> result = |
| 7534 | PolymorphicCodeCacheHashTable::New( |
| 7535 | isolate, |
| 7536 | PolymorphicCodeCacheHashTable::kInitialSize); |
| 7537 | code_cache->set_cache(*result); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7538 | } else { |
| 7539 | // This entry shouldn't be contained in the cache yet. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7540 | DCHECK(PolymorphicCodeCacheHashTable::cast(code_cache->cache()) |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7541 | ->Lookup(maps, flags)->IsUndefined()); |
| 7542 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7543 | Handle<PolymorphicCodeCacheHashTable> hash_table = |
| 7544 | handle(PolymorphicCodeCacheHashTable::cast(code_cache->cache())); |
| 7545 | Handle<PolymorphicCodeCacheHashTable> new_cache = |
| 7546 | PolymorphicCodeCacheHashTable::Put(hash_table, maps, flags, code); |
| 7547 | code_cache->set_cache(*new_cache); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7548 | } |
| 7549 | |
| 7550 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7551 | Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps, |
| 7552 | Code::Flags flags) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7553 | if (!cache()->IsUndefined()) { |
| 7554 | PolymorphicCodeCacheHashTable* hash_table = |
| 7555 | PolymorphicCodeCacheHashTable::cast(cache()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7556 | return Handle<Object>(hash_table->Lookup(maps, flags), GetIsolate()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7557 | } else { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7558 | return GetIsolate()->factory()->undefined_value(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7559 | } |
| 7560 | } |
| 7561 | |
| 7562 | |
| 7563 | // Despite their name, object of this class are not stored in the actual |
| 7564 | // hash table; instead they're temporarily used for lookups. It is therefore |
| 7565 | // safe to have a weak (non-owning) pointer to a MapList as a member field. |
| 7566 | class PolymorphicCodeCacheHashTableKey : public HashTableKey { |
| 7567 | public: |
| 7568 | // Callers must ensure that |maps| outlives the newly constructed object. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7569 | PolymorphicCodeCacheHashTableKey(MapHandleList* maps, int code_flags) |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7570 | : maps_(maps), |
| 7571 | code_flags_(code_flags) {} |
| 7572 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7573 | bool IsMatch(Object* other) OVERRIDE { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7574 | MapHandleList other_maps(kDefaultListAllocationSize); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7575 | int other_flags; |
| 7576 | FromObject(other, &other_flags, &other_maps); |
| 7577 | if (code_flags_ != other_flags) return false; |
| 7578 | if (maps_->length() != other_maps.length()) return false; |
| 7579 | // Compare just the hashes first because it's faster. |
| 7580 | int this_hash = MapsHashHelper(maps_, code_flags_); |
| 7581 | int other_hash = MapsHashHelper(&other_maps, other_flags); |
| 7582 | if (this_hash != other_hash) return false; |
| 7583 | |
| 7584 | // Full comparison: for each map in maps_, look for an equivalent map in |
| 7585 | // other_maps. This implementation is slow, but probably good enough for |
| 7586 | // now because the lists are short (<= 4 elements currently). |
| 7587 | for (int i = 0; i < maps_->length(); ++i) { |
| 7588 | bool match_found = false; |
| 7589 | for (int j = 0; j < other_maps.length(); ++j) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7590 | if (*(maps_->at(i)) == *(other_maps.at(j))) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7591 | match_found = true; |
| 7592 | break; |
| 7593 | } |
| 7594 | } |
| 7595 | if (!match_found) return false; |
| 7596 | } |
| 7597 | return true; |
| 7598 | } |
| 7599 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7600 | static uint32_t MapsHashHelper(MapHandleList* maps, int code_flags) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7601 | uint32_t hash = code_flags; |
| 7602 | for (int i = 0; i < maps->length(); ++i) { |
| 7603 | hash ^= maps->at(i)->Hash(); |
| 7604 | } |
| 7605 | return hash; |
| 7606 | } |
| 7607 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7608 | uint32_t Hash() OVERRIDE { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7609 | return MapsHashHelper(maps_, code_flags_); |
| 7610 | } |
| 7611 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7612 | uint32_t HashForObject(Object* obj) OVERRIDE { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7613 | MapHandleList other_maps(kDefaultListAllocationSize); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7614 | int other_flags; |
| 7615 | FromObject(obj, &other_flags, &other_maps); |
| 7616 | return MapsHashHelper(&other_maps, other_flags); |
| 7617 | } |
| 7618 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7619 | MUST_USE_RESULT Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7620 | // The maps in |maps_| must be copied to a newly allocated FixedArray, |
| 7621 | // both because the referenced MapList is short-lived, and because C++ |
| 7622 | // objects can't be stored in the heap anyway. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7623 | Handle<FixedArray> list = |
| 7624 | isolate->factory()->NewUninitializedFixedArray(maps_->length() + 1); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7625 | list->set(0, Smi::FromInt(code_flags_)); |
| 7626 | for (int i = 0; i < maps_->length(); ++i) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7627 | list->set(i + 1, *maps_->at(i)); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7628 | } |
| 7629 | return list; |
| 7630 | } |
| 7631 | |
| 7632 | private: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7633 | static MapHandleList* FromObject(Object* obj, |
| 7634 | int* code_flags, |
| 7635 | MapHandleList* maps) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7636 | FixedArray* list = FixedArray::cast(obj); |
| 7637 | maps->Rewind(0); |
| 7638 | *code_flags = Smi::cast(list->get(0))->value(); |
| 7639 | for (int i = 1; i < list->length(); ++i) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7640 | maps->Add(Handle<Map>(Map::cast(list->get(i)))); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7641 | } |
| 7642 | return maps; |
| 7643 | } |
| 7644 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7645 | MapHandleList* maps_; // weak. |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7646 | int code_flags_; |
| 7647 | static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1; |
| 7648 | }; |
| 7649 | |
| 7650 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7651 | Object* PolymorphicCodeCacheHashTable::Lookup(MapHandleList* maps, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7652 | int code_kind) { |
| 7653 | DisallowHeapAllocation no_alloc; |
| 7654 | PolymorphicCodeCacheHashTableKey key(maps, code_kind); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7655 | int entry = FindEntry(&key); |
| 7656 | if (entry == kNotFound) return GetHeap()->undefined_value(); |
| 7657 | return get(EntryToIndex(entry) + 1); |
| 7658 | } |
| 7659 | |
| 7660 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7661 | Handle<PolymorphicCodeCacheHashTable> PolymorphicCodeCacheHashTable::Put( |
| 7662 | Handle<PolymorphicCodeCacheHashTable> hash_table, |
| 7663 | MapHandleList* maps, |
| 7664 | int code_kind, |
| 7665 | Handle<Code> code) { |
| 7666 | PolymorphicCodeCacheHashTableKey key(maps, code_kind); |
| 7667 | Handle<PolymorphicCodeCacheHashTable> cache = |
| 7668 | EnsureCapacity(hash_table, 1, &key); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7669 | int entry = cache->FindInsertionEntry(key.Hash()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7670 | |
| 7671 | Handle<Object> obj = key.AsHandle(hash_table->GetIsolate()); |
| 7672 | cache->set(EntryToIndex(entry), *obj); |
| 7673 | cache->set(EntryToIndex(entry) + 1, *code); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 7674 | cache->ElementAdded(); |
| 7675 | return cache; |
| 7676 | } |
| 7677 | |
| 7678 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7679 | void FixedArray::Shrink(int new_length) { |
| 7680 | DCHECK(0 <= new_length && new_length <= length()); |
| 7681 | if (new_length < length()) { |
| 7682 | GetHeap()->RightTrimFixedArray<Heap::FROM_MUTATOR>( |
| 7683 | this, length() - new_length); |
| 7684 | } |
| 7685 | } |
| 7686 | |
| 7687 | |
| 7688 | MaybeHandle<FixedArray> FixedArray::AddKeysFromArrayLike( |
| 7689 | Handle<FixedArray> content, |
| 7690 | Handle<JSObject> array) { |
| 7691 | DCHECK(array->IsJSArray() || array->HasSloppyArgumentsElements()); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7692 | ElementsAccessor* accessor = array->GetElementsAccessor(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7693 | Handle<FixedArray> result; |
| 7694 | ASSIGN_RETURN_ON_EXCEPTION( |
| 7695 | array->GetIsolate(), result, |
| 7696 | accessor->AddElementsToFixedArray(array, array, content), |
| 7697 | FixedArray); |
| 7698 | |
| 7699 | #ifdef ENABLE_SLOW_DCHECKS |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7700 | if (FLAG_enable_slow_asserts) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7701 | DisallowHeapAllocation no_allocation; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7702 | for (int i = 0; i < result->length(); i++) { |
| 7703 | Object* current = result->get(i); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7704 | DCHECK(current->IsNumber() || current->IsName()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7705 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7706 | } |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7707 | #endif |
| 7708 | return result; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7709 | } |
| 7710 | |
| 7711 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7712 | MaybeHandle<FixedArray> FixedArray::UnionOfKeys(Handle<FixedArray> first, |
| 7713 | Handle<FixedArray> second) { |
| 7714 | ElementsAccessor* accessor = ElementsAccessor::ForArray(second); |
| 7715 | Handle<FixedArray> result; |
| 7716 | ASSIGN_RETURN_ON_EXCEPTION( |
| 7717 | first->GetIsolate(), result, |
| 7718 | accessor->AddElementsToFixedArray( |
| 7719 | Handle<Object>::null(), // receiver |
| 7720 | Handle<JSObject>::null(), // holder |
| 7721 | first, |
| 7722 | Handle<FixedArrayBase>::cast(second)), |
| 7723 | FixedArray); |
| 7724 | |
| 7725 | #ifdef ENABLE_SLOW_DCHECKS |
Ben Murdoch | f87a203 | 2010-10-22 12:50:53 +0100 | [diff] [blame] | 7726 | if (FLAG_enable_slow_asserts) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7727 | DisallowHeapAllocation no_allocation; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7728 | for (int i = 0; i < result->length(); i++) { |
| 7729 | Object* current = result->get(i); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7730 | DCHECK(current->IsNumber() || current->IsName()); |
Ben Murdoch | f87a203 | 2010-10-22 12:50:53 +0100 | [diff] [blame] | 7731 | } |
| 7732 | } |
| 7733 | #endif |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7734 | return result; |
| 7735 | } |
| 7736 | |
| 7737 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7738 | Handle<FixedArray> FixedArray::CopySize( |
| 7739 | Handle<FixedArray> array, int new_length, PretenureFlag pretenure) { |
| 7740 | Isolate* isolate = array->GetIsolate(); |
| 7741 | if (new_length == 0) return isolate->factory()->empty_fixed_array(); |
| 7742 | Handle<FixedArray> result = |
| 7743 | isolate->factory()->NewFixedArray(new_length, pretenure); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7744 | // Copy the content |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7745 | DisallowHeapAllocation no_gc; |
| 7746 | int len = array->length(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7747 | if (new_length < len) len = new_length; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7748 | // We are taking the map from the old fixed array so the map is sure to |
| 7749 | // be an immortal immutable object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7750 | result->set_map_no_write_barrier(array->map()); |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 7751 | WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7752 | for (int i = 0; i < len; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7753 | result->set(i, array->get(i), mode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7754 | } |
| 7755 | return result; |
| 7756 | } |
| 7757 | |
| 7758 | |
| 7759 | void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7760 | DisallowHeapAllocation no_gc; |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 7761 | WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7762 | for (int index = 0; index < len; index++) { |
| 7763 | dest->set(dest_pos+index, get(pos+index), mode); |
| 7764 | } |
| 7765 | } |
| 7766 | |
| 7767 | |
| 7768 | #ifdef DEBUG |
| 7769 | bool FixedArray::IsEqualTo(FixedArray* other) { |
| 7770 | if (length() != other->length()) return false; |
| 7771 | for (int i = 0 ; i < length(); ++i) { |
| 7772 | if (get(i) != other->get(i)) return false; |
| 7773 | } |
| 7774 | return true; |
| 7775 | } |
| 7776 | #endif |
| 7777 | |
| 7778 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7779 | Handle<DescriptorArray> DescriptorArray::Allocate(Isolate* isolate, |
| 7780 | int number_of_descriptors, |
| 7781 | int slack) { |
| 7782 | DCHECK(0 <= number_of_descriptors); |
| 7783 | Factory* factory = isolate->factory(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7784 | // Do not use DescriptorArray::cast on incomplete object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7785 | int size = number_of_descriptors + slack; |
| 7786 | if (size == 0) return factory->empty_descriptor_array(); |
| 7787 | // Allocate the array of keys. |
| 7788 | Handle<FixedArray> result = factory->NewFixedArray(LengthFor(size)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7789 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7790 | result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors)); |
| 7791 | result->set(kEnumCacheIndex, Smi::FromInt(0)); |
| 7792 | return Handle<DescriptorArray>::cast(result); |
| 7793 | } |
| 7794 | |
| 7795 | |
| 7796 | void DescriptorArray::ClearEnumCache() { |
| 7797 | set(kEnumCacheIndex, Smi::FromInt(0)); |
| 7798 | } |
| 7799 | |
| 7800 | |
| 7801 | void DescriptorArray::Replace(int index, Descriptor* descriptor) { |
| 7802 | descriptor->SetSortedKeyIndex(GetSortedKeyIndex(index)); |
| 7803 | Set(index, descriptor); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7804 | } |
| 7805 | |
| 7806 | |
| 7807 | void DescriptorArray::SetEnumCache(FixedArray* bridge_storage, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7808 | FixedArray* new_cache, |
| 7809 | Object* new_index_cache) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7810 | DCHECK(bridge_storage->length() >= kEnumCacheBridgeLength); |
| 7811 | DCHECK(new_index_cache->IsSmi() || new_index_cache->IsFixedArray()); |
| 7812 | DCHECK(!IsEmpty()); |
| 7813 | DCHECK(!HasEnumCache() || new_cache->length() > GetEnumCache()->length()); |
| 7814 | FixedArray::cast(bridge_storage)-> |
| 7815 | set(kEnumCacheBridgeCacheIndex, new_cache); |
| 7816 | FixedArray::cast(bridge_storage)-> |
| 7817 | set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache); |
| 7818 | set(kEnumCacheIndex, bridge_storage); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7819 | } |
| 7820 | |
| 7821 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7822 | void DescriptorArray::CopyFrom(int index, |
| 7823 | DescriptorArray* src, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7824 | const WhitenessWitness& witness) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7825 | Object* value = src->GetValue(index); |
| 7826 | PropertyDetails details = src->GetDetails(index); |
| 7827 | Descriptor desc(handle(src->GetKey(index)), |
| 7828 | handle(value, src->GetIsolate()), |
| 7829 | details); |
| 7830 | Set(index, &desc, witness); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7831 | } |
| 7832 | |
| 7833 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7834 | // We need the whiteness witness since sort will reshuffle the entries in the |
| 7835 | // descriptor array. If the descriptor array were to be black, the shuffling |
| 7836 | // would move a slot that was already recorded as pointing into an evacuation |
| 7837 | // candidate. This would result in missing updates upon evacuation. |
| 7838 | void DescriptorArray::Sort() { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7839 | // In-place heap sort. |
| 7840 | int len = number_of_descriptors(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7841 | // Reset sorting since the descriptor array might contain invalid pointers. |
| 7842 | for (int i = 0; i < len; ++i) SetSortedKey(i, i); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7843 | // Bottom-up max-heap construction. |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7844 | // Index of the last node with children |
| 7845 | const int max_parent_index = (len / 2) - 1; |
| 7846 | for (int i = max_parent_index; i >= 0; --i) { |
| 7847 | int parent_index = i; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7848 | const uint32_t parent_hash = GetSortedKey(i)->Hash(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7849 | while (parent_index <= max_parent_index) { |
| 7850 | int child_index = 2 * parent_index + 1; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7851 | uint32_t child_hash = GetSortedKey(child_index)->Hash(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7852 | if (child_index + 1 < len) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7853 | uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7854 | if (right_child_hash > child_hash) { |
| 7855 | child_index++; |
| 7856 | child_hash = right_child_hash; |
| 7857 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7858 | } |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7859 | if (child_hash <= parent_hash) break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7860 | SwapSortedKeys(parent_index, child_index); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7861 | // Now element at child_index could be < its children. |
| 7862 | parent_index = child_index; // parent_hash remains correct. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7863 | } |
| 7864 | } |
| 7865 | |
| 7866 | // Extract elements and create sorted array. |
| 7867 | for (int i = len - 1; i > 0; --i) { |
| 7868 | // Put max element at the back of the array. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7869 | SwapSortedKeys(0, i); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7870 | // Shift down the new top element. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7871 | int parent_index = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7872 | const uint32_t parent_hash = GetSortedKey(parent_index)->Hash(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7873 | const int max_parent_index = (i / 2) - 1; |
| 7874 | while (parent_index <= max_parent_index) { |
| 7875 | int child_index = parent_index * 2 + 1; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7876 | uint32_t child_hash = GetSortedKey(child_index)->Hash(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7877 | if (child_index + 1 < i) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7878 | uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash(); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7879 | if (right_child_hash > child_hash) { |
| 7880 | child_index++; |
| 7881 | child_hash = right_child_hash; |
| 7882 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7883 | } |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7884 | if (child_hash <= parent_hash) break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7885 | SwapSortedKeys(parent_index, child_index); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 7886 | parent_index = child_index; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7887 | } |
| 7888 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7889 | DCHECK(IsSortedNoDuplicates()); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 7890 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7891 | |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 7892 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7893 | Handle<AccessorPair> AccessorPair::Copy(Handle<AccessorPair> pair) { |
| 7894 | Handle<AccessorPair> copy = pair->GetIsolate()->factory()->NewAccessorPair(); |
| 7895 | copy->set_getter(pair->getter()); |
| 7896 | copy->set_setter(pair->setter()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7897 | return copy; |
| 7898 | } |
| 7899 | |
| 7900 | |
| 7901 | Object* AccessorPair::GetComponent(AccessorComponent component) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7902 | Object* accessor = get(component); |
| 7903 | return accessor->IsTheHole() ? GetHeap()->undefined_value() : accessor; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7904 | } |
| 7905 | |
| 7906 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7907 | Handle<DeoptimizationInputData> DeoptimizationInputData::New( |
| 7908 | Isolate* isolate, int deopt_entry_count, PretenureFlag pretenure) { |
| 7909 | DCHECK(deopt_entry_count > 0); |
| 7910 | return Handle<DeoptimizationInputData>::cast( |
| 7911 | isolate->factory()->NewFixedArray(LengthFor(deopt_entry_count), |
| 7912 | pretenure)); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 7913 | } |
| 7914 | |
| 7915 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7916 | Handle<DeoptimizationOutputData> DeoptimizationOutputData::New( |
| 7917 | Isolate* isolate, |
| 7918 | int number_of_deopt_points, |
| 7919 | PretenureFlag pretenure) { |
| 7920 | Handle<FixedArray> result; |
| 7921 | if (number_of_deopt_points == 0) { |
| 7922 | result = isolate->factory()->empty_fixed_array(); |
| 7923 | } else { |
| 7924 | result = isolate->factory()->NewFixedArray( |
| 7925 | LengthOfFixedArray(number_of_deopt_points), pretenure); |
| 7926 | } |
| 7927 | return Handle<DeoptimizationOutputData>::cast(result); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 7928 | } |
| 7929 | |
| 7930 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7931 | #ifdef DEBUG |
| 7932 | bool DescriptorArray::IsEqualTo(DescriptorArray* other) { |
| 7933 | if (IsEmpty()) return other->IsEmpty(); |
| 7934 | if (other->IsEmpty()) return false; |
| 7935 | if (length() != other->length()) return false; |
| 7936 | for (int i = 0; i < length(); ++i) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7937 | if (get(i) != other->get(i)) return false; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7938 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7939 | return true; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7940 | } |
| 7941 | #endif |
| 7942 | |
| 7943 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7944 | bool String::LooksValid() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7945 | if (!GetIsolate()->heap()->Contains(this)) return false; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7946 | return true; |
| 7947 | } |
| 7948 | |
| 7949 | |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7950 | String::FlatContent String::GetFlatContent() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7951 | DCHECK(!AllowHeapAllocation::IsAllowed()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7952 | int length = this->length(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7953 | StringShape shape(this); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7954 | String* string = this; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7955 | int offset = 0; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7956 | if (shape.representation_tag() == kConsStringTag) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7957 | ConsString* cons = ConsString::cast(string); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7958 | if (cons->second()->length() != 0) { |
| 7959 | return FlatContent(); |
| 7960 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7961 | string = cons->first(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7962 | shape = StringShape(string); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7963 | } |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7964 | if (shape.representation_tag() == kSlicedStringTag) { |
| 7965 | SlicedString* slice = SlicedString::cast(string); |
| 7966 | offset = slice->offset(); |
| 7967 | string = slice->parent(); |
| 7968 | shape = StringShape(string); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7969 | DCHECK(shape.representation_tag() != kConsStringTag && |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7970 | shape.representation_tag() != kSlicedStringTag); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7971 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7972 | if (shape.encoding_tag() == kOneByteStringTag) { |
| 7973 | const uint8_t* start; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7974 | if (shape.representation_tag() == kSeqStringTag) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7975 | start = SeqOneByteString::cast(string)->GetChars(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7976 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7977 | start = ExternalOneByteString::cast(string)->GetChars(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7978 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7979 | return FlatContent(start + offset, length); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7980 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7981 | DCHECK(shape.encoding_tag() == kTwoByteStringTag); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7982 | const uc16* start; |
| 7983 | if (shape.representation_tag() == kSeqStringTag) { |
| 7984 | start = SeqTwoByteString::cast(string)->GetChars(); |
| 7985 | } else { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 7986 | start = ExternalTwoByteString::cast(string)->GetChars(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7987 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 7988 | return FlatContent(start + offset, length); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 7989 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7990 | } |
| 7991 | |
| 7992 | |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 7993 | SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, |
| 7994 | RobustnessFlag robust_flag, |
| 7995 | int offset, |
| 7996 | int length, |
| 7997 | int* length_return) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 7998 | if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 7999 | return SmartArrayPointer<char>(NULL); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8000 | } |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8001 | Heap* heap = GetHeap(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8002 | |
| 8003 | // Negative length means the to the end of the string. |
| 8004 | if (length < 0) length = kMaxInt - offset; |
| 8005 | |
| 8006 | // Compute the size of the UTF-8 string. Start at the specified offset. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8007 | Access<ConsStringIteratorOp> op( |
| 8008 | heap->isolate()->objects_string_iterator()); |
| 8009 | StringCharacterStream stream(this, op.value(), offset); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8010 | int character_position = offset; |
| 8011 | int utf8_bytes = 0; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8012 | int last = unibrow::Utf16::kNoPreviousCharacter; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8013 | while (stream.HasMore() && character_position++ < offset + length) { |
| 8014 | uint16_t character = stream.GetNext(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8015 | utf8_bytes += unibrow::Utf8::Length(character, last); |
| 8016 | last = character; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8017 | } |
| 8018 | |
| 8019 | if (length_return) { |
| 8020 | *length_return = utf8_bytes; |
| 8021 | } |
| 8022 | |
| 8023 | char* result = NewArray<char>(utf8_bytes + 1); |
| 8024 | |
| 8025 | // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8026 | stream.Reset(this, offset); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8027 | character_position = offset; |
| 8028 | int utf8_byte_position = 0; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8029 | last = unibrow::Utf16::kNoPreviousCharacter; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8030 | while (stream.HasMore() && character_position++ < offset + length) { |
| 8031 | uint16_t character = stream.GetNext(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8032 | if (allow_nulls == DISALLOW_NULLS && character == 0) { |
| 8033 | character = ' '; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8034 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8035 | utf8_byte_position += |
| 8036 | unibrow::Utf8::Encode(result + utf8_byte_position, character, last); |
| 8037 | last = character; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8038 | } |
| 8039 | result[utf8_byte_position] = 0; |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 8040 | return SmartArrayPointer<char>(result); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8041 | } |
| 8042 | |
| 8043 | |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 8044 | SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, |
| 8045 | RobustnessFlag robust_flag, |
| 8046 | int* length_return) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8047 | return ToCString(allow_nulls, robust_flag, 0, -1, length_return); |
| 8048 | } |
| 8049 | |
| 8050 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8051 | const uc16* String::GetTwoByteData(unsigned start) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8052 | DCHECK(!IsOneByteRepresentationUnderneath()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8053 | switch (StringShape(this).representation_tag()) { |
| 8054 | case kSeqStringTag: |
| 8055 | return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start); |
| 8056 | case kExternalStringTag: |
| 8057 | return ExternalTwoByteString::cast(this)-> |
| 8058 | ExternalTwoByteStringGetData(start); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 8059 | case kSlicedStringTag: { |
| 8060 | SlicedString* slice = SlicedString::cast(this); |
| 8061 | return slice->parent()->GetTwoByteData(start + slice->offset()); |
| 8062 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8063 | case kConsStringTag: |
| 8064 | UNREACHABLE(); |
| 8065 | return NULL; |
| 8066 | } |
| 8067 | UNREACHABLE(); |
| 8068 | return NULL; |
| 8069 | } |
| 8070 | |
| 8071 | |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 8072 | SmartArrayPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8073 | if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 8074 | return SmartArrayPointer<uc16>(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8075 | } |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8076 | Heap* heap = GetHeap(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8077 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8078 | Access<ConsStringIteratorOp> op( |
| 8079 | heap->isolate()->objects_string_iterator()); |
| 8080 | StringCharacterStream stream(this, op.value()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8081 | |
| 8082 | uc16* result = NewArray<uc16>(length() + 1); |
| 8083 | |
| 8084 | int i = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8085 | while (stream.HasMore()) { |
| 8086 | uint16_t character = stream.GetNext(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8087 | result[i++] = character; |
| 8088 | } |
| 8089 | result[i] = 0; |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 8090 | return SmartArrayPointer<uc16>(result); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8091 | } |
| 8092 | |
| 8093 | |
| 8094 | const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) { |
| 8095 | return reinterpret_cast<uc16*>( |
| 8096 | reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start; |
| 8097 | } |
| 8098 | |
| 8099 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8100 | void Relocatable::PostGarbageCollectionProcessing(Isolate* isolate) { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8101 | Relocatable* current = isolate->relocatable_top(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8102 | while (current != NULL) { |
| 8103 | current->PostGarbageCollection(); |
| 8104 | current = current->prev_; |
| 8105 | } |
| 8106 | } |
| 8107 | |
| 8108 | |
| 8109 | // Reserve space for statics needing saving and restoring. |
| 8110 | int Relocatable::ArchiveSpacePerThread() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8111 | return sizeof(Relocatable*); // NOLINT |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8112 | } |
| 8113 | |
| 8114 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8115 | // Archive statics that are thread-local. |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 8116 | char* Relocatable::ArchiveState(Isolate* isolate, char* to) { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8117 | *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top(); |
| 8118 | isolate->set_relocatable_top(NULL); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8119 | return to + ArchiveSpacePerThread(); |
| 8120 | } |
| 8121 | |
| 8122 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8123 | // Restore statics that are thread-local. |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 8124 | char* Relocatable::RestoreState(Isolate* isolate, char* from) { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8125 | isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8126 | return from + ArchiveSpacePerThread(); |
| 8127 | } |
| 8128 | |
| 8129 | |
| 8130 | char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) { |
| 8131 | Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage); |
| 8132 | Iterate(v, top); |
| 8133 | return thread_storage + ArchiveSpacePerThread(); |
| 8134 | } |
| 8135 | |
| 8136 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8137 | void Relocatable::Iterate(Isolate* isolate, ObjectVisitor* v) { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8138 | Iterate(v, isolate->relocatable_top()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8139 | } |
| 8140 | |
| 8141 | |
| 8142 | void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) { |
| 8143 | Relocatable* current = top; |
| 8144 | while (current != NULL) { |
| 8145 | current->IterateInstance(v); |
| 8146 | current = current->prev_; |
| 8147 | } |
| 8148 | } |
| 8149 | |
| 8150 | |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8151 | FlatStringReader::FlatStringReader(Isolate* isolate, Handle<String> str) |
| 8152 | : Relocatable(isolate), |
| 8153 | str_(str.location()), |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8154 | length_(str->length()) { |
| 8155 | PostGarbageCollection(); |
| 8156 | } |
| 8157 | |
| 8158 | |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8159 | FlatStringReader::FlatStringReader(Isolate* isolate, Vector<const char> input) |
| 8160 | : Relocatable(isolate), |
| 8161 | str_(0), |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8162 | is_one_byte_(true), |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8163 | length_(input.length()), |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8164 | start_(input.start()) {} |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8165 | |
| 8166 | |
| 8167 | void FlatStringReader::PostGarbageCollection() { |
| 8168 | if (str_ == NULL) return; |
| 8169 | Handle<String> str(str_); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8170 | DCHECK(str->IsFlat()); |
| 8171 | DisallowHeapAllocation no_gc; |
| 8172 | // This does not actually prevent the vector from being relocated later. |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 8173 | String::FlatContent content = str->GetFlatContent(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8174 | DCHECK(content.IsFlat()); |
| 8175 | is_one_byte_ = content.IsOneByte(); |
| 8176 | if (is_one_byte_) { |
| 8177 | start_ = content.ToOneByteVector().start(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8178 | } else { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 8179 | start_ = content.ToUC16Vector().start(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8180 | } |
| 8181 | } |
| 8182 | |
| 8183 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8184 | void ConsStringIteratorOp::Initialize(ConsString* cons_string, int offset) { |
| 8185 | DCHECK(cons_string != NULL); |
| 8186 | root_ = cons_string; |
| 8187 | consumed_ = offset; |
| 8188 | // Force stack blown condition to trigger restart. |
| 8189 | depth_ = 1; |
| 8190 | maximum_depth_ = kStackSize + depth_; |
| 8191 | DCHECK(StackBlown()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8192 | } |
| 8193 | |
| 8194 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8195 | String* ConsStringIteratorOp::Continue(int* offset_out) { |
| 8196 | DCHECK(depth_ != 0); |
| 8197 | DCHECK_EQ(0, *offset_out); |
| 8198 | bool blew_stack = StackBlown(); |
| 8199 | String* string = NULL; |
| 8200 | // Get the next leaf if there is one. |
| 8201 | if (!blew_stack) string = NextLeaf(&blew_stack); |
| 8202 | // Restart search from root. |
| 8203 | if (blew_stack) { |
| 8204 | DCHECK(string == NULL); |
| 8205 | string = Search(offset_out); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8206 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8207 | // Ensure future calls return null immediately. |
| 8208 | if (string == NULL) Reset(NULL); |
| 8209 | return string; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8210 | } |
| 8211 | |
| 8212 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8213 | String* ConsStringIteratorOp::Search(int* offset_out) { |
| 8214 | ConsString* cons_string = root_; |
| 8215 | // Reset the stack, pushing the root string. |
| 8216 | depth_ = 1; |
| 8217 | maximum_depth_ = 1; |
| 8218 | frames_[0] = cons_string; |
| 8219 | const int consumed = consumed_; |
| 8220 | int offset = 0; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8221 | while (true) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8222 | // Loop until the string is found which contains the target offset. |
| 8223 | String* string = cons_string->first(); |
| 8224 | int length = string->length(); |
| 8225 | int32_t type; |
| 8226 | if (consumed < offset + length) { |
| 8227 | // Target offset is in the left branch. |
| 8228 | // Keep going if we're still in a ConString. |
| 8229 | type = string->map()->instance_type(); |
| 8230 | if ((type & kStringRepresentationMask) == kConsStringTag) { |
| 8231 | cons_string = ConsString::cast(string); |
| 8232 | PushLeft(cons_string); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8233 | continue; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8234 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8235 | // Tell the stack we're done descending. |
| 8236 | AdjustMaximumDepth(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8237 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8238 | // Descend right. |
| 8239 | // Update progress through the string. |
| 8240 | offset += length; |
| 8241 | // Keep going if we're still in a ConString. |
| 8242 | string = cons_string->second(); |
| 8243 | type = string->map()->instance_type(); |
| 8244 | if ((type & kStringRepresentationMask) == kConsStringTag) { |
| 8245 | cons_string = ConsString::cast(string); |
| 8246 | PushRight(cons_string); |
| 8247 | continue; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8248 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8249 | // Need this to be updated for the current string. |
| 8250 | length = string->length(); |
| 8251 | // Account for the possibility of an empty right leaf. |
| 8252 | // This happens only if we have asked for an offset outside the string. |
| 8253 | if (length == 0) { |
| 8254 | // Reset so future operations will return null immediately. |
| 8255 | Reset(NULL); |
| 8256 | return NULL; |
| 8257 | } |
| 8258 | // Tell the stack we're done descending. |
| 8259 | AdjustMaximumDepth(); |
| 8260 | // Pop stack so next iteration is in correct place. |
| 8261 | Pop(); |
| 8262 | } |
| 8263 | DCHECK(length != 0); |
| 8264 | // Adjust return values and exit. |
| 8265 | consumed_ = offset + length; |
| 8266 | *offset_out = consumed - offset; |
| 8267 | return string; |
| 8268 | } |
| 8269 | UNREACHABLE(); |
| 8270 | return NULL; |
| 8271 | } |
| 8272 | |
| 8273 | |
| 8274 | String* ConsStringIteratorOp::NextLeaf(bool* blew_stack) { |
| 8275 | while (true) { |
| 8276 | // Tree traversal complete. |
| 8277 | if (depth_ == 0) { |
| 8278 | *blew_stack = false; |
| 8279 | return NULL; |
| 8280 | } |
| 8281 | // We've lost track of higher nodes. |
| 8282 | if (StackBlown()) { |
| 8283 | *blew_stack = true; |
| 8284 | return NULL; |
| 8285 | } |
| 8286 | // Go right. |
| 8287 | ConsString* cons_string = frames_[OffsetForDepth(depth_ - 1)]; |
| 8288 | String* string = cons_string->second(); |
| 8289 | int32_t type = string->map()->instance_type(); |
| 8290 | if ((type & kStringRepresentationMask) != kConsStringTag) { |
| 8291 | // Pop stack so next iteration is in correct place. |
| 8292 | Pop(); |
| 8293 | int length = string->length(); |
| 8294 | // Could be a flattened ConsString. |
| 8295 | if (length == 0) continue; |
| 8296 | consumed_ += length; |
| 8297 | return string; |
| 8298 | } |
| 8299 | cons_string = ConsString::cast(string); |
| 8300 | PushRight(cons_string); |
| 8301 | // Need to traverse all the way left. |
| 8302 | while (true) { |
| 8303 | // Continue left. |
| 8304 | string = cons_string->first(); |
| 8305 | type = string->map()->instance_type(); |
| 8306 | if ((type & kStringRepresentationMask) != kConsStringTag) { |
| 8307 | AdjustMaximumDepth(); |
| 8308 | int length = string->length(); |
| 8309 | DCHECK(length != 0); |
| 8310 | consumed_ += length; |
| 8311 | return string; |
| 8312 | } |
| 8313 | cons_string = ConsString::cast(string); |
| 8314 | PushLeft(cons_string); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8315 | } |
| 8316 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8317 | UNREACHABLE(); |
| 8318 | return NULL; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8319 | } |
| 8320 | |
| 8321 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8322 | uint16_t ConsString::ConsStringGet(int index) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8323 | DCHECK(index >= 0 && index < this->length()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8324 | |
| 8325 | // Check for a flattened cons string |
| 8326 | if (second()->length() == 0) { |
| 8327 | String* left = first(); |
| 8328 | return left->Get(index); |
| 8329 | } |
| 8330 | |
| 8331 | String* string = String::cast(this); |
| 8332 | |
| 8333 | while (true) { |
| 8334 | if (StringShape(string).IsCons()) { |
| 8335 | ConsString* cons_string = ConsString::cast(string); |
| 8336 | String* left = cons_string->first(); |
| 8337 | if (left->length() > index) { |
| 8338 | string = left; |
| 8339 | } else { |
| 8340 | index -= left->length(); |
| 8341 | string = cons_string->second(); |
| 8342 | } |
| 8343 | } else { |
| 8344 | return string->Get(index); |
| 8345 | } |
| 8346 | } |
| 8347 | |
| 8348 | UNREACHABLE(); |
| 8349 | return 0; |
| 8350 | } |
| 8351 | |
| 8352 | |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 8353 | uint16_t SlicedString::SlicedStringGet(int index) { |
| 8354 | return parent()->Get(offset() + index); |
| 8355 | } |
| 8356 | |
| 8357 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8358 | template <typename sinkchar> |
| 8359 | void String::WriteToFlat(String* src, |
| 8360 | sinkchar* sink, |
| 8361 | int f, |
| 8362 | int t) { |
| 8363 | String* source = src; |
| 8364 | int from = f; |
| 8365 | int to = t; |
| 8366 | while (true) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8367 | DCHECK(0 <= from && from <= to && to <= source->length()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8368 | switch (StringShape(source).full_representation_tag()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8369 | case kOneByteStringTag | kExternalStringTag: { |
| 8370 | CopyChars(sink, ExternalOneByteString::cast(source)->GetChars() + from, |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8371 | to - from); |
| 8372 | return; |
| 8373 | } |
| 8374 | case kTwoByteStringTag | kExternalStringTag: { |
| 8375 | const uc16* data = |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8376 | ExternalTwoByteString::cast(source)->GetChars(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8377 | CopyChars(sink, |
| 8378 | data + from, |
| 8379 | to - from); |
| 8380 | return; |
| 8381 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8382 | case kOneByteStringTag | kSeqStringTag: { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8383 | CopyChars(sink, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8384 | SeqOneByteString::cast(source)->GetChars() + from, |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8385 | to - from); |
| 8386 | return; |
| 8387 | } |
| 8388 | case kTwoByteStringTag | kSeqStringTag: { |
| 8389 | CopyChars(sink, |
| 8390 | SeqTwoByteString::cast(source)->GetChars() + from, |
| 8391 | to - from); |
| 8392 | return; |
| 8393 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8394 | case kOneByteStringTag | kConsStringTag: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8395 | case kTwoByteStringTag | kConsStringTag: { |
| 8396 | ConsString* cons_string = ConsString::cast(source); |
| 8397 | String* first = cons_string->first(); |
| 8398 | int boundary = first->length(); |
| 8399 | if (to - boundary >= boundary - from) { |
| 8400 | // Right hand side is longer. Recurse over left. |
| 8401 | if (from < boundary) { |
| 8402 | WriteToFlat(first, sink, from, boundary); |
| 8403 | sink += boundary - from; |
| 8404 | from = 0; |
| 8405 | } else { |
| 8406 | from -= boundary; |
| 8407 | } |
| 8408 | to -= boundary; |
| 8409 | source = cons_string->second(); |
| 8410 | } else { |
| 8411 | // Left hand side is longer. Recurse over right. |
| 8412 | if (to > boundary) { |
| 8413 | String* second = cons_string->second(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8414 | // When repeatedly appending to a string, we get a cons string that |
| 8415 | // is unbalanced to the left, a list, essentially. We inline the |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8416 | // common case of sequential one-byte right child. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8417 | if (to - boundary == 1) { |
| 8418 | sink[boundary - from] = static_cast<sinkchar>(second->Get(0)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8419 | } else if (second->IsSeqOneByteString()) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8420 | CopyChars(sink + boundary - from, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8421 | SeqOneByteString::cast(second)->GetChars(), |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8422 | to - boundary); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8423 | } else { |
| 8424 | WriteToFlat(second, |
| 8425 | sink + boundary - from, |
| 8426 | 0, |
| 8427 | to - boundary); |
| 8428 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8429 | to = boundary; |
| 8430 | } |
| 8431 | source = first; |
| 8432 | } |
| 8433 | break; |
| 8434 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8435 | case kOneByteStringTag | kSlicedStringTag: |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 8436 | case kTwoByteStringTag | kSlicedStringTag: { |
| 8437 | SlicedString* slice = SlicedString::cast(source); |
| 8438 | unsigned offset = slice->offset(); |
| 8439 | WriteToFlat(slice->parent(), sink, from + offset, to + offset); |
| 8440 | return; |
| 8441 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8442 | } |
| 8443 | } |
| 8444 | } |
| 8445 | |
| 8446 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8447 | |
| 8448 | template <typename SourceChar> |
| 8449 | static void CalculateLineEndsImpl(Isolate* isolate, |
| 8450 | List<int>* line_ends, |
| 8451 | Vector<const SourceChar> src, |
| 8452 | bool include_ending_line) { |
| 8453 | const int src_len = src.length(); |
| 8454 | StringSearch<uint8_t, SourceChar> search(isolate, STATIC_CHAR_VECTOR("\n")); |
| 8455 | |
| 8456 | // Find and record line ends. |
| 8457 | int position = 0; |
| 8458 | while (position != -1 && position < src_len) { |
| 8459 | position = search.Search(src, position); |
| 8460 | if (position != -1) { |
| 8461 | line_ends->Add(position); |
| 8462 | position++; |
| 8463 | } else if (include_ending_line) { |
| 8464 | // Even if the last line misses a line end, it is counted. |
| 8465 | line_ends->Add(src_len); |
| 8466 | return; |
| 8467 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8468 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8469 | } |
| 8470 | |
| 8471 | |
| 8472 | Handle<FixedArray> String::CalculateLineEnds(Handle<String> src, |
| 8473 | bool include_ending_line) { |
| 8474 | src = Flatten(src); |
| 8475 | // Rough estimate of line count based on a roughly estimated average |
| 8476 | // length of (unpacked) code. |
| 8477 | int line_count_estimate = src->length() >> 4; |
| 8478 | List<int> line_ends(line_count_estimate); |
| 8479 | Isolate* isolate = src->GetIsolate(); |
| 8480 | { DisallowHeapAllocation no_allocation; // ensure vectors stay valid. |
| 8481 | // Dispatch on type of strings. |
| 8482 | String::FlatContent content = src->GetFlatContent(); |
| 8483 | DCHECK(content.IsFlat()); |
| 8484 | if (content.IsOneByte()) { |
| 8485 | CalculateLineEndsImpl(isolate, |
| 8486 | &line_ends, |
| 8487 | content.ToOneByteVector(), |
| 8488 | include_ending_line); |
| 8489 | } else { |
| 8490 | CalculateLineEndsImpl(isolate, |
| 8491 | &line_ends, |
| 8492 | content.ToUC16Vector(), |
| 8493 | include_ending_line); |
| 8494 | } |
| 8495 | } |
| 8496 | int line_count = line_ends.length(); |
| 8497 | Handle<FixedArray> array = isolate->factory()->NewFixedArray(line_count); |
| 8498 | for (int i = 0; i < line_count; i++) { |
| 8499 | array->set(i, Smi::FromInt(line_ends[i])); |
| 8500 | } |
| 8501 | return array; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8502 | } |
| 8503 | |
| 8504 | |
| 8505 | // Compares the contents of two strings by reading and comparing |
| 8506 | // int-sized blocks of characters. |
| 8507 | template <typename Char> |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8508 | static inline bool CompareRawStringContents(const Char* const a, |
| 8509 | const Char* const b, |
| 8510 | int length) { |
| 8511 | return CompareChars(a, b, length) == 0; |
| 8512 | } |
| 8513 | |
| 8514 | |
| 8515 | template<typename Chars1, typename Chars2> |
| 8516 | class RawStringComparator : public AllStatic { |
| 8517 | public: |
| 8518 | static inline bool compare(const Chars1* a, const Chars2* b, int len) { |
| 8519 | DCHECK(sizeof(Chars1) != sizeof(Chars2)); |
| 8520 | for (int i = 0; i < len; i++) { |
| 8521 | if (a[i] != b[i]) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8522 | return false; |
| 8523 | } |
| 8524 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8525 | return true; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8526 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8527 | }; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8528 | |
| 8529 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8530 | template<> |
| 8531 | class RawStringComparator<uint16_t, uint16_t> { |
| 8532 | public: |
| 8533 | static inline bool compare(const uint16_t* a, const uint16_t* b, int len) { |
| 8534 | return CompareRawStringContents(a, b, len); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8535 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8536 | }; |
| 8537 | |
| 8538 | |
| 8539 | template<> |
| 8540 | class RawStringComparator<uint8_t, uint8_t> { |
| 8541 | public: |
| 8542 | static inline bool compare(const uint8_t* a, const uint8_t* b, int len) { |
| 8543 | return CompareRawStringContents(a, b, len); |
| 8544 | } |
| 8545 | }; |
| 8546 | |
| 8547 | |
| 8548 | class StringComparator { |
| 8549 | class State { |
| 8550 | public: |
| 8551 | explicit inline State(ConsStringIteratorOp* op) |
| 8552 | : op_(op), is_one_byte_(true), length_(0), buffer8_(NULL) {} |
| 8553 | |
| 8554 | inline void Init(String* string) { |
| 8555 | ConsString* cons_string = String::VisitFlat(this, string); |
| 8556 | op_->Reset(cons_string); |
| 8557 | if (cons_string != NULL) { |
| 8558 | int offset; |
| 8559 | string = op_->Next(&offset); |
| 8560 | String::VisitFlat(this, string, offset); |
| 8561 | } |
| 8562 | } |
| 8563 | |
| 8564 | inline void VisitOneByteString(const uint8_t* chars, int length) { |
| 8565 | is_one_byte_ = true; |
| 8566 | buffer8_ = chars; |
| 8567 | length_ = length; |
| 8568 | } |
| 8569 | |
| 8570 | inline void VisitTwoByteString(const uint16_t* chars, int length) { |
| 8571 | is_one_byte_ = false; |
| 8572 | buffer16_ = chars; |
| 8573 | length_ = length; |
| 8574 | } |
| 8575 | |
| 8576 | void Advance(int consumed) { |
| 8577 | DCHECK(consumed <= length_); |
| 8578 | // Still in buffer. |
| 8579 | if (length_ != consumed) { |
| 8580 | if (is_one_byte_) { |
| 8581 | buffer8_ += consumed; |
| 8582 | } else { |
| 8583 | buffer16_ += consumed; |
| 8584 | } |
| 8585 | length_ -= consumed; |
| 8586 | return; |
| 8587 | } |
| 8588 | // Advance state. |
| 8589 | int offset; |
| 8590 | String* next = op_->Next(&offset); |
| 8591 | DCHECK_EQ(0, offset); |
| 8592 | DCHECK(next != NULL); |
| 8593 | String::VisitFlat(this, next); |
| 8594 | } |
| 8595 | |
| 8596 | ConsStringIteratorOp* const op_; |
| 8597 | bool is_one_byte_; |
| 8598 | int length_; |
| 8599 | union { |
| 8600 | const uint8_t* buffer8_; |
| 8601 | const uint16_t* buffer16_; |
| 8602 | }; |
| 8603 | |
| 8604 | private: |
| 8605 | DISALLOW_IMPLICIT_CONSTRUCTORS(State); |
| 8606 | }; |
| 8607 | |
| 8608 | public: |
| 8609 | inline StringComparator(ConsStringIteratorOp* op_1, |
| 8610 | ConsStringIteratorOp* op_2) |
| 8611 | : state_1_(op_1), |
| 8612 | state_2_(op_2) { |
| 8613 | } |
| 8614 | |
| 8615 | template<typename Chars1, typename Chars2> |
| 8616 | static inline bool Equals(State* state_1, State* state_2, int to_check) { |
| 8617 | const Chars1* a = reinterpret_cast<const Chars1*>(state_1->buffer8_); |
| 8618 | const Chars2* b = reinterpret_cast<const Chars2*>(state_2->buffer8_); |
| 8619 | return RawStringComparator<Chars1, Chars2>::compare(a, b, to_check); |
| 8620 | } |
| 8621 | |
| 8622 | bool Equals(String* string_1, String* string_2) { |
| 8623 | int length = string_1->length(); |
| 8624 | state_1_.Init(string_1); |
| 8625 | state_2_.Init(string_2); |
| 8626 | while (true) { |
| 8627 | int to_check = Min(state_1_.length_, state_2_.length_); |
| 8628 | DCHECK(to_check > 0 && to_check <= length); |
| 8629 | bool is_equal; |
| 8630 | if (state_1_.is_one_byte_) { |
| 8631 | if (state_2_.is_one_byte_) { |
| 8632 | is_equal = Equals<uint8_t, uint8_t>(&state_1_, &state_2_, to_check); |
| 8633 | } else { |
| 8634 | is_equal = Equals<uint8_t, uint16_t>(&state_1_, &state_2_, to_check); |
| 8635 | } |
| 8636 | } else { |
| 8637 | if (state_2_.is_one_byte_) { |
| 8638 | is_equal = Equals<uint16_t, uint8_t>(&state_1_, &state_2_, to_check); |
| 8639 | } else { |
| 8640 | is_equal = Equals<uint16_t, uint16_t>(&state_1_, &state_2_, to_check); |
| 8641 | } |
| 8642 | } |
| 8643 | // Looping done. |
| 8644 | if (!is_equal) return false; |
| 8645 | length -= to_check; |
| 8646 | // Exit condition. Strings are equal. |
| 8647 | if (length == 0) return true; |
| 8648 | state_1_.Advance(to_check); |
| 8649 | state_2_.Advance(to_check); |
| 8650 | } |
| 8651 | } |
| 8652 | |
| 8653 | private: |
| 8654 | State state_1_; |
| 8655 | State state_2_; |
| 8656 | DISALLOW_IMPLICIT_CONSTRUCTORS(StringComparator); |
| 8657 | }; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8658 | |
| 8659 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8660 | bool String::SlowEquals(String* other) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8661 | DisallowHeapAllocation no_gc; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8662 | // Fast check: negative check with lengths. |
| 8663 | int len = length(); |
| 8664 | if (len != other->length()) return false; |
| 8665 | if (len == 0) return true; |
| 8666 | |
| 8667 | // Fast check: if hash code is computed for both strings |
| 8668 | // a fast negative check can be performed. |
| 8669 | if (HasHashCode() && other->HasHashCode()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8670 | #ifdef ENABLE_SLOW_DCHECKS |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 8671 | if (FLAG_enable_slow_asserts) { |
| 8672 | if (Hash() != other->Hash()) { |
| 8673 | bool found_difference = false; |
| 8674 | for (int i = 0; i < len; i++) { |
| 8675 | if (Get(i) != other->Get(i)) { |
| 8676 | found_difference = true; |
| 8677 | break; |
| 8678 | } |
| 8679 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8680 | DCHECK(found_difference); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 8681 | } |
| 8682 | } |
| 8683 | #endif |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8684 | if (Hash() != other->Hash()) return false; |
| 8685 | } |
| 8686 | |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 8687 | // We know the strings are both non-empty. Compare the first chars |
| 8688 | // before we try to flatten the strings. |
| 8689 | if (this->Get(0) != other->Get(0)) return false; |
| 8690 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8691 | if (IsSeqOneByteString() && other->IsSeqOneByteString()) { |
| 8692 | const uint8_t* str1 = SeqOneByteString::cast(this)->GetChars(); |
| 8693 | const uint8_t* str2 = SeqOneByteString::cast(other)->GetChars(); |
| 8694 | return CompareRawStringContents(str1, str2, len); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8695 | } |
| 8696 | |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8697 | Isolate* isolate = GetIsolate(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8698 | StringComparator comparator(isolate->objects_string_compare_iterator_a(), |
| 8699 | isolate->objects_string_compare_iterator_b()); |
| 8700 | |
| 8701 | return comparator.Equals(this, other); |
| 8702 | } |
| 8703 | |
| 8704 | |
| 8705 | bool String::SlowEquals(Handle<String> one, Handle<String> two) { |
| 8706 | // Fast check: negative check with lengths. |
| 8707 | int one_length = one->length(); |
| 8708 | if (one_length != two->length()) return false; |
| 8709 | if (one_length == 0) return true; |
| 8710 | |
| 8711 | // Fast check: if hash code is computed for both strings |
| 8712 | // a fast negative check can be performed. |
| 8713 | if (one->HasHashCode() && two->HasHashCode()) { |
| 8714 | #ifdef ENABLE_SLOW_DCHECKS |
| 8715 | if (FLAG_enable_slow_asserts) { |
| 8716 | if (one->Hash() != two->Hash()) { |
| 8717 | bool found_difference = false; |
| 8718 | for (int i = 0; i < one_length; i++) { |
| 8719 | if (one->Get(i) != two->Get(i)) { |
| 8720 | found_difference = true; |
| 8721 | break; |
| 8722 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8723 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8724 | DCHECK(found_difference); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8725 | } |
| 8726 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8727 | #endif |
| 8728 | if (one->Hash() != two->Hash()) return false; |
| 8729 | } |
| 8730 | |
| 8731 | // We know the strings are both non-empty. Compare the first chars |
| 8732 | // before we try to flatten the strings. |
| 8733 | if (one->Get(0) != two->Get(0)) return false; |
| 8734 | |
| 8735 | one = String::Flatten(one); |
| 8736 | two = String::Flatten(two); |
| 8737 | |
| 8738 | DisallowHeapAllocation no_gc; |
| 8739 | String::FlatContent flat1 = one->GetFlatContent(); |
| 8740 | String::FlatContent flat2 = two->GetFlatContent(); |
| 8741 | |
| 8742 | if (flat1.IsOneByte() && flat2.IsOneByte()) { |
| 8743 | return CompareRawStringContents(flat1.ToOneByteVector().start(), |
| 8744 | flat2.ToOneByteVector().start(), |
| 8745 | one_length); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8746 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8747 | for (int i = 0; i < one_length; i++) { |
| 8748 | if (flat1.Get(i) != flat2.Get(i)) return false; |
| 8749 | } |
| 8750 | return true; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8751 | } |
| 8752 | } |
| 8753 | |
| 8754 | |
| 8755 | bool String::MarkAsUndetectable() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8756 | if (StringShape(this).IsInternalized()) return false; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8757 | |
| 8758 | Map* map = this->map(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8759 | Heap* heap = GetHeap(); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 8760 | if (map == heap->string_map()) { |
| 8761 | this->set_map(heap->undetectable_string_map()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8762 | return true; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8763 | } else if (map == heap->one_byte_string_map()) { |
| 8764 | this->set_map(heap->undetectable_one_byte_string_map()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8765 | return true; |
| 8766 | } |
| 8767 | // Rest cannot be marked as undetectable |
| 8768 | return false; |
| 8769 | } |
| 8770 | |
| 8771 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8772 | bool String::IsUtf8EqualTo(Vector<const char> str, bool allow_prefix_match) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8773 | int slen = length(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8774 | // Can't check exact length equality, but we can check bounds. |
| 8775 | int str_len = str.length(); |
| 8776 | if (!allow_prefix_match && |
| 8777 | (str_len < slen || |
| 8778 | str_len > slen*static_cast<int>(unibrow::Utf8::kMaxEncodedSize))) { |
| 8779 | return false; |
| 8780 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8781 | int i; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8782 | unsigned remaining_in_str = static_cast<unsigned>(str_len); |
| 8783 | const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(str.start()); |
| 8784 | for (i = 0; i < slen && remaining_in_str > 0; i++) { |
| 8785 | unsigned cursor = 0; |
| 8786 | uint32_t r = unibrow::Utf8::ValueOf(utf8_data, remaining_in_str, &cursor); |
| 8787 | DCHECK(cursor > 0 && cursor <= remaining_in_str); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 8788 | if (r > unibrow::Utf16::kMaxNonSurrogateCharCode) { |
| 8789 | if (i > slen - 1) return false; |
| 8790 | if (Get(i++) != unibrow::Utf16::LeadSurrogate(r)) return false; |
| 8791 | if (Get(i) != unibrow::Utf16::TrailSurrogate(r)) return false; |
| 8792 | } else { |
| 8793 | if (Get(i) != r) return false; |
| 8794 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8795 | utf8_data += cursor; |
| 8796 | remaining_in_str -= cursor; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8797 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8798 | return (allow_prefix_match || i == slen) && remaining_in_str == 0; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8799 | } |
| 8800 | |
| 8801 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8802 | bool String::IsOneByteEqualTo(Vector<const uint8_t> str) { |
Steve Block | 9fac840 | 2011-05-12 15:51:54 +0100 | [diff] [blame] | 8803 | int slen = length(); |
| 8804 | if (str.length() != slen) return false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8805 | DisallowHeapAllocation no_gc; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 8806 | FlatContent content = GetFlatContent(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8807 | if (content.IsOneByte()) { |
| 8808 | return CompareChars(content.ToOneByteVector().start(), |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 8809 | str.start(), slen) == 0; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 8810 | } |
| 8811 | for (int i = 0; i < slen; i++) { |
| 8812 | if (Get(i) != static_cast<uint16_t>(str[i])) return false; |
Steve Block | 9fac840 | 2011-05-12 15:51:54 +0100 | [diff] [blame] | 8813 | } |
| 8814 | return true; |
| 8815 | } |
| 8816 | |
| 8817 | |
| 8818 | bool String::IsTwoByteEqualTo(Vector<const uc16> str) { |
| 8819 | int slen = length(); |
| 8820 | if (str.length() != slen) return false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8821 | DisallowHeapAllocation no_gc; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 8822 | FlatContent content = GetFlatContent(); |
| 8823 | if (content.IsTwoByte()) { |
| 8824 | return CompareChars(content.ToUC16Vector().start(), str.start(), slen) == 0; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 8825 | } |
Steve Block | 9fac840 | 2011-05-12 15:51:54 +0100 | [diff] [blame] | 8826 | for (int i = 0; i < slen; i++) { |
| 8827 | if (Get(i) != str[i]) return false; |
| 8828 | } |
| 8829 | return true; |
| 8830 | } |
| 8831 | |
| 8832 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8833 | uint32_t String::ComputeAndSetHash() { |
| 8834 | // Should only be called if hash code has not yet been computed. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8835 | DCHECK(!HasHashCode()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8836 | |
| 8837 | // Store the hash code in the object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8838 | uint32_t field = IteratingStringHasher::Hash(this, GetHeap()->HashSeed()); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 8839 | set_hash_field(field); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8840 | |
| 8841 | // Check the hash code is there. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8842 | DCHECK(HasHashCode()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8843 | uint32_t result = field >> kHashShift; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8844 | DCHECK(result != 0); // Ensure that the hash value of 0 is never computed. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8845 | return result; |
| 8846 | } |
| 8847 | |
| 8848 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8849 | bool String::ComputeArrayIndex(uint32_t* index) { |
| 8850 | int length = this->length(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8851 | if (length == 0 || length > kMaxArrayIndexSize) return false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8852 | ConsStringIteratorOp op; |
| 8853 | StringCharacterStream stream(this, &op); |
| 8854 | return StringToArrayIndex(&stream, index); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8855 | } |
| 8856 | |
| 8857 | |
| 8858 | bool String::SlowAsArrayIndex(uint32_t* index) { |
| 8859 | if (length() <= kMaxCachedArrayIndexLength) { |
| 8860 | Hash(); // force computation of hash code |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 8861 | uint32_t field = hash_field(); |
Ben Murdoch | 7f4d5bd | 2010-06-15 11:15:29 +0100 | [diff] [blame] | 8862 | if ((field & kIsNotArrayIndexMask) != 0) return false; |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 8863 | // Isolate the array index form the full hash field. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8864 | *index = ArrayIndexValueBits::decode(field); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8865 | return true; |
| 8866 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8867 | return ComputeArrayIndex(index); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8868 | } |
| 8869 | } |
| 8870 | |
| 8871 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8872 | Handle<String> SeqString::Truncate(Handle<SeqString> string, int new_length) { |
| 8873 | int new_size, old_size; |
| 8874 | int old_length = string->length(); |
| 8875 | if (old_length <= new_length) return string; |
| 8876 | |
| 8877 | if (string->IsSeqOneByteString()) { |
| 8878 | old_size = SeqOneByteString::SizeFor(old_length); |
| 8879 | new_size = SeqOneByteString::SizeFor(new_length); |
| 8880 | } else { |
| 8881 | DCHECK(string->IsSeqTwoByteString()); |
| 8882 | old_size = SeqTwoByteString::SizeFor(old_length); |
| 8883 | new_size = SeqTwoByteString::SizeFor(new_length); |
| 8884 | } |
| 8885 | |
| 8886 | int delta = old_size - new_size; |
| 8887 | |
| 8888 | Address start_of_string = string->address(); |
| 8889 | DCHECK_OBJECT_ALIGNED(start_of_string); |
| 8890 | DCHECK_OBJECT_ALIGNED(start_of_string + new_size); |
| 8891 | |
| 8892 | Heap* heap = string->GetHeap(); |
| 8893 | NewSpace* newspace = heap->new_space(); |
| 8894 | if (newspace->Contains(start_of_string) && |
| 8895 | newspace->top() == start_of_string + old_size) { |
| 8896 | // Last allocated object in new space. Simply lower allocation top. |
| 8897 | newspace->set_top(start_of_string + new_size); |
| 8898 | } else { |
| 8899 | // Sizes are pointer size aligned, so that we can use filler objects |
| 8900 | // that are a multiple of pointer size. |
| 8901 | heap->CreateFillerObjectAt(start_of_string + new_size, delta); |
| 8902 | } |
| 8903 | heap->AdjustLiveBytes(start_of_string, -delta, Heap::FROM_MUTATOR); |
| 8904 | |
| 8905 | // We are storing the new length using release store after creating a filler |
| 8906 | // for the left-over space to avoid races with the sweeper thread. |
| 8907 | string->synchronized_set_length(new_length); |
| 8908 | |
| 8909 | if (new_length == 0) return heap->isolate()->factory()->empty_string(); |
| 8910 | return string; |
| 8911 | } |
| 8912 | |
| 8913 | |
Iain Merrick | 9ac36c9 | 2010-09-13 15:29:50 +0100 | [diff] [blame] | 8914 | uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 8915 | // For array indexes mix the length into the hash as an array index could |
| 8916 | // be zero. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8917 | DCHECK(length > 0); |
| 8918 | DCHECK(length <= String::kMaxArrayIndexSize); |
| 8919 | DCHECK(TenToThe(String::kMaxCachedArrayIndexLength) < |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 8920 | (1 << String::kArrayIndexValueBits)); |
Iain Merrick | 9ac36c9 | 2010-09-13 15:29:50 +0100 | [diff] [blame] | 8921 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8922 | value <<= String::ArrayIndexValueBits::kShift; |
| 8923 | value |= length << String::ArrayIndexLengthBits::kShift; |
Iain Merrick | 9ac36c9 | 2010-09-13 15:29:50 +0100 | [diff] [blame] | 8924 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8925 | DCHECK((value & String::kIsNotArrayIndexMask) == 0); |
| 8926 | DCHECK((length > String::kMaxCachedArrayIndexLength) || |
Iain Merrick | 9ac36c9 | 2010-09-13 15:29:50 +0100 | [diff] [blame] | 8927 | (value & String::kContainsCachedArrayIndexMask) == 0); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 8928 | return value; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8929 | } |
| 8930 | |
| 8931 | |
| 8932 | uint32_t StringHasher::GetHashField() { |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 8933 | if (length_ <= String::kMaxHashCalcLength) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8934 | if (is_array_index_) { |
| 8935 | return MakeArrayIndexHash(array_index_, length_); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8936 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8937 | return (GetHashCore(raw_running_hash_) << String::kHashShift) | |
| 8938 | String::kIsNotArrayIndexMask; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8939 | } else { |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 8940 | return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8941 | } |
| 8942 | } |
| 8943 | |
| 8944 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8945 | uint32_t StringHasher::ComputeUtf8Hash(Vector<const char> chars, |
| 8946 | uint32_t seed, |
| 8947 | int* utf16_length_out) { |
| 8948 | int vector_length = chars.length(); |
| 8949 | // Handle some edge cases |
| 8950 | if (vector_length <= 1) { |
| 8951 | DCHECK(vector_length == 0 || |
| 8952 | static_cast<uint8_t>(chars.start()[0]) <= |
| 8953 | unibrow::Utf8::kMaxOneByteChar); |
| 8954 | *utf16_length_out = vector_length; |
| 8955 | return HashSequentialString(chars.start(), vector_length, seed); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8956 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8957 | // Start with a fake length which won't affect computation. |
| 8958 | // It will be updated later. |
| 8959 | StringHasher hasher(String::kMaxArrayIndexSize, seed); |
| 8960 | unsigned remaining = static_cast<unsigned>(vector_length); |
| 8961 | const uint8_t* stream = reinterpret_cast<const uint8_t*>(chars.start()); |
| 8962 | int utf16_length = 0; |
| 8963 | bool is_index = true; |
| 8964 | DCHECK(hasher.is_array_index_); |
| 8965 | while (remaining > 0) { |
| 8966 | unsigned consumed = 0; |
| 8967 | uint32_t c = unibrow::Utf8::ValueOf(stream, remaining, &consumed); |
| 8968 | DCHECK(consumed > 0 && consumed <= remaining); |
| 8969 | stream += consumed; |
| 8970 | remaining -= consumed; |
| 8971 | bool is_two_characters = c > unibrow::Utf16::kMaxNonSurrogateCharCode; |
| 8972 | utf16_length += is_two_characters ? 2 : 1; |
| 8973 | // No need to keep hashing. But we do need to calculate utf16_length. |
| 8974 | if (utf16_length > String::kMaxHashCalcLength) continue; |
| 8975 | if (is_two_characters) { |
| 8976 | uint16_t c1 = unibrow::Utf16::LeadSurrogate(c); |
| 8977 | uint16_t c2 = unibrow::Utf16::TrailSurrogate(c); |
| 8978 | hasher.AddCharacter(c1); |
| 8979 | hasher.AddCharacter(c2); |
| 8980 | if (is_index) is_index = hasher.UpdateIndex(c1); |
| 8981 | if (is_index) is_index = hasher.UpdateIndex(c2); |
| 8982 | } else { |
| 8983 | hasher.AddCharacter(c); |
| 8984 | if (is_index) is_index = hasher.UpdateIndex(c); |
| 8985 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8986 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8987 | *utf16_length_out = static_cast<int>(utf16_length); |
| 8988 | // Must set length here so that hash computation is correct. |
| 8989 | hasher.length_ = utf16_length; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8990 | return hasher.GetHashField(); |
| 8991 | } |
| 8992 | |
| 8993 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8994 | void String::PrintOn(FILE* file) { |
| 8995 | int length = this->length(); |
| 8996 | for (int i = 0; i < length; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 8997 | PrintF(file, "%c", Get(i)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 8998 | } |
| 8999 | } |
| 9000 | |
| 9001 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 9002 | int Map::Hash() { |
| 9003 | // For performance reasons we only hash the 3 most variable fields of a map: |
| 9004 | // constructor, prototype and bit_field2. |
| 9005 | |
| 9006 | // Shift away the tag. |
| 9007 | int hash = (static_cast<uint32_t>( |
| 9008 | reinterpret_cast<uintptr_t>(constructor())) >> 2); |
| 9009 | |
| 9010 | // XOR-ing the prototype and constructor directly yields too many zero bits |
| 9011 | // when the two pointers are close (which is fairly common). |
| 9012 | // To avoid this we shift the prototype 4 bits relatively to the constructor. |
| 9013 | hash ^= (static_cast<uint32_t>( |
| 9014 | reinterpret_cast<uintptr_t>(prototype())) << 2); |
| 9015 | |
| 9016 | return hash ^ (hash >> 16) ^ bit_field2(); |
| 9017 | } |
| 9018 | |
| 9019 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9020 | static bool CheckEquivalent(Map* first, Map* second) { |
| 9021 | return |
| 9022 | first->constructor() == second->constructor() && |
| 9023 | first->prototype() == second->prototype() && |
| 9024 | first->instance_type() == second->instance_type() && |
| 9025 | first->bit_field() == second->bit_field() && |
| 9026 | first->bit_field2() == second->bit_field2() && |
| 9027 | first->is_frozen() == second->is_frozen() && |
| 9028 | first->has_instance_call_handler() == second->has_instance_call_handler(); |
| 9029 | } |
| 9030 | |
| 9031 | |
| 9032 | bool Map::EquivalentToForTransition(Map* other) { |
| 9033 | return CheckEquivalent(this, other); |
| 9034 | } |
| 9035 | |
| 9036 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 9037 | bool Map::EquivalentToForNormalization(Map* other, |
| 9038 | PropertyNormalizationMode mode) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9039 | int properties = mode == CLEAR_INOBJECT_PROPERTIES |
| 9040 | ? 0 : other->inobject_properties(); |
| 9041 | return CheckEquivalent(this, other) && inobject_properties() == properties; |
| 9042 | } |
| 9043 | |
| 9044 | |
| 9045 | void ConstantPoolArray::ConstantPoolIterateBody(ObjectVisitor* v) { |
| 9046 | // Unfortunately the serializer relies on pointers within an object being |
| 9047 | // visited in-order, so we have to iterate both the code and heap pointers in |
| 9048 | // the small section before doing so in the extended section. |
| 9049 | for (int s = 0; s <= final_section(); ++s) { |
| 9050 | LayoutSection section = static_cast<LayoutSection>(s); |
| 9051 | ConstantPoolArray::Iterator code_iter(this, ConstantPoolArray::CODE_PTR, |
| 9052 | section); |
| 9053 | while (!code_iter.is_finished()) { |
| 9054 | v->VisitCodeEntry(reinterpret_cast<Address>( |
| 9055 | RawFieldOfElementAt(code_iter.next_index()))); |
| 9056 | } |
| 9057 | |
| 9058 | ConstantPoolArray::Iterator heap_iter(this, ConstantPoolArray::HEAP_PTR, |
| 9059 | section); |
| 9060 | while (!heap_iter.is_finished()) { |
| 9061 | v->VisitPointer(RawFieldOfElementAt(heap_iter.next_index())); |
| 9062 | } |
| 9063 | } |
| 9064 | } |
| 9065 | |
| 9066 | |
| 9067 | void ConstantPoolArray::ClearPtrEntries(Isolate* isolate) { |
| 9068 | Type type[] = { CODE_PTR, HEAP_PTR }; |
| 9069 | Address default_value[] = { |
| 9070 | isolate->builtins()->builtin(Builtins::kIllegal)->entry(), |
| 9071 | reinterpret_cast<Address>(isolate->heap()->undefined_value()) }; |
| 9072 | |
| 9073 | for (int i = 0; i < 2; ++i) { |
| 9074 | for (int s = 0; s <= final_section(); ++s) { |
| 9075 | LayoutSection section = static_cast<LayoutSection>(s); |
| 9076 | if (number_of_entries(type[i], section) > 0) { |
| 9077 | int offset = OffsetOfElementAt(first_index(type[i], section)); |
| 9078 | MemsetPointer( |
| 9079 | reinterpret_cast<Address*>(HeapObject::RawField(this, offset)), |
| 9080 | default_value[i], |
| 9081 | number_of_entries(type[i], section)); |
| 9082 | } |
| 9083 | } |
| 9084 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 9085 | } |
| 9086 | |
| 9087 | |
Steve Block | 791712a | 2010-08-27 10:21:07 +0100 | [diff] [blame] | 9088 | void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) { |
| 9089 | // Iterate over all fields in the body but take care in dealing with |
| 9090 | // the code entry. |
| 9091 | IteratePointers(v, kPropertiesOffset, kCodeEntryOffset); |
| 9092 | v->VisitCodeEntry(this->address() + kCodeEntryOffset); |
| 9093 | IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size); |
| 9094 | } |
| 9095 | |
| 9096 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9097 | void JSFunction::MarkForOptimization() { |
| 9098 | DCHECK(!IsOptimized()); |
| 9099 | DCHECK(shared()->allows_lazy_compilation() || |
Ben Murdoch | b8e0da2 | 2011-05-16 14:20:40 +0100 | [diff] [blame] | 9100 | code()->optimizable()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9101 | DCHECK(!shared()->is_generator()); |
| 9102 | set_code_no_write_barrier( |
| 9103 | GetIsolate()->builtins()->builtin(Builtins::kCompileOptimized)); |
| 9104 | // No write barrier required, since the builtin is part of the root set. |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9105 | } |
| 9106 | |
| 9107 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9108 | void JSFunction::MarkForConcurrentOptimization() { |
| 9109 | DCHECK(is_compiled() || GetIsolate()->DebuggerHasBreakPoints()); |
| 9110 | DCHECK(!IsOptimized()); |
| 9111 | DCHECK(shared()->allows_lazy_compilation() || code()->optimizable()); |
| 9112 | DCHECK(!shared()->is_generator()); |
| 9113 | DCHECK(GetIsolate()->concurrent_recompilation_enabled()); |
| 9114 | if (FLAG_trace_concurrent_recompilation) { |
| 9115 | PrintF(" ** Marking "); |
| 9116 | ShortPrint(); |
| 9117 | PrintF(" for concurrent recompilation.\n"); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9118 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9119 | set_code_no_write_barrier( |
| 9120 | GetIsolate()->builtins()->builtin(Builtins::kCompileOptimizedConcurrent)); |
| 9121 | // No write barrier required, since the builtin is part of the root set. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9122 | } |
| 9123 | |
| 9124 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9125 | void JSFunction::MarkInOptimizationQueue() { |
| 9126 | // We can only arrive here via the concurrent-recompilation builtin. If |
| 9127 | // break points were set, the code would point to the lazy-compile builtin. |
| 9128 | DCHECK(!GetIsolate()->DebuggerHasBreakPoints()); |
| 9129 | DCHECK(IsMarkedForConcurrentOptimization() && !IsOptimized()); |
| 9130 | DCHECK(shared()->allows_lazy_compilation() || code()->optimizable()); |
| 9131 | DCHECK(GetIsolate()->concurrent_recompilation_enabled()); |
| 9132 | if (FLAG_trace_concurrent_recompilation) { |
| 9133 | PrintF(" ** Queueing "); |
| 9134 | ShortPrint(); |
| 9135 | PrintF(" for concurrent recompilation.\n"); |
| 9136 | } |
| 9137 | set_code_no_write_barrier( |
| 9138 | GetIsolate()->builtins()->builtin(Builtins::kInOptimizationQueue)); |
| 9139 | // No write barrier required, since the builtin is part of the root set. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9140 | } |
| 9141 | |
| 9142 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9143 | Handle<JSFunction> JSFunction::CloneClosure(Handle<JSFunction> function) { |
| 9144 | Isolate* isolate = function->GetIsolate(); |
| 9145 | Handle<Map> map(function->map()); |
| 9146 | Handle<SharedFunctionInfo> shared(function->shared()); |
| 9147 | Handle<Context> context(function->context()); |
| 9148 | Handle<JSFunction> clone = |
| 9149 | isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context); |
| 9150 | |
| 9151 | if (shared->bound()) { |
| 9152 | clone->set_function_bindings(function->function_bindings()); |
| 9153 | } |
| 9154 | |
| 9155 | // In typical case, __proto__ of ``function`` is the default Function |
| 9156 | // prototype, which means that SetPrototype below is a no-op. |
| 9157 | // In rare cases when that is not true, we mutate the clone's __proto__. |
| 9158 | Handle<Object> original_prototype(map->prototype(), isolate); |
| 9159 | if (*original_prototype != clone->map()->prototype()) { |
| 9160 | JSObject::SetPrototype(clone, original_prototype, false).Assert(); |
| 9161 | } |
| 9162 | |
| 9163 | return clone; |
| 9164 | } |
| 9165 | |
| 9166 | |
| 9167 | void SharedFunctionInfo::AddToOptimizedCodeMap( |
| 9168 | Handle<SharedFunctionInfo> shared, |
| 9169 | Handle<Context> native_context, |
| 9170 | Handle<Code> code, |
| 9171 | Handle<FixedArray> literals, |
| 9172 | BailoutId osr_ast_id) { |
| 9173 | Isolate* isolate = shared->GetIsolate(); |
| 9174 | DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION); |
| 9175 | DCHECK(native_context->IsNativeContext()); |
| 9176 | STATIC_ASSERT(kEntryLength == 4); |
| 9177 | Handle<FixedArray> new_code_map; |
| 9178 | Handle<Object> value(shared->optimized_code_map(), isolate); |
| 9179 | int old_length; |
| 9180 | if (value->IsSmi()) { |
| 9181 | // No optimized code map. |
| 9182 | DCHECK_EQ(0, Smi::cast(*value)->value()); |
| 9183 | // Create 3 entries per context {context, code, literals}. |
| 9184 | new_code_map = isolate->factory()->NewFixedArray(kInitialLength); |
| 9185 | old_length = kEntriesStart; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9186 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9187 | // Copy old map and append one new entry. |
| 9188 | Handle<FixedArray> old_code_map = Handle<FixedArray>::cast(value); |
| 9189 | DCHECK_EQ(-1, shared->SearchOptimizedCodeMap(*native_context, osr_ast_id)); |
| 9190 | old_length = old_code_map->length(); |
| 9191 | new_code_map = FixedArray::CopySize( |
| 9192 | old_code_map, old_length + kEntryLength); |
| 9193 | // Zap the old map for the sake of the heap verifier. |
| 9194 | if (Heap::ShouldZapGarbage()) { |
| 9195 | Object** data = old_code_map->data_start(); |
| 9196 | MemsetPointer(data, isolate->heap()->the_hole_value(), old_length); |
| 9197 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9198 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9199 | new_code_map->set(old_length + kContextOffset, *native_context); |
| 9200 | new_code_map->set(old_length + kCachedCodeOffset, *code); |
| 9201 | new_code_map->set(old_length + kLiteralsOffset, *literals); |
| 9202 | new_code_map->set(old_length + kOsrAstIdOffset, |
| 9203 | Smi::FromInt(osr_ast_id.ToInt())); |
| 9204 | |
| 9205 | #ifdef DEBUG |
| 9206 | for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) { |
| 9207 | DCHECK(new_code_map->get(i + kContextOffset)->IsNativeContext()); |
| 9208 | DCHECK(new_code_map->get(i + kCachedCodeOffset)->IsCode()); |
| 9209 | DCHECK(Code::cast(new_code_map->get(i + kCachedCodeOffset))->kind() == |
| 9210 | Code::OPTIMIZED_FUNCTION); |
| 9211 | DCHECK(new_code_map->get(i + kLiteralsOffset)->IsFixedArray()); |
| 9212 | DCHECK(new_code_map->get(i + kOsrAstIdOffset)->IsSmi()); |
| 9213 | } |
| 9214 | #endif |
| 9215 | shared->set_optimized_code_map(*new_code_map); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9216 | } |
| 9217 | |
| 9218 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9219 | FixedArray* SharedFunctionInfo::GetLiteralsFromOptimizedCodeMap(int index) { |
| 9220 | DCHECK(index > kEntriesStart); |
| 9221 | FixedArray* code_map = FixedArray::cast(optimized_code_map()); |
| 9222 | if (!bound()) { |
| 9223 | FixedArray* cached_literals = FixedArray::cast(code_map->get(index + 1)); |
| 9224 | DCHECK_NE(NULL, cached_literals); |
| 9225 | return cached_literals; |
| 9226 | } |
| 9227 | return NULL; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9228 | } |
| 9229 | |
| 9230 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9231 | Code* SharedFunctionInfo::GetCodeFromOptimizedCodeMap(int index) { |
| 9232 | DCHECK(index > kEntriesStart); |
| 9233 | FixedArray* code_map = FixedArray::cast(optimized_code_map()); |
| 9234 | Code* code = Code::cast(code_map->get(index)); |
| 9235 | DCHECK_NE(NULL, code); |
| 9236 | return code; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9237 | } |
| 9238 | |
| 9239 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9240 | void SharedFunctionInfo::ClearOptimizedCodeMap() { |
| 9241 | FixedArray* code_map = FixedArray::cast(optimized_code_map()); |
| 9242 | |
| 9243 | // If the next map link slot is already used then the function was |
| 9244 | // enqueued with code flushing and we remove it now. |
| 9245 | if (!code_map->get(kNextMapIndex)->IsUndefined()) { |
| 9246 | CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher(); |
| 9247 | flusher->EvictOptimizedCodeMap(this); |
| 9248 | } |
| 9249 | |
| 9250 | DCHECK(code_map->get(kNextMapIndex)->IsUndefined()); |
| 9251 | set_optimized_code_map(Smi::FromInt(0)); |
| 9252 | } |
| 9253 | |
| 9254 | |
| 9255 | void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, |
| 9256 | const char* reason) { |
| 9257 | DisallowHeapAllocation no_gc; |
| 9258 | if (optimized_code_map()->IsSmi()) return; |
| 9259 | |
| 9260 | FixedArray* code_map = FixedArray::cast(optimized_code_map()); |
| 9261 | int dst = kEntriesStart; |
| 9262 | int length = code_map->length(); |
| 9263 | for (int src = kEntriesStart; src < length; src += kEntryLength) { |
| 9264 | DCHECK(code_map->get(src)->IsNativeContext()); |
| 9265 | if (Code::cast(code_map->get(src + kCachedCodeOffset)) == optimized_code) { |
| 9266 | // Evict the src entry by not copying it to the dst entry. |
| 9267 | if (FLAG_trace_opt) { |
| 9268 | PrintF("[evicting entry from optimizing code map (%s) for ", reason); |
| 9269 | ShortPrint(); |
| 9270 | BailoutId osr(Smi::cast(code_map->get(src + kOsrAstIdOffset))->value()); |
| 9271 | if (osr.IsNone()) { |
| 9272 | PrintF("]\n"); |
| 9273 | } else { |
| 9274 | PrintF(" (osr ast id %d)]\n", osr.ToInt()); |
| 9275 | } |
| 9276 | } |
| 9277 | } else { |
| 9278 | // Keep the src entry by copying it to the dst entry. |
| 9279 | if (dst != src) { |
| 9280 | code_map->set(dst + kContextOffset, |
| 9281 | code_map->get(src + kContextOffset)); |
| 9282 | code_map->set(dst + kCachedCodeOffset, |
| 9283 | code_map->get(src + kCachedCodeOffset)); |
| 9284 | code_map->set(dst + kLiteralsOffset, |
| 9285 | code_map->get(src + kLiteralsOffset)); |
| 9286 | code_map->set(dst + kOsrAstIdOffset, |
| 9287 | code_map->get(src + kOsrAstIdOffset)); |
| 9288 | } |
| 9289 | dst += kEntryLength; |
| 9290 | } |
| 9291 | } |
| 9292 | if (dst != length) { |
| 9293 | // Always trim even when array is cleared because of heap verifier. |
| 9294 | GetHeap()->RightTrimFixedArray<Heap::FROM_MUTATOR>(code_map, length - dst); |
| 9295 | if (code_map->length() == kEntriesStart) ClearOptimizedCodeMap(); |
| 9296 | } |
| 9297 | } |
| 9298 | |
| 9299 | |
| 9300 | void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) { |
| 9301 | FixedArray* code_map = FixedArray::cast(optimized_code_map()); |
| 9302 | DCHECK(shrink_by % kEntryLength == 0); |
| 9303 | DCHECK(shrink_by <= code_map->length() - kEntriesStart); |
| 9304 | // Always trim even when array is cleared because of heap verifier. |
| 9305 | GetHeap()->RightTrimFixedArray<Heap::FROM_GC>(code_map, shrink_by); |
| 9306 | if (code_map->length() == kEntriesStart) { |
| 9307 | ClearOptimizedCodeMap(); |
| 9308 | } |
| 9309 | } |
| 9310 | |
| 9311 | |
| 9312 | void JSObject::OptimizeAsPrototype(Handle<JSObject> object, |
| 9313 | PrototypeOptimizationMode mode) { |
| 9314 | if (object->IsGlobalObject()) return; |
| 9315 | if (object->IsJSGlobalProxy()) return; |
| 9316 | if (mode == FAST_PROTOTYPE && !object->map()->is_prototype_map()) { |
| 9317 | // First normalize to ensure all JSFunctions are CONSTANT. |
| 9318 | JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0); |
| 9319 | } |
| 9320 | if (!object->HasFastProperties()) { |
| 9321 | JSObject::MigrateSlowToFast(object, 0); |
| 9322 | } |
| 9323 | if (mode == FAST_PROTOTYPE && object->HasFastProperties() && |
| 9324 | !object->map()->is_prototype_map()) { |
| 9325 | Handle<Map> new_map = Map::Copy(handle(object->map())); |
| 9326 | JSObject::MigrateToMap(object, new_map); |
| 9327 | object->map()->set_is_prototype_map(true); |
| 9328 | } |
| 9329 | } |
| 9330 | |
| 9331 | |
| 9332 | void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) { |
| 9333 | if (!object->map()->is_prototype_map()) return; |
| 9334 | OptimizeAsPrototype(object, FAST_PROTOTYPE); |
| 9335 | } |
| 9336 | |
| 9337 | |
| 9338 | Handle<Object> CacheInitialJSArrayMaps( |
| 9339 | Handle<Context> native_context, Handle<Map> initial_map) { |
| 9340 | // Replace all of the cached initial array maps in the native context with |
| 9341 | // the appropriate transitioned elements kind maps. |
| 9342 | Factory* factory = native_context->GetIsolate()->factory(); |
| 9343 | Handle<FixedArray> maps = factory->NewFixedArrayWithHoles( |
| 9344 | kElementsKindCount, TENURED); |
| 9345 | |
| 9346 | Handle<Map> current_map = initial_map; |
| 9347 | ElementsKind kind = current_map->elements_kind(); |
| 9348 | DCHECK(kind == GetInitialFastElementsKind()); |
| 9349 | maps->set(kind, *current_map); |
| 9350 | for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1; |
| 9351 | i < kFastElementsKindCount; ++i) { |
| 9352 | Handle<Map> new_map; |
| 9353 | ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i); |
| 9354 | if (current_map->HasElementsTransition()) { |
| 9355 | new_map = handle(current_map->elements_transition_map()); |
| 9356 | DCHECK(new_map->elements_kind() == next_kind); |
| 9357 | } else { |
| 9358 | new_map = Map::CopyAsElementsKind( |
| 9359 | current_map, next_kind, INSERT_TRANSITION); |
| 9360 | } |
| 9361 | maps->set(next_kind, *new_map); |
| 9362 | current_map = new_map; |
| 9363 | } |
| 9364 | native_context->set_js_array_maps(*maps); |
| 9365 | return initial_map; |
| 9366 | } |
| 9367 | |
| 9368 | |
| 9369 | void JSFunction::SetInstancePrototype(Handle<JSFunction> function, |
| 9370 | Handle<Object> value) { |
| 9371 | Isolate* isolate = function->GetIsolate(); |
| 9372 | |
| 9373 | DCHECK(value->IsJSReceiver()); |
| 9374 | |
| 9375 | // Now some logic for the maps of the objects that are created by using this |
| 9376 | // function as a constructor. |
| 9377 | if (function->has_initial_map()) { |
| 9378 | // If the function has allocated the initial map replace it with a |
| 9379 | // copy containing the new prototype. Also complete any in-object |
| 9380 | // slack tracking that is in progress at this point because it is |
| 9381 | // still tracking the old copy. |
| 9382 | if (function->IsInobjectSlackTrackingInProgress()) { |
| 9383 | function->CompleteInobjectSlackTracking(); |
| 9384 | } |
| 9385 | |
| 9386 | Handle<Map> initial_map(function->initial_map(), isolate); |
| 9387 | |
| 9388 | if (!initial_map->GetIsolate()->bootstrapper()->IsActive() && |
| 9389 | initial_map->instance_type() == JS_OBJECT_TYPE) { |
| 9390 | // Put the value in the initial map field until an initial map is needed. |
| 9391 | // At that point, a new initial map is created and the prototype is put |
| 9392 | // into the initial map where it belongs. |
| 9393 | function->set_prototype_or_initial_map(*value); |
| 9394 | } else { |
| 9395 | Handle<Map> new_map = Map::Copy(initial_map); |
| 9396 | JSFunction::SetInitialMap(function, new_map, value); |
| 9397 | |
| 9398 | // If the function is used as the global Array function, cache the |
| 9399 | // initial map (and transitioned versions) in the native context. |
| 9400 | Context* native_context = function->context()->native_context(); |
| 9401 | Object* array_function = |
| 9402 | native_context->get(Context::ARRAY_FUNCTION_INDEX); |
| 9403 | if (array_function->IsJSFunction() && |
| 9404 | *function == JSFunction::cast(array_function)) { |
| 9405 | CacheInitialJSArrayMaps(handle(native_context, isolate), new_map); |
| 9406 | } |
| 9407 | } |
| 9408 | |
| 9409 | // Deoptimize all code that embeds the previous initial map. |
| 9410 | initial_map->dependent_code()->DeoptimizeDependentCodeGroup( |
| 9411 | isolate, DependentCode::kInitialMapChangedGroup); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9412 | } else { |
| 9413 | // Put the value in the initial map field until an initial map is |
| 9414 | // needed. At that point, a new initial map is created and the |
| 9415 | // prototype is put into the initial map where it belongs. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9416 | function->set_prototype_or_initial_map(*value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9417 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9418 | isolate->heap()->ClearInstanceofCache(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9419 | } |
| 9420 | |
| 9421 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9422 | void JSFunction::SetPrototype(Handle<JSFunction> function, |
| 9423 | Handle<Object> value) { |
| 9424 | DCHECK(function->should_have_prototype()); |
| 9425 | Handle<Object> construct_prototype = value; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9426 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9427 | // If the value is not a JSReceiver, store the value in the map's |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9428 | // constructor field so it can be accessed. Also, set the prototype |
| 9429 | // used for constructing objects to the original object prototype. |
| 9430 | // See ECMA-262 13.2.2. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9431 | if (!value->IsJSReceiver()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9432 | // Copy the map so this does not affect unrelated functions. |
| 9433 | // Remove map transitions because they point to maps with a |
| 9434 | // different prototype. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9435 | Handle<Map> new_map = Map::Copy(handle(function->map())); |
| 9436 | |
| 9437 | JSObject::MigrateToMap(function, new_map); |
| 9438 | new_map->set_constructor(*value); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 9439 | new_map->set_non_instance_prototype(true); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9440 | Isolate* isolate = new_map->GetIsolate(); |
| 9441 | construct_prototype = handle( |
| 9442 | isolate->context()->native_context()->initial_object_prototype(), |
| 9443 | isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9444 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9445 | function->map()->set_non_instance_prototype(false); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9446 | } |
| 9447 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9448 | return SetInstancePrototype(function, construct_prototype); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9449 | } |
| 9450 | |
| 9451 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9452 | bool JSFunction::RemovePrototype() { |
| 9453 | Context* native_context = context()->native_context(); |
| 9454 | Map* no_prototype_map = shared()->strict_mode() == SLOPPY |
| 9455 | ? native_context->sloppy_function_without_prototype_map() |
| 9456 | : native_context->strict_function_without_prototype_map(); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 9457 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9458 | if (map() == no_prototype_map) return true; |
| 9459 | |
| 9460 | #ifdef DEBUG |
| 9461 | if (map() != (shared()->strict_mode() == SLOPPY |
| 9462 | ? native_context->sloppy_function_map() |
| 9463 | : native_context->strict_function_map())) { |
| 9464 | return false; |
Ben Murdoch | e0cee9b | 2011-05-25 10:26:03 +0100 | [diff] [blame] | 9465 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9466 | #endif |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 9467 | |
| 9468 | set_map(no_prototype_map); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9469 | set_prototype_or_initial_map(no_prototype_map->GetHeap()->the_hole_value()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9470 | return true; |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 9471 | } |
| 9472 | |
| 9473 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9474 | void JSFunction::SetInitialMap(Handle<JSFunction> function, Handle<Map> map, |
| 9475 | Handle<Object> prototype) { |
| 9476 | if (prototype->IsJSObject()) { |
| 9477 | Handle<JSObject> js_proto = Handle<JSObject>::cast(prototype); |
| 9478 | JSObject::OptimizeAsPrototype(js_proto, FAST_PROTOTYPE); |
| 9479 | } |
| 9480 | map->set_prototype(*prototype); |
| 9481 | function->set_prototype_or_initial_map(*map); |
| 9482 | map->set_constructor(*function); |
| 9483 | } |
| 9484 | |
| 9485 | |
| 9486 | void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) { |
| 9487 | if (function->has_initial_map()) return; |
| 9488 | Isolate* isolate = function->GetIsolate(); |
| 9489 | |
| 9490 | // First create a new map with the size and number of in-object properties |
| 9491 | // suggested by the function. |
| 9492 | InstanceType instance_type; |
| 9493 | int instance_size; |
| 9494 | int in_object_properties; |
| 9495 | if (function->shared()->is_generator()) { |
| 9496 | instance_type = JS_GENERATOR_OBJECT_TYPE; |
| 9497 | instance_size = JSGeneratorObject::kSize; |
| 9498 | in_object_properties = 0; |
| 9499 | } else { |
| 9500 | instance_type = JS_OBJECT_TYPE; |
| 9501 | instance_size = function->shared()->CalculateInstanceSize(); |
| 9502 | in_object_properties = function->shared()->CalculateInObjectProperties(); |
| 9503 | } |
| 9504 | Handle<Map> map = isolate->factory()->NewMap(instance_type, instance_size); |
| 9505 | |
| 9506 | // Fetch or allocate prototype. |
| 9507 | Handle<Object> prototype; |
| 9508 | if (function->has_instance_prototype()) { |
| 9509 | prototype = handle(function->instance_prototype(), isolate); |
| 9510 | } else { |
| 9511 | prototype = isolate->factory()->NewFunctionPrototype(function); |
| 9512 | } |
| 9513 | map->set_inobject_properties(in_object_properties); |
| 9514 | map->set_unused_property_fields(in_object_properties); |
| 9515 | DCHECK(map->has_fast_object_elements()); |
| 9516 | |
| 9517 | // Finally link initial map and constructor function. |
| 9518 | JSFunction::SetInitialMap(function, map, Handle<JSReceiver>::cast(prototype)); |
| 9519 | |
| 9520 | if (!function->shared()->is_generator()) { |
| 9521 | function->StartInobjectSlackTracking(); |
| 9522 | } |
| 9523 | } |
| 9524 | |
| 9525 | |
| 9526 | void JSFunction::SetInstanceClassName(String* name) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9527 | shared()->set_instance_class_name(name); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9528 | } |
| 9529 | |
| 9530 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9531 | void JSFunction::PrintName(FILE* out) { |
Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 9532 | SmartArrayPointer<char> name = shared()->DebugName()->ToCString(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9533 | PrintF(out, "%s", name.get()); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9534 | } |
| 9535 | |
| 9536 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9537 | Context* JSFunction::NativeContextFromLiterals(FixedArray* literals) { |
| 9538 | return Context::cast(literals->get(JSFunction::kLiteralNativeContextIndex)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9539 | } |
| 9540 | |
| 9541 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9542 | // The filter is a pattern that matches function names in this way: |
| 9543 | // "*" all; the default |
| 9544 | // "-" all but the top-level function |
| 9545 | // "-name" all but the function "name" |
| 9546 | // "" only the top-level function |
| 9547 | // "name" only the function "name" |
| 9548 | // "name*" only functions starting with "name" |
| 9549 | // "~" none; the tilde is not an identifier |
| 9550 | bool JSFunction::PassesFilter(const char* raw_filter) { |
| 9551 | if (*raw_filter == '*') return true; |
| 9552 | String* name = shared()->DebugName(); |
| 9553 | Vector<const char> filter = CStrVector(raw_filter); |
| 9554 | if (filter.length() == 0) return name->length() == 0; |
| 9555 | if (filter[0] == '-') { |
| 9556 | // Negative filter. |
| 9557 | if (filter.length() == 1) { |
| 9558 | return (name->length() != 0); |
| 9559 | } else if (name->IsUtf8EqualTo(filter.SubVector(1, filter.length()))) { |
| 9560 | return false; |
| 9561 | } |
| 9562 | if (filter[filter.length() - 1] == '*' && |
| 9563 | name->IsUtf8EqualTo(filter.SubVector(1, filter.length() - 1), true)) { |
| 9564 | return false; |
| 9565 | } |
| 9566 | return true; |
| 9567 | |
| 9568 | } else if (name->IsUtf8EqualTo(filter)) { |
| 9569 | return true; |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 9570 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9571 | if (filter[filter.length() - 1] == '*' && |
| 9572 | name->IsUtf8EqualTo(filter.SubVector(0, filter.length() - 1), true)) { |
| 9573 | return true; |
| 9574 | } |
| 9575 | return false; |
| 9576 | } |
| 9577 | |
| 9578 | |
| 9579 | void Oddball::Initialize(Isolate* isolate, |
| 9580 | Handle<Oddball> oddball, |
| 9581 | const char* to_string, |
| 9582 | Handle<Object> to_number, |
| 9583 | byte kind) { |
| 9584 | Handle<String> internalized_to_string = |
| 9585 | isolate->factory()->InternalizeUtf8String(to_string); |
| 9586 | oddball->set_to_string(*internalized_to_string); |
| 9587 | oddball->set_to_number(*to_number); |
| 9588 | oddball->set_kind(kind); |
| 9589 | } |
| 9590 | |
| 9591 | |
| 9592 | void Script::InitLineEnds(Handle<Script> script) { |
| 9593 | if (!script->line_ends()->IsUndefined()) return; |
| 9594 | |
| 9595 | Isolate* isolate = script->GetIsolate(); |
| 9596 | |
| 9597 | if (!script->source()->IsString()) { |
| 9598 | DCHECK(script->source()->IsUndefined()); |
| 9599 | Handle<FixedArray> empty = isolate->factory()->NewFixedArray(0); |
| 9600 | script->set_line_ends(*empty); |
| 9601 | DCHECK(script->line_ends()->IsFixedArray()); |
| 9602 | return; |
| 9603 | } |
| 9604 | |
| 9605 | Handle<String> src(String::cast(script->source()), isolate); |
| 9606 | |
| 9607 | Handle<FixedArray> array = String::CalculateLineEnds(src, true); |
| 9608 | |
| 9609 | if (*array != isolate->heap()->empty_fixed_array()) { |
| 9610 | array->set_map(isolate->heap()->fixed_cow_array_map()); |
| 9611 | } |
| 9612 | |
| 9613 | script->set_line_ends(*array); |
| 9614 | DCHECK(script->line_ends()->IsFixedArray()); |
| 9615 | } |
| 9616 | |
| 9617 | |
| 9618 | int Script::GetColumnNumber(Handle<Script> script, int code_pos) { |
| 9619 | int line_number = GetLineNumber(script, code_pos); |
| 9620 | if (line_number == -1) return -1; |
| 9621 | |
| 9622 | DisallowHeapAllocation no_allocation; |
| 9623 | FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); |
| 9624 | line_number = line_number - script->line_offset()->value(); |
| 9625 | if (line_number == 0) return code_pos + script->column_offset()->value(); |
| 9626 | int prev_line_end_pos = |
| 9627 | Smi::cast(line_ends_array->get(line_number - 1))->value(); |
| 9628 | return code_pos - (prev_line_end_pos + 1); |
| 9629 | } |
| 9630 | |
| 9631 | |
| 9632 | int Script::GetLineNumberWithArray(int code_pos) { |
| 9633 | DisallowHeapAllocation no_allocation; |
| 9634 | DCHECK(line_ends()->IsFixedArray()); |
| 9635 | FixedArray* line_ends_array = FixedArray::cast(line_ends()); |
| 9636 | int line_ends_len = line_ends_array->length(); |
| 9637 | if (line_ends_len == 0) return -1; |
| 9638 | |
| 9639 | if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) { |
| 9640 | return line_offset()->value(); |
| 9641 | } |
| 9642 | |
| 9643 | int left = 0; |
| 9644 | int right = line_ends_len; |
| 9645 | while (int half = (right - left) / 2) { |
| 9646 | if ((Smi::cast(line_ends_array->get(left + half)))->value() > code_pos) { |
| 9647 | right -= half; |
| 9648 | } else { |
| 9649 | left += half; |
| 9650 | } |
| 9651 | } |
| 9652 | return right + line_offset()->value(); |
| 9653 | } |
| 9654 | |
| 9655 | |
| 9656 | int Script::GetLineNumber(Handle<Script> script, int code_pos) { |
| 9657 | InitLineEnds(script); |
| 9658 | return script->GetLineNumberWithArray(code_pos); |
| 9659 | } |
| 9660 | |
| 9661 | |
| 9662 | int Script::GetLineNumber(int code_pos) { |
| 9663 | DisallowHeapAllocation no_allocation; |
| 9664 | if (!line_ends()->IsUndefined()) return GetLineNumberWithArray(code_pos); |
| 9665 | |
| 9666 | // Slow mode: we do not have line_ends. We have to iterate through source. |
| 9667 | if (!source()->IsString()) return -1; |
| 9668 | |
| 9669 | String* source_string = String::cast(source()); |
| 9670 | int line = 0; |
| 9671 | int len = source_string->length(); |
| 9672 | for (int pos = 0; pos < len; pos++) { |
| 9673 | if (pos == code_pos) break; |
| 9674 | if (source_string->Get(pos) == '\n') line++; |
| 9675 | } |
| 9676 | return line; |
| 9677 | } |
| 9678 | |
| 9679 | |
| 9680 | Handle<Object> Script::GetNameOrSourceURL(Handle<Script> script) { |
| 9681 | Isolate* isolate = script->GetIsolate(); |
| 9682 | Handle<String> name_or_source_url_key = |
| 9683 | isolate->factory()->InternalizeOneByteString( |
| 9684 | STATIC_CHAR_VECTOR("nameOrSourceURL")); |
| 9685 | Handle<JSObject> script_wrapper = Script::GetWrapper(script); |
| 9686 | Handle<Object> property = Object::GetProperty( |
| 9687 | script_wrapper, name_or_source_url_key).ToHandleChecked(); |
| 9688 | DCHECK(property->IsJSFunction()); |
| 9689 | Handle<JSFunction> method = Handle<JSFunction>::cast(property); |
| 9690 | Handle<Object> result; |
| 9691 | // Do not check against pending exception, since this function may be called |
| 9692 | // when an exception has already been pending. |
| 9693 | if (!Execution::TryCall(method, script_wrapper, 0, NULL).ToHandle(&result)) { |
| 9694 | return isolate->factory()->undefined_value(); |
| 9695 | } |
| 9696 | return result; |
| 9697 | } |
| 9698 | |
| 9699 | |
| 9700 | // Wrappers for scripts are kept alive and cached in weak global |
| 9701 | // handles referred from foreign objects held by the scripts as long as |
| 9702 | // they are used. When they are not used anymore, the garbage |
| 9703 | // collector will call the weak callback on the global handle |
| 9704 | // associated with the wrapper and get rid of both the wrapper and the |
| 9705 | // handle. |
| 9706 | static void ClearWrapperCacheWeakCallback( |
| 9707 | const v8::WeakCallbackData<v8::Value, void>& data) { |
| 9708 | Object** location = reinterpret_cast<Object**>(data.GetParameter()); |
| 9709 | JSValue* wrapper = JSValue::cast(*location); |
| 9710 | Script::cast(wrapper->value())->ClearWrapperCache(); |
| 9711 | } |
| 9712 | |
| 9713 | |
| 9714 | void Script::ClearWrapperCache() { |
| 9715 | Foreign* foreign = wrapper(); |
| 9716 | Object** location = reinterpret_cast<Object**>(foreign->foreign_address()); |
| 9717 | DCHECK_EQ(foreign->foreign_address(), reinterpret_cast<Address>(location)); |
| 9718 | foreign->set_foreign_address(0); |
| 9719 | GlobalHandles::Destroy(location); |
| 9720 | GetIsolate()->counters()->script_wrappers()->Decrement(); |
| 9721 | } |
| 9722 | |
| 9723 | |
| 9724 | Handle<JSObject> Script::GetWrapper(Handle<Script> script) { |
| 9725 | if (script->wrapper()->foreign_address() != NULL) { |
| 9726 | // Return a handle for the existing script wrapper from the cache. |
| 9727 | return Handle<JSValue>( |
| 9728 | *reinterpret_cast<JSValue**>(script->wrapper()->foreign_address())); |
| 9729 | } |
| 9730 | Isolate* isolate = script->GetIsolate(); |
| 9731 | // Construct a new script wrapper. |
| 9732 | isolate->counters()->script_wrappers()->Increment(); |
| 9733 | Handle<JSFunction> constructor = isolate->script_function(); |
| 9734 | Handle<JSValue> result = |
| 9735 | Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor)); |
| 9736 | |
| 9737 | result->set_value(*script); |
| 9738 | |
| 9739 | // Create a new weak global handle and use it to cache the wrapper |
| 9740 | // for future use. The cache will automatically be cleared by the |
| 9741 | // garbage collector when it is not used anymore. |
| 9742 | Handle<Object> handle = isolate->global_handles()->Create(*result); |
| 9743 | GlobalHandles::MakeWeak(handle.location(), |
| 9744 | reinterpret_cast<void*>(handle.location()), |
| 9745 | &ClearWrapperCacheWeakCallback); |
| 9746 | script->wrapper()->set_foreign_address( |
| 9747 | reinterpret_cast<Address>(handle.location())); |
| 9748 | return result; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9749 | } |
| 9750 | |
| 9751 | |
Ben Murdoch | f87a203 | 2010-10-22 12:50:53 +0100 | [diff] [blame] | 9752 | String* SharedFunctionInfo::DebugName() { |
| 9753 | Object* n = name(); |
| 9754 | if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name(); |
| 9755 | return String::cast(n); |
| 9756 | } |
| 9757 | |
| 9758 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9759 | bool SharedFunctionInfo::HasSourceCode() const { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9760 | return !script()->IsUndefined() && |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 9761 | !reinterpret_cast<Script*>(script())->source()->IsUndefined(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9762 | } |
| 9763 | |
| 9764 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9765 | Handle<Object> SharedFunctionInfo::GetSourceCode() { |
| 9766 | if (!HasSourceCode()) return GetIsolate()->factory()->undefined_value(); |
| 9767 | Handle<String> source(String::cast(Script::cast(script())->source())); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9768 | return GetIsolate()->factory()->NewSubString( |
| 9769 | source, start_position(), end_position()); |
| 9770 | } |
| 9771 | |
| 9772 | |
| 9773 | bool SharedFunctionInfo::IsInlineable() { |
| 9774 | // Check that the function has a script associated with it. |
| 9775 | if (!script()->IsScript()) return false; |
| 9776 | if (optimization_disabled()) return false; |
| 9777 | // If we never ran this (unlikely) then lets try to optimize it. |
| 9778 | if (code()->kind() != Code::FUNCTION) return true; |
| 9779 | return code()->optimizable(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9780 | } |
| 9781 | |
| 9782 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9783 | int SharedFunctionInfo::SourceSize() { |
| 9784 | return end_position() - start_position(); |
| 9785 | } |
| 9786 | |
| 9787 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9788 | int SharedFunctionInfo::CalculateInstanceSize() { |
| 9789 | int instance_size = |
| 9790 | JSObject::kHeaderSize + |
| 9791 | expected_nof_properties() * kPointerSize; |
| 9792 | if (instance_size > JSObject::kMaxInstanceSize) { |
| 9793 | instance_size = JSObject::kMaxInstanceSize; |
| 9794 | } |
| 9795 | return instance_size; |
| 9796 | } |
| 9797 | |
| 9798 | |
| 9799 | int SharedFunctionInfo::CalculateInObjectProperties() { |
| 9800 | return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize; |
| 9801 | } |
| 9802 | |
| 9803 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9804 | // Output the source code without any allocation in the heap. |
| 9805 | OStream& operator<<(OStream& os, const SourceCodeOf& v) { |
| 9806 | const SharedFunctionInfo* s = v.value; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9807 | // For some native functions there is no source. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9808 | if (!s->HasSourceCode()) return os << "<No Source>"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9809 | |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 9810 | // Get the source for the script which this function came from. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9811 | // Don't use String::cast because we don't want more assertion errors while |
| 9812 | // we are already creating a stack dump. |
| 9813 | String* script_source = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9814 | reinterpret_cast<String*>(Script::cast(s->script())->source()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9815 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9816 | if (!script_source->LooksValid()) return os << "<Invalid Source>"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9817 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9818 | if (!s->is_toplevel()) { |
| 9819 | os << "function "; |
| 9820 | Object* name = s->name(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9821 | if (name->IsString() && String::cast(name)->length() > 0) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9822 | String::cast(name)->PrintUC16(os); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9823 | } |
| 9824 | } |
| 9825 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9826 | int len = s->end_position() - s->start_position(); |
| 9827 | if (len <= v.max_length || v.max_length < 0) { |
| 9828 | script_source->PrintUC16(os, s->start_position(), s->end_position()); |
| 9829 | return os; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9830 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9831 | script_source->PrintUC16(os, s->start_position(), |
| 9832 | s->start_position() + v.max_length); |
| 9833 | return os << "...\n"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 9834 | } |
| 9835 | } |
| 9836 | |
| 9837 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9838 | static bool IsCodeEquivalent(Code* code, Code* recompiled) { |
| 9839 | if (code->instruction_size() != recompiled->instruction_size()) return false; |
| 9840 | ByteArray* code_relocation = code->relocation_info(); |
| 9841 | ByteArray* recompiled_relocation = recompiled->relocation_info(); |
| 9842 | int length = code_relocation->length(); |
| 9843 | if (length != recompiled_relocation->length()) return false; |
| 9844 | int compare = memcmp(code_relocation->GetDataStartAddress(), |
| 9845 | recompiled_relocation->GetDataStartAddress(), |
| 9846 | length); |
| 9847 | return compare == 0; |
| 9848 | } |
| 9849 | |
| 9850 | |
| 9851 | void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9852 | DCHECK(!has_deoptimization_support()); |
| 9853 | DisallowHeapAllocation no_allocation; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9854 | Code* code = this->code(); |
| 9855 | if (IsCodeEquivalent(code, recompiled)) { |
| 9856 | // Copy the deoptimization data from the recompiled code. |
| 9857 | code->set_deoptimization_data(recompiled->deoptimization_data()); |
| 9858 | code->set_has_deoptimization_support(true); |
| 9859 | } else { |
| 9860 | // TODO(3025757): In case the recompiled isn't equivalent to the |
| 9861 | // old code, we have to replace it. We should try to avoid this |
| 9862 | // altogether because it flushes valuable type feedback by |
| 9863 | // effectively resetting all IC state. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9864 | ReplaceCode(recompiled); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9865 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9866 | DCHECK(has_deoptimization_support()); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9867 | } |
| 9868 | |
| 9869 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9870 | void SharedFunctionInfo::DisableOptimization(BailoutReason reason) { |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 9871 | // Disable optimization for the shared function info and mark the |
| 9872 | // code as non-optimizable. The marker on the shared function info |
| 9873 | // is there because we flush non-optimized code thereby loosing the |
| 9874 | // non-optimizable information for the code. When the code is |
| 9875 | // regenerated and set on the shared function info it is marked as |
| 9876 | // non-optimizable if optimization is disabled for the shared |
| 9877 | // function info. |
| 9878 | set_optimization_disabled(true); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9879 | set_bailout_reason(reason); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 9880 | // Code should be the lazy compilation stub or else unoptimized. If the |
| 9881 | // latter, disable optimization for the code too. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9882 | DCHECK(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 9883 | if (code()->kind() == Code::FUNCTION) { |
| 9884 | code()->set_optimizable(false); |
| 9885 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9886 | PROFILE(GetIsolate(), CodeDisableOptEvent(code(), this)); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 9887 | if (FLAG_trace_opt) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9888 | PrintF("[disabled optimization for "); |
| 9889 | ShortPrint(); |
| 9890 | PrintF(", reason: %s]\n", GetBailoutReason(reason)); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 9891 | } |
| 9892 | } |
| 9893 | |
| 9894 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9895 | bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) { |
| 9896 | DCHECK(!id.IsNone()); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9897 | Code* unoptimized = code(); |
| 9898 | DeoptimizationOutputData* data = |
| 9899 | DeoptimizationOutputData::cast(unoptimized->deoptimization_data()); |
| 9900 | unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this); |
| 9901 | USE(ignore); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9902 | return true; // Return true if there was no DCHECK. |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 9903 | } |
| 9904 | |
| 9905 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9906 | void JSFunction::StartInobjectSlackTracking() { |
| 9907 | DCHECK(has_initial_map() && !IsInobjectSlackTrackingInProgress()); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9908 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9909 | if (!FLAG_clever_optimizations) return; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9910 | Map* map = initial_map(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9911 | |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9912 | // Only initiate the tracking the first time. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9913 | if (map->done_inobject_slack_tracking()) return; |
| 9914 | map->set_done_inobject_slack_tracking(true); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9915 | |
| 9916 | // No tracking during the snapshot construction phase. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9917 | Isolate* isolate = GetIsolate(); |
| 9918 | if (isolate->serializer_enabled()) return; |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9919 | |
| 9920 | if (map->unused_property_fields() == 0) return; |
| 9921 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9922 | map->set_construction_count(kGenerousAllocationCount); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9923 | } |
| 9924 | |
| 9925 | |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 9926 | void SharedFunctionInfo::ResetForNewContext(int new_ic_age) { |
| 9927 | code()->ClearInlineCaches(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9928 | // If we clear ICs, we need to clear the type feedback vector too, since |
| 9929 | // CallICs are synced with a feedback vector slot. |
| 9930 | ClearTypeFeedbackInfo(); |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 9931 | set_ic_age(new_ic_age); |
| 9932 | if (code()->kind() == Code::FUNCTION) { |
| 9933 | code()->set_profiler_ticks(0); |
| 9934 | if (optimization_disabled() && |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9935 | opt_count() >= FLAG_max_opt_count) { |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 9936 | // Re-enable optimizations if they were disabled due to opt_count limit. |
| 9937 | set_optimization_disabled(false); |
| 9938 | code()->set_optimizable(true); |
| 9939 | } |
| 9940 | set_opt_count(0); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9941 | set_deopt_count(0); |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 9942 | } |
| 9943 | } |
| 9944 | |
| 9945 | |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9946 | static void GetMinInobjectSlack(Map* map, void* data) { |
| 9947 | int slack = map->unused_property_fields(); |
| 9948 | if (*reinterpret_cast<int*>(data) > slack) { |
| 9949 | *reinterpret_cast<int*>(data) = slack; |
| 9950 | } |
| 9951 | } |
| 9952 | |
| 9953 | |
| 9954 | static void ShrinkInstanceSize(Map* map, void* data) { |
| 9955 | int slack = *reinterpret_cast<int*>(data); |
| 9956 | map->set_inobject_properties(map->inobject_properties() - slack); |
| 9957 | map->set_unused_property_fields(map->unused_property_fields() - slack); |
| 9958 | map->set_instance_size(map->instance_size() - slack * kPointerSize); |
| 9959 | |
| 9960 | // Visitor id might depend on the instance size, recalculate it. |
| 9961 | map->set_visitor_id(StaticVisitorBase::GetVisitorId(map)); |
| 9962 | } |
| 9963 | |
| 9964 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9965 | void JSFunction::CompleteInobjectSlackTracking() { |
| 9966 | DCHECK(has_initial_map()); |
| 9967 | Map* map = initial_map(); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9968 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9969 | DCHECK(map->done_inobject_slack_tracking()); |
| 9970 | map->set_construction_count(kNoSlackTracking); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9971 | |
| 9972 | int slack = map->unused_property_fields(); |
| 9973 | map->TraverseTransitionTree(&GetMinInobjectSlack, &slack); |
| 9974 | if (slack != 0) { |
| 9975 | // Resize the initial map and all maps in its transition tree. |
| 9976 | map->TraverseTransitionTree(&ShrinkInstanceSize, &slack); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 9977 | } |
| 9978 | } |
| 9979 | |
| 9980 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 9981 | int SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context, |
| 9982 | BailoutId osr_ast_id) { |
| 9983 | DisallowHeapAllocation no_gc; |
| 9984 | DCHECK(native_context->IsNativeContext()); |
| 9985 | if (!FLAG_cache_optimized_code) return -1; |
| 9986 | Object* value = optimized_code_map(); |
| 9987 | if (!value->IsSmi()) { |
| 9988 | FixedArray* optimized_code_map = FixedArray::cast(value); |
| 9989 | int length = optimized_code_map->length(); |
| 9990 | Smi* osr_ast_id_smi = Smi::FromInt(osr_ast_id.ToInt()); |
| 9991 | for (int i = kEntriesStart; i < length; i += kEntryLength) { |
| 9992 | if (optimized_code_map->get(i + kContextOffset) == native_context && |
| 9993 | optimized_code_map->get(i + kOsrAstIdOffset) == osr_ast_id_smi) { |
| 9994 | return i + kCachedCodeOffset; |
| 9995 | } |
| 9996 | } |
| 9997 | if (FLAG_trace_opt) { |
| 9998 | PrintF("[didn't find optimized code in optimized code map for "); |
| 9999 | ShortPrint(); |
| 10000 | PrintF("]\n"); |
| 10001 | } |
| 10002 | } |
| 10003 | return -1; |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 10004 | } |
| 10005 | |
| 10006 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10007 | #define DECLARE_TAG(ignore1, name, ignore2) name, |
| 10008 | const char* const VisitorSynchronization::kTags[ |
| 10009 | VisitorSynchronization::kNumberOfSyncTags] = { |
| 10010 | VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) |
| 10011 | }; |
| 10012 | #undef DECLARE_TAG |
| 10013 | |
| 10014 | |
| 10015 | #define DECLARE_TAG(ignore1, ignore2, name) name, |
| 10016 | const char* const VisitorSynchronization::kTagNames[ |
| 10017 | VisitorSynchronization::kNumberOfSyncTags] = { |
| 10018 | VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) |
| 10019 | }; |
| 10020 | #undef DECLARE_TAG |
| 10021 | |
| 10022 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10023 | void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10024 | DCHECK(RelocInfo::IsCodeTarget(rinfo->rmode())); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10025 | Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); |
| 10026 | Object* old_target = target; |
| 10027 | VisitPointer(&target); |
| 10028 | CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. |
| 10029 | } |
| 10030 | |
| 10031 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10032 | void ObjectVisitor::VisitCodeAgeSequence(RelocInfo* rinfo) { |
| 10033 | DCHECK(RelocInfo::IsCodeAgeSequence(rinfo->rmode())); |
| 10034 | Object* stub = rinfo->code_age_stub(); |
| 10035 | if (stub) { |
| 10036 | VisitPointer(&stub); |
| 10037 | } |
| 10038 | } |
| 10039 | |
| 10040 | |
Steve Block | 791712a | 2010-08-27 10:21:07 +0100 | [diff] [blame] | 10041 | void ObjectVisitor::VisitCodeEntry(Address entry_address) { |
| 10042 | Object* code = Code::GetObjectFromEntryAddress(entry_address); |
| 10043 | Object* old_code = code; |
| 10044 | VisitPointer(&code); |
| 10045 | if (code != old_code) { |
| 10046 | Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry(); |
| 10047 | } |
| 10048 | } |
| 10049 | |
| 10050 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10051 | void ObjectVisitor::VisitCell(RelocInfo* rinfo) { |
| 10052 | DCHECK(rinfo->rmode() == RelocInfo::CELL); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10053 | Object* cell = rinfo->target_cell(); |
| 10054 | Object* old_cell = cell; |
| 10055 | VisitPointer(&cell); |
| 10056 | if (cell != old_cell) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10057 | rinfo->set_target_cell(reinterpret_cast<Cell*>(cell)); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10058 | } |
| 10059 | } |
| 10060 | |
| 10061 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10062 | void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10063 | DCHECK((RelocInfo::IsJSReturn(rinfo->rmode()) && |
Ben Murdoch | 7f4d5bd | 2010-06-15 11:15:29 +0100 | [diff] [blame] | 10064 | rinfo->IsPatchedReturnSequence()) || |
| 10065 | (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && |
| 10066 | rinfo->IsPatchedDebugBreakSlotSequence())); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10067 | Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address()); |
| 10068 | Object* old_target = target; |
| 10069 | VisitPointer(&target); |
| 10070 | CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. |
| 10071 | } |
| 10072 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10073 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10074 | void ObjectVisitor::VisitEmbeddedPointer(RelocInfo* rinfo) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10075 | DCHECK(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); |
| 10076 | Object* p = rinfo->target_object(); |
| 10077 | VisitPointer(&p); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10078 | } |
| 10079 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10080 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10081 | void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10082 | Address p = rinfo->target_reference(); |
| 10083 | VisitExternalReference(&p); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10084 | } |
| 10085 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10086 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10087 | void Code::InvalidateRelocation() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10088 | InvalidateEmbeddedObjects(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10089 | set_relocation_info(GetHeap()->empty_byte_array()); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10090 | } |
| 10091 | |
| 10092 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10093 | void Code::InvalidateEmbeddedObjects() { |
| 10094 | Object* undefined = GetHeap()->undefined_value(); |
| 10095 | Cell* undefined_cell = GetHeap()->undefined_cell(); |
| 10096 | int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | |
| 10097 | RelocInfo::ModeMask(RelocInfo::CELL); |
| 10098 | for (RelocIterator it(this, mode_mask); !it.done(); it.next()) { |
| 10099 | RelocInfo::Mode mode = it.rinfo()->rmode(); |
| 10100 | if (mode == RelocInfo::EMBEDDED_OBJECT) { |
| 10101 | it.rinfo()->set_target_object(undefined, SKIP_WRITE_BARRIER); |
| 10102 | } else if (mode == RelocInfo::CELL) { |
| 10103 | it.rinfo()->set_target_cell(undefined_cell, SKIP_WRITE_BARRIER); |
| 10104 | } |
| 10105 | } |
| 10106 | } |
| 10107 | |
| 10108 | |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 10109 | void Code::Relocate(intptr_t delta) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10110 | for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10111 | it.rinfo()->apply(delta, SKIP_ICACHE_FLUSH); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10112 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10113 | CpuFeatures::FlushICache(instruction_start(), instruction_size()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10114 | } |
| 10115 | |
| 10116 | |
| 10117 | void Code::CopyFrom(const CodeDesc& desc) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10118 | DCHECK(Marking::Color(this) == Marking::WHITE_OBJECT); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10119 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10120 | // copy code |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10121 | CopyBytes(instruction_start(), desc.buffer, |
| 10122 | static_cast<size_t>(desc.instr_size)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10123 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10124 | // copy reloc info |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10125 | CopyBytes(relocation_start(), |
| 10126 | desc.buffer + desc.buffer_size - desc.reloc_size, |
| 10127 | static_cast<size_t>(desc.reloc_size)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10128 | |
| 10129 | // unbox handles and relocate |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 10130 | intptr_t delta = instruction_start() - desc.buffer; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10131 | int mode_mask = RelocInfo::kCodeTargetMask | |
| 10132 | RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10133 | RelocInfo::ModeMask(RelocInfo::CELL) | |
| 10134 | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10135 | RelocInfo::kApplyMask; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10136 | // Needed to find target_object and runtime_entry on X64 |
| 10137 | Assembler* origin = desc.origin; |
| 10138 | AllowDeferredHandleDereference embedding_raw_address; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10139 | for (RelocIterator it(this, mode_mask); !it.done(); it.next()) { |
| 10140 | RelocInfo::Mode mode = it.rinfo()->rmode(); |
| 10141 | if (mode == RelocInfo::EMBEDDED_OBJECT) { |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 10142 | Handle<Object> p = it.rinfo()->target_object_handle(origin); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10143 | it.rinfo()->set_target_object(*p, SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH); |
| 10144 | } else if (mode == RelocInfo::CELL) { |
| 10145 | Handle<Cell> cell = it.rinfo()->target_cell_handle(); |
| 10146 | it.rinfo()->set_target_cell(*cell, SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10147 | } else if (RelocInfo::IsCodeTarget(mode)) { |
| 10148 | // rewrite code handles in inline cache targets to direct |
| 10149 | // pointers to the first instruction in the code object |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 10150 | Handle<Object> p = it.rinfo()->target_object_handle(origin); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10151 | Code* code = Code::cast(*p); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10152 | it.rinfo()->set_target_address(code->instruction_start(), |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10153 | SKIP_WRITE_BARRIER, |
| 10154 | SKIP_ICACHE_FLUSH); |
| 10155 | } else if (RelocInfo::IsRuntimeEntry(mode)) { |
| 10156 | Address p = it.rinfo()->target_runtime_entry(origin); |
| 10157 | it.rinfo()->set_target_runtime_entry(p, SKIP_WRITE_BARRIER, |
| 10158 | SKIP_ICACHE_FLUSH); |
| 10159 | } else if (mode == RelocInfo::CODE_AGE_SEQUENCE) { |
| 10160 | Handle<Object> p = it.rinfo()->code_age_stub_handle(origin); |
| 10161 | Code* code = Code::cast(*p); |
| 10162 | it.rinfo()->set_code_age_stub(code, SKIP_ICACHE_FLUSH); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10163 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10164 | it.rinfo()->apply(delta, SKIP_ICACHE_FLUSH); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10165 | } |
| 10166 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10167 | CpuFeatures::FlushICache(instruction_start(), instruction_size()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10168 | } |
| 10169 | |
| 10170 | |
| 10171 | // Locate the source position which is closest to the address in the code. This |
| 10172 | // is using the source position information embedded in the relocation info. |
| 10173 | // The position returned is relative to the beginning of the script where the |
| 10174 | // source for this function is found. |
| 10175 | int Code::SourcePosition(Address pc) { |
| 10176 | int distance = kMaxInt; |
| 10177 | int position = RelocInfo::kNoPosition; // Initially no position found. |
| 10178 | // Run through all the relocation info to find the best matching source |
| 10179 | // position. All the code needs to be considered as the sequence of the |
| 10180 | // instructions in the code does not necessarily follow the same order as the |
| 10181 | // source. |
| 10182 | RelocIterator it(this, RelocInfo::kPositionMask); |
| 10183 | while (!it.done()) { |
| 10184 | // Only look at positions after the current pc. |
| 10185 | if (it.rinfo()->pc() < pc) { |
| 10186 | // Get position and distance. |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 10187 | |
| 10188 | int dist = static_cast<int>(pc - it.rinfo()->pc()); |
| 10189 | int pos = static_cast<int>(it.rinfo()->data()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10190 | // If this position is closer than the current candidate or if it has the |
| 10191 | // same distance as the current candidate and the position is higher then |
| 10192 | // this position is the new candidate. |
| 10193 | if ((dist < distance) || |
| 10194 | (dist == distance && pos > position)) { |
| 10195 | position = pos; |
| 10196 | distance = dist; |
| 10197 | } |
| 10198 | } |
| 10199 | it.next(); |
| 10200 | } |
| 10201 | return position; |
| 10202 | } |
| 10203 | |
| 10204 | |
| 10205 | // Same as Code::SourcePosition above except it only looks for statement |
| 10206 | // positions. |
| 10207 | int Code::SourceStatementPosition(Address pc) { |
| 10208 | // First find the position as close as possible using all position |
| 10209 | // information. |
| 10210 | int position = SourcePosition(pc); |
| 10211 | // Now find the closest statement position before the position. |
| 10212 | int statement_position = 0; |
| 10213 | RelocIterator it(this, RelocInfo::kPositionMask); |
| 10214 | while (!it.done()) { |
| 10215 | if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) { |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 10216 | int p = static_cast<int>(it.rinfo()->data()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10217 | if (statement_position < p && p <= position) { |
| 10218 | statement_position = p; |
| 10219 | } |
| 10220 | } |
| 10221 | it.next(); |
| 10222 | } |
| 10223 | return statement_position; |
| 10224 | } |
| 10225 | |
| 10226 | |
Ben Murdoch | b8e0da2 | 2011-05-16 14:20:40 +0100 | [diff] [blame] | 10227 | SafepointEntry Code::GetSafepointEntry(Address pc) { |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10228 | SafepointTable table(this); |
Ben Murdoch | b8e0da2 | 2011-05-16 14:20:40 +0100 | [diff] [blame] | 10229 | return table.FindEntry(pc); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10230 | } |
| 10231 | |
| 10232 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10233 | Object* Code::FindNthObject(int n, Map* match_map) { |
| 10234 | DCHECK(is_inline_cache_stub()); |
| 10235 | DisallowHeapAllocation no_allocation; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10236 | int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); |
| 10237 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10238 | RelocInfo* info = it.rinfo(); |
| 10239 | Object* object = info->target_object(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10240 | if (object->IsHeapObject()) { |
| 10241 | if (HeapObject::cast(object)->map() == match_map) { |
| 10242 | if (--n == 0) return object; |
| 10243 | } |
| 10244 | } |
| 10245 | } |
| 10246 | return NULL; |
| 10247 | } |
| 10248 | |
| 10249 | |
| 10250 | AllocationSite* Code::FindFirstAllocationSite() { |
| 10251 | Object* result = FindNthObject(1, GetHeap()->allocation_site_map()); |
| 10252 | return (result != NULL) ? AllocationSite::cast(result) : NULL; |
| 10253 | } |
| 10254 | |
| 10255 | |
| 10256 | Map* Code::FindFirstMap() { |
| 10257 | Object* result = FindNthObject(1, GetHeap()->meta_map()); |
| 10258 | return (result != NULL) ? Map::cast(result) : NULL; |
| 10259 | } |
| 10260 | |
| 10261 | |
| 10262 | void Code::FindAndReplace(const FindAndReplacePattern& pattern) { |
| 10263 | DCHECK(is_inline_cache_stub() || is_handler()); |
| 10264 | DisallowHeapAllocation no_allocation; |
| 10265 | int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); |
| 10266 | STATIC_ASSERT(FindAndReplacePattern::kMaxCount < 32); |
| 10267 | int current_pattern = 0; |
| 10268 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10269 | RelocInfo* info = it.rinfo(); |
| 10270 | Object* object = info->target_object(); |
| 10271 | if (object->IsHeapObject()) { |
| 10272 | Map* map = HeapObject::cast(object)->map(); |
| 10273 | if (map == *pattern.find_[current_pattern]) { |
| 10274 | info->set_target_object(*pattern.replace_[current_pattern]); |
| 10275 | if (++current_pattern == pattern.count_) return; |
| 10276 | } |
| 10277 | } |
| 10278 | } |
| 10279 | UNREACHABLE(); |
| 10280 | } |
| 10281 | |
| 10282 | |
| 10283 | void Code::FindAllMaps(MapHandleList* maps) { |
| 10284 | DCHECK(is_inline_cache_stub()); |
| 10285 | DisallowHeapAllocation no_allocation; |
| 10286 | int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); |
| 10287 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10288 | RelocInfo* info = it.rinfo(); |
| 10289 | Object* object = info->target_object(); |
| 10290 | if (object->IsMap()) maps->Add(handle(Map::cast(object))); |
| 10291 | } |
| 10292 | } |
| 10293 | |
| 10294 | |
| 10295 | Code* Code::FindFirstHandler() { |
| 10296 | DCHECK(is_inline_cache_stub()); |
| 10297 | DisallowHeapAllocation no_allocation; |
| 10298 | int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); |
| 10299 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10300 | RelocInfo* info = it.rinfo(); |
| 10301 | Code* code = Code::GetCodeFromTargetAddress(info->target_address()); |
| 10302 | if (code->kind() == Code::HANDLER) return code; |
| 10303 | } |
| 10304 | return NULL; |
| 10305 | } |
| 10306 | |
| 10307 | |
| 10308 | bool Code::FindHandlers(CodeHandleList* code_list, int length) { |
| 10309 | DCHECK(is_inline_cache_stub()); |
| 10310 | DisallowHeapAllocation no_allocation; |
| 10311 | int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); |
| 10312 | int i = 0; |
| 10313 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10314 | if (i == length) return true; |
| 10315 | RelocInfo* info = it.rinfo(); |
| 10316 | Code* code = Code::GetCodeFromTargetAddress(info->target_address()); |
| 10317 | // IC stubs with handlers never contain non-handler code objects before |
| 10318 | // handler targets. |
| 10319 | if (code->kind() != Code::HANDLER) break; |
| 10320 | code_list->Add(Handle<Code>(code)); |
| 10321 | i++; |
| 10322 | } |
| 10323 | return i == length; |
| 10324 | } |
| 10325 | |
| 10326 | |
| 10327 | MaybeHandle<Code> Code::FindHandlerForMap(Map* map) { |
| 10328 | DCHECK(is_inline_cache_stub()); |
| 10329 | int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | |
| 10330 | RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); |
| 10331 | bool return_next = false; |
| 10332 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10333 | RelocInfo* info = it.rinfo(); |
| 10334 | if (info->rmode() == RelocInfo::EMBEDDED_OBJECT) { |
| 10335 | Object* object = info->target_object(); |
| 10336 | if (object == map) return_next = true; |
| 10337 | } else if (return_next) { |
| 10338 | Code* code = Code::GetCodeFromTargetAddress(info->target_address()); |
| 10339 | DCHECK(code->kind() == Code::HANDLER); |
| 10340 | return handle(code); |
| 10341 | } |
| 10342 | } |
| 10343 | return MaybeHandle<Code>(); |
| 10344 | } |
| 10345 | |
| 10346 | |
| 10347 | Name* Code::FindFirstName() { |
| 10348 | DCHECK(is_inline_cache_stub()); |
| 10349 | DisallowHeapAllocation no_allocation; |
| 10350 | int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); |
| 10351 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10352 | RelocInfo* info = it.rinfo(); |
| 10353 | Object* object = info->target_object(); |
| 10354 | if (object->IsName()) return Name::cast(object); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10355 | } |
| 10356 | return NULL; |
| 10357 | } |
| 10358 | |
| 10359 | |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 10360 | void Code::ClearInlineCaches() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10361 | ClearInlineCaches(NULL); |
| 10362 | } |
| 10363 | |
| 10364 | |
| 10365 | void Code::ClearInlineCaches(Code::Kind kind) { |
| 10366 | ClearInlineCaches(&kind); |
| 10367 | } |
| 10368 | |
| 10369 | |
| 10370 | void Code::ClearInlineCaches(Code::Kind* kind) { |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 10371 | int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | |
| 10372 | RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10373 | RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID); |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 10374 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10375 | RelocInfo* info = it.rinfo(); |
| 10376 | Code* target(Code::GetCodeFromTargetAddress(info->target_address())); |
| 10377 | if (target->is_inline_cache_stub()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10378 | if (kind == NULL || *kind == target->kind()) { |
| 10379 | IC::Clear(this->GetIsolate(), info->pc(), |
| 10380 | info->host()->constant_pool()); |
| 10381 | } |
Ben Murdoch | 8f9999f | 2012-04-23 10:39:17 +0100 | [diff] [blame] | 10382 | } |
| 10383 | } |
| 10384 | } |
| 10385 | |
| 10386 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10387 | void SharedFunctionInfo::ClearTypeFeedbackInfo() { |
| 10388 | TypeFeedbackVector* vector = feedback_vector(); |
| 10389 | Heap* heap = GetHeap(); |
| 10390 | int length = vector->length(); |
| 10391 | |
| 10392 | for (int i = 0; i < length; i++) { |
| 10393 | Object* obj = vector->get(i); |
| 10394 | if (obj->IsHeapObject()) { |
| 10395 | InstanceType instance_type = |
| 10396 | HeapObject::cast(obj)->map()->instance_type(); |
| 10397 | switch (instance_type) { |
| 10398 | case ALLOCATION_SITE_TYPE: |
| 10399 | // AllocationSites are not cleared because they do not store |
| 10400 | // information that leaks. |
| 10401 | break; |
| 10402 | // Fall through... |
| 10403 | default: |
| 10404 | vector->set(i, TypeFeedbackVector::RawUninitializedSentinel(heap), |
| 10405 | SKIP_WRITE_BARRIER); |
| 10406 | } |
| 10407 | } |
| 10408 | } |
| 10409 | } |
| 10410 | |
| 10411 | |
| 10412 | BailoutId Code::TranslatePcOffsetToAstId(uint32_t pc_offset) { |
| 10413 | DisallowHeapAllocation no_gc; |
| 10414 | DCHECK(kind() == FUNCTION); |
| 10415 | BackEdgeTable back_edges(this, &no_gc); |
| 10416 | for (uint32_t i = 0; i < back_edges.length(); i++) { |
| 10417 | if (back_edges.pc_offset(i) == pc_offset) return back_edges.ast_id(i); |
| 10418 | } |
| 10419 | return BailoutId::None(); |
| 10420 | } |
| 10421 | |
| 10422 | |
| 10423 | uint32_t Code::TranslateAstIdToPcOffset(BailoutId ast_id) { |
| 10424 | DisallowHeapAllocation no_gc; |
| 10425 | DCHECK(kind() == FUNCTION); |
| 10426 | BackEdgeTable back_edges(this, &no_gc); |
| 10427 | for (uint32_t i = 0; i < back_edges.length(); i++) { |
| 10428 | if (back_edges.ast_id(i) == ast_id) return back_edges.pc_offset(i); |
| 10429 | } |
| 10430 | UNREACHABLE(); // We expect to find the back edge. |
| 10431 | return 0; |
| 10432 | } |
| 10433 | |
| 10434 | |
| 10435 | void Code::MakeCodeAgeSequenceYoung(byte* sequence, Isolate* isolate) { |
| 10436 | PatchPlatformCodeAge(isolate, sequence, kNoAgeCodeAge, NO_MARKING_PARITY); |
| 10437 | } |
| 10438 | |
| 10439 | |
| 10440 | void Code::MarkCodeAsExecuted(byte* sequence, Isolate* isolate) { |
| 10441 | PatchPlatformCodeAge(isolate, sequence, kExecutedOnceCodeAge, |
| 10442 | NO_MARKING_PARITY); |
| 10443 | } |
| 10444 | |
| 10445 | |
| 10446 | static Code::Age EffectiveAge(Code::Age age) { |
| 10447 | if (age == Code::kNotExecutedCodeAge) { |
| 10448 | // Treat that's never been executed as old immediately. |
| 10449 | age = Code::kIsOldCodeAge; |
| 10450 | } else if (age == Code::kExecutedOnceCodeAge) { |
| 10451 | // Pre-age code that has only been executed once. |
| 10452 | age = Code::kPreAgedCodeAge; |
| 10453 | } |
| 10454 | return age; |
| 10455 | } |
| 10456 | |
| 10457 | |
| 10458 | void Code::MakeOlder(MarkingParity current_parity) { |
| 10459 | byte* sequence = FindCodeAgeSequence(); |
| 10460 | if (sequence != NULL) { |
| 10461 | Age age; |
| 10462 | MarkingParity code_parity; |
| 10463 | Isolate* isolate = GetIsolate(); |
| 10464 | GetCodeAgeAndParity(isolate, sequence, &age, &code_parity); |
| 10465 | age = EffectiveAge(age); |
| 10466 | if (age != kLastCodeAge && code_parity != current_parity) { |
| 10467 | PatchPlatformCodeAge(isolate, |
| 10468 | sequence, |
| 10469 | static_cast<Age>(age + 1), |
| 10470 | current_parity); |
| 10471 | } |
| 10472 | } |
| 10473 | } |
| 10474 | |
| 10475 | |
| 10476 | bool Code::IsOld() { |
| 10477 | return GetAge() >= kIsOldCodeAge; |
| 10478 | } |
| 10479 | |
| 10480 | |
| 10481 | byte* Code::FindCodeAgeSequence() { |
| 10482 | return FLAG_age_code && |
| 10483 | prologue_offset() != Code::kPrologueOffsetNotSet && |
| 10484 | (kind() == OPTIMIZED_FUNCTION || |
| 10485 | (kind() == FUNCTION && !has_debug_break_slots())) |
| 10486 | ? instruction_start() + prologue_offset() |
| 10487 | : NULL; |
| 10488 | } |
| 10489 | |
| 10490 | |
| 10491 | Code::Age Code::GetAge() { |
| 10492 | return EffectiveAge(GetRawAge()); |
| 10493 | } |
| 10494 | |
| 10495 | |
| 10496 | Code::Age Code::GetRawAge() { |
| 10497 | byte* sequence = FindCodeAgeSequence(); |
| 10498 | if (sequence == NULL) { |
| 10499 | return kNoAgeCodeAge; |
| 10500 | } |
| 10501 | Age age; |
| 10502 | MarkingParity parity; |
| 10503 | GetCodeAgeAndParity(GetIsolate(), sequence, &age, &parity); |
| 10504 | return age; |
| 10505 | } |
| 10506 | |
| 10507 | |
| 10508 | void Code::GetCodeAgeAndParity(Code* code, Age* age, |
| 10509 | MarkingParity* parity) { |
| 10510 | Isolate* isolate = code->GetIsolate(); |
| 10511 | Builtins* builtins = isolate->builtins(); |
| 10512 | Code* stub = NULL; |
| 10513 | #define HANDLE_CODE_AGE(AGE) \ |
| 10514 | stub = *builtins->Make##AGE##CodeYoungAgainEvenMarking(); \ |
| 10515 | if (code == stub) { \ |
| 10516 | *age = k##AGE##CodeAge; \ |
| 10517 | *parity = EVEN_MARKING_PARITY; \ |
| 10518 | return; \ |
| 10519 | } \ |
| 10520 | stub = *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ |
| 10521 | if (code == stub) { \ |
| 10522 | *age = k##AGE##CodeAge; \ |
| 10523 | *parity = ODD_MARKING_PARITY; \ |
| 10524 | return; \ |
| 10525 | } |
| 10526 | CODE_AGE_LIST(HANDLE_CODE_AGE) |
| 10527 | #undef HANDLE_CODE_AGE |
| 10528 | stub = *builtins->MarkCodeAsExecutedOnce(); |
| 10529 | if (code == stub) { |
| 10530 | *age = kNotExecutedCodeAge; |
| 10531 | *parity = NO_MARKING_PARITY; |
| 10532 | return; |
| 10533 | } |
| 10534 | stub = *builtins->MarkCodeAsExecutedTwice(); |
| 10535 | if (code == stub) { |
| 10536 | *age = kExecutedOnceCodeAge; |
| 10537 | *parity = NO_MARKING_PARITY; |
| 10538 | return; |
| 10539 | } |
| 10540 | UNREACHABLE(); |
| 10541 | } |
| 10542 | |
| 10543 | |
| 10544 | Code* Code::GetCodeAgeStub(Isolate* isolate, Age age, MarkingParity parity) { |
| 10545 | Builtins* builtins = isolate->builtins(); |
| 10546 | switch (age) { |
| 10547 | #define HANDLE_CODE_AGE(AGE) \ |
| 10548 | case k##AGE##CodeAge: { \ |
| 10549 | Code* stub = parity == EVEN_MARKING_PARITY \ |
| 10550 | ? *builtins->Make##AGE##CodeYoungAgainEvenMarking() \ |
| 10551 | : *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ |
| 10552 | return stub; \ |
| 10553 | } |
| 10554 | CODE_AGE_LIST(HANDLE_CODE_AGE) |
| 10555 | #undef HANDLE_CODE_AGE |
| 10556 | case kNotExecutedCodeAge: { |
| 10557 | DCHECK(parity == NO_MARKING_PARITY); |
| 10558 | return *builtins->MarkCodeAsExecutedOnce(); |
| 10559 | } |
| 10560 | case kExecutedOnceCodeAge: { |
| 10561 | DCHECK(parity == NO_MARKING_PARITY); |
| 10562 | return *builtins->MarkCodeAsExecutedTwice(); |
| 10563 | } |
| 10564 | default: |
| 10565 | UNREACHABLE(); |
| 10566 | break; |
| 10567 | } |
| 10568 | return NULL; |
| 10569 | } |
| 10570 | |
| 10571 | |
| 10572 | void Code::PrintDeoptLocation(FILE* out, int bailout_id) { |
| 10573 | const char* last_comment = NULL; |
| 10574 | int mask = RelocInfo::ModeMask(RelocInfo::COMMENT) |
| 10575 | | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); |
| 10576 | for (RelocIterator it(this, mask); !it.done(); it.next()) { |
| 10577 | RelocInfo* info = it.rinfo(); |
| 10578 | if (info->rmode() == RelocInfo::COMMENT) { |
| 10579 | last_comment = reinterpret_cast<const char*>(info->data()); |
| 10580 | } else if (last_comment != NULL) { |
| 10581 | if ((bailout_id == Deoptimizer::GetDeoptimizationId( |
| 10582 | GetIsolate(), info->target_address(), Deoptimizer::EAGER)) || |
| 10583 | (bailout_id == Deoptimizer::GetDeoptimizationId( |
| 10584 | GetIsolate(), info->target_address(), Deoptimizer::SOFT)) || |
| 10585 | (bailout_id == Deoptimizer::GetDeoptimizationId( |
| 10586 | GetIsolate(), info->target_address(), Deoptimizer::LAZY))) { |
| 10587 | CHECK(RelocInfo::IsRuntimeEntry(info->rmode())); |
| 10588 | PrintF(out, " %s\n", last_comment); |
| 10589 | return; |
| 10590 | } |
| 10591 | } |
| 10592 | } |
| 10593 | } |
| 10594 | |
| 10595 | |
| 10596 | bool Code::CanDeoptAt(Address pc) { |
| 10597 | DeoptimizationInputData* deopt_data = |
| 10598 | DeoptimizationInputData::cast(deoptimization_data()); |
| 10599 | Address code_start_address = instruction_start(); |
| 10600 | for (int i = 0; i < deopt_data->DeoptCount(); i++) { |
| 10601 | if (deopt_data->Pc(i)->value() == -1) continue; |
| 10602 | Address address = code_start_address + deopt_data->Pc(i)->value(); |
| 10603 | if (address == pc) return true; |
| 10604 | } |
| 10605 | return false; |
| 10606 | } |
| 10607 | |
| 10608 | |
| 10609 | // Identify kind of code. |
| 10610 | const char* Code::Kind2String(Kind kind) { |
| 10611 | switch (kind) { |
| 10612 | #define CASE(name) case name: return #name; |
| 10613 | CODE_KIND_LIST(CASE) |
| 10614 | #undef CASE |
| 10615 | case NUMBER_OF_KINDS: break; |
| 10616 | } |
| 10617 | UNREACHABLE(); |
| 10618 | return NULL; |
| 10619 | } |
| 10620 | |
| 10621 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10622 | #ifdef ENABLE_DISASSEMBLER |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10623 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10624 | void DeoptimizationInputData::DeoptimizationInputDataPrint( |
| 10625 | OStream& os) { // NOLINT |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10626 | disasm::NameConverter converter; |
| 10627 | int deopt_count = DeoptCount(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10628 | os << "Deoptimization Input Data (deopt points = " << deopt_count << ")\n"; |
| 10629 | if (0 != deopt_count) { |
| 10630 | os << " index ast id argc pc"; |
| 10631 | if (FLAG_print_code_verbose) os << " commands"; |
| 10632 | os << "\n"; |
| 10633 | } |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10634 | for (int i = 0; i < deopt_count; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10635 | // TODO(svenpanne) Add some basic formatting to our streams. |
| 10636 | Vector<char> buf1 = Vector<char>::New(128); |
| 10637 | SNPrintF(buf1, "%6d %6d %6d %6d", i, AstId(i).ToInt(), |
| 10638 | ArgumentsStackHeight(i)->value(), Pc(i)->value()); |
| 10639 | os << buf1.start(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10640 | |
| 10641 | if (!FLAG_print_code_verbose) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10642 | os << "\n"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10643 | continue; |
| 10644 | } |
| 10645 | // Print details of the frame translation. |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10646 | int translation_index = TranslationIndex(i)->value(); |
| 10647 | TranslationIterator iterator(TranslationByteArray(), translation_index); |
| 10648 | Translation::Opcode opcode = |
| 10649 | static_cast<Translation::Opcode>(iterator.Next()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10650 | DCHECK(Translation::BEGIN == opcode); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10651 | int frame_count = iterator.Next(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10652 | int jsframe_count = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10653 | os << " " << Translation::StringFor(opcode) |
| 10654 | << " {frame count=" << frame_count |
| 10655 | << ", js frame count=" << jsframe_count << "}\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10656 | |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10657 | while (iterator.HasNext() && |
| 10658 | Translation::BEGIN != |
| 10659 | (opcode = static_cast<Translation::Opcode>(iterator.Next()))) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10660 | Vector<char> buf2 = Vector<char>::New(128); |
| 10661 | SNPrintF(buf2, "%27s %s ", "", Translation::StringFor(opcode)); |
| 10662 | os << buf2.start(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10663 | |
| 10664 | switch (opcode) { |
| 10665 | case Translation::BEGIN: |
| 10666 | UNREACHABLE(); |
| 10667 | break; |
| 10668 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10669 | case Translation::JS_FRAME: { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10670 | int ast_id = iterator.Next(); |
| 10671 | int function_id = iterator.Next(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10672 | unsigned height = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10673 | os << "{ast_id=" << ast_id << ", function="; |
| 10674 | if (function_id != Translation::kSelfLiteralId) { |
| 10675 | Object* function = LiteralArray()->get(function_id); |
| 10676 | os << Brief(JSFunction::cast(function)->shared()->DebugName()); |
| 10677 | } else { |
| 10678 | os << "<self>"; |
| 10679 | } |
| 10680 | os << ", height=" << height << "}"; |
| 10681 | break; |
| 10682 | } |
| 10683 | |
| 10684 | case Translation::COMPILED_STUB_FRAME: { |
| 10685 | Code::Kind stub_kind = static_cast<Code::Kind>(iterator.Next()); |
| 10686 | os << "{kind=" << stub_kind << "}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10687 | break; |
| 10688 | } |
| 10689 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10690 | case Translation::ARGUMENTS_ADAPTOR_FRAME: |
| 10691 | case Translation::CONSTRUCT_STUB_FRAME: { |
| 10692 | int function_id = iterator.Next(); |
| 10693 | JSFunction* function = |
| 10694 | JSFunction::cast(LiteralArray()->get(function_id)); |
| 10695 | unsigned height = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10696 | os << "{function=" << Brief(function->shared()->DebugName()) |
| 10697 | << ", height=" << height << "}"; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10698 | break; |
| 10699 | } |
| 10700 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10701 | case Translation::GETTER_STUB_FRAME: |
| 10702 | case Translation::SETTER_STUB_FRAME: { |
| 10703 | int function_id = iterator.Next(); |
| 10704 | JSFunction* function = |
| 10705 | JSFunction::cast(LiteralArray()->get(function_id)); |
| 10706 | os << "{function=" << Brief(function->shared()->DebugName()) << "}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10707 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10708 | } |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10709 | |
| 10710 | case Translation::REGISTER: { |
| 10711 | int reg_code = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10712 | os << "{input=" << converter.NameOfCPURegister(reg_code) << "}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10713 | break; |
| 10714 | } |
| 10715 | |
| 10716 | case Translation::INT32_REGISTER: { |
| 10717 | int reg_code = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10718 | os << "{input=" << converter.NameOfCPURegister(reg_code) << "}"; |
| 10719 | break; |
| 10720 | } |
| 10721 | |
| 10722 | case Translation::UINT32_REGISTER: { |
| 10723 | int reg_code = iterator.Next(); |
| 10724 | os << "{input=" << converter.NameOfCPURegister(reg_code) |
| 10725 | << " (unsigned)}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10726 | break; |
| 10727 | } |
| 10728 | |
| 10729 | case Translation::DOUBLE_REGISTER: { |
| 10730 | int reg_code = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10731 | os << "{input=" << DoubleRegister::AllocationIndexToString(reg_code) |
| 10732 | << "}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10733 | break; |
| 10734 | } |
| 10735 | |
| 10736 | case Translation::STACK_SLOT: { |
| 10737 | int input_slot_index = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10738 | os << "{input=" << input_slot_index << "}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10739 | break; |
| 10740 | } |
| 10741 | |
| 10742 | case Translation::INT32_STACK_SLOT: { |
| 10743 | int input_slot_index = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10744 | os << "{input=" << input_slot_index << "}"; |
| 10745 | break; |
| 10746 | } |
| 10747 | |
| 10748 | case Translation::UINT32_STACK_SLOT: { |
| 10749 | int input_slot_index = iterator.Next(); |
| 10750 | os << "{input=" << input_slot_index << " (unsigned)}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10751 | break; |
| 10752 | } |
| 10753 | |
| 10754 | case Translation::DOUBLE_STACK_SLOT: { |
| 10755 | int input_slot_index = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10756 | os << "{input=" << input_slot_index << "}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10757 | break; |
| 10758 | } |
| 10759 | |
| 10760 | case Translation::LITERAL: { |
| 10761 | unsigned literal_index = iterator.Next(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10762 | os << "{literal_id=" << literal_index << "}"; |
| 10763 | break; |
| 10764 | } |
| 10765 | |
| 10766 | case Translation::DUPLICATED_OBJECT: { |
| 10767 | int object_index = iterator.Next(); |
| 10768 | os << "{object_index=" << object_index << "}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10769 | break; |
| 10770 | } |
| 10771 | |
| 10772 | case Translation::ARGUMENTS_OBJECT: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10773 | case Translation::CAPTURED_OBJECT: { |
| 10774 | int args_length = iterator.Next(); |
| 10775 | os << "{length=" << args_length << "}"; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 10776 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10777 | } |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10778 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10779 | os << "\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10780 | } |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10781 | } |
| 10782 | } |
| 10783 | |
| 10784 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10785 | void DeoptimizationOutputData::DeoptimizationOutputDataPrint( |
| 10786 | OStream& os) { // NOLINT |
| 10787 | os << "Deoptimization Output Data (deopt points = " << this->DeoptPoints() |
| 10788 | << ")\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10789 | if (this->DeoptPoints() == 0) return; |
| 10790 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10791 | os << "ast id pc state\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10792 | for (int i = 0; i < this->DeoptPoints(); i++) { |
| 10793 | int pc_and_state = this->PcAndState(i)->value(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10794 | // TODO(svenpanne) Add some basic formatting to our streams. |
| 10795 | Vector<char> buf = Vector<char>::New(100); |
| 10796 | SNPrintF(buf, "%6d %8d %s\n", this->AstId(i).ToInt(), |
| 10797 | FullCodeGenerator::PcField::decode(pc_and_state), |
| 10798 | FullCodeGenerator::State2String( |
| 10799 | FullCodeGenerator::StateField::decode(pc_and_state))); |
| 10800 | os << buf.start(); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10801 | } |
| 10802 | } |
| 10803 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10804 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10805 | const char* Code::ICState2String(InlineCacheState state) { |
| 10806 | switch (state) { |
| 10807 | case UNINITIALIZED: return "UNINITIALIZED"; |
| 10808 | case PREMONOMORPHIC: return "PREMONOMORPHIC"; |
| 10809 | case MONOMORPHIC: return "MONOMORPHIC"; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10810 | case PROTOTYPE_FAILURE: |
| 10811 | return "PROTOTYPE_FAILURE"; |
| 10812 | case POLYMORPHIC: return "POLYMORPHIC"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10813 | case MEGAMORPHIC: return "MEGAMORPHIC"; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10814 | case GENERIC: return "GENERIC"; |
| 10815 | case DEBUG_STUB: return "DEBUG_STUB"; |
| 10816 | case DEFAULT: |
| 10817 | return "DEFAULT"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10818 | } |
| 10819 | UNREACHABLE(); |
| 10820 | return NULL; |
| 10821 | } |
| 10822 | |
| 10823 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10824 | const char* Code::StubType2String(StubType type) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10825 | switch (type) { |
| 10826 | case NORMAL: return "NORMAL"; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10827 | case FAST: return "FAST"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10828 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10829 | UNREACHABLE(); // keep the compiler happy |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10830 | return NULL; |
| 10831 | } |
| 10832 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10833 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10834 | void Code::PrintExtraICState(OStream& os, // NOLINT |
| 10835 | Kind kind, ExtraICState extra) { |
| 10836 | os << "extra_ic_state = "; |
| 10837 | if ((kind == STORE_IC || kind == KEYED_STORE_IC) && (extra == STRICT)) { |
| 10838 | os << "STRICT\n"; |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 10839 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10840 | os << extra << "\n"; |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 10841 | } |
| 10842 | } |
| 10843 | |
| 10844 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10845 | void Code::Disassemble(const char* name, OStream& os) { // NOLINT |
| 10846 | os << "kind = " << Kind2String(kind()) << "\n"; |
| 10847 | if (IsCodeStubOrIC()) { |
| 10848 | const char* n = CodeStub::MajorName(CodeStub::GetMajorKey(this), true); |
| 10849 | os << "major_key = " << (n == NULL ? "null" : n) << "\n"; |
| 10850 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10851 | if (is_inline_cache_stub()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10852 | os << "ic_state = " << ICState2String(ic_state()) << "\n"; |
| 10853 | PrintExtraICState(os, kind(), extra_ic_state()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10854 | if (ic_state() == MONOMORPHIC) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10855 | os << "type = " << StubType2String(type()) << "\n"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10856 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10857 | if (is_compare_ic_stub()) { |
| 10858 | DCHECK(CodeStub::GetMajorKey(this) == CodeStub::CompareIC); |
| 10859 | CompareICStub stub(stub_key(), GetIsolate()); |
| 10860 | os << "compare_state = " << CompareICState::GetStateName(stub.left()) |
| 10861 | << "*" << CompareICState::GetStateName(stub.right()) << " -> " |
| 10862 | << CompareICState::GetStateName(stub.state()) << "\n"; |
| 10863 | os << "compare_operation = " << Token::Name(stub.op()) << "\n"; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 10864 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10865 | } |
| 10866 | if ((name != NULL) && (name[0] != '\0')) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10867 | os << "name = " << name << "\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10868 | } |
| 10869 | if (kind() == OPTIMIZED_FUNCTION) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10870 | os << "stack_slots = " << stack_slots() << "\n"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10871 | } |
| 10872 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10873 | os << "Instructions (size = " << instruction_size() << ")\n"; |
| 10874 | // TODO(svenpanne) The Disassembler should use streams, too! |
| 10875 | { |
| 10876 | CodeTracer::Scope trace_scope(GetIsolate()->GetCodeTracer()); |
| 10877 | Disassembler::Decode(trace_scope.file(), this); |
| 10878 | } |
| 10879 | os << "\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10880 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10881 | if (kind() == FUNCTION) { |
| 10882 | DeoptimizationOutputData* data = |
| 10883 | DeoptimizationOutputData::cast(this->deoptimization_data()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10884 | data->DeoptimizationOutputDataPrint(os); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10885 | } else if (kind() == OPTIMIZED_FUNCTION) { |
| 10886 | DeoptimizationInputData* data = |
| 10887 | DeoptimizationInputData::cast(this->deoptimization_data()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10888 | data->DeoptimizationInputDataPrint(os); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10889 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10890 | os << "\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10891 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10892 | if (is_crankshafted()) { |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10893 | SafepointTable table(this); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10894 | os << "Safepoints (size = " << table.size() << ")\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10895 | for (unsigned i = 0; i < table.length(); i++) { |
| 10896 | unsigned pc_offset = table.GetPcOffset(i); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10897 | os << (instruction_start() + pc_offset) << " "; |
| 10898 | // TODO(svenpanne) Add some basic formatting to our streams. |
| 10899 | Vector<char> buf1 = Vector<char>::New(30); |
| 10900 | SNPrintF(buf1, "%4d", pc_offset); |
| 10901 | os << buf1.start() << " "; |
| 10902 | table.PrintEntry(i, os); |
| 10903 | os << " (sp -> fp) "; |
Ben Murdoch | b8e0da2 | 2011-05-16 14:20:40 +0100 | [diff] [blame] | 10904 | SafepointEntry entry = table.GetEntry(i); |
| 10905 | if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10906 | Vector<char> buf2 = Vector<char>::New(30); |
| 10907 | SNPrintF(buf2, "%6d", entry.deoptimization_index()); |
| 10908 | os << buf2.start(); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10909 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10910 | os << "<none>"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10911 | } |
Ben Murdoch | b8e0da2 | 2011-05-16 14:20:40 +0100 | [diff] [blame] | 10912 | if (entry.argument_count() > 0) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10913 | os << " argc: " << entry.argument_count(); |
Ben Murdoch | b8e0da2 | 2011-05-16 14:20:40 +0100 | [diff] [blame] | 10914 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10915 | os << "\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10916 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10917 | os << "\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10918 | } else if (kind() == FUNCTION) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10919 | unsigned offset = back_edge_table_offset(); |
| 10920 | // If there is no back edge table, the "table start" will be at or after |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10921 | // (due to alignment) the end of the instruction stream. |
| 10922 | if (static_cast<int>(offset) < instruction_size()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10923 | DisallowHeapAllocation no_gc; |
| 10924 | BackEdgeTable back_edges(this, &no_gc); |
| 10925 | |
| 10926 | os << "Back edges (size = " << back_edges.length() << ")\n"; |
| 10927 | os << "ast_id pc_offset loop_depth\n"; |
| 10928 | |
| 10929 | for (uint32_t i = 0; i < back_edges.length(); i++) { |
| 10930 | Vector<char> buf = Vector<char>::New(100); |
| 10931 | SNPrintF(buf, "%6d %9u %10u\n", back_edges.ast_id(i).ToInt(), |
| 10932 | back_edges.pc_offset(i), back_edges.loop_depth(i)); |
| 10933 | os << buf.start(); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10934 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10935 | |
| 10936 | os << "\n"; |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10937 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10938 | #ifdef OBJECT_PRINT |
| 10939 | if (!type_feedback_info()->IsUndefined()) { |
| 10940 | OFStream os(stdout); |
| 10941 | TypeFeedbackInfo::cast(type_feedback_info())->TypeFeedbackInfoPrint(os); |
| 10942 | os << "\n"; |
| 10943 | } |
| 10944 | #endif |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 10945 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10946 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10947 | os << "RelocInfo (size = " << relocation_size() << ")\n"; |
| 10948 | for (RelocIterator it(this); !it.done(); it.next()) { |
| 10949 | it.rinfo()->Print(GetIsolate(), os); |
| 10950 | } |
| 10951 | os << "\n"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 10952 | } |
| 10953 | #endif // ENABLE_DISASSEMBLER |
| 10954 | |
| 10955 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10956 | Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength( |
| 10957 | Handle<JSObject> object, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10958 | int capacity, |
| 10959 | int length, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10960 | SetFastElementsCapacitySmiMode smi_mode) { |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 10961 | // We should never end in here with a pixel or external array. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10962 | DCHECK(!object->HasExternalArrayElements()); |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 10963 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 10964 | // Allocate a new fast elements backing store. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10965 | Handle<FixedArray> new_elements = |
| 10966 | object->GetIsolate()->factory()->NewUninitializedFixedArray(capacity); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 10967 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10968 | ElementsKind elements_kind = object->GetElementsKind(); |
| 10969 | ElementsKind new_elements_kind; |
| 10970 | // The resized array has FAST_*_SMI_ELEMENTS if the capacity mode forces it, |
| 10971 | // or if it's allowed and the old elements array contained only SMIs. |
| 10972 | bool has_fast_smi_elements = |
| 10973 | (smi_mode == kForceSmiElements) || |
| 10974 | ((smi_mode == kAllowSmiElements) && object->HasFastSmiElements()); |
| 10975 | if (has_fast_smi_elements) { |
| 10976 | if (IsHoleyElementsKind(elements_kind)) { |
| 10977 | new_elements_kind = FAST_HOLEY_SMI_ELEMENTS; |
| 10978 | } else { |
| 10979 | new_elements_kind = FAST_SMI_ELEMENTS; |
| 10980 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 10981 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 10982 | if (IsHoleyElementsKind(elements_kind)) { |
| 10983 | new_elements_kind = FAST_HOLEY_ELEMENTS; |
| 10984 | } else { |
| 10985 | new_elements_kind = FAST_ELEMENTS; |
| 10986 | } |
| 10987 | } |
| 10988 | Handle<FixedArrayBase> old_elements(object->elements()); |
| 10989 | ElementsAccessor* accessor = ElementsAccessor::ForKind(new_elements_kind); |
| 10990 | accessor->CopyElements(object, new_elements, elements_kind); |
| 10991 | |
| 10992 | if (elements_kind != SLOPPY_ARGUMENTS_ELEMENTS) { |
| 10993 | Handle<Map> new_map = (new_elements_kind != elements_kind) |
| 10994 | ? GetElementsTransitionMap(object, new_elements_kind) |
| 10995 | : handle(object->map()); |
| 10996 | JSObject::ValidateElements(object); |
| 10997 | JSObject::SetMapAndElements(object, new_map, new_elements); |
| 10998 | |
| 10999 | // Transition through the allocation site as well if present. |
| 11000 | JSObject::UpdateAllocationSite(object, new_elements_kind); |
| 11001 | } else { |
| 11002 | Handle<FixedArray> parameter_map = Handle<FixedArray>::cast(old_elements); |
| 11003 | parameter_map->set(1, *new_elements); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11004 | } |
| 11005 | |
| 11006 | if (FLAG_trace_elements_transitions) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11007 | PrintElementsTransition(stdout, object, elements_kind, old_elements, |
| 11008 | object->GetElementsKind(), new_elements); |
Ben Murdoch | 592a9fc | 2012-03-05 11:04:45 +0000 | [diff] [blame] | 11009 | } |
| 11010 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11011 | if (object->IsJSArray()) { |
| 11012 | Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length)); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11013 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11014 | return new_elements; |
| 11015 | } |
| 11016 | |
| 11017 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11018 | void JSObject::SetFastDoubleElementsCapacityAndLength(Handle<JSObject> object, |
| 11019 | int capacity, |
| 11020 | int length) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11021 | // We should never end in here with a pixel or external array. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11022 | DCHECK(!object->HasExternalArrayElements()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11023 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11024 | Handle<FixedArrayBase> elems = |
| 11025 | object->GetIsolate()->factory()->NewFixedDoubleArray(capacity); |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 11026 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11027 | ElementsKind elements_kind = object->GetElementsKind(); |
| 11028 | CHECK(elements_kind != SLOPPY_ARGUMENTS_ELEMENTS); |
| 11029 | ElementsKind new_elements_kind = elements_kind; |
| 11030 | if (IsHoleyElementsKind(elements_kind)) { |
| 11031 | new_elements_kind = FAST_HOLEY_DOUBLE_ELEMENTS; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11032 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11033 | new_elements_kind = FAST_DOUBLE_ELEMENTS; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11034 | } |
| 11035 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11036 | Handle<Map> new_map = GetElementsTransitionMap(object, new_elements_kind); |
| 11037 | |
| 11038 | Handle<FixedArrayBase> old_elements(object->elements()); |
| 11039 | ElementsAccessor* accessor = ElementsAccessor::ForKind(FAST_DOUBLE_ELEMENTS); |
| 11040 | accessor->CopyElements(object, elems, elements_kind); |
| 11041 | |
| 11042 | JSObject::ValidateElements(object); |
| 11043 | JSObject::SetMapAndElements(object, new_map, elems); |
| 11044 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11045 | if (FLAG_trace_elements_transitions) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11046 | PrintElementsTransition(stdout, object, elements_kind, old_elements, |
| 11047 | object->GetElementsKind(), elems); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11048 | } |
Ben Murdoch | 592a9fc | 2012-03-05 11:04:45 +0000 | [diff] [blame] | 11049 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11050 | if (object->IsJSArray()) { |
| 11051 | Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length)); |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 11052 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11053 | } |
| 11054 | |
| 11055 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11056 | // static |
| 11057 | void JSArray::Initialize(Handle<JSArray> array, int capacity, int length) { |
| 11058 | DCHECK(capacity >= 0); |
| 11059 | array->GetIsolate()->factory()->NewJSArrayStorage( |
| 11060 | array, length, capacity, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); |
| 11061 | } |
| 11062 | |
| 11063 | |
| 11064 | void JSArray::Expand(Handle<JSArray> array, int required_size) { |
| 11065 | ElementsAccessor* accessor = array->GetElementsAccessor(); |
| 11066 | accessor->SetCapacityAndLength(array, required_size, required_size); |
| 11067 | } |
| 11068 | |
| 11069 | |
| 11070 | // Returns false if the passed-in index is marked non-configurable, |
| 11071 | // which will cause the ES5 truncation operation to halt, and thus |
| 11072 | // no further old values need be collected. |
| 11073 | static bool GetOldValue(Isolate* isolate, |
| 11074 | Handle<JSObject> object, |
| 11075 | uint32_t index, |
| 11076 | List<Handle<Object> >* old_values, |
| 11077 | List<uint32_t>* indices) { |
| 11078 | Maybe<PropertyAttributes> maybe = |
| 11079 | JSReceiver::GetOwnElementAttribute(object, index); |
| 11080 | DCHECK(maybe.has_value); |
| 11081 | DCHECK(maybe.value != ABSENT); |
| 11082 | if (maybe.value == DONT_DELETE) return false; |
| 11083 | Handle<Object> value; |
| 11084 | if (!JSObject::GetOwnElementAccessorPair(object, index).is_null()) { |
| 11085 | value = Handle<Object>::cast(isolate->factory()->the_hole_value()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11086 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11087 | value = Object::GetElement(isolate, object, index).ToHandleChecked(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11088 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11089 | old_values->Add(value); |
| 11090 | indices->Add(index); |
| 11091 | return true; |
| 11092 | } |
| 11093 | |
| 11094 | static void EnqueueSpliceRecord(Handle<JSArray> object, |
| 11095 | uint32_t index, |
| 11096 | Handle<JSArray> deleted, |
| 11097 | uint32_t add_count) { |
| 11098 | Isolate* isolate = object->GetIsolate(); |
| 11099 | HandleScope scope(isolate); |
| 11100 | Handle<Object> index_object = isolate->factory()->NewNumberFromUint(index); |
| 11101 | Handle<Object> add_count_object = |
| 11102 | isolate->factory()->NewNumberFromUint(add_count); |
| 11103 | |
| 11104 | Handle<Object> args[] = |
| 11105 | { object, index_object, deleted, add_count_object }; |
| 11106 | |
| 11107 | Execution::Call(isolate, |
| 11108 | Handle<JSFunction>(isolate->observers_enqueue_splice()), |
| 11109 | isolate->factory()->undefined_value(), |
| 11110 | arraysize(args), |
| 11111 | args).Assert(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11112 | } |
| 11113 | |
| 11114 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11115 | static void BeginPerformSplice(Handle<JSArray> object) { |
| 11116 | Isolate* isolate = object->GetIsolate(); |
| 11117 | HandleScope scope(isolate); |
| 11118 | Handle<Object> args[] = { object }; |
| 11119 | |
| 11120 | Execution::Call(isolate, |
| 11121 | Handle<JSFunction>(isolate->observers_begin_perform_splice()), |
| 11122 | isolate->factory()->undefined_value(), |
| 11123 | arraysize(args), |
| 11124 | args).Assert(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11125 | } |
| 11126 | |
| 11127 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11128 | static void EndPerformSplice(Handle<JSArray> object) { |
| 11129 | Isolate* isolate = object->GetIsolate(); |
| 11130 | HandleScope scope(isolate); |
| 11131 | Handle<Object> args[] = { object }; |
| 11132 | |
| 11133 | Execution::Call(isolate, |
| 11134 | Handle<JSFunction>(isolate->observers_end_perform_splice()), |
| 11135 | isolate->factory()->undefined_value(), |
| 11136 | arraysize(args), |
| 11137 | args).Assert(); |
| 11138 | } |
| 11139 | |
| 11140 | |
| 11141 | MaybeHandle<Object> JSArray::SetElementsLength( |
| 11142 | Handle<JSArray> array, |
| 11143 | Handle<Object> new_length_handle) { |
| 11144 | if (array->HasFastElements()) { |
| 11145 | // If the new array won't fit in a some non-trivial fraction of the max old |
| 11146 | // space size, then force it to go dictionary mode. |
| 11147 | int max_fast_array_size = static_cast<int>( |
| 11148 | (array->GetHeap()->MaxOldGenerationSize() / kDoubleSize) / 4); |
| 11149 | if (new_length_handle->IsNumber() && |
| 11150 | NumberToInt32(*new_length_handle) >= max_fast_array_size) { |
| 11151 | NormalizeElements(array); |
| 11152 | } |
| 11153 | } |
| 11154 | |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 11155 | // We should never end in here with a pixel or external array. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11156 | DCHECK(array->AllowsSetElementsLength()); |
| 11157 | if (!array->map()->is_observed()) { |
| 11158 | return array->GetElementsAccessor()->SetLength(array, new_length_handle); |
| 11159 | } |
| 11160 | |
| 11161 | Isolate* isolate = array->GetIsolate(); |
| 11162 | List<uint32_t> indices; |
| 11163 | List<Handle<Object> > old_values; |
| 11164 | Handle<Object> old_length_handle(array->length(), isolate); |
| 11165 | uint32_t old_length = 0; |
| 11166 | CHECK(old_length_handle->ToArrayIndex(&old_length)); |
| 11167 | uint32_t new_length = 0; |
| 11168 | CHECK(new_length_handle->ToArrayIndex(&new_length)); |
| 11169 | |
| 11170 | static const PropertyAttributes kNoAttrFilter = NONE; |
| 11171 | int num_elements = array->NumberOfOwnElements(kNoAttrFilter); |
| 11172 | if (num_elements > 0) { |
| 11173 | if (old_length == static_cast<uint32_t>(num_elements)) { |
| 11174 | // Simple case for arrays without holes. |
| 11175 | for (uint32_t i = old_length - 1; i + 1 > new_length; --i) { |
| 11176 | if (!GetOldValue(isolate, array, i, &old_values, &indices)) break; |
| 11177 | } |
| 11178 | } else { |
| 11179 | // For sparse arrays, only iterate over existing elements. |
| 11180 | // TODO(rafaelw): For fast, sparse arrays, we can avoid iterating over |
| 11181 | // the to-be-removed indices twice. |
| 11182 | Handle<FixedArray> keys = isolate->factory()->NewFixedArray(num_elements); |
| 11183 | array->GetOwnElementKeys(*keys, kNoAttrFilter); |
| 11184 | while (num_elements-- > 0) { |
| 11185 | uint32_t index = NumberToUint32(keys->get(num_elements)); |
| 11186 | if (index < new_length) break; |
| 11187 | if (!GetOldValue(isolate, array, index, &old_values, &indices)) break; |
| 11188 | } |
| 11189 | } |
| 11190 | } |
| 11191 | |
| 11192 | Handle<Object> hresult; |
| 11193 | ASSIGN_RETURN_ON_EXCEPTION( |
| 11194 | isolate, hresult, |
| 11195 | array->GetElementsAccessor()->SetLength(array, new_length_handle), |
| 11196 | Object); |
| 11197 | |
| 11198 | CHECK(array->length()->ToArrayIndex(&new_length)); |
| 11199 | if (old_length == new_length) return hresult; |
| 11200 | |
| 11201 | BeginPerformSplice(array); |
| 11202 | |
| 11203 | for (int i = 0; i < indices.length(); ++i) { |
| 11204 | // For deletions where the property was an accessor, old_values[i] |
| 11205 | // will be the hole, which instructs EnqueueChangeRecord to elide |
| 11206 | // the "oldValue" property. |
| 11207 | JSObject::EnqueueChangeRecord( |
| 11208 | array, "delete", isolate->factory()->Uint32ToString(indices[i]), |
| 11209 | old_values[i]); |
| 11210 | } |
| 11211 | JSObject::EnqueueChangeRecord( |
| 11212 | array, "update", isolate->factory()->length_string(), |
| 11213 | old_length_handle); |
| 11214 | |
| 11215 | EndPerformSplice(array); |
| 11216 | |
| 11217 | uint32_t index = Min(old_length, new_length); |
| 11218 | uint32_t add_count = new_length > old_length ? new_length - old_length : 0; |
| 11219 | uint32_t delete_count = new_length < old_length ? old_length - new_length : 0; |
| 11220 | Handle<JSArray> deleted = isolate->factory()->NewJSArray(0); |
| 11221 | if (delete_count > 0) { |
| 11222 | for (int i = indices.length() - 1; i >= 0; i--) { |
| 11223 | // Skip deletions where the property was an accessor, leaving holes |
| 11224 | // in the array of old values. |
| 11225 | if (old_values[i]->IsTheHole()) continue; |
| 11226 | JSObject::SetElement( |
| 11227 | deleted, indices[i] - index, old_values[i], NONE, SLOPPY).Assert(); |
| 11228 | } |
| 11229 | |
| 11230 | SetProperty(deleted, isolate->factory()->length_string(), |
| 11231 | isolate->factory()->NewNumberFromUint(delete_count), |
| 11232 | STRICT).Assert(); |
| 11233 | } |
| 11234 | |
| 11235 | EnqueueSpliceRecord(array, index, deleted, add_count); |
| 11236 | |
| 11237 | return hresult; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11238 | } |
| 11239 | |
| 11240 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11241 | Handle<Map> Map::GetPrototypeTransition(Handle<Map> map, |
| 11242 | Handle<Object> prototype) { |
| 11243 | FixedArray* cache = map->GetPrototypeTransitions(); |
| 11244 | int number_of_transitions = map->NumberOfProtoTransitions(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11245 | const int proto_offset = |
| 11246 | kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset; |
| 11247 | const int map_offset = kProtoTransitionHeaderSize + kProtoTransitionMapOffset; |
| 11248 | const int step = kProtoTransitionElementsPerEntry; |
| 11249 | for (int i = 0; i < number_of_transitions; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11250 | if (cache->get(proto_offset + i * step) == *prototype) { |
| 11251 | Object* result = cache->get(map_offset + i * step); |
| 11252 | return Handle<Map>(Map::cast(result)); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11253 | } |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11254 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11255 | return Handle<Map>(); |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11256 | } |
| 11257 | |
| 11258 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11259 | Handle<Map> Map::PutPrototypeTransition(Handle<Map> map, |
| 11260 | Handle<Object> prototype, |
| 11261 | Handle<Map> target_map) { |
| 11262 | DCHECK(target_map->IsMap()); |
| 11263 | DCHECK(HeapObject::cast(*prototype)->map()->IsMap()); |
| 11264 | // Don't cache prototype transition if this map is either shared, or a map of |
| 11265 | // a prototype. |
| 11266 | if (map->is_prototype_map()) return map; |
| 11267 | if (map->is_dictionary_map() || !FLAG_cache_prototype_transitions) return map; |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11268 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11269 | const int step = kProtoTransitionElementsPerEntry; |
| 11270 | const int header = kProtoTransitionHeaderSize; |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11271 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11272 | Handle<FixedArray> cache(map->GetPrototypeTransitions()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11273 | int capacity = (cache->length() - header) / step; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11274 | int transitions = map->NumberOfProtoTransitions() + 1; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11275 | |
| 11276 | if (transitions > capacity) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11277 | if (capacity > kMaxCachedPrototypeTransitions) return map; |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11278 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11279 | // Grow array by factor 2 over and above what we need. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11280 | cache = FixedArray::CopySize(cache, transitions * 2 * step + header); |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11281 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11282 | SetPrototypeTransitions(map, cache); |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11283 | } |
| 11284 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11285 | // Reload number of transitions as GC might shrink them. |
| 11286 | int last = map->NumberOfProtoTransitions(); |
| 11287 | int entry = header + last * step; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11288 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11289 | cache->set(entry + kProtoTransitionPrototypeOffset, *prototype); |
| 11290 | cache->set(entry + kProtoTransitionMapOffset, *target_map); |
| 11291 | map->SetNumberOfProtoTransitions(last + 1); |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11292 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11293 | return map; |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11294 | } |
| 11295 | |
| 11296 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11297 | void Map::ZapTransitions() { |
| 11298 | TransitionArray* transition_array = transitions(); |
| 11299 | // TODO(mstarzinger): Temporarily use a slower version instead of the faster |
| 11300 | // MemsetPointer to investigate a crasher. Switch back to MemsetPointer. |
| 11301 | Object** data = transition_array->data_start(); |
| 11302 | Object* the_hole = GetHeap()->the_hole_value(); |
| 11303 | int length = transition_array->length(); |
| 11304 | for (int i = 0; i < length; i++) { |
| 11305 | data[i] = the_hole; |
| 11306 | } |
| 11307 | } |
| 11308 | |
| 11309 | |
| 11310 | void Map::ZapPrototypeTransitions() { |
| 11311 | FixedArray* proto_transitions = GetPrototypeTransitions(); |
| 11312 | MemsetPointer(proto_transitions->data_start(), |
| 11313 | GetHeap()->the_hole_value(), |
| 11314 | proto_transitions->length()); |
| 11315 | } |
| 11316 | |
| 11317 | |
| 11318 | // static |
| 11319 | void Map::AddDependentCompilationInfo(Handle<Map> map, |
| 11320 | DependentCode::DependencyGroup group, |
| 11321 | CompilationInfo* info) { |
| 11322 | Handle<DependentCode> codes = |
| 11323 | DependentCode::Insert(handle(map->dependent_code(), info->isolate()), |
| 11324 | group, info->object_wrapper()); |
| 11325 | if (*codes != map->dependent_code()) map->set_dependent_code(*codes); |
| 11326 | info->dependencies(group)->Add(map, info->zone()); |
| 11327 | } |
| 11328 | |
| 11329 | |
| 11330 | // static |
| 11331 | void Map::AddDependentCode(Handle<Map> map, |
| 11332 | DependentCode::DependencyGroup group, |
| 11333 | Handle<Code> code) { |
| 11334 | Handle<DependentCode> codes = DependentCode::Insert( |
| 11335 | Handle<DependentCode>(map->dependent_code()), group, code); |
| 11336 | if (*codes != map->dependent_code()) map->set_dependent_code(*codes); |
| 11337 | } |
| 11338 | |
| 11339 | |
| 11340 | // static |
| 11341 | void Map::AddDependentIC(Handle<Map> map, |
| 11342 | Handle<Code> stub) { |
| 11343 | DCHECK(stub->next_code_link()->IsUndefined()); |
| 11344 | int n = map->dependent_code()->number_of_entries(DependentCode::kWeakICGroup); |
| 11345 | if (n == 0) { |
| 11346 | // Slow path: insert the head of the list with possible heap allocation. |
| 11347 | Map::AddDependentCode(map, DependentCode::kWeakICGroup, stub); |
| 11348 | } else { |
| 11349 | // Fast path: link the stub to the existing head of the list without any |
| 11350 | // heap allocation. |
| 11351 | DCHECK(n == 1); |
| 11352 | map->dependent_code()->AddToDependentICList(stub); |
| 11353 | } |
| 11354 | } |
| 11355 | |
| 11356 | |
| 11357 | DependentCode::GroupStartIndexes::GroupStartIndexes(DependentCode* entries) { |
| 11358 | Recompute(entries); |
| 11359 | } |
| 11360 | |
| 11361 | |
| 11362 | void DependentCode::GroupStartIndexes::Recompute(DependentCode* entries) { |
| 11363 | start_indexes_[0] = 0; |
| 11364 | for (int g = 1; g <= kGroupCount; g++) { |
| 11365 | int count = entries->number_of_entries(static_cast<DependencyGroup>(g - 1)); |
| 11366 | start_indexes_[g] = start_indexes_[g - 1] + count; |
| 11367 | } |
| 11368 | } |
| 11369 | |
| 11370 | |
| 11371 | DependentCode* DependentCode::ForObject(Handle<HeapObject> object, |
| 11372 | DependencyGroup group) { |
| 11373 | AllowDeferredHandleDereference dependencies_are_safe; |
| 11374 | if (group == DependentCode::kPropertyCellChangedGroup) { |
| 11375 | return Handle<PropertyCell>::cast(object)->dependent_code(); |
| 11376 | } else if (group == DependentCode::kAllocationSiteTenuringChangedGroup || |
| 11377 | group == DependentCode::kAllocationSiteTransitionChangedGroup) { |
| 11378 | return Handle<AllocationSite>::cast(object)->dependent_code(); |
| 11379 | } |
| 11380 | return Handle<Map>::cast(object)->dependent_code(); |
| 11381 | } |
| 11382 | |
| 11383 | |
| 11384 | Handle<DependentCode> DependentCode::Insert(Handle<DependentCode> entries, |
| 11385 | DependencyGroup group, |
| 11386 | Handle<Object> object) { |
| 11387 | GroupStartIndexes starts(*entries); |
| 11388 | int start = starts.at(group); |
| 11389 | int end = starts.at(group + 1); |
| 11390 | int number_of_entries = starts.number_of_entries(); |
| 11391 | // Check for existing entry to avoid duplicates. |
| 11392 | for (int i = start; i < end; i++) { |
| 11393 | if (entries->object_at(i) == *object) return entries; |
| 11394 | } |
| 11395 | if (entries->length() < kCodesStartIndex + number_of_entries + 1) { |
| 11396 | int capacity = kCodesStartIndex + number_of_entries + 1; |
| 11397 | if (capacity > 5) capacity = capacity * 5 / 4; |
| 11398 | Handle<DependentCode> new_entries = Handle<DependentCode>::cast( |
| 11399 | FixedArray::CopySize(entries, capacity, TENURED)); |
| 11400 | // The number of codes can change after GC. |
| 11401 | starts.Recompute(*entries); |
| 11402 | start = starts.at(group); |
| 11403 | end = starts.at(group + 1); |
| 11404 | number_of_entries = starts.number_of_entries(); |
| 11405 | for (int i = 0; i < number_of_entries; i++) { |
| 11406 | entries->clear_at(i); |
| 11407 | } |
| 11408 | // If the old fixed array was empty, we need to reset counters of the |
| 11409 | // new array. |
| 11410 | if (number_of_entries == 0) { |
| 11411 | for (int g = 0; g < kGroupCount; g++) { |
| 11412 | new_entries->set_number_of_entries(static_cast<DependencyGroup>(g), 0); |
| 11413 | } |
| 11414 | } |
| 11415 | entries = new_entries; |
| 11416 | } |
| 11417 | entries->ExtendGroup(group); |
| 11418 | entries->set_object_at(end, *object); |
| 11419 | entries->set_number_of_entries(group, end + 1 - start); |
| 11420 | return entries; |
| 11421 | } |
| 11422 | |
| 11423 | |
| 11424 | void DependentCode::UpdateToFinishedCode(DependencyGroup group, |
| 11425 | CompilationInfo* info, |
| 11426 | Code* code) { |
| 11427 | DisallowHeapAllocation no_gc; |
| 11428 | AllowDeferredHandleDereference get_object_wrapper; |
| 11429 | Foreign* info_wrapper = *info->object_wrapper(); |
| 11430 | GroupStartIndexes starts(this); |
| 11431 | int start = starts.at(group); |
| 11432 | int end = starts.at(group + 1); |
| 11433 | for (int i = start; i < end; i++) { |
| 11434 | if (object_at(i) == info_wrapper) { |
| 11435 | set_object_at(i, code); |
| 11436 | break; |
| 11437 | } |
| 11438 | } |
| 11439 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11440 | #ifdef DEBUG |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11441 | for (int i = start; i < end; i++) { |
| 11442 | DCHECK(is_code_at(i) || compilation_info_at(i) != info); |
| 11443 | } |
| 11444 | #endif |
| 11445 | } |
| 11446 | |
| 11447 | |
| 11448 | void DependentCode::RemoveCompilationInfo(DependentCode::DependencyGroup group, |
| 11449 | CompilationInfo* info) { |
| 11450 | DisallowHeapAllocation no_allocation; |
| 11451 | AllowDeferredHandleDereference get_object_wrapper; |
| 11452 | Foreign* info_wrapper = *info->object_wrapper(); |
| 11453 | GroupStartIndexes starts(this); |
| 11454 | int start = starts.at(group); |
| 11455 | int end = starts.at(group + 1); |
| 11456 | // Find compilation info wrapper. |
| 11457 | int info_pos = -1; |
| 11458 | for (int i = start; i < end; i++) { |
| 11459 | if (object_at(i) == info_wrapper) { |
| 11460 | info_pos = i; |
| 11461 | break; |
| 11462 | } |
| 11463 | } |
| 11464 | if (info_pos == -1) return; // Not found. |
| 11465 | int gap = info_pos; |
| 11466 | // Use the last of each group to fill the gap in the previous group. |
| 11467 | for (int i = group; i < kGroupCount; i++) { |
| 11468 | int last_of_group = starts.at(i + 1) - 1; |
| 11469 | DCHECK(last_of_group >= gap); |
| 11470 | if (last_of_group == gap) continue; |
| 11471 | copy(last_of_group, gap); |
| 11472 | gap = last_of_group; |
| 11473 | } |
| 11474 | DCHECK(gap == starts.number_of_entries() - 1); |
| 11475 | clear_at(gap); // Clear last gap. |
| 11476 | set_number_of_entries(group, end - start - 1); |
| 11477 | |
| 11478 | #ifdef DEBUG |
| 11479 | for (int i = start; i < end - 1; i++) { |
| 11480 | DCHECK(is_code_at(i) || compilation_info_at(i) != info); |
| 11481 | } |
| 11482 | #endif |
| 11483 | } |
| 11484 | |
| 11485 | |
| 11486 | static bool CodeListContains(Object* head, Code* code) { |
| 11487 | while (!head->IsUndefined()) { |
| 11488 | if (head == code) return true; |
| 11489 | head = Code::cast(head)->next_code_link(); |
| 11490 | } |
| 11491 | return false; |
| 11492 | } |
| 11493 | |
| 11494 | |
| 11495 | bool DependentCode::Contains(DependencyGroup group, Code* code) { |
| 11496 | GroupStartIndexes starts(this); |
| 11497 | int start = starts.at(group); |
| 11498 | int end = starts.at(group + 1); |
| 11499 | if (group == kWeakICGroup) { |
| 11500 | return CodeListContains(object_at(start), code); |
| 11501 | } |
| 11502 | for (int i = start; i < end; i++) { |
| 11503 | if (object_at(i) == code) return true; |
| 11504 | } |
| 11505 | return false; |
| 11506 | } |
| 11507 | |
| 11508 | |
| 11509 | bool DependentCode::MarkCodeForDeoptimization( |
| 11510 | Isolate* isolate, |
| 11511 | DependentCode::DependencyGroup group) { |
| 11512 | DisallowHeapAllocation no_allocation_scope; |
| 11513 | DependentCode::GroupStartIndexes starts(this); |
| 11514 | int start = starts.at(group); |
| 11515 | int end = starts.at(group + 1); |
| 11516 | int code_entries = starts.number_of_entries(); |
| 11517 | if (start == end) return false; |
| 11518 | |
| 11519 | // Mark all the code that needs to be deoptimized. |
| 11520 | bool marked = false; |
| 11521 | for (int i = start; i < end; i++) { |
| 11522 | if (is_code_at(i)) { |
| 11523 | Code* code = code_at(i); |
| 11524 | if (!code->marked_for_deoptimization()) { |
| 11525 | SetMarkedForDeoptimization(code, group); |
| 11526 | marked = true; |
| 11527 | } |
| 11528 | } else { |
| 11529 | CompilationInfo* info = compilation_info_at(i); |
| 11530 | info->AbortDueToDependencyChange(); |
| 11531 | } |
| 11532 | } |
| 11533 | // Compact the array by moving all subsequent groups to fill in the new holes. |
| 11534 | for (int src = end, dst = start; src < code_entries; src++, dst++) { |
| 11535 | copy(src, dst); |
| 11536 | } |
| 11537 | // Now the holes are at the end of the array, zap them for heap-verifier. |
| 11538 | int removed = end - start; |
| 11539 | for (int i = code_entries - removed; i < code_entries; i++) { |
| 11540 | clear_at(i); |
| 11541 | } |
| 11542 | set_number_of_entries(group, 0); |
| 11543 | return marked; |
| 11544 | } |
| 11545 | |
| 11546 | |
| 11547 | void DependentCode::DeoptimizeDependentCodeGroup( |
| 11548 | Isolate* isolate, |
| 11549 | DependentCode::DependencyGroup group) { |
| 11550 | DCHECK(AllowCodeDependencyChange::IsAllowed()); |
| 11551 | DisallowHeapAllocation no_allocation_scope; |
| 11552 | bool marked = MarkCodeForDeoptimization(isolate, group); |
| 11553 | |
| 11554 | if (marked) Deoptimizer::DeoptimizeMarkedCode(isolate); |
| 11555 | } |
| 11556 | |
| 11557 | |
| 11558 | void DependentCode::AddToDependentICList(Handle<Code> stub) { |
| 11559 | DisallowHeapAllocation no_heap_allocation; |
| 11560 | GroupStartIndexes starts(this); |
| 11561 | int i = starts.at(kWeakICGroup); |
| 11562 | Object* head = object_at(i); |
| 11563 | // Try to insert the stub after the head of the list to minimize number of |
| 11564 | // writes to the DependentCode array, since a write to the array can make it |
| 11565 | // strong if it was alread marked by incremental marker. |
| 11566 | if (head->IsCode()) { |
| 11567 | stub->set_next_code_link(Code::cast(head)->next_code_link()); |
| 11568 | Code::cast(head)->set_next_code_link(*stub); |
| 11569 | } else { |
| 11570 | stub->set_next_code_link(head); |
| 11571 | set_object_at(i, *stub); |
| 11572 | } |
| 11573 | } |
| 11574 | |
| 11575 | |
| 11576 | void DependentCode::SetMarkedForDeoptimization(Code* code, |
| 11577 | DependencyGroup group) { |
| 11578 | code->set_marked_for_deoptimization(true); |
| 11579 | if (FLAG_trace_deopt && |
| 11580 | (code->deoptimization_data() != code->GetHeap()->empty_fixed_array())) { |
| 11581 | DeoptimizationInputData* deopt_data = |
| 11582 | DeoptimizationInputData::cast(code->deoptimization_data()); |
| 11583 | CodeTracer::Scope scope(code->GetHeap()->isolate()->GetCodeTracer()); |
| 11584 | PrintF(scope.file(), "[marking dependent code 0x%08" V8PRIxPTR |
| 11585 | " (opt #%d) for deoptimization, reason: %s]\n", |
| 11586 | reinterpret_cast<intptr_t>(code), |
| 11587 | deopt_data->OptimizationId()->value(), DependencyGroupName(group)); |
| 11588 | } |
| 11589 | } |
| 11590 | |
| 11591 | |
| 11592 | const char* DependentCode::DependencyGroupName(DependencyGroup group) { |
| 11593 | switch (group) { |
| 11594 | case kWeakICGroup: |
| 11595 | return "weak-ic"; |
| 11596 | case kWeakCodeGroup: |
| 11597 | return "weak-code"; |
| 11598 | case kTransitionGroup: |
| 11599 | return "transition"; |
| 11600 | case kPrototypeCheckGroup: |
| 11601 | return "prototype-check"; |
| 11602 | case kElementsCantBeAddedGroup: |
| 11603 | return "elements-cant-be-added"; |
| 11604 | case kPropertyCellChangedGroup: |
| 11605 | return "property-cell-changed"; |
| 11606 | case kFieldTypeGroup: |
| 11607 | return "field-type"; |
| 11608 | case kInitialMapChangedGroup: |
| 11609 | return "initial-map-changed"; |
| 11610 | case kAllocationSiteTenuringChangedGroup: |
| 11611 | return "allocation-site-tenuring-changed"; |
| 11612 | case kAllocationSiteTransitionChangedGroup: |
| 11613 | return "allocation-site-transition-changed"; |
| 11614 | } |
| 11615 | UNREACHABLE(); |
| 11616 | return "?"; |
| 11617 | } |
| 11618 | |
| 11619 | |
| 11620 | Handle<Map> Map::TransitionToPrototype(Handle<Map> map, |
| 11621 | Handle<Object> prototype) { |
| 11622 | Handle<Map> new_map = GetPrototypeTransition(map, prototype); |
| 11623 | if (new_map.is_null()) { |
| 11624 | new_map = Copy(map); |
| 11625 | PutPrototypeTransition(map, prototype, new_map); |
| 11626 | new_map->set_prototype(*prototype); |
| 11627 | } |
| 11628 | return new_map; |
| 11629 | } |
| 11630 | |
| 11631 | |
| 11632 | MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> object, |
| 11633 | Handle<Object> value, |
| 11634 | bool from_javascript) { |
| 11635 | #ifdef DEBUG |
| 11636 | int size = object->Size(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11637 | #endif |
| 11638 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11639 | Isolate* isolate = object->GetIsolate(); |
| 11640 | Heap* heap = isolate->heap(); |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11641 | // Silently ignore the change if value is not a JSObject or null. |
| 11642 | // SpiderMonkey behaves this way. |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11643 | if (!value->IsJSReceiver() && !value->IsNull()) return value; |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11644 | |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 11645 | // From 8.6.2 Object Internal Methods |
| 11646 | // ... |
| 11647 | // In addition, if [[Extensible]] is false the value of the [[Class]] and |
| 11648 | // [[Prototype]] internal properties of the object may not be modified. |
| 11649 | // ... |
| 11650 | // Implementation specific extensions that modify [[Class]], [[Prototype]] |
| 11651 | // or [[Extensible]] must not violate the invariants defined in the preceding |
| 11652 | // paragraph. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11653 | if (!object->map()->is_extensible()) { |
| 11654 | Handle<Object> args[] = { object }; |
| 11655 | THROW_NEW_ERROR(isolate, NewTypeError("non_extensible_proto", |
| 11656 | HandleVector(args, arraysize(args))), |
| 11657 | Object); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 11658 | } |
| 11659 | |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11660 | // Before we can set the prototype we need to be sure |
| 11661 | // prototype cycles are prevented. |
| 11662 | // It is sufficient to validate that the receiver is not in the new prototype |
| 11663 | // chain. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11664 | for (PrototypeIterator iter(isolate, *value, |
| 11665 | PrototypeIterator::START_AT_RECEIVER); |
| 11666 | !iter.IsAtEnd(); iter.Advance()) { |
| 11667 | if (JSReceiver::cast(iter.GetCurrent()) == *object) { |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11668 | // Cycle detected. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11669 | THROW_NEW_ERROR(isolate, |
| 11670 | NewError("cyclic_proto", HandleVector<Object>(NULL, 0)), |
| 11671 | Object); |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11672 | } |
| 11673 | } |
| 11674 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11675 | bool dictionary_elements_in_chain = |
| 11676 | object->map()->DictionaryElementsInPrototypeChainOnly(); |
| 11677 | Handle<JSObject> real_receiver = object; |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11678 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11679 | if (from_javascript) { |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11680 | // Find the first object in the chain whose prototype object is not |
| 11681 | // hidden and set the new prototype on that object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11682 | PrototypeIterator iter(isolate, real_receiver); |
| 11683 | while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { |
| 11684 | real_receiver = |
| 11685 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); |
| 11686 | iter.Advance(); |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11687 | } |
| 11688 | } |
| 11689 | |
| 11690 | // Set the new prototype of the object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11691 | Handle<Map> map(real_receiver->map()); |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11692 | |
| 11693 | // Nothing to do if prototype is already set. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11694 | if (map->prototype() == *value) return value; |
Steve Block | 053d10c | 2011-06-13 19:13:29 +0100 | [diff] [blame] | 11695 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11696 | if (value->IsJSObject()) { |
| 11697 | PrototypeOptimizationMode mode = |
| 11698 | from_javascript ? REGULAR_PROTOTYPE : FAST_PROTOTYPE; |
| 11699 | JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value), mode); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 11700 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11701 | |
| 11702 | Handle<Map> new_map = Map::TransitionToPrototype(map, value); |
| 11703 | DCHECK(new_map->prototype() == *value); |
| 11704 | JSObject::MigrateToMap(real_receiver, new_map); |
| 11705 | |
| 11706 | if (!dictionary_elements_in_chain && |
| 11707 | new_map->DictionaryElementsInPrototypeChainOnly()) { |
| 11708 | // If the prototype chain didn't previously have element callbacks, then |
| 11709 | // KeyedStoreICs need to be cleared to ensure any that involve this |
| 11710 | // map go generic. |
| 11711 | object->GetHeap()->ClearAllICsByKind(Code::KEYED_STORE_IC); |
| 11712 | } |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11713 | |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 11714 | heap->ClearInstanceofCache(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11715 | DCHECK(size == object->Size()); |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 11716 | return value; |
| 11717 | } |
| 11718 | |
| 11719 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11720 | void JSObject::EnsureCanContainElements(Handle<JSObject> object, |
| 11721 | Arguments* args, |
| 11722 | uint32_t first_arg, |
| 11723 | uint32_t arg_count, |
| 11724 | EnsureElementsMode mode) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11725 | // Elements in |Arguments| are ordered backwards (because they're on the |
| 11726 | // stack), but the method that's called here iterates over them in forward |
| 11727 | // direction. |
| 11728 | return EnsureCanContainElements( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11729 | object, args->arguments() - first_arg - (arg_count - 1), arg_count, mode); |
Ben Murdoch | 592a9fc | 2012-03-05 11:04:45 +0000 | [diff] [blame] | 11730 | } |
| 11731 | |
| 11732 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11733 | MaybeHandle<AccessorPair> JSObject::GetOwnElementAccessorPair( |
| 11734 | Handle<JSObject> object, |
| 11735 | uint32_t index) { |
| 11736 | if (object->IsJSGlobalProxy()) { |
| 11737 | PrototypeIterator iter(object->GetIsolate(), object); |
| 11738 | if (iter.IsAtEnd()) return MaybeHandle<AccessorPair>(); |
| 11739 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 11740 | return GetOwnElementAccessorPair( |
| 11741 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index); |
| 11742 | } |
| 11743 | |
| 11744 | // Check for lookup interceptor. |
| 11745 | if (object->HasIndexedInterceptor()) return MaybeHandle<AccessorPair>(); |
| 11746 | |
| 11747 | return object->GetElementsAccessor()->GetAccessorPair(object, object, index); |
| 11748 | } |
| 11749 | |
| 11750 | |
| 11751 | MaybeHandle<Object> JSObject::SetElementWithInterceptor( |
| 11752 | Handle<JSObject> object, |
| 11753 | uint32_t index, |
| 11754 | Handle<Object> value, |
| 11755 | PropertyAttributes attributes, |
| 11756 | StrictMode strict_mode, |
| 11757 | bool check_prototype, |
| 11758 | SetPropertyMode set_mode) { |
| 11759 | Isolate* isolate = object->GetIsolate(); |
| 11760 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11761 | // Make sure that the top context does not change when doing |
| 11762 | // callbacks or interceptor calls. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11763 | AssertNoContextChange ncc(isolate); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11764 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11765 | Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11766 | if (!interceptor->setter()->IsUndefined()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11767 | v8::IndexedPropertySetterCallback setter = |
| 11768 | v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter()); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 11769 | LOG(isolate, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11770 | ApiIndexedPropertyAccess("interceptor-indexed-set", *object, index)); |
| 11771 | PropertyCallbackArguments args(isolate, interceptor->data(), *object, |
| 11772 | *object); |
| 11773 | v8::Handle<v8::Value> result = |
| 11774 | args.Call(setter, index, v8::Utils::ToLocal(value)); |
| 11775 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 11776 | if (!result.IsEmpty()) return value; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11777 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11778 | |
| 11779 | return SetElementWithoutInterceptor(object, index, value, attributes, |
| 11780 | strict_mode, |
| 11781 | check_prototype, |
| 11782 | set_mode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11783 | } |
| 11784 | |
| 11785 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11786 | MaybeHandle<Object> JSObject::GetElementWithCallback( |
| 11787 | Handle<JSObject> object, |
| 11788 | Handle<Object> receiver, |
| 11789 | Handle<Object> structure, |
| 11790 | uint32_t index, |
| 11791 | Handle<Object> holder) { |
| 11792 | Isolate* isolate = object->GetIsolate(); |
| 11793 | DCHECK(!structure->IsForeign()); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11794 | // api style callbacks. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11795 | if (structure->IsExecutableAccessorInfo()) { |
| 11796 | Handle<ExecutableAccessorInfo> data = |
| 11797 | Handle<ExecutableAccessorInfo>::cast(structure); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11798 | Object* fun_obj = data->getter(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11799 | v8::AccessorNameGetterCallback call_fun = |
| 11800 | v8::ToCData<v8::AccessorNameGetterCallback>(fun_obj); |
| 11801 | if (call_fun == NULL) return isolate->factory()->undefined_value(); |
| 11802 | Handle<JSObject> holder_handle = Handle<JSObject>::cast(holder); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 11803 | Handle<Object> number = isolate->factory()->NewNumberFromUint(index); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11804 | Handle<String> key = isolate->factory()->NumberToString(number); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11805 | LOG(isolate, ApiNamedPropertyAccess("load", *holder_handle, *key)); |
| 11806 | PropertyCallbackArguments |
| 11807 | args(isolate, data->data(), *receiver, *holder_handle); |
| 11808 | v8::Handle<v8::Value> result = args.Call(call_fun, v8::Utils::ToLocal(key)); |
| 11809 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 11810 | if (result.IsEmpty()) return isolate->factory()->undefined_value(); |
| 11811 | Handle<Object> result_internal = v8::Utils::OpenHandle(*result); |
| 11812 | result_internal->VerifyApiCallResultType(); |
| 11813 | // Rebox handle before return. |
| 11814 | return handle(*result_internal, isolate); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11815 | } |
| 11816 | |
| 11817 | // __defineGetter__ callback |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11818 | if (structure->IsAccessorPair()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11819 | Handle<Object> getter(Handle<AccessorPair>::cast(structure)->getter(), |
| 11820 | isolate); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11821 | if (getter->IsSpecFunction()) { |
| 11822 | // TODO(rossberg): nicer would be to cast to some JSCallable here... |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11823 | return GetPropertyWithDefinedGetter( |
| 11824 | receiver, Handle<JSReceiver>::cast(getter)); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11825 | } |
| 11826 | // Getter is not a function. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11827 | return isolate->factory()->undefined_value(); |
| 11828 | } |
| 11829 | |
| 11830 | if (structure->IsDeclaredAccessorInfo()) { |
| 11831 | return GetDeclaredAccessorProperty( |
| 11832 | receiver, Handle<DeclaredAccessorInfo>::cast(structure), isolate); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11833 | } |
| 11834 | |
| 11835 | UNREACHABLE(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11836 | return MaybeHandle<Object>(); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11837 | } |
| 11838 | |
| 11839 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11840 | MaybeHandle<Object> JSObject::SetElementWithCallback(Handle<JSObject> object, |
| 11841 | Handle<Object> structure, |
| 11842 | uint32_t index, |
| 11843 | Handle<Object> value, |
| 11844 | Handle<JSObject> holder, |
| 11845 | StrictMode strict_mode) { |
| 11846 | Isolate* isolate = object->GetIsolate(); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11847 | |
| 11848 | // We should never get here to initialize a const with the hole |
| 11849 | // value since a const declaration would conflict with the setter. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11850 | DCHECK(!value->IsTheHole()); |
| 11851 | DCHECK(!structure->IsForeign()); |
| 11852 | if (structure->IsExecutableAccessorInfo()) { |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11853 | // api style callbacks |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11854 | Handle<ExecutableAccessorInfo> data = |
| 11855 | Handle<ExecutableAccessorInfo>::cast(structure); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11856 | Object* call_obj = data->setter(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11857 | v8::AccessorNameSetterCallback call_fun = |
| 11858 | v8::ToCData<v8::AccessorNameSetterCallback>(call_obj); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11859 | if (call_fun == NULL) return value; |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 11860 | Handle<Object> number = isolate->factory()->NewNumberFromUint(index); |
| 11861 | Handle<String> key(isolate->factory()->NumberToString(number)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11862 | LOG(isolate, ApiNamedPropertyAccess("store", *object, *key)); |
| 11863 | PropertyCallbackArguments |
| 11864 | args(isolate, data->data(), *object, *holder); |
| 11865 | args.Call(call_fun, |
| 11866 | v8::Utils::ToLocal(key), |
| 11867 | v8::Utils::ToLocal(value)); |
| 11868 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 11869 | return value; |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11870 | } |
| 11871 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11872 | if (structure->IsAccessorPair()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11873 | Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11874 | if (setter->IsSpecFunction()) { |
| 11875 | // TODO(rossberg): nicer would be to cast to some JSCallable here... |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11876 | return SetPropertyWithDefinedSetter( |
| 11877 | object, Handle<JSReceiver>::cast(setter), value); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11878 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11879 | if (strict_mode == SLOPPY) return value; |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 11880 | Handle<Object> key(isolate->factory()->NewNumberFromUint(index)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11881 | Handle<Object> args[2] = { key, holder }; |
| 11882 | THROW_NEW_ERROR( |
| 11883 | isolate, NewTypeError("no_setter_in_callback", HandleVector(args, 2)), |
| 11884 | Object); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11885 | } |
| 11886 | } |
| 11887 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11888 | // TODO(dcarney): Handle correctly. |
| 11889 | if (structure->IsDeclaredAccessorInfo()) return value; |
| 11890 | |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11891 | UNREACHABLE(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11892 | return MaybeHandle<Object>(); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 11893 | } |
| 11894 | |
| 11895 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11896 | bool JSObject::HasFastArgumentsElements() { |
| 11897 | Heap* heap = GetHeap(); |
| 11898 | if (!elements()->IsFixedArray()) return false; |
| 11899 | FixedArray* elements = FixedArray::cast(this->elements()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11900 | if (elements->map() != heap->sloppy_arguments_elements_map()) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11901 | return false; |
| 11902 | } |
| 11903 | FixedArray* arguments = FixedArray::cast(elements->get(1)); |
| 11904 | return !arguments->IsDictionary(); |
| 11905 | } |
| 11906 | |
| 11907 | |
| 11908 | bool JSObject::HasDictionaryArgumentsElements() { |
| 11909 | Heap* heap = GetHeap(); |
| 11910 | if (!elements()->IsFixedArray()) return false; |
| 11911 | FixedArray* elements = FixedArray::cast(this->elements()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11912 | if (elements->map() != heap->sloppy_arguments_elements_map()) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11913 | return false; |
| 11914 | } |
| 11915 | FixedArray* arguments = FixedArray::cast(elements->get(1)); |
| 11916 | return arguments->IsDictionary(); |
| 11917 | } |
| 11918 | |
| 11919 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11920 | // Adding n elements in fast case is O(n*n). |
| 11921 | // Note: revisit design to have dual undefined values to capture absent |
| 11922 | // elements. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11923 | MaybeHandle<Object> JSObject::SetFastElement(Handle<JSObject> object, |
| 11924 | uint32_t index, |
| 11925 | Handle<Object> value, |
| 11926 | StrictMode strict_mode, |
| 11927 | bool check_prototype) { |
| 11928 | DCHECK(object->HasFastSmiOrObjectElements() || |
| 11929 | object->HasFastArgumentsElements()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11930 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11931 | Isolate* isolate = object->GetIsolate(); |
| 11932 | |
| 11933 | // Array optimizations rely on the prototype lookups of Array objects always |
| 11934 | // returning undefined. If there is a store to the initial prototype object, |
| 11935 | // make sure all of these optimizations are invalidated. |
| 11936 | if (isolate->is_initial_object_prototype(*object) || |
| 11937 | isolate->is_initial_array_prototype(*object)) { |
| 11938 | object->map()->dependent_code()->DeoptimizeDependentCodeGroup(isolate, |
| 11939 | DependentCode::kElementsCantBeAddedGroup); |
| 11940 | } |
| 11941 | |
| 11942 | Handle<FixedArray> backing_store(FixedArray::cast(object->elements())); |
| 11943 | if (backing_store->map() == |
| 11944 | isolate->heap()->sloppy_arguments_elements_map()) { |
| 11945 | backing_store = handle(FixedArray::cast(backing_store->get(1))); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11946 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11947 | backing_store = EnsureWritableFastElements(object); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 11948 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11949 | uint32_t capacity = static_cast<uint32_t>(backing_store->length()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11950 | |
Steve Block | 9fac840 | 2011-05-12 15:51:54 +0100 | [diff] [blame] | 11951 | if (check_prototype && |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11952 | (index >= capacity || backing_store->get(index)->IsTheHole())) { |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 11953 | bool found; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11954 | MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes( |
| 11955 | object, index, value, &found, strict_mode); |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 11956 | if (found) return result; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 11957 | } |
| 11958 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11959 | uint32_t new_capacity = capacity; |
| 11960 | // Check if the length property of this object needs to be updated. |
| 11961 | uint32_t array_length = 0; |
| 11962 | bool must_update_array_length = false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11963 | bool introduces_holes = true; |
| 11964 | if (object->IsJSArray()) { |
| 11965 | CHECK(Handle<JSArray>::cast(object)->length()->ToArrayIndex(&array_length)); |
| 11966 | introduces_holes = index > array_length; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11967 | if (index >= array_length) { |
| 11968 | must_update_array_length = true; |
| 11969 | array_length = index + 1; |
| 11970 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11971 | } else { |
| 11972 | introduces_holes = index >= capacity; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11973 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11974 | |
| 11975 | // If the array is growing, and it's not growth by a single element at the |
| 11976 | // end, make sure that the ElementsKind is HOLEY. |
| 11977 | ElementsKind elements_kind = object->GetElementsKind(); |
| 11978 | if (introduces_holes && |
| 11979 | IsFastElementsKind(elements_kind) && |
| 11980 | !IsFastHoleyElementsKind(elements_kind)) { |
| 11981 | ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind); |
| 11982 | TransitionElementsKind(object, transitioned_kind); |
| 11983 | } |
| 11984 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11985 | // Check if the capacity of the backing store needs to be increased, or if |
| 11986 | // a transition to slow elements is necessary. |
| 11987 | if (index >= capacity) { |
| 11988 | bool convert_to_slow = true; |
| 11989 | if ((index - capacity) < kMaxGap) { |
| 11990 | new_capacity = NewElementsCapacity(index + 1); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11991 | DCHECK(new_capacity > index); |
| 11992 | if (!object->ShouldConvertToSlowElements(new_capacity)) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11993 | convert_to_slow = false; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 11994 | } |
| 11995 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11996 | if (convert_to_slow) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 11997 | NormalizeElements(object); |
| 11998 | return SetDictionaryElement(object, index, value, NONE, strict_mode, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 11999 | check_prototype); |
Ben Murdoch | 592a9fc | 2012-03-05 11:04:45 +0000 | [diff] [blame] | 12000 | } |
| 12001 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12002 | // Convert to fast double elements if appropriate. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12003 | if (object->HasFastSmiElements() && !value->IsSmi() && value->IsNumber()) { |
| 12004 | // Consider fixing the boilerplate as well if we have one. |
| 12005 | ElementsKind to_kind = IsHoleyElementsKind(elements_kind) |
| 12006 | ? FAST_HOLEY_DOUBLE_ELEMENTS |
| 12007 | : FAST_DOUBLE_ELEMENTS; |
| 12008 | |
| 12009 | UpdateAllocationSite(object, to_kind); |
| 12010 | |
| 12011 | SetFastDoubleElementsCapacityAndLength(object, new_capacity, array_length); |
| 12012 | FixedDoubleArray::cast(object->elements())->set(index, value->Number()); |
| 12013 | JSObject::ValidateElements(object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12014 | return value; |
| 12015 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12016 | // Change elements kind from Smi-only to generic FAST if necessary. |
| 12017 | if (object->HasFastSmiElements() && !value->IsSmi()) { |
| 12018 | ElementsKind kind = object->HasFastHoleyElements() |
| 12019 | ? FAST_HOLEY_ELEMENTS |
| 12020 | : FAST_ELEMENTS; |
| 12021 | |
| 12022 | UpdateAllocationSite(object, kind); |
| 12023 | Handle<Map> new_map = GetElementsTransitionMap(object, kind); |
| 12024 | JSObject::MigrateToMap(object, new_map); |
| 12025 | DCHECK(IsFastObjectElementsKind(object->GetElementsKind())); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12026 | } |
| 12027 | // Increase backing store capacity if that's been decided previously. |
| 12028 | if (new_capacity != capacity) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12029 | SetFastElementsCapacitySmiMode smi_mode = |
| 12030 | value->IsSmi() && object->HasFastSmiElements() |
| 12031 | ? kAllowSmiElements |
| 12032 | : kDontAllowSmiElements; |
| 12033 | Handle<FixedArray> new_elements = |
| 12034 | SetFastElementsCapacityAndLength(object, new_capacity, array_length, |
| 12035 | smi_mode); |
| 12036 | new_elements->set(index, *value); |
| 12037 | JSObject::ValidateElements(object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12038 | return value; |
| 12039 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12040 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12041 | // Finally, set the new element and length. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12042 | DCHECK(object->elements()->IsFixedArray()); |
| 12043 | backing_store->set(index, *value); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12044 | if (must_update_array_length) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12045 | Handle<JSArray>::cast(object)->set_length(Smi::FromInt(array_length)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12046 | } |
| 12047 | return value; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12048 | } |
| 12049 | |
| 12050 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12051 | MaybeHandle<Object> JSObject::SetDictionaryElement( |
| 12052 | Handle<JSObject> object, |
| 12053 | uint32_t index, |
| 12054 | Handle<Object> value, |
| 12055 | PropertyAttributes attributes, |
| 12056 | StrictMode strict_mode, |
| 12057 | bool check_prototype, |
| 12058 | SetPropertyMode set_mode) { |
| 12059 | DCHECK(object->HasDictionaryElements() || |
| 12060 | object->HasDictionaryArgumentsElements()); |
| 12061 | Isolate* isolate = object->GetIsolate(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12062 | |
| 12063 | // Insert element in the dictionary. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12064 | Handle<FixedArray> elements(FixedArray::cast(object->elements())); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12065 | bool is_arguments = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12066 | (elements->map() == isolate->heap()->sloppy_arguments_elements_map()); |
| 12067 | Handle<SeededNumberDictionary> dictionary(is_arguments |
| 12068 | ? SeededNumberDictionary::cast(elements->get(1)) |
| 12069 | : SeededNumberDictionary::cast(*elements)); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12070 | |
| 12071 | int entry = dictionary->FindEntry(index); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 12072 | if (entry != SeededNumberDictionary::kNotFound) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12073 | Handle<Object> element(dictionary->ValueAt(entry), isolate); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12074 | PropertyDetails details = dictionary->DetailsAt(entry); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12075 | if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12076 | return SetElementWithCallback(object, element, index, value, object, |
| 12077 | strict_mode); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12078 | } else { |
| 12079 | dictionary->UpdateMaxNumberKey(index); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12080 | // If a value has not been initialized we allow writing to it even if it |
| 12081 | // is read-only (a declared const that has not been initialized). If a |
| 12082 | // value is being defined we skip attribute checks completely. |
| 12083 | if (set_mode == DEFINE_PROPERTY) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12084 | details = PropertyDetails( |
| 12085 | attributes, NORMAL, details.dictionary_index()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12086 | dictionary->DetailsAtPut(entry, details); |
| 12087 | } else if (details.IsReadOnly() && !element->IsTheHole()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12088 | if (strict_mode == SLOPPY) { |
| 12089 | return isolate->factory()->undefined_value(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12090 | } else { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12091 | Handle<Object> number = isolate->factory()->NewNumberFromUint(index); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12092 | Handle<Object> args[2] = { number, object }; |
| 12093 | THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property", |
| 12094 | HandleVector(args, 2)), |
| 12095 | Object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12096 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12097 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12098 | // Elements of the arguments object in slow mode might be slow aliases. |
| 12099 | if (is_arguments && element->IsAliasedArgumentsEntry()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12100 | Handle<AliasedArgumentsEntry> entry = |
| 12101 | Handle<AliasedArgumentsEntry>::cast(element); |
| 12102 | Handle<Context> context(Context::cast(elements->get(0))); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12103 | int context_index = entry->aliased_context_slot(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12104 | DCHECK(!context->get(context_index)->IsTheHole()); |
| 12105 | context->set(context_index, *value); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12106 | // For elements that are still writable we keep slow aliasing. |
| 12107 | if (!details.IsReadOnly()) value = element; |
| 12108 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12109 | dictionary->ValueAtPut(entry, *value); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12110 | } |
| 12111 | } else { |
| 12112 | // Index not already used. Look for an accessor in the prototype chain. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12113 | // Can cause GC! |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12114 | if (check_prototype) { |
| 12115 | bool found; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12116 | MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes( |
| 12117 | object, index, value, &found, strict_mode); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12118 | if (found) return result; |
| 12119 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12120 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12121 | // When we set the is_extensible flag to false we always force the |
| 12122 | // element into dictionary mode (and force them to stay there). |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12123 | if (!object->map()->is_extensible()) { |
| 12124 | if (strict_mode == SLOPPY) { |
| 12125 | return isolate->factory()->undefined_value(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12126 | } else { |
| 12127 | Handle<Object> number = isolate->factory()->NewNumberFromUint(index); |
| 12128 | Handle<String> name = isolate->factory()->NumberToString(number); |
| 12129 | Handle<Object> args[1] = { name }; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12130 | THROW_NEW_ERROR(isolate, NewTypeError("object_not_extensible", |
| 12131 | HandleVector(args, 1)), |
| 12132 | Object); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12133 | } |
| 12134 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12135 | |
| 12136 | PropertyDetails details = PropertyDetails(attributes, NORMAL, 0); |
| 12137 | Handle<SeededNumberDictionary> new_dictionary = |
| 12138 | SeededNumberDictionary::AddNumberEntry(dictionary, index, value, |
| 12139 | details); |
| 12140 | if (*dictionary != *new_dictionary) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12141 | if (is_arguments) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12142 | elements->set(1, *new_dictionary); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12143 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12144 | object->set_elements(*new_dictionary); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12145 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12146 | dictionary = new_dictionary; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12147 | } |
| 12148 | } |
| 12149 | |
| 12150 | // Update the array length if this JSObject is an array. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12151 | if (object->IsJSArray()) { |
| 12152 | JSArray::JSArrayUpdateLengthFromIndex(Handle<JSArray>::cast(object), index, |
| 12153 | value); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12154 | } |
| 12155 | |
| 12156 | // Attempt to put this object back in fast case. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12157 | if (object->ShouldConvertToFastElements()) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12158 | uint32_t new_length = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12159 | if (object->IsJSArray()) { |
| 12160 | CHECK(Handle<JSArray>::cast(object)->length()->ToArrayIndex(&new_length)); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12161 | } else { |
| 12162 | new_length = dictionary->max_number_key() + 1; |
| 12163 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12164 | bool has_smi_only_elements = false; |
| 12165 | bool should_convert_to_fast_double_elements = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12166 | object->ShouldConvertToFastDoubleElements(&has_smi_only_elements); |
| 12167 | SetFastElementsCapacitySmiMode smi_mode = |
| 12168 | has_smi_only_elements ? kForceSmiElements : kAllowSmiElements; |
| 12169 | |
| 12170 | if (should_convert_to_fast_double_elements) { |
| 12171 | SetFastDoubleElementsCapacityAndLength(object, new_length, new_length); |
| 12172 | } else { |
| 12173 | SetFastElementsCapacityAndLength(object, new_length, new_length, |
| 12174 | smi_mode); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12175 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12176 | JSObject::ValidateElements(object); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12177 | #ifdef DEBUG |
| 12178 | if (FLAG_trace_normalization) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12179 | OFStream os(stdout); |
| 12180 | os << "Object elements are fast case again:\n"; |
| 12181 | object->Print(os); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12182 | } |
| 12183 | #endif |
| 12184 | } |
| 12185 | return value; |
| 12186 | } |
| 12187 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12188 | MaybeHandle<Object> JSObject::SetFastDoubleElement( |
| 12189 | Handle<JSObject> object, |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12190 | uint32_t index, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12191 | Handle<Object> value, |
| 12192 | StrictMode strict_mode, |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12193 | bool check_prototype) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12194 | DCHECK(object->HasFastDoubleElements()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12195 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12196 | Handle<FixedArrayBase> base_elms(FixedArrayBase::cast(object->elements())); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12197 | uint32_t elms_length = static_cast<uint32_t>(base_elms->length()); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12198 | |
| 12199 | // If storing to an element that isn't in the array, pass the store request |
| 12200 | // up the prototype chain before storing in the receiver's elements. |
| 12201 | if (check_prototype && |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12202 | (index >= elms_length || |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12203 | Handle<FixedDoubleArray>::cast(base_elms)->is_the_hole(index))) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12204 | bool found; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12205 | MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes( |
| 12206 | object, index, value, &found, strict_mode); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12207 | if (found) return result; |
| 12208 | } |
| 12209 | |
| 12210 | // If the value object is not a heap number, switch to fast elements and try |
| 12211 | // again. |
| 12212 | bool value_is_smi = value->IsSmi(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12213 | bool introduces_holes = true; |
| 12214 | uint32_t length = elms_length; |
| 12215 | if (object->IsJSArray()) { |
| 12216 | CHECK(Handle<JSArray>::cast(object)->length()->ToArrayIndex(&length)); |
| 12217 | introduces_holes = index > length; |
| 12218 | } else { |
| 12219 | introduces_holes = index >= elms_length; |
| 12220 | } |
| 12221 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12222 | if (!value->IsNumber()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12223 | SetFastElementsCapacityAndLength(object, elms_length, length, |
| 12224 | kDontAllowSmiElements); |
| 12225 | Handle<Object> result; |
| 12226 | ASSIGN_RETURN_ON_EXCEPTION( |
| 12227 | object->GetIsolate(), result, |
| 12228 | SetFastElement(object, index, value, strict_mode, check_prototype), |
| 12229 | Object); |
| 12230 | JSObject::ValidateElements(object); |
| 12231 | return result; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12232 | } |
| 12233 | |
| 12234 | double double_value = value_is_smi |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12235 | ? static_cast<double>(Handle<Smi>::cast(value)->value()) |
| 12236 | : Handle<HeapNumber>::cast(value)->value(); |
| 12237 | |
| 12238 | // If the array is growing, and it's not growth by a single element at the |
| 12239 | // end, make sure that the ElementsKind is HOLEY. |
| 12240 | ElementsKind elements_kind = object->GetElementsKind(); |
| 12241 | if (introduces_holes && !IsFastHoleyElementsKind(elements_kind)) { |
| 12242 | ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind); |
| 12243 | TransitionElementsKind(object, transitioned_kind); |
| 12244 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12245 | |
| 12246 | // Check whether there is extra space in the fixed array. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12247 | if (index < elms_length) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12248 | Handle<FixedDoubleArray> elms(FixedDoubleArray::cast(object->elements())); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12249 | elms->set(index, double_value); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12250 | if (object->IsJSArray()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12251 | // Update the length of the array if needed. |
| 12252 | uint32_t array_length = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12253 | CHECK( |
| 12254 | Handle<JSArray>::cast(object)->length()->ToArrayIndex(&array_length)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12255 | if (index >= array_length) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12256 | Handle<JSArray>::cast(object)->set_length(Smi::FromInt(index + 1)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12257 | } |
| 12258 | } |
| 12259 | return value; |
| 12260 | } |
| 12261 | |
| 12262 | // Allow gap in fast case. |
| 12263 | if ((index - elms_length) < kMaxGap) { |
| 12264 | // Try allocating extra space. |
| 12265 | int new_capacity = NewElementsCapacity(index+1); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12266 | if (!object->ShouldConvertToSlowElements(new_capacity)) { |
| 12267 | DCHECK(static_cast<uint32_t>(new_capacity) > index); |
| 12268 | SetFastDoubleElementsCapacityAndLength(object, new_capacity, index + 1); |
| 12269 | FixedDoubleArray::cast(object->elements())->set(index, double_value); |
| 12270 | JSObject::ValidateElements(object); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12271 | return value; |
| 12272 | } |
| 12273 | } |
| 12274 | |
| 12275 | // Otherwise default to slow case. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12276 | DCHECK(object->HasFastDoubleElements()); |
| 12277 | DCHECK(object->map()->has_fast_double_elements()); |
| 12278 | DCHECK(object->elements()->IsFixedDoubleArray() || |
| 12279 | object->elements()->length() == 0); |
| 12280 | |
| 12281 | NormalizeElements(object); |
| 12282 | DCHECK(object->HasDictionaryElements()); |
| 12283 | return SetElement(object, index, value, NONE, strict_mode, check_prototype); |
| 12284 | } |
| 12285 | |
| 12286 | |
| 12287 | MaybeHandle<Object> JSReceiver::SetElement(Handle<JSReceiver> object, |
| 12288 | uint32_t index, |
| 12289 | Handle<Object> value, |
| 12290 | PropertyAttributes attributes, |
| 12291 | StrictMode strict_mode) { |
| 12292 | if (object->IsJSProxy()) { |
| 12293 | return JSProxy::SetElementWithHandler( |
| 12294 | Handle<JSProxy>::cast(object), object, index, value, strict_mode); |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 12295 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12296 | return JSObject::SetElement( |
| 12297 | Handle<JSObject>::cast(object), index, value, attributes, strict_mode); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12298 | } |
| 12299 | |
| 12300 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12301 | MaybeHandle<Object> JSObject::SetOwnElement(Handle<JSObject> object, |
| 12302 | uint32_t index, |
| 12303 | Handle<Object> value, |
| 12304 | StrictMode strict_mode) { |
| 12305 | DCHECK(!object->HasExternalArrayElements()); |
| 12306 | return JSObject::SetElement(object, index, value, NONE, strict_mode, false); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12307 | } |
| 12308 | |
| 12309 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12310 | MaybeHandle<Object> JSObject::SetElement(Handle<JSObject> object, |
| 12311 | uint32_t index, |
| 12312 | Handle<Object> value, |
| 12313 | PropertyAttributes attributes, |
| 12314 | StrictMode strict_mode, |
| 12315 | bool check_prototype, |
| 12316 | SetPropertyMode set_mode) { |
| 12317 | Isolate* isolate = object->GetIsolate(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12318 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12319 | if (object->HasExternalArrayElements() || |
| 12320 | object->HasFixedTypedArrayElements()) { |
| 12321 | if (!value->IsNumber() && !value->IsUndefined()) { |
| 12322 | ASSIGN_RETURN_ON_EXCEPTION( |
| 12323 | isolate, value, |
| 12324 | Execution::ToNumber(isolate, value), Object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12325 | } |
| 12326 | } |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 12327 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12328 | // Check access rights if needed. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12329 | if (object->IsAccessCheckNeeded()) { |
| 12330 | if (!isolate->MayIndexedAccess(object, index, v8::ACCESS_SET)) { |
| 12331 | isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET); |
| 12332 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 12333 | return value; |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 12334 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12335 | } |
| 12336 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12337 | if (object->IsJSGlobalProxy()) { |
| 12338 | PrototypeIterator iter(isolate, object); |
| 12339 | if (iter.IsAtEnd()) return value; |
| 12340 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 12341 | return SetElement( |
| 12342 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index, |
| 12343 | value, attributes, strict_mode, check_prototype, set_mode); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12344 | } |
| 12345 | |
| 12346 | // Don't allow element properties to be redefined for external arrays. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12347 | if ((object->HasExternalArrayElements() || |
| 12348 | object->HasFixedTypedArrayElements()) && |
| 12349 | set_mode == DEFINE_PROPERTY) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12350 | Handle<Object> number = isolate->factory()->NewNumberFromUint(index); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12351 | Handle<Object> args[] = { object, number }; |
| 12352 | THROW_NEW_ERROR(isolate, NewTypeError("redef_external_array_element", |
| 12353 | HandleVector(args, arraysize(args))), |
| 12354 | Object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12355 | } |
| 12356 | |
| 12357 | // Normalize the elements to enable attributes on the property. |
| 12358 | if ((attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12359 | Handle<SeededNumberDictionary> dictionary = NormalizeElements(object); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12360 | // Make sure that we never go back to fast case. |
| 12361 | dictionary->set_requires_slow_elements(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12362 | } |
| 12363 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12364 | if (!object->map()->is_observed()) { |
| 12365 | return object->HasIndexedInterceptor() |
| 12366 | ? SetElementWithInterceptor(object, index, value, attributes, |
| 12367 | strict_mode, check_prototype, set_mode) |
| 12368 | : SetElementWithoutInterceptor(object, index, value, attributes, |
| 12369 | strict_mode, check_prototype, set_mode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12370 | } |
| 12371 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12372 | Maybe<PropertyAttributes> maybe = |
| 12373 | JSReceiver::GetOwnElementAttribute(object, index); |
| 12374 | if (!maybe.has_value) return MaybeHandle<Object>(); |
| 12375 | PropertyAttributes old_attributes = maybe.value; |
| 12376 | |
| 12377 | Handle<Object> old_value = isolate->factory()->the_hole_value(); |
| 12378 | Handle<Object> old_length_handle; |
| 12379 | Handle<Object> new_length_handle; |
| 12380 | |
| 12381 | if (old_attributes != ABSENT) { |
| 12382 | if (GetOwnElementAccessorPair(object, index).is_null()) { |
| 12383 | old_value = Object::GetElement(isolate, object, index).ToHandleChecked(); |
| 12384 | } |
| 12385 | } else if (object->IsJSArray()) { |
| 12386 | // Store old array length in case adding an element grows the array. |
| 12387 | old_length_handle = handle(Handle<JSArray>::cast(object)->length(), |
| 12388 | isolate); |
| 12389 | } |
| 12390 | |
| 12391 | // Check for lookup interceptor |
| 12392 | Handle<Object> result; |
| 12393 | ASSIGN_RETURN_ON_EXCEPTION( |
| 12394 | isolate, result, |
| 12395 | object->HasIndexedInterceptor() |
| 12396 | ? SetElementWithInterceptor( |
| 12397 | object, index, value, attributes, |
| 12398 | strict_mode, check_prototype, set_mode) |
| 12399 | : SetElementWithoutInterceptor( |
| 12400 | object, index, value, attributes, |
| 12401 | strict_mode, check_prototype, set_mode), |
| 12402 | Object); |
| 12403 | |
| 12404 | Handle<String> name = isolate->factory()->Uint32ToString(index); |
| 12405 | maybe = GetOwnElementAttribute(object, index); |
| 12406 | if (!maybe.has_value) return MaybeHandle<Object>(); |
| 12407 | PropertyAttributes new_attributes = maybe.value; |
| 12408 | |
| 12409 | if (old_attributes == ABSENT) { |
| 12410 | if (object->IsJSArray() && |
| 12411 | !old_length_handle->SameValue( |
| 12412 | Handle<JSArray>::cast(object)->length())) { |
| 12413 | new_length_handle = handle(Handle<JSArray>::cast(object)->length(), |
| 12414 | isolate); |
| 12415 | uint32_t old_length = 0; |
| 12416 | uint32_t new_length = 0; |
| 12417 | CHECK(old_length_handle->ToArrayIndex(&old_length)); |
| 12418 | CHECK(new_length_handle->ToArrayIndex(&new_length)); |
| 12419 | |
| 12420 | BeginPerformSplice(Handle<JSArray>::cast(object)); |
| 12421 | EnqueueChangeRecord(object, "add", name, old_value); |
| 12422 | EnqueueChangeRecord(object, "update", isolate->factory()->length_string(), |
| 12423 | old_length_handle); |
| 12424 | EndPerformSplice(Handle<JSArray>::cast(object)); |
| 12425 | Handle<JSArray> deleted = isolate->factory()->NewJSArray(0); |
| 12426 | EnqueueSpliceRecord(Handle<JSArray>::cast(object), old_length, deleted, |
| 12427 | new_length - old_length); |
| 12428 | } else { |
| 12429 | EnqueueChangeRecord(object, "add", name, old_value); |
| 12430 | } |
| 12431 | } else if (old_value->IsTheHole()) { |
| 12432 | EnqueueChangeRecord(object, "reconfigure", name, old_value); |
| 12433 | } else { |
| 12434 | Handle<Object> new_value = |
| 12435 | Object::GetElement(isolate, object, index).ToHandleChecked(); |
| 12436 | bool value_changed = !old_value->SameValue(*new_value); |
| 12437 | if (old_attributes != new_attributes) { |
| 12438 | if (!value_changed) old_value = isolate->factory()->the_hole_value(); |
| 12439 | EnqueueChangeRecord(object, "reconfigure", name, old_value); |
| 12440 | } else if (value_changed) { |
| 12441 | EnqueueChangeRecord(object, "update", name, old_value); |
| 12442 | } |
| 12443 | } |
| 12444 | |
| 12445 | return result; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12446 | } |
| 12447 | |
| 12448 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12449 | MaybeHandle<Object> JSObject::SetElementWithoutInterceptor( |
| 12450 | Handle<JSObject> object, |
| 12451 | uint32_t index, |
| 12452 | Handle<Object> value, |
| 12453 | PropertyAttributes attributes, |
| 12454 | StrictMode strict_mode, |
| 12455 | bool check_prototype, |
| 12456 | SetPropertyMode set_mode) { |
| 12457 | DCHECK(object->HasDictionaryElements() || |
| 12458 | object->HasDictionaryArgumentsElements() || |
| 12459 | (attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0); |
| 12460 | Isolate* isolate = object->GetIsolate(); |
| 12461 | if (FLAG_trace_external_array_abuse && |
| 12462 | IsExternalArrayElementsKind(object->GetElementsKind())) { |
| 12463 | CheckArrayAbuse(object, "external elements write", index); |
| 12464 | } |
| 12465 | if (FLAG_trace_js_array_abuse && |
| 12466 | !IsExternalArrayElementsKind(object->GetElementsKind())) { |
| 12467 | if (object->IsJSArray()) { |
| 12468 | CheckArrayAbuse(object, "elements write", index, true); |
| 12469 | } |
| 12470 | } |
| 12471 | if (object->IsJSArray() && JSArray::WouldChangeReadOnlyLength( |
| 12472 | Handle<JSArray>::cast(object), index)) { |
| 12473 | if (strict_mode == SLOPPY) { |
| 12474 | return value; |
| 12475 | } else { |
| 12476 | return JSArray::ReadOnlyLengthError(Handle<JSArray>::cast(object)); |
| 12477 | } |
| 12478 | } |
| 12479 | switch (object->GetElementsKind()) { |
| 12480 | case FAST_SMI_ELEMENTS: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12481 | case FAST_ELEMENTS: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12482 | case FAST_HOLEY_SMI_ELEMENTS: |
| 12483 | case FAST_HOLEY_ELEMENTS: |
| 12484 | return SetFastElement(object, index, value, strict_mode, check_prototype); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12485 | case FAST_DOUBLE_ELEMENTS: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12486 | case FAST_HOLEY_DOUBLE_ELEMENTS: |
| 12487 | return SetFastDoubleElement(object, index, value, strict_mode, |
| 12488 | check_prototype); |
| 12489 | |
| 12490 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 12491 | case EXTERNAL_##TYPE##_ELEMENTS: { \ |
| 12492 | Handle<External##Type##Array> array( \ |
| 12493 | External##Type##Array::cast(object->elements())); \ |
| 12494 | return External##Type##Array::SetValue(array, index, value); \ |
| 12495 | } \ |
| 12496 | case TYPE##_ELEMENTS: { \ |
| 12497 | Handle<Fixed##Type##Array> array( \ |
| 12498 | Fixed##Type##Array::cast(object->elements())); \ |
| 12499 | return Fixed##Type##Array::SetValue(array, index, value); \ |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12500 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12501 | |
| 12502 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 12503 | |
| 12504 | #undef TYPED_ARRAY_CASE |
| 12505 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12506 | case DICTIONARY_ELEMENTS: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12507 | return SetDictionaryElement(object, index, value, attributes, strict_mode, |
| 12508 | check_prototype, |
| 12509 | set_mode); |
| 12510 | case SLOPPY_ARGUMENTS_ELEMENTS: { |
| 12511 | Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12512 | uint32_t length = parameter_map->length(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12513 | Handle<Object> probe = index < length - 2 ? |
| 12514 | Handle<Object>(parameter_map->get(index + 2), isolate) : |
| 12515 | Handle<Object>(); |
| 12516 | if (!probe.is_null() && !probe->IsTheHole()) { |
| 12517 | Handle<Context> context(Context::cast(parameter_map->get(0))); |
| 12518 | int context_index = Handle<Smi>::cast(probe)->value(); |
| 12519 | DCHECK(!context->get(context_index)->IsTheHole()); |
| 12520 | context->set(context_index, *value); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12521 | // Redefining attributes of an aliased element destroys fast aliasing. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12522 | if (set_mode == SET_PROPERTY || attributes == NONE) return value; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12523 | parameter_map->set_the_hole(index + 2); |
| 12524 | // For elements that are still writable we re-establish slow aliasing. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12525 | if ((attributes & READ_ONLY) == 0) { |
| 12526 | value = Handle<Object>::cast( |
| 12527 | isolate->factory()->NewAliasedArgumentsEntry(context_index)); |
Ben Murdoch | 85b7179 | 2012-04-11 18:30:58 +0100 | [diff] [blame] | 12528 | } |
Ben Murdoch | 5d4cdbf | 2012-04-11 10:23:59 +0100 | [diff] [blame] | 12529 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12530 | Handle<FixedArray> arguments(FixedArray::cast(parameter_map->get(1))); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12531 | if (arguments->IsDictionary()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12532 | return SetDictionaryElement(object, index, value, attributes, |
| 12533 | strict_mode, |
| 12534 | check_prototype, |
| 12535 | set_mode); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12536 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12537 | return SetFastElement(object, index, value, strict_mode, |
| 12538 | check_prototype); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12539 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12540 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12541 | } |
| 12542 | // All possible cases have been handled above. Add a return to avoid the |
| 12543 | // complaints from the compiler. |
| 12544 | UNREACHABLE(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12545 | return isolate->factory()->null_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12546 | } |
| 12547 | |
| 12548 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12549 | const double AllocationSite::kPretenureRatio = 0.85; |
| 12550 | |
| 12551 | |
| 12552 | void AllocationSite::ResetPretenureDecision() { |
| 12553 | set_pretenure_decision(kUndecided); |
| 12554 | set_memento_found_count(0); |
| 12555 | set_memento_create_count(0); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12556 | } |
| 12557 | |
| 12558 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12559 | PretenureFlag AllocationSite::GetPretenureMode() { |
| 12560 | PretenureDecision mode = pretenure_decision(); |
| 12561 | // Zombie objects "decide" to be untenured. |
| 12562 | return mode == kTenure ? TENURED : NOT_TENURED; |
| 12563 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12564 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12565 | |
| 12566 | bool AllocationSite::IsNestedSite() { |
| 12567 | DCHECK(FLAG_trace_track_allocation_sites); |
| 12568 | Object* current = GetHeap()->allocation_sites_list(); |
| 12569 | while (current->IsAllocationSite()) { |
| 12570 | AllocationSite* current_site = AllocationSite::cast(current); |
| 12571 | if (current_site->nested_site() == this) { |
| 12572 | return true; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12573 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12574 | current = current_site->weak_next(); |
| 12575 | } |
| 12576 | return false; |
| 12577 | } |
| 12578 | |
| 12579 | |
| 12580 | void AllocationSite::DigestTransitionFeedback(Handle<AllocationSite> site, |
| 12581 | ElementsKind to_kind) { |
| 12582 | Isolate* isolate = site->GetIsolate(); |
| 12583 | |
| 12584 | if (site->SitePointsToLiteral() && site->transition_info()->IsJSArray()) { |
| 12585 | Handle<JSArray> transition_info = |
| 12586 | handle(JSArray::cast(site->transition_info())); |
| 12587 | ElementsKind kind = transition_info->GetElementsKind(); |
| 12588 | // if kind is holey ensure that to_kind is as well. |
| 12589 | if (IsHoleyElementsKind(kind)) { |
| 12590 | to_kind = GetHoleyElementsKind(to_kind); |
| 12591 | } |
| 12592 | if (IsMoreGeneralElementsKindTransition(kind, to_kind)) { |
| 12593 | // If the array is huge, it's not likely to be defined in a local |
| 12594 | // function, so we shouldn't make new instances of it very often. |
| 12595 | uint32_t length = 0; |
| 12596 | CHECK(transition_info->length()->ToArrayIndex(&length)); |
| 12597 | if (length <= kMaximumArrayBytesToPretransition) { |
| 12598 | if (FLAG_trace_track_allocation_sites) { |
| 12599 | bool is_nested = site->IsNestedSite(); |
| 12600 | PrintF( |
| 12601 | "AllocationSite: JSArray %p boilerplate %s updated %s->%s\n", |
| 12602 | reinterpret_cast<void*>(*site), |
| 12603 | is_nested ? "(nested)" : "", |
| 12604 | ElementsKindToString(kind), |
| 12605 | ElementsKindToString(to_kind)); |
| 12606 | } |
| 12607 | JSObject::TransitionElementsKind(transition_info, to_kind); |
| 12608 | site->dependent_code()->DeoptimizeDependentCodeGroup( |
| 12609 | isolate, DependentCode::kAllocationSiteTransitionChangedGroup); |
| 12610 | } |
| 12611 | } |
| 12612 | } else { |
| 12613 | ElementsKind kind = site->GetElementsKind(); |
| 12614 | // if kind is holey ensure that to_kind is as well. |
| 12615 | if (IsHoleyElementsKind(kind)) { |
| 12616 | to_kind = GetHoleyElementsKind(to_kind); |
| 12617 | } |
| 12618 | if (IsMoreGeneralElementsKindTransition(kind, to_kind)) { |
| 12619 | if (FLAG_trace_track_allocation_sites) { |
| 12620 | PrintF("AllocationSite: JSArray %p site updated %s->%s\n", |
| 12621 | reinterpret_cast<void*>(*site), |
| 12622 | ElementsKindToString(kind), |
| 12623 | ElementsKindToString(to_kind)); |
| 12624 | } |
| 12625 | site->SetElementsKind(to_kind); |
| 12626 | site->dependent_code()->DeoptimizeDependentCodeGroup( |
| 12627 | isolate, DependentCode::kAllocationSiteTransitionChangedGroup); |
| 12628 | } |
| 12629 | } |
| 12630 | } |
| 12631 | |
| 12632 | |
| 12633 | // static |
| 12634 | void AllocationSite::AddDependentCompilationInfo(Handle<AllocationSite> site, |
| 12635 | Reason reason, |
| 12636 | CompilationInfo* info) { |
| 12637 | DependentCode::DependencyGroup group = site->ToDependencyGroup(reason); |
| 12638 | Handle<DependentCode> dep(site->dependent_code()); |
| 12639 | Handle<DependentCode> codes = |
| 12640 | DependentCode::Insert(dep, group, info->object_wrapper()); |
| 12641 | if (*codes != site->dependent_code()) site->set_dependent_code(*codes); |
| 12642 | info->dependencies(group)->Add(Handle<HeapObject>(*site), info->zone()); |
| 12643 | } |
| 12644 | |
| 12645 | |
| 12646 | const char* AllocationSite::PretenureDecisionName(PretenureDecision decision) { |
| 12647 | switch (decision) { |
| 12648 | case kUndecided: return "undecided"; |
| 12649 | case kDontTenure: return "don't tenure"; |
| 12650 | case kMaybeTenure: return "maybe tenure"; |
| 12651 | case kTenure: return "tenure"; |
| 12652 | case kZombie: return "zombie"; |
| 12653 | default: UNREACHABLE(); |
| 12654 | } |
| 12655 | return NULL; |
| 12656 | } |
| 12657 | |
| 12658 | |
| 12659 | void JSObject::UpdateAllocationSite(Handle<JSObject> object, |
| 12660 | ElementsKind to_kind) { |
| 12661 | if (!object->IsJSArray()) return; |
| 12662 | |
| 12663 | Heap* heap = object->GetHeap(); |
| 12664 | if (!heap->InNewSpace(*object)) return; |
| 12665 | |
| 12666 | Handle<AllocationSite> site; |
| 12667 | { |
| 12668 | DisallowHeapAllocation no_allocation; |
| 12669 | |
| 12670 | AllocationMemento* memento = heap->FindAllocationMemento(*object); |
| 12671 | if (memento == NULL) return; |
| 12672 | |
| 12673 | // Walk through to the Allocation Site |
| 12674 | site = handle(memento->GetAllocationSite()); |
| 12675 | } |
| 12676 | AllocationSite::DigestTransitionFeedback(site, to_kind); |
| 12677 | } |
| 12678 | |
| 12679 | |
| 12680 | void JSObject::TransitionElementsKind(Handle<JSObject> object, |
| 12681 | ElementsKind to_kind) { |
| 12682 | ElementsKind from_kind = object->map()->elements_kind(); |
| 12683 | |
| 12684 | if (IsFastHoleyElementsKind(from_kind)) { |
| 12685 | to_kind = GetHoleyElementsKind(to_kind); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12686 | } |
| 12687 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12688 | if (from_kind == to_kind) return; |
| 12689 | // Don't update the site if to_kind isn't fast |
| 12690 | if (IsFastElementsKind(to_kind)) { |
| 12691 | UpdateAllocationSite(object, to_kind); |
| 12692 | } |
| 12693 | |
| 12694 | Isolate* isolate = object->GetIsolate(); |
| 12695 | if (object->elements() == isolate->heap()->empty_fixed_array() || |
| 12696 | (IsFastSmiOrObjectElementsKind(from_kind) && |
| 12697 | IsFastSmiOrObjectElementsKind(to_kind)) || |
| 12698 | (from_kind == FAST_DOUBLE_ELEMENTS && |
| 12699 | to_kind == FAST_HOLEY_DOUBLE_ELEMENTS)) { |
| 12700 | DCHECK(from_kind != TERMINAL_FAST_ELEMENTS_KIND); |
| 12701 | // No change is needed to the elements() buffer, the transition |
| 12702 | // only requires a map change. |
| 12703 | Handle<Map> new_map = GetElementsTransitionMap(object, to_kind); |
| 12704 | MigrateToMap(object, new_map); |
| 12705 | if (FLAG_trace_elements_transitions) { |
| 12706 | Handle<FixedArrayBase> elms(object->elements()); |
| 12707 | PrintElementsTransition(stdout, object, from_kind, elms, to_kind, elms); |
| 12708 | } |
| 12709 | return; |
| 12710 | } |
| 12711 | |
| 12712 | Handle<FixedArrayBase> elms(object->elements()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12713 | uint32_t capacity = static_cast<uint32_t>(elms->length()); |
| 12714 | uint32_t length = capacity; |
| 12715 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12716 | if (object->IsJSArray()) { |
| 12717 | Object* raw_length = Handle<JSArray>::cast(object)->length(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12718 | if (raw_length->IsUndefined()) { |
| 12719 | // If length is undefined, then JSArray is being initialized and has no |
| 12720 | // elements, assume a length of zero. |
| 12721 | length = 0; |
| 12722 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12723 | CHECK(raw_length->ToArrayIndex(&length)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12724 | } |
| 12725 | } |
| 12726 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12727 | if (IsFastSmiElementsKind(from_kind) && |
| 12728 | IsFastDoubleElementsKind(to_kind)) { |
| 12729 | SetFastDoubleElementsCapacityAndLength(object, capacity, length); |
| 12730 | JSObject::ValidateElements(object); |
| 12731 | return; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12732 | } |
| 12733 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12734 | if (IsFastDoubleElementsKind(from_kind) && |
| 12735 | IsFastObjectElementsKind(to_kind)) { |
| 12736 | SetFastElementsCapacityAndLength(object, capacity, length, |
| 12737 | kDontAllowSmiElements); |
| 12738 | JSObject::ValidateElements(object); |
| 12739 | return; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12740 | } |
| 12741 | |
| 12742 | // This method should never be called for any other case than the ones |
| 12743 | // handled above. |
| 12744 | UNREACHABLE(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12745 | } |
| 12746 | |
| 12747 | |
| 12748 | // static |
| 12749 | bool Map::IsValidElementsTransition(ElementsKind from_kind, |
| 12750 | ElementsKind to_kind) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12751 | // Transitions can't go backwards. |
| 12752 | if (!IsMoreGeneralElementsKindTransition(from_kind, to_kind)) { |
| 12753 | return false; |
| 12754 | } |
| 12755 | |
| 12756 | // Transitions from HOLEY -> PACKED are not allowed. |
| 12757 | return !IsFastHoleyElementsKind(from_kind) || |
| 12758 | IsFastHoleyElementsKind(to_kind); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 12759 | } |
| 12760 | |
| 12761 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12762 | void JSArray::JSArrayUpdateLengthFromIndex(Handle<JSArray> array, |
| 12763 | uint32_t index, |
| 12764 | Handle<Object> value) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12765 | uint32_t old_len = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12766 | CHECK(array->length()->ToArrayIndex(&old_len)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12767 | // Check to see if we need to update the length. For now, we make |
| 12768 | // sure that the length stays within 32-bits (unsigned). |
| 12769 | if (index >= old_len && index != 0xffffffff) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12770 | Handle<Object> len = array->GetIsolate()->factory()->NewNumber( |
| 12771 | static_cast<double>(index) + 1); |
| 12772 | array->set_length(*len); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12773 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12774 | } |
| 12775 | |
| 12776 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12777 | bool JSArray::IsReadOnlyLengthDescriptor(Handle<Map> jsarray_map) { |
| 12778 | Isolate* isolate = jsarray_map->GetIsolate(); |
| 12779 | DCHECK(!jsarray_map->is_dictionary_map()); |
| 12780 | LookupResult lookup(isolate); |
| 12781 | Handle<Name> length_string = isolate->factory()->length_string(); |
| 12782 | jsarray_map->LookupDescriptor(NULL, *length_string, &lookup); |
| 12783 | return lookup.IsReadOnly(); |
| 12784 | } |
| 12785 | |
| 12786 | |
| 12787 | bool JSArray::WouldChangeReadOnlyLength(Handle<JSArray> array, |
| 12788 | uint32_t index) { |
| 12789 | uint32_t length = 0; |
| 12790 | CHECK(array->length()->ToArrayIndex(&length)); |
| 12791 | if (length <= index) { |
| 12792 | LookupIterator it(array, array->GetIsolate()->factory()->length_string(), |
| 12793 | LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 12794 | CHECK_NE(LookupIterator::ACCESS_CHECK, it.state()); |
| 12795 | CHECK(it.IsFound()); |
| 12796 | CHECK_EQ(LookupIterator::ACCESSOR, it.state()); |
| 12797 | return it.IsReadOnly(); |
| 12798 | } |
| 12799 | return false; |
| 12800 | } |
| 12801 | |
| 12802 | |
| 12803 | MaybeHandle<Object> JSArray::ReadOnlyLengthError(Handle<JSArray> array) { |
| 12804 | Isolate* isolate = array->GetIsolate(); |
| 12805 | Handle<Name> length = isolate->factory()->length_string(); |
| 12806 | Handle<Object> args[2] = { length, array }; |
| 12807 | THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property", |
| 12808 | HandleVector(args, arraysize(args))), |
| 12809 | Object); |
| 12810 | } |
| 12811 | |
| 12812 | |
| 12813 | MaybeHandle<Object> JSObject::GetElementWithInterceptor( |
| 12814 | Handle<JSObject> object, |
| 12815 | Handle<Object> receiver, |
| 12816 | uint32_t index) { |
| 12817 | Isolate* isolate = object->GetIsolate(); |
| 12818 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12819 | // Make sure that the top context does not change when doing |
| 12820 | // callbacks or interceptor calls. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12821 | AssertNoContextChange ncc(isolate); |
| 12822 | |
| 12823 | Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor(), isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12824 | if (!interceptor->getter()->IsUndefined()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12825 | v8::IndexedPropertyGetterCallback getter = |
| 12826 | v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter()); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 12827 | LOG(isolate, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12828 | ApiIndexedPropertyAccess("interceptor-indexed-get", *object, index)); |
| 12829 | PropertyCallbackArguments |
| 12830 | args(isolate, interceptor->data(), *receiver, *object); |
| 12831 | v8::Handle<v8::Value> result = args.Call(getter, index); |
| 12832 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 12833 | if (!result.IsEmpty()) { |
| 12834 | Handle<Object> result_internal = v8::Utils::OpenHandle(*result); |
| 12835 | result_internal->VerifyApiCallResultType(); |
| 12836 | // Rebox handle before return. |
| 12837 | return handle(*result_internal, isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12838 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12839 | } |
| 12840 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12841 | ElementsAccessor* handler = object->GetElementsAccessor(); |
| 12842 | Handle<Object> result; |
| 12843 | ASSIGN_RETURN_ON_EXCEPTION( |
| 12844 | isolate, result, handler->Get(receiver, object, index), |
| 12845 | Object); |
| 12846 | if (!result->IsTheHole()) return result; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12847 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12848 | PrototypeIterator iter(isolate, object); |
| 12849 | if (iter.IsAtEnd()) return isolate->factory()->undefined_value(); |
| 12850 | return Object::GetElementWithReceiver( |
| 12851 | isolate, PrototypeIterator::GetCurrent(iter), receiver, index); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12852 | } |
| 12853 | |
| 12854 | |
| 12855 | bool JSObject::HasDenseElements() { |
| 12856 | int capacity = 0; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12857 | int used = 0; |
| 12858 | GetElementsCapacityAndUsage(&capacity, &used); |
| 12859 | return (capacity == 0) || (used > (capacity / 2)); |
| 12860 | } |
| 12861 | |
| 12862 | |
| 12863 | void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { |
| 12864 | *capacity = 0; |
| 12865 | *used = 0; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12866 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12867 | FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements()); |
| 12868 | FixedArray* backing_store = NULL; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12869 | switch (GetElementsKind()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12870 | case SLOPPY_ARGUMENTS_ELEMENTS: |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12871 | backing_store_base = |
| 12872 | FixedArray::cast(FixedArray::cast(backing_store_base)->get(1)); |
| 12873 | backing_store = FixedArray::cast(backing_store_base); |
| 12874 | if (backing_store->IsDictionary()) { |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 12875 | SeededNumberDictionary* dictionary = |
| 12876 | SeededNumberDictionary::cast(backing_store); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12877 | *capacity = dictionary->Capacity(); |
| 12878 | *used = dictionary->NumberOfElements(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12879 | break; |
| 12880 | } |
| 12881 | // Fall through. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12882 | case FAST_SMI_ELEMENTS: |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12883 | case FAST_ELEMENTS: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12884 | if (IsJSArray()) { |
| 12885 | *capacity = backing_store_base->length(); |
| 12886 | *used = Smi::cast(JSArray::cast(this)->length())->value(); |
| 12887 | break; |
| 12888 | } |
| 12889 | // Fall through if packing is not guaranteed. |
| 12890 | case FAST_HOLEY_SMI_ELEMENTS: |
| 12891 | case FAST_HOLEY_ELEMENTS: |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12892 | backing_store = FixedArray::cast(backing_store_base); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12893 | *capacity = backing_store->length(); |
| 12894 | for (int i = 0; i < *capacity; ++i) { |
| 12895 | if (!backing_store->get(i)->IsTheHole()) ++(*used); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12896 | } |
| 12897 | break; |
| 12898 | case DICTIONARY_ELEMENTS: { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12899 | SeededNumberDictionary* dictionary = element_dictionary(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12900 | *capacity = dictionary->Capacity(); |
| 12901 | *used = dictionary->NumberOfElements(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12902 | break; |
| 12903 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12904 | case FAST_DOUBLE_ELEMENTS: |
| 12905 | if (IsJSArray()) { |
| 12906 | *capacity = backing_store_base->length(); |
| 12907 | *used = Smi::cast(JSArray::cast(this)->length())->value(); |
| 12908 | break; |
| 12909 | } |
| 12910 | // Fall through if packing is not guaranteed. |
| 12911 | case FAST_HOLEY_DOUBLE_ELEMENTS: { |
| 12912 | *capacity = elements()->length(); |
| 12913 | if (*capacity == 0) break; |
| 12914 | FixedDoubleArray * elms = FixedDoubleArray::cast(elements()); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12915 | for (int i = 0; i < *capacity; i++) { |
| 12916 | if (!elms->is_the_hole(i)) ++(*used); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12917 | } |
| 12918 | break; |
| 12919 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12920 | |
| 12921 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 12922 | case EXTERNAL_##TYPE##_ELEMENTS: \ |
| 12923 | case TYPE##_ELEMENTS: \ |
| 12924 | |
| 12925 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 12926 | #undef TYPED_ARRAY_CASE |
| 12927 | { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12928 | // External arrays are considered 100% used. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12929 | FixedArrayBase* external_array = FixedArrayBase::cast(elements()); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12930 | *capacity = external_array->length(); |
| 12931 | *used = external_array->length(); |
| 12932 | break; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12933 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12934 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12935 | } |
| 12936 | |
| 12937 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12938 | bool JSObject::WouldConvertToSlowElements(Handle<Object> key) { |
| 12939 | uint32_t index; |
| 12940 | if (HasFastElements() && key->ToArrayIndex(&index)) { |
| 12941 | Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements())); |
| 12942 | uint32_t capacity = static_cast<uint32_t>(backing_store->length()); |
| 12943 | if (index >= capacity) { |
| 12944 | if ((index - capacity) >= kMaxGap) return true; |
| 12945 | uint32_t new_capacity = NewElementsCapacity(index + 1); |
| 12946 | return ShouldConvertToSlowElements(new_capacity); |
| 12947 | } |
| 12948 | } |
| 12949 | return false; |
| 12950 | } |
| 12951 | |
| 12952 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12953 | bool JSObject::ShouldConvertToSlowElements(int new_capacity) { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12954 | STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <= |
| 12955 | kMaxUncheckedFastElementsLength); |
| 12956 | if (new_capacity <= kMaxUncheckedOldFastElementsLength || |
| 12957 | (new_capacity <= kMaxUncheckedFastElementsLength && |
| 12958 | GetHeap()->InNewSpace(this))) { |
| 12959 | return false; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12960 | } |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12961 | // If the fast-case backing storage takes up roughly three times as |
| 12962 | // much space (in machine words) as a dictionary backing storage |
| 12963 | // would, the object should have slow elements. |
| 12964 | int old_capacity = 0; |
| 12965 | int used_elements = 0; |
| 12966 | GetElementsCapacityAndUsage(&old_capacity, &used_elements); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 12967 | int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) * |
| 12968 | SeededNumberDictionary::kEntrySize; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12969 | return 3 * dictionary_size <= new_capacity; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12970 | } |
| 12971 | |
| 12972 | |
| 12973 | bool JSObject::ShouldConvertToFastElements() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12974 | DCHECK(HasDictionaryElements() || HasDictionaryArgumentsElements()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12975 | // If the elements are sparse, we should not go back to fast case. |
| 12976 | if (!HasDenseElements()) return false; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12977 | // An object requiring access checks is never allowed to have fast |
| 12978 | // elements. If it had fast elements we would skip security checks. |
| 12979 | if (IsAccessCheckNeeded()) return false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12980 | // Observed objects may not go to fast mode because they rely on map checks, |
| 12981 | // and for fast element accesses we sometimes check element kinds only. |
| 12982 | if (map()->is_observed()) return false; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12983 | |
| 12984 | FixedArray* elements = FixedArray::cast(this->elements()); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 12985 | SeededNumberDictionary* dictionary = NULL; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 12986 | if (elements->map() == GetHeap()->sloppy_arguments_elements_map()) { |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 12987 | dictionary = SeededNumberDictionary::cast(elements->get(1)); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12988 | } else { |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 12989 | dictionary = SeededNumberDictionary::cast(elements); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 12990 | } |
| 12991 | // If an element has been added at a very high index in the elements |
| 12992 | // dictionary, we cannot go back to fast case. |
| 12993 | if (dictionary->requires_slow_elements()) return false; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12994 | // If the dictionary backing storage takes up roughly half as much |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12995 | // space (in machine words) as a fast-case backing storage would, |
| 12996 | // the object should have fast elements. |
| 12997 | uint32_t array_size = 0; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 12998 | if (IsJSArray()) { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 12999 | CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13000 | } else { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 13001 | array_size = dictionary->max_number_key(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13002 | } |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 13003 | uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) * |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 13004 | SeededNumberDictionary::kEntrySize; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 13005 | return 2 * dictionary_size >= array_size; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13006 | } |
| 13007 | |
| 13008 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13009 | bool JSObject::ShouldConvertToFastDoubleElements( |
| 13010 | bool* has_smi_only_elements) { |
| 13011 | *has_smi_only_elements = false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13012 | if (HasSloppyArgumentsElements()) return false; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13013 | if (FLAG_unbox_double_arrays) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13014 | DCHECK(HasDictionaryElements()); |
| 13015 | SeededNumberDictionary* dictionary = element_dictionary(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13016 | bool found_double = false; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13017 | for (int i = 0; i < dictionary->Capacity(); i++) { |
| 13018 | Object* key = dictionary->KeyAt(i); |
| 13019 | if (key->IsNumber()) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13020 | Object* value = dictionary->ValueAt(i); |
| 13021 | if (!value->IsNumber()) return false; |
| 13022 | if (!value->IsSmi()) { |
| 13023 | found_double = true; |
| 13024 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13025 | } |
| 13026 | } |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13027 | *has_smi_only_elements = !found_double; |
| 13028 | return found_double; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13029 | } else { |
| 13030 | return false; |
| 13031 | } |
| 13032 | } |
| 13033 | |
| 13034 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13035 | // Certain compilers request function template instantiation when they |
| 13036 | // see the definition of the other template functions in the |
| 13037 | // class. This requires us to have the template functions put |
| 13038 | // together, so even though this function belongs in objects-debug.cc, |
| 13039 | // we keep it here instead to satisfy certain compilers. |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 13040 | #ifdef OBJECT_PRINT |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13041 | template <typename Derived, typename Shape, typename Key> |
| 13042 | void Dictionary<Derived, Shape, Key>::Print(OStream& os) { // NOLINT |
| 13043 | int capacity = DerivedHashTable::Capacity(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13044 | for (int i = 0; i < capacity; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13045 | Object* k = DerivedHashTable::KeyAt(i); |
| 13046 | if (DerivedHashTable::IsKey(k)) { |
| 13047 | os << " "; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13048 | if (k->IsString()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13049 | String::cast(k)->StringPrint(os); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13050 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13051 | os << Brief(k); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13052 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13053 | os << ": " << Brief(ValueAt(i)) << "\n"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13054 | } |
| 13055 | } |
| 13056 | } |
| 13057 | #endif |
| 13058 | |
| 13059 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13060 | template<typename Derived, typename Shape, typename Key> |
| 13061 | void Dictionary<Derived, Shape, Key>::CopyValuesTo(FixedArray* elements) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13062 | int pos = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13063 | int capacity = DerivedHashTable::Capacity(); |
| 13064 | DisallowHeapAllocation no_gc; |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 13065 | WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13066 | for (int i = 0; i < capacity; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13067 | Object* k = Dictionary::KeyAt(i); |
| 13068 | if (Dictionary::IsKey(k)) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13069 | elements->set(pos++, ValueAt(i), mode); |
| 13070 | } |
| 13071 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13072 | DCHECK(pos == elements->length()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13073 | } |
| 13074 | |
| 13075 | |
| 13076 | InterceptorInfo* JSObject::GetNamedInterceptor() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13077 | DCHECK(map()->has_named_interceptor()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13078 | JSFunction* constructor = JSFunction::cast(map()->constructor()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13079 | DCHECK(constructor->shared()->IsApiFunction()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13080 | Object* result = |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 13081 | constructor->shared()->get_api_func_data()->named_property_handler(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13082 | return InterceptorInfo::cast(result); |
| 13083 | } |
| 13084 | |
| 13085 | |
| 13086 | InterceptorInfo* JSObject::GetIndexedInterceptor() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13087 | DCHECK(map()->has_indexed_interceptor()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13088 | JSFunction* constructor = JSFunction::cast(map()->constructor()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13089 | DCHECK(constructor->shared()->IsApiFunction()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13090 | Object* result = |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 13091 | constructor->shared()->get_api_func_data()->indexed_property_handler(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13092 | return InterceptorInfo::cast(result); |
| 13093 | } |
| 13094 | |
| 13095 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13096 | MaybeHandle<Object> JSObject::GetPropertyWithInterceptor( |
| 13097 | Handle<JSObject> holder, |
| 13098 | Handle<Object> receiver, |
| 13099 | Handle<Name> name) { |
| 13100 | Isolate* isolate = holder->GetIsolate(); |
| 13101 | |
| 13102 | // TODO(rossberg): Support symbols in the API. |
| 13103 | if (name->IsSymbol()) return isolate->factory()->undefined_value(); |
| 13104 | |
| 13105 | Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor(), isolate); |
| 13106 | Handle<String> name_string = Handle<String>::cast(name); |
| 13107 | |
| 13108 | if (interceptor->getter()->IsUndefined()) return MaybeHandle<Object>(); |
| 13109 | |
| 13110 | v8::NamedPropertyGetterCallback getter = |
| 13111 | v8::ToCData<v8::NamedPropertyGetterCallback>(interceptor->getter()); |
| 13112 | LOG(isolate, |
| 13113 | ApiNamedPropertyAccess("interceptor-named-get", *holder, *name)); |
| 13114 | PropertyCallbackArguments |
| 13115 | args(isolate, interceptor->data(), *receiver, *holder); |
| 13116 | v8::Handle<v8::Value> result = |
| 13117 | args.Call(getter, v8::Utils::ToLocal(name_string)); |
| 13118 | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| 13119 | if (result.IsEmpty()) return MaybeHandle<Object>(); |
| 13120 | |
| 13121 | Handle<Object> result_internal = v8::Utils::OpenHandle(*result); |
| 13122 | result_internal->VerifyApiCallResultType(); |
| 13123 | // Rebox handle before return |
| 13124 | return handle(*result_internal, isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13125 | } |
| 13126 | |
| 13127 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13128 | // Compute the property keys from the interceptor. |
| 13129 | // TODO(rossberg): support symbols in API, and filter here if needed. |
| 13130 | MaybeHandle<JSObject> JSObject::GetKeysForNamedInterceptor( |
| 13131 | Handle<JSObject> object, Handle<JSReceiver> receiver) { |
| 13132 | Isolate* isolate = receiver->GetIsolate(); |
| 13133 | Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor()); |
| 13134 | PropertyCallbackArguments |
| 13135 | args(isolate, interceptor->data(), *receiver, *object); |
| 13136 | v8::Handle<v8::Object> result; |
| 13137 | if (!interceptor->enumerator()->IsUndefined()) { |
| 13138 | v8::NamedPropertyEnumeratorCallback enum_fun = |
| 13139 | v8::ToCData<v8::NamedPropertyEnumeratorCallback>( |
| 13140 | interceptor->enumerator()); |
| 13141 | LOG(isolate, ApiObjectAccess("interceptor-named-enum", *object)); |
| 13142 | result = args.Call(enum_fun); |
Andrei Popescu | 402d937 | 2010-02-26 13:31:12 +0000 | [diff] [blame] | 13143 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13144 | if (result.IsEmpty()) return MaybeHandle<JSObject>(); |
| 13145 | #if ENABLE_EXTRA_CHECKS |
| 13146 | CHECK(v8::Utils::OpenHandle(*result)->IsJSArray() || |
| 13147 | v8::Utils::OpenHandle(*result)->HasSloppyArgumentsElements()); |
| 13148 | #endif |
| 13149 | // Rebox before returning. |
| 13150 | return handle(*v8::Utils::OpenHandle(*result), isolate); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 13151 | } |
| 13152 | |
| 13153 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13154 | // Compute the element keys from the interceptor. |
| 13155 | MaybeHandle<JSObject> JSObject::GetKeysForIndexedInterceptor( |
| 13156 | Handle<JSObject> object, Handle<JSReceiver> receiver) { |
| 13157 | Isolate* isolate = receiver->GetIsolate(); |
| 13158 | Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor()); |
| 13159 | PropertyCallbackArguments |
| 13160 | args(isolate, interceptor->data(), *receiver, *object); |
| 13161 | v8::Handle<v8::Object> result; |
| 13162 | if (!interceptor->enumerator()->IsUndefined()) { |
| 13163 | v8::IndexedPropertyEnumeratorCallback enum_fun = |
| 13164 | v8::ToCData<v8::IndexedPropertyEnumeratorCallback>( |
| 13165 | interceptor->enumerator()); |
| 13166 | LOG(isolate, ApiObjectAccess("interceptor-indexed-enum", *object)); |
| 13167 | result = args.Call(enum_fun); |
| 13168 | } |
| 13169 | if (result.IsEmpty()) return MaybeHandle<JSObject>(); |
| 13170 | #if ENABLE_EXTRA_CHECKS |
| 13171 | CHECK(v8::Utils::OpenHandle(*result)->IsJSArray() || |
| 13172 | v8::Utils::OpenHandle(*result)->HasSloppyArgumentsElements()); |
| 13173 | #endif |
| 13174 | // Rebox before returning. |
| 13175 | return handle(*v8::Utils::OpenHandle(*result), isolate); |
| 13176 | } |
| 13177 | |
| 13178 | |
| 13179 | Maybe<bool> JSObject::HasRealNamedProperty(Handle<JSObject> object, |
| 13180 | Handle<Name> key) { |
| 13181 | LookupIterator it(object, key, LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 13182 | Maybe<PropertyAttributes> maybe_result = GetPropertyAttributes(&it); |
| 13183 | if (!maybe_result.has_value) return Maybe<bool>(); |
| 13184 | return maybe(it.IsFound()); |
| 13185 | } |
| 13186 | |
| 13187 | |
| 13188 | Maybe<bool> JSObject::HasRealElementProperty(Handle<JSObject> object, |
| 13189 | uint32_t index) { |
| 13190 | Isolate* isolate = object->GetIsolate(); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 13191 | HandleScope scope(isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13192 | // Check access rights if needed. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13193 | if (object->IsAccessCheckNeeded()) { |
| 13194 | if (!isolate->MayIndexedAccess(object, index, v8::ACCESS_HAS)) { |
| 13195 | isolate->ReportFailedAccessCheck(object, v8::ACCESS_HAS); |
| 13196 | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe<bool>()); |
| 13197 | return maybe(false); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 13198 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13199 | } |
| 13200 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13201 | if (object->IsJSGlobalProxy()) { |
| 13202 | HandleScope scope(isolate); |
| 13203 | PrototypeIterator iter(isolate, object); |
| 13204 | if (iter.IsAtEnd()) return maybe(false); |
| 13205 | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| 13206 | return HasRealElementProperty( |
| 13207 | Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index); |
| 13208 | } |
| 13209 | |
| 13210 | Maybe<PropertyAttributes> result = |
| 13211 | GetElementAttributeWithoutInterceptor(object, object, index, false); |
| 13212 | if (!result.has_value) return Maybe<bool>(); |
| 13213 | return maybe(result.value != ABSENT); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13214 | } |
| 13215 | |
| 13216 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13217 | Maybe<bool> JSObject::HasRealNamedCallbackProperty(Handle<JSObject> object, |
| 13218 | Handle<Name> key) { |
| 13219 | LookupIterator it(object, key, LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 13220 | Maybe<PropertyAttributes> maybe_result = GetPropertyAttributes(&it); |
| 13221 | if (!maybe_result.has_value) return Maybe<bool>(); |
| 13222 | return maybe(it.state() == LookupIterator::ACCESSOR); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13223 | } |
| 13224 | |
| 13225 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13226 | int JSObject::NumberOfOwnProperties(PropertyAttributes filter) { |
| 13227 | if (HasFastProperties()) { |
| 13228 | Map* map = this->map(); |
| 13229 | if (filter == NONE) return map->NumberOfOwnDescriptors(); |
| 13230 | if (filter & DONT_ENUM) { |
| 13231 | int result = map->EnumLength(); |
| 13232 | if (result != kInvalidEnumCacheSentinel) return result; |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 13233 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13234 | return map->NumberOfDescribedProperties(OWN_DESCRIPTORS, filter); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13235 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13236 | return property_dictionary()->NumberOfElementsFilterAttributes(filter); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13237 | } |
| 13238 | |
| 13239 | |
| 13240 | void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) { |
| 13241 | Object* temp = get(i); |
| 13242 | set(i, get(j)); |
| 13243 | set(j, temp); |
| 13244 | if (this != numbers) { |
| 13245 | temp = numbers->get(i); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13246 | numbers->set(i, Smi::cast(numbers->get(j))); |
| 13247 | numbers->set(j, Smi::cast(temp)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13248 | } |
| 13249 | } |
| 13250 | |
| 13251 | |
| 13252 | static void InsertionSortPairs(FixedArray* content, |
| 13253 | FixedArray* numbers, |
| 13254 | int len) { |
| 13255 | for (int i = 1; i < len; i++) { |
| 13256 | int j = i; |
| 13257 | while (j > 0 && |
| 13258 | (NumberToUint32(numbers->get(j - 1)) > |
| 13259 | NumberToUint32(numbers->get(j)))) { |
| 13260 | content->SwapPairs(numbers, j - 1, j); |
| 13261 | j--; |
| 13262 | } |
| 13263 | } |
| 13264 | } |
| 13265 | |
| 13266 | |
| 13267 | void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) { |
| 13268 | // In-place heap sort. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13269 | DCHECK(content->length() == numbers->length()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13270 | |
| 13271 | // Bottom-up max-heap construction. |
| 13272 | for (int i = 1; i < len; ++i) { |
| 13273 | int child_index = i; |
| 13274 | while (child_index > 0) { |
| 13275 | int parent_index = ((child_index + 1) >> 1) - 1; |
| 13276 | uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); |
| 13277 | uint32_t child_value = NumberToUint32(numbers->get(child_index)); |
| 13278 | if (parent_value < child_value) { |
| 13279 | content->SwapPairs(numbers, parent_index, child_index); |
| 13280 | } else { |
| 13281 | break; |
| 13282 | } |
| 13283 | child_index = parent_index; |
| 13284 | } |
| 13285 | } |
| 13286 | |
| 13287 | // Extract elements and create sorted array. |
| 13288 | for (int i = len - 1; i > 0; --i) { |
| 13289 | // Put max element at the back of the array. |
| 13290 | content->SwapPairs(numbers, 0, i); |
| 13291 | // Sift down the new top element. |
| 13292 | int parent_index = 0; |
| 13293 | while (true) { |
| 13294 | int child_index = ((parent_index + 1) << 1) - 1; |
| 13295 | if (child_index >= i) break; |
| 13296 | uint32_t child1_value = NumberToUint32(numbers->get(child_index)); |
| 13297 | uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1)); |
| 13298 | uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); |
| 13299 | if (child_index + 1 >= i || child1_value > child2_value) { |
| 13300 | if (parent_value > child1_value) break; |
| 13301 | content->SwapPairs(numbers, parent_index, child_index); |
| 13302 | parent_index = child_index; |
| 13303 | } else { |
| 13304 | if (parent_value > child2_value) break; |
| 13305 | content->SwapPairs(numbers, parent_index, child_index + 1); |
| 13306 | parent_index = child_index + 1; |
| 13307 | } |
| 13308 | } |
| 13309 | } |
| 13310 | } |
| 13311 | |
| 13312 | |
| 13313 | // Sort this array and the numbers as pairs wrt. the (distinct) numbers. |
| 13314 | void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13315 | DCHECK(this->length() == numbers->length()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13316 | // For small arrays, simply use insertion sort. |
| 13317 | if (len <= 10) { |
| 13318 | InsertionSortPairs(this, numbers, len); |
| 13319 | return; |
| 13320 | } |
| 13321 | // Check the range of indices. |
| 13322 | uint32_t min_index = NumberToUint32(numbers->get(0)); |
| 13323 | uint32_t max_index = min_index; |
| 13324 | uint32_t i; |
| 13325 | for (i = 1; i < len; i++) { |
| 13326 | if (NumberToUint32(numbers->get(i)) < min_index) { |
| 13327 | min_index = NumberToUint32(numbers->get(i)); |
| 13328 | } else if (NumberToUint32(numbers->get(i)) > max_index) { |
| 13329 | max_index = NumberToUint32(numbers->get(i)); |
| 13330 | } |
| 13331 | } |
| 13332 | if (max_index - min_index + 1 == len) { |
| 13333 | // Indices form a contiguous range, unless there are duplicates. |
| 13334 | // Do an in-place linear time sort assuming distinct numbers, but |
| 13335 | // avoid hanging in case they are not. |
| 13336 | for (i = 0; i < len; i++) { |
| 13337 | uint32_t p; |
| 13338 | uint32_t j = 0; |
| 13339 | // While the current element at i is not at its correct position p, |
| 13340 | // swap the elements at these two positions. |
| 13341 | while ((p = NumberToUint32(numbers->get(i)) - min_index) != i && |
| 13342 | j++ < len) { |
| 13343 | SwapPairs(numbers, i, p); |
| 13344 | } |
| 13345 | } |
| 13346 | } else { |
| 13347 | HeapSortPairs(this, numbers, len); |
| 13348 | return; |
| 13349 | } |
| 13350 | } |
| 13351 | |
| 13352 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13353 | // Fill in the names of own properties into the supplied storage. The main |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13354 | // purpose of this function is to provide reflection information for the object |
| 13355 | // mirrors. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13356 | void JSObject::GetOwnPropertyNames( |
| 13357 | FixedArray* storage, int index, PropertyAttributes filter) { |
| 13358 | DCHECK(storage->length() >= (NumberOfOwnProperties(filter) - index)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13359 | if (HasFastProperties()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13360 | int real_size = map()->NumberOfOwnDescriptors(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13361 | DescriptorArray* descs = map()->instance_descriptors(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13362 | for (int i = 0; i < real_size; i++) { |
| 13363 | if ((descs->GetDetails(i).attributes() & filter) == 0 && |
| 13364 | !FilterKey(descs->GetKey(i), filter)) { |
| 13365 | storage->set(index++, descs->GetKey(i)); |
| 13366 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13367 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13368 | } else { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13369 | property_dictionary()->CopyKeysTo(storage, |
| 13370 | index, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13371 | filter, |
| 13372 | NameDictionary::UNSORTED); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13373 | } |
| 13374 | } |
| 13375 | |
| 13376 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13377 | int JSObject::NumberOfOwnElements(PropertyAttributes filter) { |
| 13378 | return GetOwnElementKeys(NULL, filter); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13379 | } |
| 13380 | |
| 13381 | |
| 13382 | int JSObject::NumberOfEnumElements() { |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 13383 | // Fast case for objects with no elements. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13384 | if (!IsJSValue() && HasFastObjectElements()) { |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 13385 | uint32_t length = IsJSArray() ? |
| 13386 | static_cast<uint32_t>( |
| 13387 | Smi::cast(JSArray::cast(this)->length())->value()) : |
| 13388 | static_cast<uint32_t>(FixedArray::cast(elements())->length()); |
| 13389 | if (length == 0) return 0; |
| 13390 | } |
| 13391 | // Compute the number of enumerable elements. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13392 | return NumberOfOwnElements(static_cast<PropertyAttributes>(DONT_ENUM)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13393 | } |
| 13394 | |
| 13395 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13396 | int JSObject::GetOwnElementKeys(FixedArray* storage, |
| 13397 | PropertyAttributes filter) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13398 | int counter = 0; |
| 13399 | switch (GetElementsKind()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13400 | case FAST_SMI_ELEMENTS: |
| 13401 | case FAST_ELEMENTS: |
| 13402 | case FAST_HOLEY_SMI_ELEMENTS: |
| 13403 | case FAST_HOLEY_ELEMENTS: { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13404 | int length = IsJSArray() ? |
| 13405 | Smi::cast(JSArray::cast(this)->length())->value() : |
| 13406 | FixedArray::cast(elements())->length(); |
| 13407 | for (int i = 0; i < length; i++) { |
| 13408 | if (!FixedArray::cast(elements())->get(i)->IsTheHole()) { |
| 13409 | if (storage != NULL) { |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 13410 | storage->set(counter, Smi::FromInt(i)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13411 | } |
| 13412 | counter++; |
| 13413 | } |
| 13414 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13415 | DCHECK(!storage || storage->length() >= counter); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13416 | break; |
| 13417 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13418 | case FAST_DOUBLE_ELEMENTS: |
| 13419 | case FAST_HOLEY_DOUBLE_ELEMENTS: { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13420 | int length = IsJSArray() ? |
| 13421 | Smi::cast(JSArray::cast(this)->length())->value() : |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13422 | FixedArrayBase::cast(elements())->length(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13423 | for (int i = 0; i < length; i++) { |
| 13424 | if (!FixedDoubleArray::cast(elements())->is_the_hole(i)) { |
| 13425 | if (storage != NULL) { |
| 13426 | storage->set(counter, Smi::FromInt(i)); |
| 13427 | } |
| 13428 | counter++; |
| 13429 | } |
| 13430 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13431 | DCHECK(!storage || storage->length() >= counter); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13432 | break; |
| 13433 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13434 | |
| 13435 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 13436 | case EXTERNAL_##TYPE##_ELEMENTS: \ |
| 13437 | case TYPE##_ELEMENTS: \ |
| 13438 | |
| 13439 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 13440 | #undef TYPED_ARRAY_CASE |
| 13441 | { |
| 13442 | int length = FixedArrayBase::cast(elements())->length(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13443 | while (counter < length) { |
| 13444 | if (storage != NULL) { |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 13445 | storage->set(counter, Smi::FromInt(counter)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13446 | } |
| 13447 | counter++; |
| 13448 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13449 | DCHECK(!storage || storage->length() >= counter); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13450 | break; |
| 13451 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13452 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13453 | case DICTIONARY_ELEMENTS: { |
| 13454 | if (storage != NULL) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13455 | element_dictionary()->CopyKeysTo(storage, |
| 13456 | filter, |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 13457 | SeededNumberDictionary::SORTED); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13458 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13459 | counter += element_dictionary()->NumberOfElementsFilterAttributes(filter); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13460 | break; |
| 13461 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13462 | case SLOPPY_ARGUMENTS_ELEMENTS: { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13463 | FixedArray* parameter_map = FixedArray::cast(elements()); |
| 13464 | int mapped_length = parameter_map->length() - 2; |
| 13465 | FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| 13466 | if (arguments->IsDictionary()) { |
| 13467 | // Copy the keys from arguments first, because Dictionary::CopyKeysTo |
| 13468 | // will insert in storage starting at index 0. |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 13469 | SeededNumberDictionary* dictionary = |
| 13470 | SeededNumberDictionary::cast(arguments); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13471 | if (storage != NULL) { |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 13472 | dictionary->CopyKeysTo( |
| 13473 | storage, filter, SeededNumberDictionary::UNSORTED); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13474 | } |
| 13475 | counter += dictionary->NumberOfElementsFilterAttributes(filter); |
| 13476 | for (int i = 0; i < mapped_length; ++i) { |
| 13477 | if (!parameter_map->get(i + 2)->IsTheHole()) { |
| 13478 | if (storage != NULL) storage->set(counter, Smi::FromInt(i)); |
| 13479 | ++counter; |
| 13480 | } |
| 13481 | } |
| 13482 | if (storage != NULL) storage->SortPairs(storage, counter); |
| 13483 | |
| 13484 | } else { |
| 13485 | int backing_length = arguments->length(); |
| 13486 | int i = 0; |
| 13487 | for (; i < mapped_length; ++i) { |
| 13488 | if (!parameter_map->get(i + 2)->IsTheHole()) { |
| 13489 | if (storage != NULL) storage->set(counter, Smi::FromInt(i)); |
| 13490 | ++counter; |
| 13491 | } else if (i < backing_length && !arguments->get(i)->IsTheHole()) { |
| 13492 | if (storage != NULL) storage->set(counter, Smi::FromInt(i)); |
| 13493 | ++counter; |
| 13494 | } |
| 13495 | } |
| 13496 | for (; i < backing_length; ++i) { |
| 13497 | if (storage != NULL) storage->set(counter, Smi::FromInt(i)); |
| 13498 | ++counter; |
| 13499 | } |
| 13500 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13501 | break; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13502 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13503 | } |
| 13504 | |
| 13505 | if (this->IsJSValue()) { |
| 13506 | Object* val = JSValue::cast(this)->value(); |
| 13507 | if (val->IsString()) { |
| 13508 | String* str = String::cast(val); |
| 13509 | if (storage) { |
| 13510 | for (int i = 0; i < str->length(); i++) { |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 13511 | storage->set(counter + i, Smi::FromInt(i)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13512 | } |
| 13513 | } |
| 13514 | counter += str->length(); |
| 13515 | } |
| 13516 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13517 | DCHECK(!storage || storage->length() == counter); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13518 | return counter; |
| 13519 | } |
| 13520 | |
| 13521 | |
| 13522 | int JSObject::GetEnumElementKeys(FixedArray* storage) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13523 | return GetOwnElementKeys(storage, static_cast<PropertyAttributes>(DONT_ENUM)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13524 | } |
| 13525 | |
| 13526 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13527 | // StringSharedKeys are used as keys in the eval cache. |
| 13528 | class StringSharedKey : public HashTableKey { |
| 13529 | public: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13530 | StringSharedKey(Handle<String> source, |
| 13531 | Handle<SharedFunctionInfo> shared, |
| 13532 | StrictMode strict_mode, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13533 | int scope_position) |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 13534 | : source_(source), |
| 13535 | shared_(shared), |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13536 | strict_mode_(strict_mode), |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13537 | scope_position_(scope_position) { } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13538 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13539 | bool IsMatch(Object* other) OVERRIDE { |
| 13540 | DisallowHeapAllocation no_allocation; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13541 | if (!other->IsFixedArray()) return false; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13542 | FixedArray* other_array = FixedArray::cast(other); |
| 13543 | SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13544 | if (shared != *shared_) return false; |
| 13545 | int strict_unchecked = Smi::cast(other_array->get(2))->value(); |
| 13546 | DCHECK(strict_unchecked == SLOPPY || strict_unchecked == STRICT); |
| 13547 | StrictMode strict_mode = static_cast<StrictMode>(strict_unchecked); |
| 13548 | if (strict_mode != strict_mode_) return false; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13549 | int scope_position = Smi::cast(other_array->get(3))->value(); |
| 13550 | if (scope_position != scope_position_) return false; |
| 13551 | String* source = String::cast(other_array->get(1)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13552 | return source->Equals(*source_); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13553 | } |
| 13554 | |
| 13555 | static uint32_t StringSharedHashHelper(String* source, |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 13556 | SharedFunctionInfo* shared, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13557 | StrictMode strict_mode, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13558 | int scope_position) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13559 | uint32_t hash = source->Hash(); |
| 13560 | if (shared->HasSourceCode()) { |
| 13561 | // Instead of using the SharedFunctionInfo pointer in the hash |
| 13562 | // code computation, we use a combination of the hash of the |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13563 | // script source code and the start position of the calling scope. |
| 13564 | // We do this to ensure that the cache entries can survive garbage |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13565 | // collection. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13566 | Script* script(Script::cast(shared->script())); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13567 | hash ^= String::cast(script->source())->Hash(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13568 | if (strict_mode == STRICT) hash ^= 0x8000; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13569 | hash += scope_position; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13570 | } |
| 13571 | return hash; |
| 13572 | } |
| 13573 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13574 | uint32_t Hash() OVERRIDE { |
| 13575 | return StringSharedHashHelper(*source_, *shared_, strict_mode_, |
| 13576 | scope_position_); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13577 | } |
| 13578 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13579 | uint32_t HashForObject(Object* obj) OVERRIDE { |
| 13580 | DisallowHeapAllocation no_allocation; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13581 | FixedArray* other_array = FixedArray::cast(obj); |
| 13582 | SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0)); |
| 13583 | String* source = String::cast(other_array->get(1)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13584 | int strict_unchecked = Smi::cast(other_array->get(2))->value(); |
| 13585 | DCHECK(strict_unchecked == SLOPPY || strict_unchecked == STRICT); |
| 13586 | StrictMode strict_mode = static_cast<StrictMode>(strict_unchecked); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13587 | int scope_position = Smi::cast(other_array->get(3))->value(); |
| 13588 | return StringSharedHashHelper( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13589 | source, shared, strict_mode, scope_position); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13590 | } |
| 13591 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13592 | |
| 13593 | Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { |
| 13594 | Handle<FixedArray> array = isolate->factory()->NewFixedArray(4); |
| 13595 | array->set(0, *shared_); |
| 13596 | array->set(1, *source_); |
| 13597 | array->set(2, Smi::FromInt(strict_mode_)); |
| 13598 | array->set(3, Smi::FromInt(scope_position_)); |
| 13599 | return array; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13600 | } |
| 13601 | |
| 13602 | private: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13603 | Handle<String> source_; |
| 13604 | Handle<SharedFunctionInfo> shared_; |
| 13605 | StrictMode strict_mode_; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13606 | int scope_position_; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13607 | }; |
| 13608 | |
| 13609 | |
| 13610 | // RegExpKey carries the source and flags of a regular expression as key. |
| 13611 | class RegExpKey : public HashTableKey { |
| 13612 | public: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13613 | RegExpKey(Handle<String> string, JSRegExp::Flags flags) |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13614 | : string_(string), |
| 13615 | flags_(Smi::FromInt(flags.value())) { } |
| 13616 | |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 13617 | // Rather than storing the key in the hash table, a pointer to the |
| 13618 | // stored value is stored where the key should be. IsMatch then |
| 13619 | // compares the search key to the found object, rather than comparing |
| 13620 | // a key to a key. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13621 | bool IsMatch(Object* obj) OVERRIDE { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13622 | FixedArray* val = FixedArray::cast(obj); |
| 13623 | return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex))) |
| 13624 | && (flags_ == val->get(JSRegExp::kFlagsIndex)); |
| 13625 | } |
| 13626 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13627 | uint32_t Hash() OVERRIDE { return RegExpHash(*string_, flags_); } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13628 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13629 | Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13630 | // Plain hash maps, which is where regexp keys are used, don't |
| 13631 | // use this function. |
| 13632 | UNREACHABLE(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13633 | return MaybeHandle<Object>().ToHandleChecked(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13634 | } |
| 13635 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13636 | uint32_t HashForObject(Object* obj) OVERRIDE { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13637 | FixedArray* val = FixedArray::cast(obj); |
| 13638 | return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)), |
| 13639 | Smi::cast(val->get(JSRegExp::kFlagsIndex))); |
| 13640 | } |
| 13641 | |
| 13642 | static uint32_t RegExpHash(String* string, Smi* flags) { |
| 13643 | return string->Hash() + flags->value(); |
| 13644 | } |
| 13645 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13646 | Handle<String> string_; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13647 | Smi* flags_; |
| 13648 | }; |
| 13649 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13650 | |
| 13651 | Handle<Object> OneByteStringKey::AsHandle(Isolate* isolate) { |
| 13652 | if (hash_field_ == 0) Hash(); |
| 13653 | return isolate->factory()->NewOneByteInternalizedString(string_, hash_field_); |
| 13654 | } |
| 13655 | |
| 13656 | |
| 13657 | Handle<Object> TwoByteStringKey::AsHandle(Isolate* isolate) { |
| 13658 | if (hash_field_ == 0) Hash(); |
| 13659 | return isolate->factory()->NewTwoByteInternalizedString(string_, hash_field_); |
| 13660 | } |
| 13661 | |
| 13662 | |
| 13663 | Handle<Object> SeqOneByteSubStringKey::AsHandle(Isolate* isolate) { |
| 13664 | if (hash_field_ == 0) Hash(); |
| 13665 | return isolate->factory()->NewOneByteInternalizedSubString( |
| 13666 | string_, from_, length_, hash_field_); |
| 13667 | } |
| 13668 | |
| 13669 | |
| 13670 | bool SeqOneByteSubStringKey::IsMatch(Object* string) { |
| 13671 | Vector<const uint8_t> chars(string_->GetChars() + from_, length_); |
| 13672 | return String::cast(string)->IsOneByteEqualTo(chars); |
| 13673 | } |
| 13674 | |
| 13675 | |
| 13676 | // InternalizedStringKey carries a string/internalized-string object as key. |
| 13677 | class InternalizedStringKey : public HashTableKey { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13678 | public: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13679 | explicit InternalizedStringKey(Handle<String> string) |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 13680 | : string_(string) { } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13681 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13682 | virtual bool IsMatch(Object* string) OVERRIDE { |
| 13683 | return String::cast(string)->Equals(*string_); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13684 | } |
| 13685 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13686 | virtual uint32_t Hash() OVERRIDE { return string_->Hash(); } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13687 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13688 | virtual uint32_t HashForObject(Object* other) OVERRIDE { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13689 | return String::cast(other)->Hash(); |
| 13690 | } |
| 13691 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13692 | virtual Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { |
| 13693 | // Internalize the string if possible. |
| 13694 | MaybeHandle<Map> maybe_map = |
| 13695 | isolate->factory()->InternalizedStringMapForString(string_); |
| 13696 | Handle<Map> map; |
| 13697 | if (maybe_map.ToHandle(&map)) { |
| 13698 | string_->set_map_no_write_barrier(*map); |
| 13699 | DCHECK(string_->IsInternalizedString()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13700 | return string_; |
| 13701 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13702 | // Otherwise allocate a new internalized string. |
| 13703 | return isolate->factory()->NewInternalizedStringImpl( |
| 13704 | string_, string_->length(), string_->hash_field()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13705 | } |
| 13706 | |
| 13707 | static uint32_t StringHash(Object* obj) { |
| 13708 | return String::cast(obj)->Hash(); |
| 13709 | } |
| 13710 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13711 | Handle<String> string_; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13712 | }; |
| 13713 | |
| 13714 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13715 | template<typename Derived, typename Shape, typename Key> |
| 13716 | void HashTable<Derived, Shape, Key>::IteratePrefix(ObjectVisitor* v) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13717 | IteratePointers(v, 0, kElementsStartOffset); |
| 13718 | } |
| 13719 | |
| 13720 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13721 | template<typename Derived, typename Shape, typename Key> |
| 13722 | void HashTable<Derived, Shape, Key>::IterateElements(ObjectVisitor* v) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13723 | IteratePointers(v, |
| 13724 | kElementsStartOffset, |
| 13725 | kHeaderSize + length() * kPointerSize); |
| 13726 | } |
| 13727 | |
| 13728 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13729 | template<typename Derived, typename Shape, typename Key> |
| 13730 | Handle<Derived> HashTable<Derived, Shape, Key>::New( |
| 13731 | Isolate* isolate, |
| 13732 | int at_least_space_for, |
| 13733 | MinimumCapacity capacity_option, |
| 13734 | PretenureFlag pretenure) { |
| 13735 | DCHECK(0 <= at_least_space_for); |
| 13736 | DCHECK(!capacity_option || base::bits::IsPowerOfTwo32(at_least_space_for)); |
| 13737 | int capacity = (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY) |
| 13738 | ? at_least_space_for |
| 13739 | : ComputeCapacity(at_least_space_for); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 13740 | if (capacity > HashTable::kMaxCapacity) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13741 | v8::internal::Heap::FatalProcessOutOfMemory("invalid table size", true); |
Leon Clarke | e46be81 | 2010-01-19 14:06:41 +0000 | [diff] [blame] | 13742 | } |
| 13743 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13744 | Factory* factory = isolate->factory(); |
| 13745 | int length = EntryToIndex(capacity); |
| 13746 | Handle<FixedArray> array = factory->NewFixedArray(length, pretenure); |
| 13747 | array->set_map_no_write_barrier(*factory->hash_table_map()); |
| 13748 | Handle<Derived> table = Handle<Derived>::cast(array); |
| 13749 | |
| 13750 | table->SetNumberOfElements(0); |
| 13751 | table->SetNumberOfDeletedElements(0); |
| 13752 | table->SetCapacity(capacity); |
| 13753 | return table; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13754 | } |
| 13755 | |
| 13756 | |
Leon Clarke | e46be81 | 2010-01-19 14:06:41 +0000 | [diff] [blame] | 13757 | // Find entry for key otherwise return kNotFound. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13758 | int NameDictionary::FindEntry(Handle<Name> key) { |
| 13759 | if (!key->IsUniqueName()) { |
| 13760 | return DerivedHashTable::FindEntry(key); |
Ben Murdoch | 3bec4d2 | 2010-07-22 14:51:16 +0100 | [diff] [blame] | 13761 | } |
| 13762 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13763 | // Optimized for unique names. Knowledge of the key type allows: |
| 13764 | // 1. Move the check if the key is unique out of the loop. |
| 13765 | // 2. Avoid comparing hash codes in unique-to-unique comparison. |
| 13766 | // 3. Detect a case when a dictionary key is not unique but the key is. |
| 13767 | // In case of positive result the dictionary key may be replaced by the |
| 13768 | // internalized string with minimal performance penalty. It gives a chance |
| 13769 | // to perform further lookups in code stubs (and significant performance |
| 13770 | // boost a certain style of code). |
Ben Murdoch | 3bec4d2 | 2010-07-22 14:51:16 +0100 | [diff] [blame] | 13771 | |
| 13772 | // EnsureCapacity will guarantee the hash table is never full. |
| 13773 | uint32_t capacity = Capacity(); |
| 13774 | uint32_t entry = FirstProbe(key->Hash(), capacity); |
| 13775 | uint32_t count = 1; |
| 13776 | |
| 13777 | while (true) { |
| 13778 | int index = EntryToIndex(entry); |
| 13779 | Object* element = get(index); |
| 13780 | if (element->IsUndefined()) break; // Empty entry. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13781 | if (*key == element) return entry; |
| 13782 | if (!element->IsUniqueName() && |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13783 | !element->IsTheHole() && |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13784 | Name::cast(element)->Equals(*key)) { |
| 13785 | // Replace a key that is a non-internalized string by the equivalent |
| 13786 | // internalized string for faster further lookups. |
| 13787 | set(index, *key); |
Ben Murdoch | 3bec4d2 | 2010-07-22 14:51:16 +0100 | [diff] [blame] | 13788 | return entry; |
| 13789 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13790 | DCHECK(element->IsTheHole() || !Name::cast(element)->Equals(*key)); |
Ben Murdoch | 3bec4d2 | 2010-07-22 14:51:16 +0100 | [diff] [blame] | 13791 | entry = NextProbe(entry, count++, capacity); |
| 13792 | } |
| 13793 | return kNotFound; |
| 13794 | } |
| 13795 | |
| 13796 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13797 | template<typename Derived, typename Shape, typename Key> |
| 13798 | void HashTable<Derived, Shape, Key>::Rehash( |
| 13799 | Handle<Derived> new_table, |
| 13800 | Key key) { |
| 13801 | DCHECK(NumberOfElements() < new_table->Capacity()); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13802 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13803 | DisallowHeapAllocation no_gc; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13804 | WriteBarrierMode mode = new_table->GetWriteBarrierMode(no_gc); |
| 13805 | |
| 13806 | // Copy prefix to new array. |
| 13807 | for (int i = kPrefixStartIndex; |
| 13808 | i < kPrefixStartIndex + Shape::kPrefixSize; |
| 13809 | i++) { |
| 13810 | new_table->set(i, get(i), mode); |
| 13811 | } |
| 13812 | |
| 13813 | // Rehash the elements. |
| 13814 | int capacity = Capacity(); |
| 13815 | for (int i = 0; i < capacity; i++) { |
| 13816 | uint32_t from_index = EntryToIndex(i); |
| 13817 | Object* k = get(from_index); |
| 13818 | if (IsKey(k)) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13819 | uint32_t hash = HashTable::HashForObject(key, k); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13820 | uint32_t insertion_index = |
| 13821 | EntryToIndex(new_table->FindInsertionEntry(hash)); |
| 13822 | for (int j = 0; j < Shape::kEntrySize; j++) { |
| 13823 | new_table->set(insertion_index + j, get(from_index + j), mode); |
| 13824 | } |
| 13825 | } |
| 13826 | } |
| 13827 | new_table->SetNumberOfElements(NumberOfElements()); |
| 13828 | new_table->SetNumberOfDeletedElements(0); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13829 | } |
| 13830 | |
| 13831 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13832 | template<typename Derived, typename Shape, typename Key> |
| 13833 | uint32_t HashTable<Derived, Shape, Key>::EntryForProbe( |
| 13834 | Key key, |
| 13835 | Object* k, |
| 13836 | int probe, |
| 13837 | uint32_t expected) { |
| 13838 | uint32_t hash = HashTable::HashForObject(key, k); |
| 13839 | uint32_t capacity = Capacity(); |
| 13840 | uint32_t entry = FirstProbe(hash, capacity); |
| 13841 | for (int i = 1; i < probe; i++) { |
| 13842 | if (entry == expected) return expected; |
| 13843 | entry = NextProbe(entry, i, capacity); |
| 13844 | } |
| 13845 | return entry; |
| 13846 | } |
| 13847 | |
| 13848 | |
| 13849 | template<typename Derived, typename Shape, typename Key> |
| 13850 | void HashTable<Derived, Shape, Key>::Swap(uint32_t entry1, |
| 13851 | uint32_t entry2, |
| 13852 | WriteBarrierMode mode) { |
| 13853 | int index1 = EntryToIndex(entry1); |
| 13854 | int index2 = EntryToIndex(entry2); |
| 13855 | Object* temp[Shape::kEntrySize]; |
| 13856 | for (int j = 0; j < Shape::kEntrySize; j++) { |
| 13857 | temp[j] = get(index1 + j); |
| 13858 | } |
| 13859 | for (int j = 0; j < Shape::kEntrySize; j++) { |
| 13860 | set(index1 + j, get(index2 + j), mode); |
| 13861 | } |
| 13862 | for (int j = 0; j < Shape::kEntrySize; j++) { |
| 13863 | set(index2 + j, temp[j], mode); |
| 13864 | } |
| 13865 | } |
| 13866 | |
| 13867 | |
| 13868 | template<typename Derived, typename Shape, typename Key> |
| 13869 | void HashTable<Derived, Shape, Key>::Rehash(Key key) { |
| 13870 | DisallowHeapAllocation no_gc; |
| 13871 | WriteBarrierMode mode = GetWriteBarrierMode(no_gc); |
| 13872 | uint32_t capacity = Capacity(); |
| 13873 | bool done = false; |
| 13874 | for (int probe = 1; !done; probe++) { |
| 13875 | // All elements at entries given by one of the first _probe_ probes |
| 13876 | // are placed correctly. Other elements might need to be moved. |
| 13877 | done = true; |
| 13878 | for (uint32_t current = 0; current < capacity; current++) { |
| 13879 | Object* current_key = get(EntryToIndex(current)); |
| 13880 | if (IsKey(current_key)) { |
| 13881 | uint32_t target = EntryForProbe(key, current_key, probe, current); |
| 13882 | if (current == target) continue; |
| 13883 | Object* target_key = get(EntryToIndex(target)); |
| 13884 | if (!IsKey(target_key) || |
| 13885 | EntryForProbe(key, target_key, probe, target) != target) { |
| 13886 | // Put the current element into the correct position. |
| 13887 | Swap(current, target, mode); |
| 13888 | // The other element will be processed on the next iteration. |
| 13889 | current--; |
| 13890 | } else { |
| 13891 | // The place for the current element is occupied. Leave the element |
| 13892 | // for the next probe. |
| 13893 | done = false; |
| 13894 | } |
| 13895 | } |
| 13896 | } |
| 13897 | } |
| 13898 | } |
| 13899 | |
| 13900 | |
| 13901 | template<typename Derived, typename Shape, typename Key> |
| 13902 | Handle<Derived> HashTable<Derived, Shape, Key>::EnsureCapacity( |
| 13903 | Handle<Derived> table, |
| 13904 | int n, |
| 13905 | Key key, |
| 13906 | PretenureFlag pretenure) { |
| 13907 | Isolate* isolate = table->GetIsolate(); |
| 13908 | int capacity = table->Capacity(); |
| 13909 | int nof = table->NumberOfElements() + n; |
| 13910 | int nod = table->NumberOfDeletedElements(); |
Leon Clarke | e46be81 | 2010-01-19 14:06:41 +0000 | [diff] [blame] | 13911 | // Return if: |
| 13912 | // 50% is still free after adding n elements and |
| 13913 | // at most 50% of the free elements are deleted elements. |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 13914 | if (nod <= (capacity - nof) >> 1) { |
| 13915 | int needed_free = nof >> 1; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13916 | if (nof + needed_free <= capacity) return table; |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 13917 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13918 | |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 13919 | const int kMinCapacityForPretenure = 256; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13920 | bool should_pretenure = pretenure == TENURED || |
| 13921 | ((capacity > kMinCapacityForPretenure) && |
| 13922 | !isolate->heap()->InNewSpace(*table)); |
| 13923 | Handle<Derived> new_table = HashTable::New( |
| 13924 | isolate, |
| 13925 | nof * 2, |
| 13926 | USE_DEFAULT_MINIMUM_CAPACITY, |
| 13927 | should_pretenure ? TENURED : NOT_TENURED); |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 13928 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13929 | table->Rehash(new_table, key); |
| 13930 | return new_table; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13931 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13932 | |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13933 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13934 | template<typename Derived, typename Shape, typename Key> |
| 13935 | Handle<Derived> HashTable<Derived, Shape, Key>::Shrink(Handle<Derived> table, |
| 13936 | Key key) { |
| 13937 | int capacity = table->Capacity(); |
| 13938 | int nof = table->NumberOfElements(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13939 | |
| 13940 | // Shrink to fit the number of elements if only a quarter of the |
| 13941 | // capacity is filled with elements. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13942 | if (nof > (capacity >> 2)) return table; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13943 | // Allocate a new dictionary with room for at least the current |
| 13944 | // number of elements. The allocation method will make sure that |
| 13945 | // there is extra room in the dictionary for additions. Don't go |
| 13946 | // lower than room for 16 elements. |
| 13947 | int at_least_room_for = nof; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13948 | if (at_least_room_for < 16) return table; |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13949 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13950 | Isolate* isolate = table->GetIsolate(); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13951 | const int kMinCapacityForPretenure = 256; |
| 13952 | bool pretenure = |
| 13953 | (at_least_room_for > kMinCapacityForPretenure) && |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13954 | !isolate->heap()->InNewSpace(*table); |
| 13955 | Handle<Derived> new_table = HashTable::New( |
| 13956 | isolate, |
| 13957 | at_least_room_for, |
| 13958 | USE_DEFAULT_MINIMUM_CAPACITY, |
| 13959 | pretenure ? TENURED : NOT_TENURED); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 13960 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13961 | table->Rehash(new_table, key); |
| 13962 | return new_table; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13963 | } |
| 13964 | |
| 13965 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13966 | template<typename Derived, typename Shape, typename Key> |
| 13967 | uint32_t HashTable<Derived, Shape, Key>::FindInsertionEntry(uint32_t hash) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13968 | uint32_t capacity = Capacity(); |
Leon Clarke | e46be81 | 2010-01-19 14:06:41 +0000 | [diff] [blame] | 13969 | uint32_t entry = FirstProbe(hash, capacity); |
| 13970 | uint32_t count = 1; |
| 13971 | // EnsureCapacity will guarantee the hash table is never full. |
| 13972 | while (true) { |
| 13973 | Object* element = KeyAt(entry); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13974 | if (element->IsUndefined() || element->IsTheHole()) break; |
Leon Clarke | e46be81 | 2010-01-19 14:06:41 +0000 | [diff] [blame] | 13975 | entry = NextProbe(entry, count++, capacity); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13976 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13977 | return entry; |
| 13978 | } |
| 13979 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13980 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13981 | // Force instantiation of template instances class. |
| 13982 | // Please note this list is compiler dependent. |
| 13983 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13984 | template class HashTable<StringTable, StringTableShape, HashTableKey*>; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13985 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13986 | template class HashTable<CompilationCacheTable, |
| 13987 | CompilationCacheShape, |
| 13988 | HashTableKey*>; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13989 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13990 | template class HashTable<MapCache, MapCacheShape, HashTableKey*>; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13991 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13992 | template class HashTable<ObjectHashTable, |
| 13993 | ObjectHashTableShape, |
| 13994 | Handle<Object> >; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 13995 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13996 | template class HashTable<WeakHashTable, WeakHashTableShape<2>, Handle<Object> >; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 13997 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 13998 | template class Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 13999 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14000 | template class Dictionary<SeededNumberDictionary, |
| 14001 | SeededNumberDictionaryShape, |
| 14002 | uint32_t>; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14003 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14004 | template class Dictionary<UnseededNumberDictionary, |
| 14005 | UnseededNumberDictionaryShape, |
| 14006 | uint32_t>; |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14007 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14008 | template Handle<SeededNumberDictionary> |
| 14009 | Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14010 | New(Isolate*, int at_least_space_for, PretenureFlag pretenure); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14011 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14012 | template Handle<UnseededNumberDictionary> |
| 14013 | Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>:: |
| 14014 | New(Isolate*, int at_least_space_for, PretenureFlag pretenure); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14015 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14016 | template Handle<NameDictionary> |
| 14017 | Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
| 14018 | New(Isolate*, int n, PretenureFlag pretenure); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14019 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14020 | template Handle<SeededNumberDictionary> |
| 14021 | Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14022 | AtPut(Handle<SeededNumberDictionary>, uint32_t, Handle<Object>); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14023 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14024 | template Handle<UnseededNumberDictionary> |
| 14025 | Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>:: |
| 14026 | AtPut(Handle<UnseededNumberDictionary>, uint32_t, Handle<Object>); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14027 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14028 | template Object* |
| 14029 | Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 14030 | SlowReverseLookup(Object* value); |
| 14031 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14032 | template Object* |
| 14033 | Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14034 | SlowReverseLookup(Object* value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14035 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14036 | template void |
| 14037 | Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14038 | CopyKeysTo( |
| 14039 | FixedArray*, |
| 14040 | PropertyAttributes, |
| 14041 | Dictionary<SeededNumberDictionary, |
| 14042 | SeededNumberDictionaryShape, |
| 14043 | uint32_t>::SortMode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14044 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14045 | template Handle<Object> |
| 14046 | Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::DeleteProperty( |
| 14047 | Handle<NameDictionary>, int, JSObject::DeleteMode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14048 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14049 | template Handle<Object> |
| 14050 | Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14051 | DeleteProperty(Handle<SeededNumberDictionary>, int, JSObject::DeleteMode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14052 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14053 | template Handle<NameDictionary> |
| 14054 | HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
| 14055 | New(Isolate*, int, MinimumCapacity, PretenureFlag); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14056 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14057 | template Handle<NameDictionary> |
| 14058 | HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
| 14059 | Shrink(Handle<NameDictionary>, Handle<Name>); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 14060 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14061 | template Handle<SeededNumberDictionary> |
| 14062 | HashTable<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14063 | Shrink(Handle<SeededNumberDictionary>, uint32_t); |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 14064 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14065 | template void Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
| 14066 | CopyKeysTo( |
| 14067 | FixedArray*, |
| 14068 | int, |
| 14069 | PropertyAttributes, |
| 14070 | Dictionary< |
| 14071 | NameDictionary, NameDictionaryShape, Handle<Name> >::SortMode); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14072 | |
| 14073 | template int |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14074 | Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14075 | NumberOfElementsFilterAttributes(PropertyAttributes); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14076 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14077 | template Handle<NameDictionary> |
| 14078 | Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::Add( |
| 14079 | Handle<NameDictionary>, Handle<Name>, Handle<Object>, PropertyDetails); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14080 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14081 | template void |
| 14082 | Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
| 14083 | GenerateNewEnumerationIndices(Handle<NameDictionary>); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14084 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14085 | template int |
| 14086 | Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14087 | NumberOfElementsFilterAttributes(PropertyAttributes); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14088 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14089 | template Handle<SeededNumberDictionary> |
| 14090 | Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14091 | Add(Handle<SeededNumberDictionary>, |
| 14092 | uint32_t, |
| 14093 | Handle<Object>, |
| 14094 | PropertyDetails); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14095 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14096 | template Handle<UnseededNumberDictionary> |
| 14097 | Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>:: |
| 14098 | Add(Handle<UnseededNumberDictionary>, |
| 14099 | uint32_t, |
| 14100 | Handle<Object>, |
| 14101 | PropertyDetails); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14102 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14103 | template Handle<SeededNumberDictionary> |
| 14104 | Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14105 | EnsureCapacity(Handle<SeededNumberDictionary>, int, uint32_t); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14106 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14107 | template Handle<UnseededNumberDictionary> |
| 14108 | Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>:: |
| 14109 | EnsureCapacity(Handle<UnseededNumberDictionary>, int, uint32_t); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14110 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14111 | template Handle<NameDictionary> |
| 14112 | Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
| 14113 | EnsureCapacity(Handle<NameDictionary>, int, Handle<Name>); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14114 | |
| 14115 | template |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14116 | int Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14117 | NumberOfEnumElements(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14118 | |
| 14119 | template |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14120 | int Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: |
| 14121 | NumberOfEnumElements(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14122 | |
Leon Clarke | e46be81 | 2010-01-19 14:06:41 +0000 | [diff] [blame] | 14123 | template |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14124 | int HashTable<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: |
| 14125 | FindEntry(uint32_t); |
Leon Clarke | e46be81 | 2010-01-19 14:06:41 +0000 | [diff] [blame] | 14126 | |
| 14127 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14128 | Handle<Object> JSObject::PrepareSlowElementsForSort( |
| 14129 | Handle<JSObject> object, uint32_t limit) { |
| 14130 | DCHECK(object->HasDictionaryElements()); |
| 14131 | Isolate* isolate = object->GetIsolate(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14132 | // Must stay in dictionary mode, either because of requires_slow_elements, |
| 14133 | // or because we are not going to sort (and therefore compact) all of the |
| 14134 | // elements. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14135 | Handle<SeededNumberDictionary> dict(object->element_dictionary(), isolate); |
| 14136 | Handle<SeededNumberDictionary> new_dict = |
| 14137 | SeededNumberDictionary::New(isolate, dict->NumberOfElements()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14138 | |
| 14139 | uint32_t pos = 0; |
| 14140 | uint32_t undefs = 0; |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 14141 | int capacity = dict->Capacity(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14142 | Handle<Smi> bailout(Smi::FromInt(-1), isolate); |
| 14143 | // Entry to the new dictionary does not cause it to grow, as we have |
| 14144 | // allocated one that is large enough for all entries. |
| 14145 | DisallowHeapAllocation no_gc; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14146 | for (int i = 0; i < capacity; i++) { |
| 14147 | Object* k = dict->KeyAt(i); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14148 | if (!dict->IsKey(k)) continue; |
| 14149 | |
| 14150 | DCHECK(k->IsNumber()); |
| 14151 | DCHECK(!k->IsSmi() || Smi::cast(k)->value() >= 0); |
| 14152 | DCHECK(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0); |
| 14153 | DCHECK(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32); |
| 14154 | |
| 14155 | HandleScope scope(isolate); |
| 14156 | Handle<Object> value(dict->ValueAt(i), isolate); |
| 14157 | PropertyDetails details = dict->DetailsAt(i); |
| 14158 | if (details.type() == CALLBACKS || details.IsReadOnly()) { |
| 14159 | // Bail out and do the sorting of undefineds and array holes in JS. |
| 14160 | // Also bail out if the element is not supposed to be moved. |
| 14161 | return bailout; |
| 14162 | } |
| 14163 | |
| 14164 | uint32_t key = NumberToUint32(k); |
| 14165 | if (key < limit) { |
| 14166 | if (value->IsUndefined()) { |
| 14167 | undefs++; |
| 14168 | } else if (pos > static_cast<uint32_t>(Smi::kMaxValue)) { |
| 14169 | // Adding an entry with the key beyond smi-range requires |
| 14170 | // allocation. Bailout. |
| 14171 | return bailout; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14172 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14173 | Handle<Object> result = SeededNumberDictionary::AddNumberEntry( |
| 14174 | new_dict, pos, value, details); |
| 14175 | DCHECK(result.is_identical_to(new_dict)); |
| 14176 | USE(result); |
| 14177 | pos++; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14178 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14179 | } else if (key > static_cast<uint32_t>(Smi::kMaxValue)) { |
| 14180 | // Adding an entry with the key beyond smi-range requires |
| 14181 | // allocation. Bailout. |
| 14182 | return bailout; |
| 14183 | } else { |
| 14184 | Handle<Object> result = SeededNumberDictionary::AddNumberEntry( |
| 14185 | new_dict, key, value, details); |
| 14186 | DCHECK(result.is_identical_to(new_dict)); |
| 14187 | USE(result); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14188 | } |
| 14189 | } |
| 14190 | |
| 14191 | uint32_t result = pos; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14192 | PropertyDetails no_details = PropertyDetails(NONE, NORMAL, 0); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14193 | while (undefs > 0) { |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 14194 | if (pos > static_cast<uint32_t>(Smi::kMaxValue)) { |
| 14195 | // Adding an entry with the key beyond smi-range requires |
| 14196 | // allocation. Bailout. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14197 | return bailout; |
Steve Block | 1e0659c | 2011-05-24 12:43:12 +0100 | [diff] [blame] | 14198 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14199 | HandleScope scope(isolate); |
| 14200 | Handle<Object> result = SeededNumberDictionary::AddNumberEntry( |
| 14201 | new_dict, pos, isolate->factory()->undefined_value(), no_details); |
| 14202 | DCHECK(result.is_identical_to(new_dict)); |
| 14203 | USE(result); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14204 | pos++; |
| 14205 | undefs--; |
| 14206 | } |
| 14207 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14208 | object->set_elements(*new_dict); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14209 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14210 | AllowHeapAllocation allocate_return_value; |
| 14211 | return isolate->factory()->NewNumberFromUint(result); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14212 | } |
| 14213 | |
| 14214 | |
| 14215 | // Collects all defined (non-hole) and non-undefined (array) elements at |
| 14216 | // the start of the elements array. |
| 14217 | // If the object is in dictionary mode, it is converted to fast elements |
| 14218 | // mode. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14219 | Handle<Object> JSObject::PrepareElementsForSort(Handle<JSObject> object, |
| 14220 | uint32_t limit) { |
| 14221 | Isolate* isolate = object->GetIsolate(); |
| 14222 | if (object->HasSloppyArgumentsElements() || |
| 14223 | object->map()->is_observed()) { |
| 14224 | return handle(Smi::FromInt(-1), isolate); |
| 14225 | } |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 14226 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14227 | if (object->HasDictionaryElements()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14228 | // Convert to fast elements containing only the existing properties. |
| 14229 | // Ordering is irrelevant, since we are going to sort anyway. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14230 | Handle<SeededNumberDictionary> dict(object->element_dictionary()); |
| 14231 | if (object->IsJSArray() || dict->requires_slow_elements() || |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14232 | dict->max_number_key() >= limit) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14233 | return JSObject::PrepareSlowElementsForSort(object, limit); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14234 | } |
| 14235 | // Convert to fast elements. |
| 14236 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14237 | Handle<Map> new_map = |
| 14238 | JSObject::GetElementsTransitionMap(object, FAST_HOLEY_ELEMENTS); |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 14239 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14240 | PretenureFlag tenure = isolate->heap()->InNewSpace(*object) ? |
| 14241 | NOT_TENURED: TENURED; |
| 14242 | Handle<FixedArray> fast_elements = |
| 14243 | isolate->factory()->NewFixedArray(dict->NumberOfElements(), tenure); |
| 14244 | dict->CopyValuesTo(*fast_elements); |
| 14245 | JSObject::ValidateElements(object); |
Steve Block | 8defd9f | 2010-07-08 12:39:36 +0100 | [diff] [blame] | 14246 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14247 | JSObject::SetMapAndElements(object, new_map, fast_elements); |
| 14248 | } else if (object->HasExternalArrayElements() || |
| 14249 | object->HasFixedTypedArrayElements()) { |
| 14250 | // Typed arrays cannot have holes or undefined elements. |
| 14251 | return handle(Smi::FromInt( |
| 14252 | FixedArrayBase::cast(object->elements())->length()), isolate); |
| 14253 | } else if (!object->HasFastDoubleElements()) { |
| 14254 | EnsureWritableFastElements(object); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14255 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14256 | DCHECK(object->HasFastSmiOrObjectElements() || |
| 14257 | object->HasFastDoubleElements()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14258 | |
| 14259 | // Collect holes at the end, undefined before that and the rest at the |
| 14260 | // start, and return the number of non-hole, non-undefined values. |
| 14261 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14262 | Handle<FixedArrayBase> elements_base(object->elements()); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 14263 | uint32_t elements_length = static_cast<uint32_t>(elements_base->length()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14264 | if (limit > elements_length) { |
| 14265 | limit = elements_length ; |
| 14266 | } |
| 14267 | if (limit == 0) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14268 | return handle(Smi::FromInt(0), isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14269 | } |
| 14270 | |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 14271 | uint32_t result = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14272 | if (elements_base->map() == isolate->heap()->fixed_double_array_map()) { |
| 14273 | FixedDoubleArray* elements = FixedDoubleArray::cast(*elements_base); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 14274 | // Split elements into defined and the_hole, in that order. |
| 14275 | unsigned int holes = limit; |
| 14276 | // Assume most arrays contain no holes and undefined values, so minimize the |
| 14277 | // number of stores of non-undefined, non-the-hole values. |
| 14278 | for (unsigned int i = 0; i < holes; i++) { |
| 14279 | if (elements->is_the_hole(i)) { |
| 14280 | holes--; |
| 14281 | } else { |
| 14282 | continue; |
| 14283 | } |
| 14284 | // Position i needs to be filled. |
| 14285 | while (holes > i) { |
| 14286 | if (elements->is_the_hole(holes)) { |
| 14287 | holes--; |
| 14288 | } else { |
| 14289 | elements->set(i, elements->get_scalar(holes)); |
| 14290 | break; |
| 14291 | } |
| 14292 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14293 | } |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 14294 | result = holes; |
| 14295 | while (holes < limit) { |
| 14296 | elements->set_the_hole(holes); |
| 14297 | holes++; |
| 14298 | } |
| 14299 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14300 | FixedArray* elements = FixedArray::cast(*elements_base); |
| 14301 | DisallowHeapAllocation no_gc; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 14302 | |
| 14303 | // Split elements into defined, undefined and the_hole, in that order. Only |
| 14304 | // count locations for undefined and the hole, and fill them afterwards. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14305 | WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_gc); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 14306 | unsigned int undefs = limit; |
| 14307 | unsigned int holes = limit; |
| 14308 | // Assume most arrays contain no holes and undefined values, so minimize the |
| 14309 | // number of stores of non-undefined, non-the-hole values. |
| 14310 | for (unsigned int i = 0; i < undefs; i++) { |
| 14311 | Object* current = elements->get(i); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14312 | if (current->IsTheHole()) { |
| 14313 | holes--; |
| 14314 | undefs--; |
| 14315 | } else if (current->IsUndefined()) { |
| 14316 | undefs--; |
| 14317 | } else { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 14318 | continue; |
| 14319 | } |
| 14320 | // Position i needs to be filled. |
| 14321 | while (undefs > i) { |
| 14322 | current = elements->get(undefs); |
| 14323 | if (current->IsTheHole()) { |
| 14324 | holes--; |
| 14325 | undefs--; |
| 14326 | } else if (current->IsUndefined()) { |
| 14327 | undefs--; |
| 14328 | } else { |
| 14329 | elements->set(i, current, write_barrier); |
| 14330 | break; |
| 14331 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14332 | } |
| 14333 | } |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 14334 | result = undefs; |
| 14335 | while (undefs < holes) { |
| 14336 | elements->set_undefined(undefs); |
| 14337 | undefs++; |
| 14338 | } |
| 14339 | while (holes < limit) { |
| 14340 | elements->set_the_hole(holes); |
| 14341 | holes++; |
| 14342 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14343 | } |
| 14344 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14345 | return isolate->factory()->NewNumberFromUint(result); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14346 | } |
| 14347 | |
| 14348 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14349 | ExternalArrayType JSTypedArray::type() { |
| 14350 | switch (elements()->map()->instance_type()) { |
| 14351 | #define INSTANCE_TYPE_TO_ARRAY_TYPE(Type, type, TYPE, ctype, size) \ |
| 14352 | case EXTERNAL_##TYPE##_ARRAY_TYPE: \ |
| 14353 | case FIXED_##TYPE##_ARRAY_TYPE: \ |
| 14354 | return kExternal##Type##Array; |
| 14355 | |
| 14356 | TYPED_ARRAYS(INSTANCE_TYPE_TO_ARRAY_TYPE) |
| 14357 | #undef INSTANCE_TYPE_TO_ARRAY_TYPE |
| 14358 | |
| 14359 | default: |
| 14360 | UNREACHABLE(); |
| 14361 | return static_cast<ExternalArrayType>(-1); |
| 14362 | } |
| 14363 | } |
| 14364 | |
| 14365 | |
| 14366 | size_t JSTypedArray::element_size() { |
| 14367 | switch (elements()->map()->instance_type()) { |
| 14368 | #define INSTANCE_TYPE_TO_ELEMENT_SIZE(Type, type, TYPE, ctype, size) \ |
| 14369 | case EXTERNAL_##TYPE##_ARRAY_TYPE: \ |
| 14370 | return size; |
| 14371 | |
| 14372 | TYPED_ARRAYS(INSTANCE_TYPE_TO_ELEMENT_SIZE) |
| 14373 | #undef INSTANCE_TYPE_TO_ELEMENT_SIZE |
| 14374 | |
| 14375 | default: |
| 14376 | UNREACHABLE(); |
| 14377 | return 0; |
| 14378 | } |
| 14379 | } |
| 14380 | |
| 14381 | |
| 14382 | Handle<Object> ExternalUint8ClampedArray::SetValue( |
| 14383 | Handle<ExternalUint8ClampedArray> array, |
| 14384 | uint32_t index, |
| 14385 | Handle<Object> value) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14386 | uint8_t clamped_value = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14387 | if (index < static_cast<uint32_t>(array->length())) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14388 | if (value->IsSmi()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14389 | int int_value = Handle<Smi>::cast(value)->value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14390 | if (int_value < 0) { |
| 14391 | clamped_value = 0; |
| 14392 | } else if (int_value > 255) { |
| 14393 | clamped_value = 255; |
| 14394 | } else { |
| 14395 | clamped_value = static_cast<uint8_t>(int_value); |
| 14396 | } |
| 14397 | } else if (value->IsHeapNumber()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14398 | double double_value = Handle<HeapNumber>::cast(value)->value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14399 | if (!(double_value > 0)) { |
| 14400 | // NaN and less than zero clamp to zero. |
| 14401 | clamped_value = 0; |
| 14402 | } else if (double_value > 255) { |
| 14403 | // Greater than 255 clamp to 255. |
| 14404 | clamped_value = 255; |
| 14405 | } else { |
| 14406 | // Other doubles are rounded to the nearest integer. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14407 | clamped_value = static_cast<uint8_t>(lrint(double_value)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14408 | } |
| 14409 | } else { |
| 14410 | // Clamp undefined to zero (default). All other types have been |
| 14411 | // converted to a number type further up in the call chain. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14412 | DCHECK(value->IsUndefined()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14413 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14414 | array->set(index, clamped_value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14415 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14416 | return handle(Smi::FromInt(clamped_value), array->GetIsolate()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14417 | } |
| 14418 | |
| 14419 | |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14420 | template<typename ExternalArrayClass, typename ValueType> |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14421 | static Handle<Object> ExternalArrayIntSetter( |
| 14422 | Isolate* isolate, |
| 14423 | Handle<ExternalArrayClass> receiver, |
| 14424 | uint32_t index, |
| 14425 | Handle<Object> value) { |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14426 | ValueType cast_value = 0; |
| 14427 | if (index < static_cast<uint32_t>(receiver->length())) { |
| 14428 | if (value->IsSmi()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14429 | int int_value = Handle<Smi>::cast(value)->value(); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14430 | cast_value = static_cast<ValueType>(int_value); |
| 14431 | } else if (value->IsHeapNumber()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14432 | double double_value = Handle<HeapNumber>::cast(value)->value(); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14433 | cast_value = static_cast<ValueType>(DoubleToInt32(double_value)); |
| 14434 | } else { |
| 14435 | // Clamp undefined to zero (default). All other types have been |
| 14436 | // converted to a number type further up in the call chain. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14437 | DCHECK(value->IsUndefined()); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14438 | } |
| 14439 | receiver->set(index, cast_value); |
| 14440 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14441 | return isolate->factory()->NewNumberFromInt(cast_value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14442 | } |
| 14443 | |
| 14444 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14445 | Handle<Object> ExternalInt8Array::SetValue(Handle<ExternalInt8Array> array, |
| 14446 | uint32_t index, |
| 14447 | Handle<Object> value) { |
| 14448 | return ExternalArrayIntSetter<ExternalInt8Array, int8_t>( |
| 14449 | array->GetIsolate(), array, index, value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14450 | } |
| 14451 | |
| 14452 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14453 | Handle<Object> ExternalUint8Array::SetValue(Handle<ExternalUint8Array> array, |
| 14454 | uint32_t index, |
| 14455 | Handle<Object> value) { |
| 14456 | return ExternalArrayIntSetter<ExternalUint8Array, uint8_t>( |
| 14457 | array->GetIsolate(), array, index, value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14458 | } |
| 14459 | |
| 14460 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14461 | Handle<Object> ExternalInt16Array::SetValue(Handle<ExternalInt16Array> array, |
| 14462 | uint32_t index, |
| 14463 | Handle<Object> value) { |
| 14464 | return ExternalArrayIntSetter<ExternalInt16Array, int16_t>( |
| 14465 | array->GetIsolate(), array, index, value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14466 | } |
| 14467 | |
| 14468 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14469 | Handle<Object> ExternalUint16Array::SetValue(Handle<ExternalUint16Array> array, |
| 14470 | uint32_t index, |
| 14471 | Handle<Object> value) { |
| 14472 | return ExternalArrayIntSetter<ExternalUint16Array, uint16_t>( |
| 14473 | array->GetIsolate(), array, index, value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14474 | } |
| 14475 | |
| 14476 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14477 | Handle<Object> ExternalInt32Array::SetValue(Handle<ExternalInt32Array> array, |
| 14478 | uint32_t index, |
| 14479 | Handle<Object> value) { |
| 14480 | return ExternalArrayIntSetter<ExternalInt32Array, int32_t>( |
| 14481 | array->GetIsolate(), array, index, value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14482 | } |
| 14483 | |
| 14484 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14485 | Handle<Object> ExternalUint32Array::SetValue( |
| 14486 | Handle<ExternalUint32Array> array, |
| 14487 | uint32_t index, |
| 14488 | Handle<Object> value) { |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14489 | uint32_t cast_value = 0; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14490 | if (index < static_cast<uint32_t>(array->length())) { |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14491 | if (value->IsSmi()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14492 | int int_value = Handle<Smi>::cast(value)->value(); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14493 | cast_value = static_cast<uint32_t>(int_value); |
| 14494 | } else if (value->IsHeapNumber()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14495 | double double_value = Handle<HeapNumber>::cast(value)->value(); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14496 | cast_value = static_cast<uint32_t>(DoubleToUint32(double_value)); |
| 14497 | } else { |
| 14498 | // Clamp undefined to zero (default). All other types have been |
| 14499 | // converted to a number type further up in the call chain. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14500 | DCHECK(value->IsUndefined()); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14501 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14502 | array->set(index, cast_value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14503 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14504 | return array->GetIsolate()->factory()->NewNumberFromUint(cast_value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14505 | } |
| 14506 | |
| 14507 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14508 | Handle<Object> ExternalFloat32Array::SetValue( |
| 14509 | Handle<ExternalFloat32Array> array, |
| 14510 | uint32_t index, |
| 14511 | Handle<Object> value) { |
| 14512 | float cast_value = static_cast<float>(base::OS::nan_value()); |
| 14513 | if (index < static_cast<uint32_t>(array->length())) { |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14514 | if (value->IsSmi()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14515 | int int_value = Handle<Smi>::cast(value)->value(); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14516 | cast_value = static_cast<float>(int_value); |
| 14517 | } else if (value->IsHeapNumber()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14518 | double double_value = Handle<HeapNumber>::cast(value)->value(); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14519 | cast_value = static_cast<float>(double_value); |
| 14520 | } else { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 14521 | // Clamp undefined to NaN (default). All other types have been |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14522 | // converted to a number type further up in the call chain. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14523 | DCHECK(value->IsUndefined()); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14524 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14525 | array->set(index, cast_value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14526 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14527 | return array->GetIsolate()->factory()->NewNumber(cast_value); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14528 | } |
| 14529 | |
| 14530 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14531 | Handle<Object> ExternalFloat64Array::SetValue( |
| 14532 | Handle<ExternalFloat64Array> array, |
| 14533 | uint32_t index, |
| 14534 | Handle<Object> value) { |
| 14535 | double double_value = base::OS::nan_value(); |
| 14536 | if (index < static_cast<uint32_t>(array->length())) { |
| 14537 | if (value->IsNumber()) { |
| 14538 | double_value = value->Number(); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 14539 | } else { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 14540 | // Clamp undefined to NaN (default). All other types have been |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 14541 | // converted to a number type further up in the call chain. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14542 | DCHECK(value->IsUndefined()); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 14543 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14544 | array->set(index, double_value); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 14545 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14546 | return array->GetIsolate()->factory()->NewNumber(double_value); |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 14547 | } |
| 14548 | |
| 14549 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14550 | Handle<PropertyCell> JSGlobalObject::EnsurePropertyCell( |
| 14551 | Handle<JSGlobalObject> global, |
| 14552 | Handle<Name> name) { |
| 14553 | DCHECK(!global->HasFastProperties()); |
| 14554 | int entry = global->property_dictionary()->FindEntry(name); |
| 14555 | if (entry == NameDictionary::kNotFound) { |
| 14556 | Isolate* isolate = global->GetIsolate(); |
| 14557 | Handle<PropertyCell> cell = isolate->factory()->NewPropertyCell( |
| 14558 | isolate->factory()->the_hole_value()); |
| 14559 | PropertyDetails details(NONE, NORMAL, 0); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14560 | details = details.AsDeleted(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14561 | Handle<NameDictionary> dictionary = NameDictionary::Add( |
| 14562 | handle(global->property_dictionary()), name, cell, details); |
| 14563 | global->set_properties(*dictionary); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14564 | return cell; |
| 14565 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14566 | Object* value = global->property_dictionary()->ValueAt(entry); |
| 14567 | DCHECK(value->IsPropertyCell()); |
| 14568 | return handle(PropertyCell::cast(value)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14569 | } |
| 14570 | } |
| 14571 | |
| 14572 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14573 | // This class is used for looking up two character strings in the string table. |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14574 | // If we don't have a hit we don't want to waste much time so we unroll the |
| 14575 | // string hash calculation loop here for speed. Doesn't work if the two |
| 14576 | // characters form a decimal integer, since such strings have a different hash |
| 14577 | // algorithm. |
| 14578 | class TwoCharHashTableKey : public HashTableKey { |
| 14579 | public: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14580 | TwoCharHashTableKey(uint16_t c1, uint16_t c2, uint32_t seed) |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14581 | : c1_(c1), c2_(c2) { |
| 14582 | // Char 1. |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 14583 | uint32_t hash = seed; |
| 14584 | hash += c1; |
| 14585 | hash += hash << 10; |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14586 | hash ^= hash >> 6; |
| 14587 | // Char 2. |
| 14588 | hash += c2; |
| 14589 | hash += hash << 10; |
| 14590 | hash ^= hash >> 6; |
| 14591 | // GetHash. |
| 14592 | hash += hash << 3; |
| 14593 | hash ^= hash >> 11; |
| 14594 | hash += hash << 15; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14595 | if ((hash & String::kHashBitMask) == 0) hash = StringHasher::kZeroHash; |
| 14596 | hash_ = hash; |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14597 | #ifdef DEBUG |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14598 | // If this assert fails then we failed to reproduce the two-character |
| 14599 | // version of the string hashing algorithm above. One reason could be |
| 14600 | // that we were passed two digits as characters, since the hash |
| 14601 | // algorithm is different in that case. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14602 | uint16_t chars[2] = {c1, c2}; |
| 14603 | uint32_t check_hash = StringHasher::HashSequentialString(chars, 2, seed); |
| 14604 | hash = (hash << String::kHashShift) | String::kIsNotArrayIndexMask; |
| 14605 | DCHECK_EQ(static_cast<int32_t>(hash), static_cast<int32_t>(check_hash)); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14606 | #endif |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14607 | } |
| 14608 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14609 | bool IsMatch(Object* o) OVERRIDE { |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14610 | if (!o->IsString()) return false; |
| 14611 | String* other = String::cast(o); |
| 14612 | if (other->length() != 2) return false; |
| 14613 | if (other->Get(0) != c1_) return false; |
| 14614 | return other->Get(1) == c2_; |
| 14615 | } |
| 14616 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14617 | uint32_t Hash() OVERRIDE { return hash_; } |
| 14618 | uint32_t HashForObject(Object* key) OVERRIDE { |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14619 | if (!key->IsString()) return 0; |
| 14620 | return String::cast(key)->Hash(); |
| 14621 | } |
| 14622 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14623 | Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { |
| 14624 | // The TwoCharHashTableKey is only used for looking in the string |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14625 | // table, not for adding to it. |
| 14626 | UNREACHABLE(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14627 | return MaybeHandle<Object>().ToHandleChecked(); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14628 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 14629 | |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14630 | private: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14631 | uint16_t c1_; |
| 14632 | uint16_t c2_; |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14633 | uint32_t hash_; |
| 14634 | }; |
| 14635 | |
| 14636 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14637 | MaybeHandle<String> StringTable::InternalizeStringIfExists( |
| 14638 | Isolate* isolate, |
| 14639 | Handle<String> string) { |
| 14640 | if (string->IsInternalizedString()) { |
| 14641 | return string; |
| 14642 | } |
| 14643 | return LookupStringIfExists(isolate, string); |
| 14644 | } |
| 14645 | |
| 14646 | |
| 14647 | MaybeHandle<String> StringTable::LookupStringIfExists( |
| 14648 | Isolate* isolate, |
| 14649 | Handle<String> string) { |
| 14650 | Handle<StringTable> string_table = isolate->factory()->string_table(); |
| 14651 | InternalizedStringKey key(string); |
| 14652 | int entry = string_table->FindEntry(&key); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14653 | if (entry == kNotFound) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14654 | return MaybeHandle<String>(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14655 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14656 | Handle<String> result(String::cast(string_table->KeyAt(entry)), isolate); |
| 14657 | DCHECK(StringShape(*result).IsInternalized()); |
| 14658 | return result; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14659 | } |
| 14660 | } |
| 14661 | |
| 14662 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14663 | MaybeHandle<String> StringTable::LookupTwoCharsStringIfExists( |
| 14664 | Isolate* isolate, |
| 14665 | uint16_t c1, |
| 14666 | uint16_t c2) { |
| 14667 | Handle<StringTable> string_table = isolate->factory()->string_table(); |
| 14668 | TwoCharHashTableKey key(c1, c2, isolate->heap()->HashSeed()); |
| 14669 | int entry = string_table->FindEntry(&key); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14670 | if (entry == kNotFound) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14671 | return MaybeHandle<String>(); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14672 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14673 | Handle<String> result(String::cast(string_table->KeyAt(entry)), isolate); |
| 14674 | DCHECK(StringShape(*result).IsInternalized()); |
| 14675 | return result; |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 14676 | } |
| 14677 | } |
| 14678 | |
| 14679 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14680 | Handle<String> StringTable::LookupString(Isolate* isolate, |
| 14681 | Handle<String> string) { |
| 14682 | InternalizedStringKey key(string); |
| 14683 | return LookupKey(isolate, &key); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14684 | } |
| 14685 | |
| 14686 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14687 | Handle<String> StringTable::LookupKey(Isolate* isolate, HashTableKey* key) { |
| 14688 | Handle<StringTable> table = isolate->factory()->string_table(); |
| 14689 | int entry = table->FindEntry(key); |
Steve Block | 9fac840 | 2011-05-12 15:51:54 +0100 | [diff] [blame] | 14690 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14691 | // String already in table. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14692 | if (entry != kNotFound) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14693 | return handle(String::cast(table->KeyAt(entry)), isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14694 | } |
| 14695 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14696 | // Adding new string. Grow table if needed. |
| 14697 | table = StringTable::EnsureCapacity(table, 1, key); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14698 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14699 | // Create string object. |
| 14700 | Handle<Object> string = key->AsHandle(isolate); |
| 14701 | // There must be no attempts to internalize strings that could throw |
| 14702 | // InvalidStringLength error. |
| 14703 | CHECK(!string.is_null()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14704 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14705 | // Add the new string and return it along with the string table. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14706 | entry = table->FindInsertionEntry(key->Hash()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14707 | table->set(EntryToIndex(entry), *string); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14708 | table->ElementAdded(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14709 | |
| 14710 | isolate->factory()->set_string_table(table); |
| 14711 | return Handle<String>::cast(string); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14712 | } |
| 14713 | |
| 14714 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14715 | Handle<Object> CompilationCacheTable::Lookup(Handle<String> src, |
| 14716 | Handle<Context> context) { |
| 14717 | Isolate* isolate = GetIsolate(); |
| 14718 | Handle<SharedFunctionInfo> shared(context->closure()->shared()); |
| 14719 | StringSharedKey key(src, shared, FLAG_use_strict ? STRICT : SLOPPY, |
| 14720 | RelocInfo::kNoPosition); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14721 | int entry = FindEntry(&key); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14722 | if (entry == kNotFound) return isolate->factory()->undefined_value(); |
| 14723 | return Handle<Object>(get(EntryToIndex(entry) + 1), isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14724 | } |
| 14725 | |
| 14726 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14727 | Handle<Object> CompilationCacheTable::LookupEval( |
| 14728 | Handle<String> src, Handle<SharedFunctionInfo> outer_info, |
| 14729 | StrictMode strict_mode, int scope_position) { |
| 14730 | Isolate* isolate = GetIsolate(); |
| 14731 | // Cache key is the tuple (source, outer shared function info, scope position) |
| 14732 | // to unambiguously identify the context chain the cached eval code assumes. |
| 14733 | StringSharedKey key(src, outer_info, strict_mode, scope_position); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14734 | int entry = FindEntry(&key); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14735 | if (entry == kNotFound) return isolate->factory()->undefined_value(); |
| 14736 | return Handle<Object>(get(EntryToIndex(entry) + 1), isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14737 | } |
| 14738 | |
| 14739 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14740 | Handle<Object> CompilationCacheTable::LookupRegExp(Handle<String> src, |
| 14741 | JSRegExp::Flags flags) { |
| 14742 | Isolate* isolate = GetIsolate(); |
| 14743 | DisallowHeapAllocation no_allocation; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14744 | RegExpKey key(src, flags); |
| 14745 | int entry = FindEntry(&key); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14746 | if (entry == kNotFound) return isolate->factory()->undefined_value(); |
| 14747 | return Handle<Object>(get(EntryToIndex(entry) + 1), isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14748 | } |
| 14749 | |
| 14750 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14751 | Handle<CompilationCacheTable> CompilationCacheTable::Put( |
| 14752 | Handle<CompilationCacheTable> cache, Handle<String> src, |
| 14753 | Handle<Context> context, Handle<Object> value) { |
| 14754 | Isolate* isolate = cache->GetIsolate(); |
| 14755 | Handle<SharedFunctionInfo> shared(context->closure()->shared()); |
| 14756 | StringSharedKey key(src, shared, FLAG_use_strict ? STRICT : SLOPPY, |
| 14757 | RelocInfo::kNoPosition); |
| 14758 | cache = EnsureCapacity(cache, 1, &key); |
| 14759 | Handle<Object> k = key.AsHandle(isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14760 | int entry = cache->FindInsertionEntry(key.Hash()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14761 | cache->set(EntryToIndex(entry), *k); |
| 14762 | cache->set(EntryToIndex(entry) + 1, *value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14763 | cache->ElementAdded(); |
| 14764 | return cache; |
| 14765 | } |
| 14766 | |
| 14767 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14768 | Handle<CompilationCacheTable> CompilationCacheTable::PutEval( |
| 14769 | Handle<CompilationCacheTable> cache, Handle<String> src, |
| 14770 | Handle<SharedFunctionInfo> outer_info, Handle<SharedFunctionInfo> value, |
| 14771 | int scope_position) { |
| 14772 | Isolate* isolate = cache->GetIsolate(); |
| 14773 | StringSharedKey key(src, outer_info, value->strict_mode(), scope_position); |
| 14774 | cache = EnsureCapacity(cache, 1, &key); |
| 14775 | Handle<Object> k = key.AsHandle(isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14776 | int entry = cache->FindInsertionEntry(key.Hash()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14777 | cache->set(EntryToIndex(entry), *k); |
| 14778 | cache->set(EntryToIndex(entry) + 1, *value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14779 | cache->ElementAdded(); |
| 14780 | return cache; |
| 14781 | } |
| 14782 | |
| 14783 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14784 | Handle<CompilationCacheTable> CompilationCacheTable::PutRegExp( |
| 14785 | Handle<CompilationCacheTable> cache, Handle<String> src, |
| 14786 | JSRegExp::Flags flags, Handle<FixedArray> value) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14787 | RegExpKey key(src, flags); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14788 | cache = EnsureCapacity(cache, 1, &key); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14789 | int entry = cache->FindInsertionEntry(key.Hash()); |
Steve Block | 3ce2e20 | 2009-11-05 08:53:23 +0000 | [diff] [blame] | 14790 | // We store the value in the key slot, and compare the search key |
| 14791 | // to the stored value with a custon IsMatch function during lookups. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14792 | cache->set(EntryToIndex(entry), *value); |
| 14793 | cache->set(EntryToIndex(entry) + 1, *value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14794 | cache->ElementAdded(); |
| 14795 | return cache; |
| 14796 | } |
| 14797 | |
| 14798 | |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 14799 | void CompilationCacheTable::Remove(Object* value) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14800 | DisallowHeapAllocation no_allocation; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 14801 | Object* the_hole_value = GetHeap()->the_hole_value(); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 14802 | for (int entry = 0, size = Capacity(); entry < size; entry++) { |
| 14803 | int entry_index = EntryToIndex(entry); |
| 14804 | int value_index = entry_index + 1; |
| 14805 | if (get(value_index) == value) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 14806 | NoWriteBarrierSet(this, entry_index, the_hole_value); |
| 14807 | NoWriteBarrierSet(this, value_index, the_hole_value); |
Ben Murdoch | b0fe162 | 2011-05-05 13:52:32 +0100 | [diff] [blame] | 14808 | ElementRemoved(); |
| 14809 | } |
| 14810 | } |
| 14811 | return; |
| 14812 | } |
| 14813 | |
| 14814 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14815 | // StringsKey used for HashTable where key is array of internalized strings. |
| 14816 | class StringsKey : public HashTableKey { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14817 | public: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14818 | explicit StringsKey(Handle<FixedArray> strings) : strings_(strings) { } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14819 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14820 | bool IsMatch(Object* strings) OVERRIDE { |
| 14821 | FixedArray* o = FixedArray::cast(strings); |
| 14822 | int len = strings_->length(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14823 | if (o->length() != len) return false; |
| 14824 | for (int i = 0; i < len; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14825 | if (o->get(i) != strings_->get(i)) return false; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14826 | } |
| 14827 | return true; |
| 14828 | } |
| 14829 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14830 | uint32_t Hash() OVERRIDE { return HashForObject(*strings_); } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14831 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14832 | uint32_t HashForObject(Object* obj) OVERRIDE { |
| 14833 | FixedArray* strings = FixedArray::cast(obj); |
| 14834 | int len = strings->length(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14835 | uint32_t hash = 0; |
| 14836 | for (int i = 0; i < len; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14837 | hash ^= String::cast(strings->get(i))->Hash(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14838 | } |
| 14839 | return hash; |
| 14840 | } |
| 14841 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14842 | Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { return strings_; } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14843 | |
| 14844 | private: |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14845 | Handle<FixedArray> strings_; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14846 | }; |
| 14847 | |
| 14848 | |
| 14849 | Object* MapCache::Lookup(FixedArray* array) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14850 | DisallowHeapAllocation no_alloc; |
| 14851 | StringsKey key(handle(array)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14852 | int entry = FindEntry(&key); |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 14853 | if (entry == kNotFound) return GetHeap()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14854 | return get(EntryToIndex(entry) + 1); |
| 14855 | } |
| 14856 | |
| 14857 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14858 | Handle<MapCache> MapCache::Put( |
| 14859 | Handle<MapCache> map_cache, Handle<FixedArray> array, Handle<Map> value) { |
| 14860 | StringsKey key(array); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14861 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14862 | Handle<MapCache> new_cache = EnsureCapacity(map_cache, 1, &key); |
| 14863 | int entry = new_cache->FindInsertionEntry(key.Hash()); |
| 14864 | new_cache->set(EntryToIndex(entry), *array); |
| 14865 | new_cache->set(EntryToIndex(entry) + 1, *value); |
| 14866 | new_cache->ElementAdded(); |
| 14867 | return new_cache; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14868 | } |
| 14869 | |
| 14870 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14871 | template<typename Derived, typename Shape, typename Key> |
| 14872 | Handle<Derived> Dictionary<Derived, Shape, Key>::New( |
| 14873 | Isolate* isolate, |
| 14874 | int at_least_space_for, |
| 14875 | PretenureFlag pretenure) { |
| 14876 | DCHECK(0 <= at_least_space_for); |
| 14877 | Handle<Derived> dict = DerivedHashTable::New(isolate, |
| 14878 | at_least_space_for, |
| 14879 | USE_DEFAULT_MINIMUM_CAPACITY, |
| 14880 | pretenure); |
| 14881 | |
John Reck | 5913587 | 2010-11-02 12:39:01 -0700 | [diff] [blame] | 14882 | // Initialize the next enumeration index. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14883 | dict->SetNextEnumerationIndex(PropertyDetails::kInitialIndex); |
| 14884 | return dict; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14885 | } |
| 14886 | |
| 14887 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14888 | template<typename Derived, typename Shape, typename Key> |
| 14889 | void Dictionary<Derived, Shape, Key>::GenerateNewEnumerationIndices( |
| 14890 | Handle<Derived> dictionary) { |
| 14891 | Factory* factory = dictionary->GetIsolate()->factory(); |
| 14892 | int length = dictionary->NumberOfElements(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14893 | |
| 14894 | // Allocate and initialize iteration order array. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14895 | Handle<FixedArray> iteration_order = factory->NewFixedArray(length); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14896 | for (int i = 0; i < length; i++) { |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 14897 | iteration_order->set(i, Smi::FromInt(i)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14898 | } |
| 14899 | |
| 14900 | // Allocate array with enumeration order. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14901 | Handle<FixedArray> enumeration_order = factory->NewFixedArray(length); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14902 | |
| 14903 | // Fill the enumeration order array with property details. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14904 | int capacity = dictionary->Capacity(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14905 | int pos = 0; |
| 14906 | for (int i = 0; i < capacity; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14907 | if (dictionary->IsKey(dictionary->KeyAt(i))) { |
| 14908 | int index = dictionary->DetailsAt(i).dictionary_index(); |
| 14909 | enumeration_order->set(pos++, Smi::FromInt(index)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14910 | } |
| 14911 | } |
| 14912 | |
| 14913 | // Sort the arrays wrt. enumeration order. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14914 | iteration_order->SortPairs(*enumeration_order, enumeration_order->length()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14915 | |
| 14916 | // Overwrite the enumeration_order with the enumeration indices. |
| 14917 | for (int i = 0; i < length; i++) { |
| 14918 | int index = Smi::cast(iteration_order->get(i))->value(); |
| 14919 | int enum_index = PropertyDetails::kInitialIndex + i; |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 14920 | enumeration_order->set(index, Smi::FromInt(enum_index)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14921 | } |
| 14922 | |
| 14923 | // Update the dictionary with new indices. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14924 | capacity = dictionary->Capacity(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14925 | pos = 0; |
| 14926 | for (int i = 0; i < capacity; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14927 | if (dictionary->IsKey(dictionary->KeyAt(i))) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14928 | int enum_index = Smi::cast(enumeration_order->get(pos++))->value(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14929 | PropertyDetails details = dictionary->DetailsAt(i); |
| 14930 | PropertyDetails new_details = PropertyDetails( |
| 14931 | details.attributes(), details.type(), enum_index); |
| 14932 | dictionary->DetailsAtPut(i, new_details); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14933 | } |
| 14934 | } |
| 14935 | |
| 14936 | // Set the next enumeration index. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14937 | dictionary->SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14938 | } |
| 14939 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14940 | |
| 14941 | template<typename Derived, typename Shape, typename Key> |
| 14942 | Handle<Derived> Dictionary<Derived, Shape, Key>::EnsureCapacity( |
| 14943 | Handle<Derived> dictionary, int n, Key key) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14944 | // Check whether there are enough enumeration indices to add n elements. |
| 14945 | if (Shape::kIsEnumerable && |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14946 | !PropertyDetails::IsValidIndex(dictionary->NextEnumerationIndex() + n)) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14947 | // If not, we generate new indices for the properties. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14948 | GenerateNewEnumerationIndices(dictionary); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14949 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14950 | return DerivedHashTable::EnsureCapacity(dictionary, n, key); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14951 | } |
| 14952 | |
| 14953 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14954 | template<typename Derived, typename Shape, typename Key> |
| 14955 | Handle<Object> Dictionary<Derived, Shape, Key>::DeleteProperty( |
| 14956 | Handle<Derived> dictionary, |
| 14957 | int entry, |
| 14958 | JSObject::DeleteMode mode) { |
| 14959 | Factory* factory = dictionary->GetIsolate()->factory(); |
| 14960 | PropertyDetails details = dictionary->DetailsAt(entry); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14961 | // Ignore attributes if forcing a deletion. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14962 | if (!details.IsConfigurable() && mode != JSReceiver::FORCE_DELETION) { |
| 14963 | return factory->false_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14964 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14965 | |
| 14966 | dictionary->SetEntry( |
| 14967 | entry, factory->the_hole_value(), factory->the_hole_value()); |
| 14968 | dictionary->ElementRemoved(); |
| 14969 | return factory->true_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14970 | } |
| 14971 | |
| 14972 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14973 | template<typename Derived, typename Shape, typename Key> |
| 14974 | Handle<Derived> Dictionary<Derived, Shape, Key>::AtPut( |
| 14975 | Handle<Derived> dictionary, Key key, Handle<Object> value) { |
| 14976 | int entry = dictionary->FindEntry(key); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14977 | |
| 14978 | // If the entry is present set the value; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14979 | if (entry != Dictionary::kNotFound) { |
| 14980 | dictionary->ValueAtPut(entry, *value); |
| 14981 | return dictionary; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14982 | } |
| 14983 | |
| 14984 | // Check whether the dictionary should be extended. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14985 | dictionary = EnsureCapacity(dictionary, 1, key); |
| 14986 | #ifdef DEBUG |
| 14987 | USE(Shape::AsHandle(dictionary->GetIsolate(), key)); |
| 14988 | #endif |
| 14989 | PropertyDetails details = PropertyDetails(NONE, NORMAL, 0); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14990 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14991 | AddEntry(dictionary, key, value, details, dictionary->Hash(key)); |
| 14992 | return dictionary; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 14993 | } |
| 14994 | |
| 14995 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 14996 | template<typename Derived, typename Shape, typename Key> |
| 14997 | Handle<Derived> Dictionary<Derived, Shape, Key>::Add( |
| 14998 | Handle<Derived> dictionary, |
| 14999 | Key key, |
| 15000 | Handle<Object> value, |
| 15001 | PropertyDetails details) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15002 | // Valdate key is absent. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15003 | SLOW_DCHECK((dictionary->FindEntry(key) == Dictionary::kNotFound)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15004 | // Check whether the dictionary should be extended. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15005 | dictionary = EnsureCapacity(dictionary, 1, key); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 15006 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15007 | AddEntry(dictionary, key, value, details, dictionary->Hash(key)); |
| 15008 | return dictionary; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15009 | } |
| 15010 | |
| 15011 | |
| 15012 | // Add a key, value pair to the dictionary. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15013 | template<typename Derived, typename Shape, typename Key> |
| 15014 | void Dictionary<Derived, Shape, Key>::AddEntry( |
| 15015 | Handle<Derived> dictionary, |
| 15016 | Key key, |
| 15017 | Handle<Object> value, |
| 15018 | PropertyDetails details, |
| 15019 | uint32_t hash) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15020 | // Compute the key object. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15021 | Handle<Object> k = Shape::AsHandle(dictionary->GetIsolate(), key); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15022 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15023 | uint32_t entry = dictionary->FindInsertionEntry(hash); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15024 | // Insert element at empty or deleted entry |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15025 | if (!details.IsDeleted() && |
| 15026 | details.dictionary_index() == 0 && |
| 15027 | Shape::kIsEnumerable) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15028 | // Assign an enumeration index to the property and update |
| 15029 | // SetNextEnumerationIndex. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15030 | int index = dictionary->NextEnumerationIndex(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15031 | details = PropertyDetails(details.attributes(), details.type(), index); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15032 | dictionary->SetNextEnumerationIndex(index + 1); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15033 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15034 | dictionary->SetEntry(entry, k, value, details); |
| 15035 | DCHECK((dictionary->KeyAt(entry)->IsNumber() || |
| 15036 | dictionary->KeyAt(entry)->IsName())); |
| 15037 | dictionary->ElementAdded(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15038 | } |
| 15039 | |
| 15040 | |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 15041 | void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15042 | DisallowHeapAllocation no_allocation; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15043 | // If the dictionary requires slow elements an element has already |
| 15044 | // been added at a high index. |
| 15045 | if (requires_slow_elements()) return; |
| 15046 | // Check if this index is high enough that we should require slow |
| 15047 | // elements. |
| 15048 | if (key > kRequiresSlowElementsLimit) { |
| 15049 | set_requires_slow_elements(); |
| 15050 | return; |
| 15051 | } |
| 15052 | // Update max key value. |
| 15053 | Object* max_index_object = get(kMaxNumberKeyIndex); |
| 15054 | if (!max_index_object->IsSmi() || max_number_key() < key) { |
| 15055 | FixedArray::set(kMaxNumberKeyIndex, |
Leon Clarke | 4515c47 | 2010-02-03 11:58:03 +0000 | [diff] [blame] | 15056 | Smi::FromInt(key << kRequiresSlowElementsTagSize)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15057 | } |
| 15058 | } |
| 15059 | |
| 15060 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15061 | Handle<SeededNumberDictionary> SeededNumberDictionary::AddNumberEntry( |
| 15062 | Handle<SeededNumberDictionary> dictionary, |
| 15063 | uint32_t key, |
| 15064 | Handle<Object> value, |
| 15065 | PropertyDetails details) { |
| 15066 | dictionary->UpdateMaxNumberKey(key); |
| 15067 | SLOW_DCHECK(dictionary->FindEntry(key) == kNotFound); |
| 15068 | return Add(dictionary, key, value, details); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15069 | } |
| 15070 | |
| 15071 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15072 | Handle<UnseededNumberDictionary> UnseededNumberDictionary::AddNumberEntry( |
| 15073 | Handle<UnseededNumberDictionary> dictionary, |
| 15074 | uint32_t key, |
| 15075 | Handle<Object> value) { |
| 15076 | SLOW_DCHECK(dictionary->FindEntry(key) == kNotFound); |
| 15077 | return Add(dictionary, key, value, PropertyDetails(NONE, NORMAL, 0)); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 15078 | } |
| 15079 | |
| 15080 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15081 | Handle<SeededNumberDictionary> SeededNumberDictionary::AtNumberPut( |
| 15082 | Handle<SeededNumberDictionary> dictionary, |
| 15083 | uint32_t key, |
| 15084 | Handle<Object> value) { |
| 15085 | dictionary->UpdateMaxNumberKey(key); |
| 15086 | return AtPut(dictionary, key, value); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15087 | } |
| 15088 | |
| 15089 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15090 | Handle<UnseededNumberDictionary> UnseededNumberDictionary::AtNumberPut( |
| 15091 | Handle<UnseededNumberDictionary> dictionary, |
| 15092 | uint32_t key, |
| 15093 | Handle<Object> value) { |
| 15094 | return AtPut(dictionary, key, value); |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 15095 | } |
| 15096 | |
| 15097 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15098 | Handle<SeededNumberDictionary> SeededNumberDictionary::Set( |
| 15099 | Handle<SeededNumberDictionary> dictionary, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15100 | uint32_t key, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15101 | Handle<Object> value, |
| 15102 | PropertyDetails details) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15103 | int entry = dictionary->FindEntry(key); |
| 15104 | if (entry == kNotFound) { |
| 15105 | return AddNumberEntry(dictionary, key, value, details); |
| 15106 | } |
| 15107 | // Preserve enumeration index. |
| 15108 | details = PropertyDetails(details.attributes(), |
| 15109 | details.type(), |
| 15110 | dictionary->DetailsAt(entry).dictionary_index()); |
| 15111 | Handle<Object> object_key = |
| 15112 | SeededNumberDictionaryShape::AsHandle(dictionary->GetIsolate(), key); |
| 15113 | dictionary->SetEntry(entry, object_key, value, details); |
| 15114 | return dictionary; |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15115 | } |
| 15116 | |
| 15117 | |
| 15118 | Handle<UnseededNumberDictionary> UnseededNumberDictionary::Set( |
| 15119 | Handle<UnseededNumberDictionary> dictionary, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15120 | uint32_t key, |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15121 | Handle<Object> value) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15122 | int entry = dictionary->FindEntry(key); |
| 15123 | if (entry == kNotFound) return AddNumberEntry(dictionary, key, value); |
| 15124 | Handle<Object> object_key = |
| 15125 | UnseededNumberDictionaryShape::AsHandle(dictionary->GetIsolate(), key); |
| 15126 | dictionary->SetEntry(entry, object_key, value); |
| 15127 | return dictionary; |
Ben Murdoch | c7cc028 | 2012-03-05 14:35:55 +0000 | [diff] [blame] | 15128 | } |
| 15129 | |
| 15130 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15131 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15132 | template<typename Derived, typename Shape, typename Key> |
| 15133 | int Dictionary<Derived, Shape, Key>::NumberOfElementsFilterAttributes( |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15134 | PropertyAttributes filter) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15135 | int capacity = DerivedHashTable::Capacity(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15136 | int result = 0; |
| 15137 | for (int i = 0; i < capacity; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15138 | Object* k = DerivedHashTable::KeyAt(i); |
| 15139 | if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15140 | PropertyDetails details = DetailsAt(i); |
| 15141 | if (details.IsDeleted()) continue; |
| 15142 | PropertyAttributes attr = details.attributes(); |
| 15143 | if ((attr & filter) == 0) result++; |
| 15144 | } |
| 15145 | } |
| 15146 | return result; |
| 15147 | } |
| 15148 | |
| 15149 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15150 | template<typename Derived, typename Shape, typename Key> |
| 15151 | int Dictionary<Derived, Shape, Key>::NumberOfEnumElements() { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15152 | return NumberOfElementsFilterAttributes( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15153 | static_cast<PropertyAttributes>(DONT_ENUM | SYMBOLIC)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15154 | } |
| 15155 | |
| 15156 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15157 | template<typename Derived, typename Shape, typename Key> |
| 15158 | void Dictionary<Derived, Shape, Key>::CopyKeysTo( |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 15159 | FixedArray* storage, |
| 15160 | PropertyAttributes filter, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15161 | typename Dictionary<Derived, Shape, Key>::SortMode sort_mode) { |
| 15162 | DCHECK(storage->length() >= NumberOfElementsFilterAttributes(filter)); |
| 15163 | int capacity = DerivedHashTable::Capacity(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15164 | int index = 0; |
| 15165 | for (int i = 0; i < capacity; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15166 | Object* k = DerivedHashTable::KeyAt(i); |
| 15167 | if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15168 | PropertyDetails details = DetailsAt(i); |
| 15169 | if (details.IsDeleted()) continue; |
| 15170 | PropertyAttributes attr = details.attributes(); |
| 15171 | if ((attr & filter) == 0) storage->set(index++, k); |
| 15172 | } |
| 15173 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15174 | if (sort_mode == Dictionary::SORTED) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 15175 | storage->SortPairs(storage, index); |
| 15176 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15177 | DCHECK(storage->length() >= index); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15178 | } |
| 15179 | |
| 15180 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15181 | struct EnumIndexComparator { |
| 15182 | explicit EnumIndexComparator(NameDictionary* dict) : dict(dict) { } |
| 15183 | bool operator() (Smi* a, Smi* b) { |
| 15184 | PropertyDetails da(dict->DetailsAt(a->value())); |
| 15185 | PropertyDetails db(dict->DetailsAt(b->value())); |
| 15186 | return da.dictionary_index() < db.dictionary_index(); |
| 15187 | } |
| 15188 | NameDictionary* dict; |
| 15189 | }; |
| 15190 | |
| 15191 | |
| 15192 | void NameDictionary::CopyEnumKeysTo(FixedArray* storage) { |
| 15193 | int length = storage->length(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15194 | int capacity = Capacity(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15195 | int properties = 0; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15196 | for (int i = 0; i < capacity; i++) { |
| 15197 | Object* k = KeyAt(i); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15198 | if (IsKey(k) && !k->IsSymbol()) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15199 | PropertyDetails details = DetailsAt(i); |
| 15200 | if (details.IsDeleted() || details.IsDontEnum()) continue; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15201 | storage->set(properties, Smi::FromInt(i)); |
| 15202 | properties++; |
| 15203 | if (properties == length) break; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15204 | } |
| 15205 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15206 | CHECK_EQ(length, properties); |
| 15207 | EnumIndexComparator cmp(this); |
| 15208 | Smi** start = reinterpret_cast<Smi**>(storage->GetFirstElementAddress()); |
| 15209 | std::sort(start, start + length, cmp); |
| 15210 | for (int i = 0; i < length; i++) { |
| 15211 | int index = Smi::cast(storage->get(i))->value(); |
| 15212 | storage->set(i, KeyAt(index)); |
| 15213 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15214 | } |
| 15215 | |
| 15216 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15217 | template<typename Derived, typename Shape, typename Key> |
| 15218 | void Dictionary<Derived, Shape, Key>::CopyKeysTo( |
Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 15219 | FixedArray* storage, |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 15220 | int index, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15221 | PropertyAttributes filter, |
| 15222 | typename Dictionary<Derived, Shape, Key>::SortMode sort_mode) { |
| 15223 | DCHECK(storage->length() >= NumberOfElementsFilterAttributes(filter)); |
| 15224 | int capacity = DerivedHashTable::Capacity(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15225 | for (int i = 0; i < capacity; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15226 | Object* k = DerivedHashTable::KeyAt(i); |
| 15227 | if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15228 | PropertyDetails details = DetailsAt(i); |
| 15229 | if (details.IsDeleted()) continue; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15230 | PropertyAttributes attr = details.attributes(); |
| 15231 | if ((attr & filter) == 0) storage->set(index++, k); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15232 | } |
| 15233 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15234 | if (sort_mode == Dictionary::SORTED) { |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 15235 | storage->SortPairs(storage, index); |
| 15236 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15237 | DCHECK(storage->length() >= index); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15238 | } |
| 15239 | |
| 15240 | |
| 15241 | // Backwards lookup (slow). |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15242 | template<typename Derived, typename Shape, typename Key> |
| 15243 | Object* Dictionary<Derived, Shape, Key>::SlowReverseLookup(Object* value) { |
| 15244 | int capacity = DerivedHashTable::Capacity(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15245 | for (int i = 0; i < capacity; i++) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15246 | Object* k = DerivedHashTable::KeyAt(i); |
| 15247 | if (Dictionary::IsKey(k)) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15248 | Object* e = ValueAt(i); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15249 | if (e->IsPropertyCell()) { |
| 15250 | e = PropertyCell::cast(e)->value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15251 | } |
| 15252 | if (e == value) return k; |
| 15253 | } |
| 15254 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15255 | Heap* heap = Dictionary::GetHeap(); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 15256 | return heap->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15257 | } |
| 15258 | |
| 15259 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15260 | Object* ObjectHashTable::Lookup(Handle<Object> key) { |
| 15261 | DisallowHeapAllocation no_gc; |
| 15262 | DCHECK(IsKey(*key)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15263 | |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15264 | // If the object does not have an identity hash, it was never used as a key. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15265 | Object* hash = key->GetHash(); |
| 15266 | if (hash->IsUndefined()) { |
| 15267 | return GetHeap()->the_hole_value(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15268 | } |
| 15269 | int entry = FindEntry(key); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15270 | if (entry == kNotFound) return GetHeap()->the_hole_value(); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15271 | return get(EntryToIndex(entry) + 1); |
| 15272 | } |
| 15273 | |
| 15274 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15275 | Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table, |
| 15276 | Handle<Object> key, |
| 15277 | Handle<Object> value) { |
| 15278 | DCHECK(table->IsKey(*key)); |
| 15279 | DCHECK(!value->IsTheHole()); |
| 15280 | |
| 15281 | Isolate* isolate = table->GetIsolate(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15282 | |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15283 | // Make sure the key object has an identity hash code. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15284 | Handle<Smi> hash = Object::GetOrCreateHash(isolate, key); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15285 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15286 | int entry = table->FindEntry(key); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15287 | |
| 15288 | // Key is already in table, just overwrite value. |
| 15289 | if (entry != kNotFound) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15290 | table->set(EntryToIndex(entry) + 1, *value); |
| 15291 | return table; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15292 | } |
| 15293 | |
| 15294 | // Check whether the hash table should be extended. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15295 | table = EnsureCapacity(table, 1, key); |
| 15296 | table->AddEntry(table->FindInsertionEntry(hash->value()), |
| 15297 | *key, |
| 15298 | *value); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15299 | return table; |
| 15300 | } |
| 15301 | |
| 15302 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15303 | Handle<ObjectHashTable> ObjectHashTable::Remove(Handle<ObjectHashTable> table, |
| 15304 | Handle<Object> key, |
| 15305 | bool* was_present) { |
| 15306 | DCHECK(table->IsKey(*key)); |
| 15307 | |
| 15308 | Object* hash = key->GetHash(); |
| 15309 | if (hash->IsUndefined()) { |
| 15310 | *was_present = false; |
| 15311 | return table; |
| 15312 | } |
| 15313 | |
| 15314 | int entry = table->FindEntry(key); |
| 15315 | if (entry == kNotFound) { |
| 15316 | *was_present = false; |
| 15317 | return table; |
| 15318 | } |
| 15319 | |
| 15320 | *was_present = true; |
| 15321 | table->RemoveEntry(entry); |
| 15322 | return Shrink(table, key); |
| 15323 | } |
| 15324 | |
| 15325 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15326 | void ObjectHashTable::AddEntry(int entry, Object* key, Object* value) { |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15327 | set(EntryToIndex(entry), key); |
| 15328 | set(EntryToIndex(entry) + 1, value); |
| 15329 | ElementAdded(); |
| 15330 | } |
| 15331 | |
| 15332 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 15333 | void ObjectHashTable::RemoveEntry(int entry) { |
| 15334 | set_the_hole(EntryToIndex(entry)); |
| 15335 | set_the_hole(EntryToIndex(entry) + 1); |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 15336 | ElementRemoved(); |
| 15337 | } |
| 15338 | |
| 15339 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15340 | Object* WeakHashTable::Lookup(Handle<Object> key) { |
| 15341 | DisallowHeapAllocation no_gc; |
| 15342 | DCHECK(IsKey(*key)); |
| 15343 | int entry = FindEntry(key); |
| 15344 | if (entry == kNotFound) return GetHeap()->the_hole_value(); |
| 15345 | return get(EntryToValueIndex(entry)); |
| 15346 | } |
| 15347 | |
| 15348 | |
| 15349 | Handle<WeakHashTable> WeakHashTable::Put(Handle<WeakHashTable> table, |
| 15350 | Handle<Object> key, |
| 15351 | Handle<Object> value) { |
| 15352 | DCHECK(table->IsKey(*key)); |
| 15353 | int entry = table->FindEntry(key); |
| 15354 | // Key is already in table, just overwrite value. |
| 15355 | if (entry != kNotFound) { |
| 15356 | // TODO(ulan): Skipping write barrier is a temporary solution to avoid |
| 15357 | // memory leaks. Remove this once we have special visitor for weak fixed |
| 15358 | // arrays. |
| 15359 | table->set(EntryToValueIndex(entry), *value, SKIP_WRITE_BARRIER); |
| 15360 | return table; |
| 15361 | } |
| 15362 | |
| 15363 | // Check whether the hash table should be extended. |
| 15364 | table = EnsureCapacity(table, 1, key, TENURED); |
| 15365 | |
| 15366 | table->AddEntry(table->FindInsertionEntry(table->Hash(key)), key, value); |
| 15367 | return table; |
| 15368 | } |
| 15369 | |
| 15370 | |
| 15371 | void WeakHashTable::AddEntry(int entry, |
| 15372 | Handle<Object> key, |
| 15373 | Handle<Object> value) { |
| 15374 | DisallowHeapAllocation no_allocation; |
| 15375 | // TODO(ulan): Skipping write barrier is a temporary solution to avoid |
| 15376 | // memory leaks. Remove this once we have special visitor for weak fixed |
| 15377 | // arrays. |
| 15378 | set(EntryToIndex(entry), *key, SKIP_WRITE_BARRIER); |
| 15379 | set(EntryToValueIndex(entry), *value, SKIP_WRITE_BARRIER); |
| 15380 | ElementAdded(); |
| 15381 | } |
| 15382 | |
| 15383 | |
| 15384 | template<class Derived, class Iterator, int entrysize> |
| 15385 | Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Allocate( |
| 15386 | Isolate* isolate, int capacity, PretenureFlag pretenure) { |
| 15387 | // Capacity must be a power of two, since we depend on being able |
| 15388 | // to divide and multiple by 2 (kLoadFactor) to derive capacity |
| 15389 | // from number of buckets. If we decide to change kLoadFactor |
| 15390 | // to something other than 2, capacity should be stored as another |
| 15391 | // field of this object. |
| 15392 | capacity = base::bits::RoundUpToPowerOfTwo32(Max(kMinCapacity, capacity)); |
| 15393 | if (capacity > kMaxCapacity) { |
| 15394 | v8::internal::Heap::FatalProcessOutOfMemory("invalid table size", true); |
| 15395 | } |
| 15396 | int num_buckets = capacity / kLoadFactor; |
| 15397 | Handle<FixedArray> backing_store = isolate->factory()->NewFixedArray( |
| 15398 | kHashTableStartIndex + num_buckets + (capacity * kEntrySize), pretenure); |
| 15399 | backing_store->set_map_no_write_barrier( |
| 15400 | isolate->heap()->ordered_hash_table_map()); |
| 15401 | Handle<Derived> table = Handle<Derived>::cast(backing_store); |
| 15402 | for (int i = 0; i < num_buckets; ++i) { |
| 15403 | table->set(kHashTableStartIndex + i, Smi::FromInt(kNotFound)); |
| 15404 | } |
| 15405 | table->SetNumberOfBuckets(num_buckets); |
| 15406 | table->SetNumberOfElements(0); |
| 15407 | table->SetNumberOfDeletedElements(0); |
| 15408 | return table; |
| 15409 | } |
| 15410 | |
| 15411 | |
| 15412 | template<class Derived, class Iterator, int entrysize> |
| 15413 | Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::EnsureGrowable( |
| 15414 | Handle<Derived> table) { |
| 15415 | DCHECK(!table->IsObsolete()); |
| 15416 | |
| 15417 | int nof = table->NumberOfElements(); |
| 15418 | int nod = table->NumberOfDeletedElements(); |
| 15419 | int capacity = table->Capacity(); |
| 15420 | if ((nof + nod) < capacity) return table; |
| 15421 | // Don't need to grow if we can simply clear out deleted entries instead. |
| 15422 | // Note that we can't compact in place, though, so we always allocate |
| 15423 | // a new table. |
| 15424 | return Rehash(table, (nod < (capacity >> 1)) ? capacity << 1 : capacity); |
| 15425 | } |
| 15426 | |
| 15427 | |
| 15428 | template<class Derived, class Iterator, int entrysize> |
| 15429 | Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Shrink( |
| 15430 | Handle<Derived> table) { |
| 15431 | DCHECK(!table->IsObsolete()); |
| 15432 | |
| 15433 | int nof = table->NumberOfElements(); |
| 15434 | int capacity = table->Capacity(); |
| 15435 | if (nof >= (capacity >> 2)) return table; |
| 15436 | return Rehash(table, capacity / 2); |
| 15437 | } |
| 15438 | |
| 15439 | |
| 15440 | template<class Derived, class Iterator, int entrysize> |
| 15441 | Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Clear( |
| 15442 | Handle<Derived> table) { |
| 15443 | DCHECK(!table->IsObsolete()); |
| 15444 | |
| 15445 | Handle<Derived> new_table = |
| 15446 | Allocate(table->GetIsolate(), |
| 15447 | kMinCapacity, |
| 15448 | table->GetHeap()->InNewSpace(*table) ? NOT_TENURED : TENURED); |
| 15449 | |
| 15450 | table->SetNextTable(*new_table); |
| 15451 | table->SetNumberOfDeletedElements(-1); |
| 15452 | |
| 15453 | return new_table; |
| 15454 | } |
| 15455 | |
| 15456 | |
| 15457 | template<class Derived, class Iterator, int entrysize> |
| 15458 | Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Remove( |
| 15459 | Handle<Derived> table, Handle<Object> key, bool* was_present) { |
| 15460 | int entry = table->FindEntry(key); |
| 15461 | if (entry == kNotFound) { |
| 15462 | *was_present = false; |
| 15463 | return table; |
| 15464 | } |
| 15465 | *was_present = true; |
| 15466 | table->RemoveEntry(entry); |
| 15467 | return Shrink(table); |
| 15468 | } |
| 15469 | |
| 15470 | |
| 15471 | template<class Derived, class Iterator, int entrysize> |
| 15472 | Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Rehash( |
| 15473 | Handle<Derived> table, int new_capacity) { |
| 15474 | DCHECK(!table->IsObsolete()); |
| 15475 | |
| 15476 | Handle<Derived> new_table = |
| 15477 | Allocate(table->GetIsolate(), |
| 15478 | new_capacity, |
| 15479 | table->GetHeap()->InNewSpace(*table) ? NOT_TENURED : TENURED); |
| 15480 | int nof = table->NumberOfElements(); |
| 15481 | int nod = table->NumberOfDeletedElements(); |
| 15482 | int new_buckets = new_table->NumberOfBuckets(); |
| 15483 | int new_entry = 0; |
| 15484 | int removed_holes_index = 0; |
| 15485 | |
| 15486 | for (int old_entry = 0; old_entry < (nof + nod); ++old_entry) { |
| 15487 | Object* key = table->KeyAt(old_entry); |
| 15488 | if (key->IsTheHole()) { |
| 15489 | table->SetRemovedIndexAt(removed_holes_index++, old_entry); |
| 15490 | continue; |
| 15491 | } |
| 15492 | |
| 15493 | Object* hash = key->GetHash(); |
| 15494 | int bucket = Smi::cast(hash)->value() & (new_buckets - 1); |
| 15495 | Object* chain_entry = new_table->get(kHashTableStartIndex + bucket); |
| 15496 | new_table->set(kHashTableStartIndex + bucket, Smi::FromInt(new_entry)); |
| 15497 | int new_index = new_table->EntryToIndex(new_entry); |
| 15498 | int old_index = table->EntryToIndex(old_entry); |
| 15499 | for (int i = 0; i < entrysize; ++i) { |
| 15500 | Object* value = table->get(old_index + i); |
| 15501 | new_table->set(new_index + i, value); |
| 15502 | } |
| 15503 | new_table->set(new_index + kChainOffset, chain_entry); |
| 15504 | ++new_entry; |
| 15505 | } |
| 15506 | |
| 15507 | DCHECK_EQ(nod, removed_holes_index); |
| 15508 | |
| 15509 | new_table->SetNumberOfElements(nof); |
| 15510 | table->SetNextTable(*new_table); |
| 15511 | |
| 15512 | return new_table; |
| 15513 | } |
| 15514 | |
| 15515 | |
| 15516 | template <class Derived, class Iterator, int entrysize> |
| 15517 | int OrderedHashTable<Derived, Iterator, entrysize>::FindEntry( |
| 15518 | Handle<Object> key, int hash) { |
| 15519 | DCHECK(!IsObsolete()); |
| 15520 | |
| 15521 | DisallowHeapAllocation no_gc; |
| 15522 | DCHECK(!key->IsTheHole()); |
| 15523 | for (int entry = HashToEntry(hash); entry != kNotFound; |
| 15524 | entry = ChainAt(entry)) { |
| 15525 | Object* candidate = KeyAt(entry); |
| 15526 | if (candidate->SameValueZero(*key)) |
| 15527 | return entry; |
| 15528 | } |
| 15529 | return kNotFound; |
| 15530 | } |
| 15531 | |
| 15532 | |
| 15533 | template <class Derived, class Iterator, int entrysize> |
| 15534 | int OrderedHashTable<Derived, Iterator, entrysize>::FindEntry( |
| 15535 | Handle<Object> key) { |
| 15536 | DisallowHeapAllocation no_gc; |
| 15537 | Object* hash = key->GetHash(); |
| 15538 | if (!hash->IsSmi()) return kNotFound; |
| 15539 | return FindEntry(key, Smi::cast(hash)->value()); |
| 15540 | } |
| 15541 | |
| 15542 | |
| 15543 | template <class Derived, class Iterator, int entrysize> |
| 15544 | int OrderedHashTable<Derived, Iterator, entrysize>::AddEntry(int hash) { |
| 15545 | DCHECK(!IsObsolete()); |
| 15546 | |
| 15547 | int entry = UsedCapacity(); |
| 15548 | int bucket = HashToBucket(hash); |
| 15549 | int index = EntryToIndex(entry); |
| 15550 | Object* chain_entry = get(kHashTableStartIndex + bucket); |
| 15551 | set(kHashTableStartIndex + bucket, Smi::FromInt(entry)); |
| 15552 | set(index + kChainOffset, chain_entry); |
| 15553 | SetNumberOfElements(NumberOfElements() + 1); |
| 15554 | return index; |
| 15555 | } |
| 15556 | |
| 15557 | |
| 15558 | template<class Derived, class Iterator, int entrysize> |
| 15559 | void OrderedHashTable<Derived, Iterator, entrysize>::RemoveEntry(int entry) { |
| 15560 | DCHECK(!IsObsolete()); |
| 15561 | |
| 15562 | int index = EntryToIndex(entry); |
| 15563 | for (int i = 0; i < entrysize; ++i) { |
| 15564 | set_the_hole(index + i); |
| 15565 | } |
| 15566 | SetNumberOfElements(NumberOfElements() - 1); |
| 15567 | SetNumberOfDeletedElements(NumberOfDeletedElements() + 1); |
| 15568 | } |
| 15569 | |
| 15570 | |
| 15571 | template Handle<OrderedHashSet> |
| 15572 | OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Allocate( |
| 15573 | Isolate* isolate, int capacity, PretenureFlag pretenure); |
| 15574 | |
| 15575 | template Handle<OrderedHashSet> |
| 15576 | OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::EnsureGrowable( |
| 15577 | Handle<OrderedHashSet> table); |
| 15578 | |
| 15579 | template Handle<OrderedHashSet> |
| 15580 | OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Shrink( |
| 15581 | Handle<OrderedHashSet> table); |
| 15582 | |
| 15583 | template Handle<OrderedHashSet> |
| 15584 | OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Clear( |
| 15585 | Handle<OrderedHashSet> table); |
| 15586 | |
| 15587 | template Handle<OrderedHashSet> |
| 15588 | OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Remove( |
| 15589 | Handle<OrderedHashSet> table, Handle<Object> key, bool* was_present); |
| 15590 | |
| 15591 | template int OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::FindEntry( |
| 15592 | Handle<Object> key, int hash); |
| 15593 | template int OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::FindEntry( |
| 15594 | Handle<Object> key); |
| 15595 | |
| 15596 | template int |
| 15597 | OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::AddEntry(int hash); |
| 15598 | |
| 15599 | template void |
| 15600 | OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::RemoveEntry(int entry); |
| 15601 | |
| 15602 | |
| 15603 | template Handle<OrderedHashMap> |
| 15604 | OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Allocate( |
| 15605 | Isolate* isolate, int capacity, PretenureFlag pretenure); |
| 15606 | |
| 15607 | template Handle<OrderedHashMap> |
| 15608 | OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::EnsureGrowable( |
| 15609 | Handle<OrderedHashMap> table); |
| 15610 | |
| 15611 | template Handle<OrderedHashMap> |
| 15612 | OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Shrink( |
| 15613 | Handle<OrderedHashMap> table); |
| 15614 | |
| 15615 | template Handle<OrderedHashMap> |
| 15616 | OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Clear( |
| 15617 | Handle<OrderedHashMap> table); |
| 15618 | |
| 15619 | template Handle<OrderedHashMap> |
| 15620 | OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Remove( |
| 15621 | Handle<OrderedHashMap> table, Handle<Object> key, bool* was_present); |
| 15622 | |
| 15623 | template int OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::FindEntry( |
| 15624 | Handle<Object> key, int hash); |
| 15625 | template int OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::FindEntry( |
| 15626 | Handle<Object> key); |
| 15627 | |
| 15628 | template int |
| 15629 | OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::AddEntry(int hash); |
| 15630 | |
| 15631 | template void |
| 15632 | OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::RemoveEntry(int entry); |
| 15633 | |
| 15634 | |
| 15635 | bool OrderedHashSet::Contains(Handle<Object> key) { |
| 15636 | return FindEntry(key) != kNotFound; |
| 15637 | } |
| 15638 | |
| 15639 | |
| 15640 | Handle<OrderedHashSet> OrderedHashSet::Add(Handle<OrderedHashSet> table, |
| 15641 | Handle<Object> key) { |
| 15642 | int hash = GetOrCreateHash(table->GetIsolate(), key)->value(); |
| 15643 | if (table->FindEntry(key, hash) != kNotFound) return table; |
| 15644 | |
| 15645 | table = EnsureGrowable(table); |
| 15646 | |
| 15647 | int index = table->AddEntry(hash); |
| 15648 | table->set(index, *key); |
| 15649 | return table; |
| 15650 | } |
| 15651 | |
| 15652 | |
| 15653 | Object* OrderedHashMap::Lookup(Handle<Object> key) { |
| 15654 | DisallowHeapAllocation no_gc; |
| 15655 | int entry = FindEntry(key); |
| 15656 | if (entry == kNotFound) return GetHeap()->the_hole_value(); |
| 15657 | return ValueAt(entry); |
| 15658 | } |
| 15659 | |
| 15660 | |
| 15661 | Handle<OrderedHashMap> OrderedHashMap::Put(Handle<OrderedHashMap> table, |
| 15662 | Handle<Object> key, |
| 15663 | Handle<Object> value) { |
| 15664 | DCHECK(!key->IsTheHole()); |
| 15665 | |
| 15666 | int hash = GetOrCreateHash(table->GetIsolate(), key)->value(); |
| 15667 | int entry = table->FindEntry(key, hash); |
| 15668 | |
| 15669 | if (entry != kNotFound) { |
| 15670 | table->set(table->EntryToIndex(entry) + kValueOffset, *value); |
| 15671 | return table; |
| 15672 | } |
| 15673 | |
| 15674 | table = EnsureGrowable(table); |
| 15675 | |
| 15676 | int index = table->AddEntry(hash); |
| 15677 | table->set(index, *key); |
| 15678 | table->set(index + kValueOffset, *value); |
| 15679 | return table; |
| 15680 | } |
| 15681 | |
| 15682 | |
| 15683 | template<class Derived, class TableType> |
| 15684 | void OrderedHashTableIterator<Derived, TableType>::Transition() { |
| 15685 | DisallowHeapAllocation no_allocation; |
| 15686 | TableType* table = TableType::cast(this->table()); |
| 15687 | if (!table->IsObsolete()) return; |
| 15688 | |
| 15689 | int index = Smi::cast(this->index())->value(); |
| 15690 | while (table->IsObsolete()) { |
| 15691 | TableType* next_table = table->NextTable(); |
| 15692 | |
| 15693 | if (index > 0) { |
| 15694 | int nod = table->NumberOfDeletedElements(); |
| 15695 | |
| 15696 | // When we clear the table we set the number of deleted elements to -1. |
| 15697 | if (nod == -1) { |
| 15698 | index = 0; |
| 15699 | } else { |
| 15700 | int old_index = index; |
| 15701 | for (int i = 0; i < nod; ++i) { |
| 15702 | int removed_index = table->RemovedIndexAt(i); |
| 15703 | if (removed_index >= old_index) break; |
| 15704 | --index; |
| 15705 | } |
| 15706 | } |
| 15707 | } |
| 15708 | |
| 15709 | table = next_table; |
| 15710 | } |
| 15711 | |
| 15712 | set_table(table); |
| 15713 | set_index(Smi::FromInt(index)); |
| 15714 | } |
| 15715 | |
| 15716 | |
| 15717 | template<class Derived, class TableType> |
| 15718 | bool OrderedHashTableIterator<Derived, TableType>::HasMore() { |
| 15719 | DisallowHeapAllocation no_allocation; |
| 15720 | if (this->table()->IsUndefined()) return false; |
| 15721 | |
| 15722 | Transition(); |
| 15723 | |
| 15724 | TableType* table = TableType::cast(this->table()); |
| 15725 | int index = Smi::cast(this->index())->value(); |
| 15726 | int used_capacity = table->UsedCapacity(); |
| 15727 | |
| 15728 | while (index < used_capacity && table->KeyAt(index)->IsTheHole()) { |
| 15729 | index++; |
| 15730 | } |
| 15731 | |
| 15732 | set_index(Smi::FromInt(index)); |
| 15733 | |
| 15734 | if (index < used_capacity) return true; |
| 15735 | |
| 15736 | set_table(GetHeap()->undefined_value()); |
| 15737 | return false; |
| 15738 | } |
| 15739 | |
| 15740 | |
| 15741 | template<class Derived, class TableType> |
| 15742 | Smi* OrderedHashTableIterator<Derived, TableType>::Next(JSArray* value_array) { |
| 15743 | DisallowHeapAllocation no_allocation; |
| 15744 | if (HasMore()) { |
| 15745 | FixedArray* array = FixedArray::cast(value_array->elements()); |
| 15746 | static_cast<Derived*>(this)->PopulateValueArray(array); |
| 15747 | MoveNext(); |
| 15748 | return Smi::cast(kind()); |
| 15749 | } |
| 15750 | return Smi::FromInt(0); |
| 15751 | } |
| 15752 | |
| 15753 | |
| 15754 | template Smi* |
| 15755 | OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Next( |
| 15756 | JSArray* value_array); |
| 15757 | |
| 15758 | template bool |
| 15759 | OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::HasMore(); |
| 15760 | |
| 15761 | template void |
| 15762 | OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::MoveNext(); |
| 15763 | |
| 15764 | template Object* |
| 15765 | OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::CurrentKey(); |
| 15766 | |
| 15767 | template void |
| 15768 | OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Transition(); |
| 15769 | |
| 15770 | |
| 15771 | template Smi* |
| 15772 | OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Next( |
| 15773 | JSArray* value_array); |
| 15774 | |
| 15775 | template bool |
| 15776 | OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::HasMore(); |
| 15777 | |
| 15778 | template void |
| 15779 | OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::MoveNext(); |
| 15780 | |
| 15781 | template Object* |
| 15782 | OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::CurrentKey(); |
| 15783 | |
| 15784 | template void |
| 15785 | OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Transition(); |
| 15786 | |
| 15787 | |
| 15788 | DeclaredAccessorDescriptorIterator::DeclaredAccessorDescriptorIterator( |
| 15789 | DeclaredAccessorDescriptor* descriptor) |
| 15790 | : array_(descriptor->serialized_data()->GetDataStartAddress()), |
| 15791 | length_(descriptor->serialized_data()->length()), |
| 15792 | offset_(0) { |
| 15793 | } |
| 15794 | |
| 15795 | |
| 15796 | const DeclaredAccessorDescriptorData* |
| 15797 | DeclaredAccessorDescriptorIterator::Next() { |
| 15798 | DCHECK(offset_ < length_); |
| 15799 | uint8_t* ptr = &array_[offset_]; |
| 15800 | DCHECK(reinterpret_cast<uintptr_t>(ptr) % sizeof(uintptr_t) == 0); |
| 15801 | const DeclaredAccessorDescriptorData* data = |
| 15802 | reinterpret_cast<const DeclaredAccessorDescriptorData*>(ptr); |
| 15803 | offset_ += sizeof(*data); |
| 15804 | DCHECK(offset_ <= length_); |
| 15805 | return data; |
| 15806 | } |
| 15807 | |
| 15808 | |
| 15809 | Handle<DeclaredAccessorDescriptor> DeclaredAccessorDescriptor::Create( |
| 15810 | Isolate* isolate, |
| 15811 | const DeclaredAccessorDescriptorData& descriptor, |
| 15812 | Handle<DeclaredAccessorDescriptor> previous) { |
| 15813 | int previous_length = |
| 15814 | previous.is_null() ? 0 : previous->serialized_data()->length(); |
| 15815 | int length = sizeof(descriptor) + previous_length; |
| 15816 | Handle<ByteArray> serialized_descriptor = |
| 15817 | isolate->factory()->NewByteArray(length); |
| 15818 | Handle<DeclaredAccessorDescriptor> value = |
| 15819 | isolate->factory()->NewDeclaredAccessorDescriptor(); |
| 15820 | value->set_serialized_data(*serialized_descriptor); |
| 15821 | // Copy in the data. |
| 15822 | { |
| 15823 | DisallowHeapAllocation no_allocation; |
| 15824 | uint8_t* array = serialized_descriptor->GetDataStartAddress(); |
| 15825 | if (previous_length != 0) { |
| 15826 | uint8_t* previous_array = |
| 15827 | previous->serialized_data()->GetDataStartAddress(); |
| 15828 | MemCopy(array, previous_array, previous_length); |
| 15829 | array += previous_length; |
| 15830 | } |
| 15831 | DCHECK(reinterpret_cast<uintptr_t>(array) % sizeof(uintptr_t) == 0); |
| 15832 | DeclaredAccessorDescriptorData* data = |
| 15833 | reinterpret_cast<DeclaredAccessorDescriptorData*>(array); |
| 15834 | *data = descriptor; |
| 15835 | } |
| 15836 | return value; |
| 15837 | } |
| 15838 | |
| 15839 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15840 | // Check if there is a break point at this code position. |
| 15841 | bool DebugInfo::HasBreakPoint(int code_position) { |
| 15842 | // Get the break point info object for this code position. |
| 15843 | Object* break_point_info = GetBreakPointInfo(code_position); |
| 15844 | |
| 15845 | // If there is no break point info object or no break points in the break |
| 15846 | // point info object there is no break point at this code position. |
| 15847 | if (break_point_info->IsUndefined()) return false; |
| 15848 | return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0; |
| 15849 | } |
| 15850 | |
| 15851 | |
| 15852 | // Get the break point info object for this code position. |
| 15853 | Object* DebugInfo::GetBreakPointInfo(int code_position) { |
| 15854 | // Find the index of the break point info object for this code position. |
| 15855 | int index = GetBreakPointInfoIndex(code_position); |
| 15856 | |
| 15857 | // Return the break point info object if any. |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 15858 | if (index == kNoBreakPointInfo) return GetHeap()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15859 | return BreakPointInfo::cast(break_points()->get(index)); |
| 15860 | } |
| 15861 | |
| 15862 | |
| 15863 | // Clear a break point at the specified code position. |
| 15864 | void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info, |
| 15865 | int code_position, |
| 15866 | Handle<Object> break_point_object) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15867 | Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position), |
| 15868 | debug_info->GetIsolate()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15869 | if (break_point_info->IsUndefined()) return; |
| 15870 | BreakPointInfo::ClearBreakPoint( |
| 15871 | Handle<BreakPointInfo>::cast(break_point_info), |
| 15872 | break_point_object); |
| 15873 | } |
| 15874 | |
| 15875 | |
| 15876 | void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, |
| 15877 | int code_position, |
| 15878 | int source_position, |
| 15879 | int statement_position, |
| 15880 | Handle<Object> break_point_object) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15881 | Isolate* isolate = debug_info->GetIsolate(); |
| 15882 | Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position), |
| 15883 | isolate); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15884 | if (!break_point_info->IsUndefined()) { |
| 15885 | BreakPointInfo::SetBreakPoint( |
| 15886 | Handle<BreakPointInfo>::cast(break_point_info), |
| 15887 | break_point_object); |
| 15888 | return; |
| 15889 | } |
| 15890 | |
| 15891 | // Adding a new break point for a code position which did not have any |
| 15892 | // break points before. Try to find a free slot. |
| 15893 | int index = kNoBreakPointInfo; |
| 15894 | for (int i = 0; i < debug_info->break_points()->length(); i++) { |
| 15895 | if (debug_info->break_points()->get(i)->IsUndefined()) { |
| 15896 | index = i; |
| 15897 | break; |
| 15898 | } |
| 15899 | } |
| 15900 | if (index == kNoBreakPointInfo) { |
| 15901 | // No free slot - extend break point info array. |
| 15902 | Handle<FixedArray> old_break_points = |
| 15903 | Handle<FixedArray>(FixedArray::cast(debug_info->break_points())); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15904 | Handle<FixedArray> new_break_points = |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 15905 | isolate->factory()->NewFixedArray( |
| 15906 | old_break_points->length() + |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15907 | DebugInfo::kEstimatedNofBreakPointsInFunction); |
Kristian Monsen | 0d5e116 | 2010-09-30 15:31:59 +0100 | [diff] [blame] | 15908 | |
| 15909 | debug_info->set_break_points(*new_break_points); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15910 | for (int i = 0; i < old_break_points->length(); i++) { |
| 15911 | new_break_points->set(i, old_break_points->get(i)); |
| 15912 | } |
| 15913 | index = old_break_points->length(); |
| 15914 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15915 | DCHECK(index != kNoBreakPointInfo); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15916 | |
| 15917 | // Allocate new BreakPointInfo object and set the break point. |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 15918 | Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast( |
| 15919 | isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15920 | new_break_point_info->set_code_position(Smi::FromInt(code_position)); |
| 15921 | new_break_point_info->set_source_position(Smi::FromInt(source_position)); |
| 15922 | new_break_point_info-> |
| 15923 | set_statement_position(Smi::FromInt(statement_position)); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 15924 | new_break_point_info->set_break_point_objects( |
| 15925 | isolate->heap()->undefined_value()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15926 | BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object); |
| 15927 | debug_info->break_points()->set(index, *new_break_point_info); |
| 15928 | } |
| 15929 | |
| 15930 | |
| 15931 | // Get the break point objects for a code position. |
| 15932 | Object* DebugInfo::GetBreakPointObjects(int code_position) { |
| 15933 | Object* break_point_info = GetBreakPointInfo(code_position); |
| 15934 | if (break_point_info->IsUndefined()) { |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 15935 | return GetHeap()->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15936 | } |
| 15937 | return BreakPointInfo::cast(break_point_info)->break_point_objects(); |
| 15938 | } |
| 15939 | |
| 15940 | |
| 15941 | // Get the total number of break points. |
| 15942 | int DebugInfo::GetBreakPointCount() { |
| 15943 | if (break_points()->IsUndefined()) return 0; |
| 15944 | int count = 0; |
| 15945 | for (int i = 0; i < break_points()->length(); i++) { |
| 15946 | if (!break_points()->get(i)->IsUndefined()) { |
| 15947 | BreakPointInfo* break_point_info = |
| 15948 | BreakPointInfo::cast(break_points()->get(i)); |
| 15949 | count += break_point_info->GetBreakPointCount(); |
| 15950 | } |
| 15951 | } |
| 15952 | return count; |
| 15953 | } |
| 15954 | |
| 15955 | |
| 15956 | Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info, |
| 15957 | Handle<Object> break_point_object) { |
Ben Murdoch | 8b112d2 | 2011-06-08 16:22:53 +0100 | [diff] [blame] | 15958 | Heap* heap = debug_info->GetHeap(); |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 15959 | if (debug_info->break_points()->IsUndefined()) return heap->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15960 | for (int i = 0; i < debug_info->break_points()->length(); i++) { |
| 15961 | if (!debug_info->break_points()->get(i)->IsUndefined()) { |
| 15962 | Handle<BreakPointInfo> break_point_info = |
| 15963 | Handle<BreakPointInfo>(BreakPointInfo::cast( |
| 15964 | debug_info->break_points()->get(i))); |
| 15965 | if (BreakPointInfo::HasBreakPointObject(break_point_info, |
| 15966 | break_point_object)) { |
| 15967 | return *break_point_info; |
| 15968 | } |
| 15969 | } |
| 15970 | } |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 15971 | return heap->undefined_value(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15972 | } |
| 15973 | |
| 15974 | |
| 15975 | // Find the index of the break point info object for the specified code |
| 15976 | // position. |
| 15977 | int DebugInfo::GetBreakPointInfoIndex(int code_position) { |
| 15978 | if (break_points()->IsUndefined()) return kNoBreakPointInfo; |
| 15979 | for (int i = 0; i < break_points()->length(); i++) { |
| 15980 | if (!break_points()->get(i)->IsUndefined()) { |
| 15981 | BreakPointInfo* break_point_info = |
| 15982 | BreakPointInfo::cast(break_points()->get(i)); |
| 15983 | if (break_point_info->code_position()->value() == code_position) { |
| 15984 | return i; |
| 15985 | } |
| 15986 | } |
| 15987 | } |
| 15988 | return kNoBreakPointInfo; |
| 15989 | } |
| 15990 | |
| 15991 | |
| 15992 | // Remove the specified break point object. |
| 15993 | void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info, |
| 15994 | Handle<Object> break_point_object) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 15995 | Isolate* isolate = break_point_info->GetIsolate(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 15996 | // If there are no break points just ignore. |
| 15997 | if (break_point_info->break_point_objects()->IsUndefined()) return; |
| 15998 | // If there is a single break point clear it if it is the same. |
| 15999 | if (!break_point_info->break_point_objects()->IsFixedArray()) { |
| 16000 | if (break_point_info->break_point_objects() == *break_point_object) { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 16001 | break_point_info->set_break_point_objects( |
| 16002 | isolate->heap()->undefined_value()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16003 | } |
| 16004 | return; |
| 16005 | } |
| 16006 | // If there are multiple break points shrink the array |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16007 | DCHECK(break_point_info->break_point_objects()->IsFixedArray()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16008 | Handle<FixedArray> old_array = |
| 16009 | Handle<FixedArray>( |
| 16010 | FixedArray::cast(break_point_info->break_point_objects())); |
| 16011 | Handle<FixedArray> new_array = |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 16012 | isolate->factory()->NewFixedArray(old_array->length() - 1); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16013 | int found_count = 0; |
| 16014 | for (int i = 0; i < old_array->length(); i++) { |
| 16015 | if (old_array->get(i) == *break_point_object) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16016 | DCHECK(found_count == 0); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16017 | found_count++; |
| 16018 | } else { |
| 16019 | new_array->set(i - found_count, old_array->get(i)); |
| 16020 | } |
| 16021 | } |
| 16022 | // If the break point was found in the list change it. |
| 16023 | if (found_count > 0) break_point_info->set_break_point_objects(*new_array); |
| 16024 | } |
| 16025 | |
| 16026 | |
| 16027 | // Add the specified break point object. |
| 16028 | void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info, |
| 16029 | Handle<Object> break_point_object) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16030 | Isolate* isolate = break_point_info->GetIsolate(); |
| 16031 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16032 | // If there was no break point objects before just set it. |
| 16033 | if (break_point_info->break_point_objects()->IsUndefined()) { |
| 16034 | break_point_info->set_break_point_objects(*break_point_object); |
| 16035 | return; |
| 16036 | } |
| 16037 | // If the break point object is the same as before just ignore. |
| 16038 | if (break_point_info->break_point_objects() == *break_point_object) return; |
| 16039 | // If there was one break point object before replace with array. |
| 16040 | if (!break_point_info->break_point_objects()->IsFixedArray()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16041 | Handle<FixedArray> array = isolate->factory()->NewFixedArray(2); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16042 | array->set(0, break_point_info->break_point_objects()); |
| 16043 | array->set(1, *break_point_object); |
| 16044 | break_point_info->set_break_point_objects(*array); |
| 16045 | return; |
| 16046 | } |
| 16047 | // If there was more than one break point before extend array. |
| 16048 | Handle<FixedArray> old_array = |
| 16049 | Handle<FixedArray>( |
| 16050 | FixedArray::cast(break_point_info->break_point_objects())); |
| 16051 | Handle<FixedArray> new_array = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16052 | isolate->factory()->NewFixedArray(old_array->length() + 1); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16053 | for (int i = 0; i < old_array->length(); i++) { |
| 16054 | // If the break point was there before just ignore. |
| 16055 | if (old_array->get(i) == *break_point_object) return; |
| 16056 | new_array->set(i, old_array->get(i)); |
| 16057 | } |
| 16058 | // Add the new break point. |
| 16059 | new_array->set(old_array->length(), *break_point_object); |
| 16060 | break_point_info->set_break_point_objects(*new_array); |
| 16061 | } |
| 16062 | |
| 16063 | |
| 16064 | bool BreakPointInfo::HasBreakPointObject( |
| 16065 | Handle<BreakPointInfo> break_point_info, |
| 16066 | Handle<Object> break_point_object) { |
| 16067 | // No break point. |
| 16068 | if (break_point_info->break_point_objects()->IsUndefined()) return false; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 16069 | // Single break point. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16070 | if (!break_point_info->break_point_objects()->IsFixedArray()) { |
| 16071 | return break_point_info->break_point_objects() == *break_point_object; |
| 16072 | } |
| 16073 | // Multiple break points. |
| 16074 | FixedArray* array = FixedArray::cast(break_point_info->break_point_objects()); |
| 16075 | for (int i = 0; i < array->length(); i++) { |
| 16076 | if (array->get(i) == *break_point_object) { |
| 16077 | return true; |
| 16078 | } |
| 16079 | } |
| 16080 | return false; |
| 16081 | } |
| 16082 | |
| 16083 | |
| 16084 | // Get the number of break points. |
| 16085 | int BreakPointInfo::GetBreakPointCount() { |
| 16086 | // No break point. |
| 16087 | if (break_point_objects()->IsUndefined()) return 0; |
Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 16088 | // Single break point. |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16089 | if (!break_point_objects()->IsFixedArray()) return 1; |
| 16090 | // Multiple break points. |
| 16091 | return FixedArray::cast(break_point_objects())->length(); |
| 16092 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16093 | |
| 16094 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16095 | Object* JSDate::GetField(Object* object, Smi* index) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16096 | return JSDate::cast(object)->DoGetField( |
| 16097 | static_cast<FieldIndex>(index->value())); |
| 16098 | } |
| 16099 | |
| 16100 | |
| 16101 | Object* JSDate::DoGetField(FieldIndex index) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16102 | DCHECK(index != kDateValue); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16103 | |
| 16104 | DateCache* date_cache = GetIsolate()->date_cache(); |
| 16105 | |
| 16106 | if (index < kFirstUncachedField) { |
| 16107 | Object* stamp = cache_stamp(); |
| 16108 | if (stamp != date_cache->stamp() && stamp->IsSmi()) { |
| 16109 | // Since the stamp is not NaN, the value is also not NaN. |
| 16110 | int64_t local_time_ms = |
| 16111 | date_cache->ToLocal(static_cast<int64_t>(value()->Number())); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16112 | SetCachedFields(local_time_ms, date_cache); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16113 | } |
| 16114 | switch (index) { |
| 16115 | case kYear: return year(); |
| 16116 | case kMonth: return month(); |
| 16117 | case kDay: return day(); |
| 16118 | case kWeekday: return weekday(); |
| 16119 | case kHour: return hour(); |
| 16120 | case kMinute: return min(); |
| 16121 | case kSecond: return sec(); |
| 16122 | default: UNREACHABLE(); |
| 16123 | } |
| 16124 | } |
| 16125 | |
| 16126 | if (index >= kFirstUTCField) { |
| 16127 | return GetUTCField(index, value()->Number(), date_cache); |
| 16128 | } |
| 16129 | |
| 16130 | double time = value()->Number(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16131 | if (std::isnan(time)) return GetIsolate()->heap()->nan_value(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16132 | |
| 16133 | int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(time)); |
| 16134 | int days = DateCache::DaysFromTime(local_time_ms); |
| 16135 | |
| 16136 | if (index == kDays) return Smi::FromInt(days); |
| 16137 | |
| 16138 | int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days); |
| 16139 | if (index == kMillisecond) return Smi::FromInt(time_in_day_ms % 1000); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16140 | DCHECK(index == kTimeInDay); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16141 | return Smi::FromInt(time_in_day_ms); |
| 16142 | } |
| 16143 | |
| 16144 | |
| 16145 | Object* JSDate::GetUTCField(FieldIndex index, |
| 16146 | double value, |
| 16147 | DateCache* date_cache) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16148 | DCHECK(index >= kFirstUTCField); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16149 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16150 | if (std::isnan(value)) return GetIsolate()->heap()->nan_value(); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16151 | |
| 16152 | int64_t time_ms = static_cast<int64_t>(value); |
| 16153 | |
| 16154 | if (index == kTimezoneOffset) { |
| 16155 | return Smi::FromInt(date_cache->TimezoneOffset(time_ms)); |
| 16156 | } |
| 16157 | |
| 16158 | int days = DateCache::DaysFromTime(time_ms); |
| 16159 | |
| 16160 | if (index == kWeekdayUTC) return Smi::FromInt(date_cache->Weekday(days)); |
| 16161 | |
| 16162 | if (index <= kDayUTC) { |
| 16163 | int year, month, day; |
| 16164 | date_cache->YearMonthDayFromDays(days, &year, &month, &day); |
| 16165 | if (index == kYearUTC) return Smi::FromInt(year); |
| 16166 | if (index == kMonthUTC) return Smi::FromInt(month); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16167 | DCHECK(index == kDayUTC); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16168 | return Smi::FromInt(day); |
| 16169 | } |
| 16170 | |
| 16171 | int time_in_day_ms = DateCache::TimeInDay(time_ms, days); |
| 16172 | switch (index) { |
| 16173 | case kHourUTC: return Smi::FromInt(time_in_day_ms / (60 * 60 * 1000)); |
| 16174 | case kMinuteUTC: return Smi::FromInt((time_in_day_ms / (60 * 1000)) % 60); |
| 16175 | case kSecondUTC: return Smi::FromInt((time_in_day_ms / 1000) % 60); |
| 16176 | case kMillisecondUTC: return Smi::FromInt(time_in_day_ms % 1000); |
| 16177 | case kDaysUTC: return Smi::FromInt(days); |
| 16178 | case kTimeInDayUTC: return Smi::FromInt(time_in_day_ms); |
| 16179 | default: UNREACHABLE(); |
| 16180 | } |
| 16181 | |
| 16182 | UNREACHABLE(); |
| 16183 | return NULL; |
| 16184 | } |
| 16185 | |
| 16186 | |
| 16187 | void JSDate::SetValue(Object* value, bool is_value_nan) { |
| 16188 | set_value(value); |
| 16189 | if (is_value_nan) { |
| 16190 | HeapNumber* nan = GetIsolate()->heap()->nan_value(); |
| 16191 | set_cache_stamp(nan, SKIP_WRITE_BARRIER); |
| 16192 | set_year(nan, SKIP_WRITE_BARRIER); |
| 16193 | set_month(nan, SKIP_WRITE_BARRIER); |
| 16194 | set_day(nan, SKIP_WRITE_BARRIER); |
| 16195 | set_hour(nan, SKIP_WRITE_BARRIER); |
| 16196 | set_min(nan, SKIP_WRITE_BARRIER); |
| 16197 | set_sec(nan, SKIP_WRITE_BARRIER); |
| 16198 | set_weekday(nan, SKIP_WRITE_BARRIER); |
| 16199 | } else { |
| 16200 | set_cache_stamp(Smi::FromInt(DateCache::kInvalidStamp), SKIP_WRITE_BARRIER); |
| 16201 | } |
| 16202 | } |
| 16203 | |
| 16204 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16205 | void JSDate::SetCachedFields(int64_t local_time_ms, DateCache* date_cache) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 16206 | int days = DateCache::DaysFromTime(local_time_ms); |
| 16207 | int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days); |
| 16208 | int year, month, day; |
| 16209 | date_cache->YearMonthDayFromDays(days, &year, &month, &day); |
| 16210 | int weekday = date_cache->Weekday(days); |
| 16211 | int hour = time_in_day_ms / (60 * 60 * 1000); |
| 16212 | int min = (time_in_day_ms / (60 * 1000)) % 60; |
| 16213 | int sec = (time_in_day_ms / 1000) % 60; |
| 16214 | set_cache_stamp(date_cache->stamp()); |
| 16215 | set_year(Smi::FromInt(year), SKIP_WRITE_BARRIER); |
| 16216 | set_month(Smi::FromInt(month), SKIP_WRITE_BARRIER); |
| 16217 | set_day(Smi::FromInt(day), SKIP_WRITE_BARRIER); |
| 16218 | set_weekday(Smi::FromInt(weekday), SKIP_WRITE_BARRIER); |
| 16219 | set_hour(Smi::FromInt(hour), SKIP_WRITE_BARRIER); |
| 16220 | set_min(Smi::FromInt(min), SKIP_WRITE_BARRIER); |
| 16221 | set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER); |
| 16222 | } |
| 16223 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 16224 | |
| 16225 | void JSArrayBuffer::Neuter() { |
| 16226 | DCHECK(is_external()); |
| 16227 | set_backing_store(NULL); |
| 16228 | set_byte_length(Smi::FromInt(0)); |
| 16229 | } |
| 16230 | |
| 16231 | |
| 16232 | void JSArrayBufferView::NeuterView() { |
| 16233 | set_byte_offset(Smi::FromInt(0)); |
| 16234 | set_byte_length(Smi::FromInt(0)); |
| 16235 | } |
| 16236 | |
| 16237 | |
| 16238 | void JSDataView::Neuter() { |
| 16239 | NeuterView(); |
| 16240 | } |
| 16241 | |
| 16242 | |
| 16243 | void JSTypedArray::Neuter() { |
| 16244 | NeuterView(); |
| 16245 | set_length(Smi::FromInt(0)); |
| 16246 | set_elements(GetHeap()->EmptyExternalArrayForMap(map())); |
| 16247 | } |
| 16248 | |
| 16249 | |
| 16250 | static ElementsKind FixedToExternalElementsKind(ElementsKind elements_kind) { |
| 16251 | switch (elements_kind) { |
| 16252 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| 16253 | case TYPE##_ELEMENTS: return EXTERNAL_##TYPE##_ELEMENTS; |
| 16254 | |
| 16255 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 16256 | #undef TYPED_ARRAY_CASE |
| 16257 | |
| 16258 | default: |
| 16259 | UNREACHABLE(); |
| 16260 | return FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND; |
| 16261 | } |
| 16262 | } |
| 16263 | |
| 16264 | |
| 16265 | Handle<JSArrayBuffer> JSTypedArray::MaterializeArrayBuffer( |
| 16266 | Handle<JSTypedArray> typed_array) { |
| 16267 | |
| 16268 | Handle<Map> map(typed_array->map()); |
| 16269 | Isolate* isolate = typed_array->GetIsolate(); |
| 16270 | |
| 16271 | DCHECK(IsFixedTypedArrayElementsKind(map->elements_kind())); |
| 16272 | |
| 16273 | Handle<Map> new_map = Map::TransitionElementsTo( |
| 16274 | map, |
| 16275 | FixedToExternalElementsKind(map->elements_kind())); |
| 16276 | |
| 16277 | Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); |
| 16278 | Handle<FixedTypedArrayBase> fixed_typed_array( |
| 16279 | FixedTypedArrayBase::cast(typed_array->elements())); |
| 16280 | Runtime::SetupArrayBufferAllocatingData(isolate, buffer, |
| 16281 | fixed_typed_array->DataSize(), false); |
| 16282 | memcpy(buffer->backing_store(), |
| 16283 | fixed_typed_array->DataPtr(), |
| 16284 | fixed_typed_array->DataSize()); |
| 16285 | Handle<ExternalArray> new_elements = |
| 16286 | isolate->factory()->NewExternalArray( |
| 16287 | fixed_typed_array->length(), typed_array->type(), |
| 16288 | static_cast<uint8_t*>(buffer->backing_store())); |
| 16289 | |
| 16290 | buffer->set_weak_first_view(*typed_array); |
| 16291 | DCHECK(typed_array->weak_next() == isolate->heap()->undefined_value()); |
| 16292 | typed_array->set_buffer(*buffer); |
| 16293 | JSObject::SetMapAndElements(typed_array, new_map, new_elements); |
| 16294 | |
| 16295 | return buffer; |
| 16296 | } |
| 16297 | |
| 16298 | |
| 16299 | Handle<JSArrayBuffer> JSTypedArray::GetBuffer() { |
| 16300 | Handle<Object> result(buffer(), GetIsolate()); |
| 16301 | if (*result != Smi::FromInt(0)) { |
| 16302 | DCHECK(IsExternalArrayElementsKind(map()->elements_kind())); |
| 16303 | return Handle<JSArrayBuffer>::cast(result); |
| 16304 | } |
| 16305 | Handle<JSTypedArray> self(this); |
| 16306 | return MaterializeArrayBuffer(self); |
| 16307 | } |
| 16308 | |
| 16309 | |
| 16310 | HeapType* PropertyCell::type() { |
| 16311 | return static_cast<HeapType*>(type_raw()); |
| 16312 | } |
| 16313 | |
| 16314 | |
| 16315 | void PropertyCell::set_type(HeapType* type, WriteBarrierMode ignored) { |
| 16316 | DCHECK(IsPropertyCell()); |
| 16317 | set_type_raw(type, ignored); |
| 16318 | } |
| 16319 | |
| 16320 | |
| 16321 | Handle<HeapType> PropertyCell::UpdatedType(Handle<PropertyCell> cell, |
| 16322 | Handle<Object> value) { |
| 16323 | Isolate* isolate = cell->GetIsolate(); |
| 16324 | Handle<HeapType> old_type(cell->type(), isolate); |
| 16325 | Handle<HeapType> new_type = HeapType::Constant(value, isolate); |
| 16326 | |
| 16327 | if (new_type->Is(old_type)) return old_type; |
| 16328 | |
| 16329 | cell->dependent_code()->DeoptimizeDependentCodeGroup( |
| 16330 | isolate, DependentCode::kPropertyCellChangedGroup); |
| 16331 | |
| 16332 | if (old_type->Is(HeapType::None()) || old_type->Is(HeapType::Undefined())) { |
| 16333 | return new_type; |
| 16334 | } |
| 16335 | |
| 16336 | return HeapType::Any(isolate); |
| 16337 | } |
| 16338 | |
| 16339 | |
| 16340 | void PropertyCell::SetValueInferType(Handle<PropertyCell> cell, |
| 16341 | Handle<Object> value) { |
| 16342 | cell->set_value(*value); |
| 16343 | if (!HeapType::Any()->Is(cell->type())) { |
| 16344 | Handle<HeapType> new_type = UpdatedType(cell, value); |
| 16345 | cell->set_type(*new_type); |
| 16346 | } |
| 16347 | } |
| 16348 | |
| 16349 | |
| 16350 | // static |
| 16351 | void PropertyCell::AddDependentCompilationInfo(Handle<PropertyCell> cell, |
| 16352 | CompilationInfo* info) { |
| 16353 | Handle<DependentCode> codes = |
| 16354 | DependentCode::Insert(handle(cell->dependent_code(), info->isolate()), |
| 16355 | DependentCode::kPropertyCellChangedGroup, |
| 16356 | info->object_wrapper()); |
| 16357 | if (*codes != cell->dependent_code()) cell->set_dependent_code(*codes); |
| 16358 | info->dependencies(DependentCode::kPropertyCellChangedGroup)->Add( |
| 16359 | cell, info->zone()); |
| 16360 | } |
| 16361 | |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 16362 | } } // namespace v8::internal |