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.
4
5 #include "src/v8.h"
6
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"
38
39 #ifdef ENABLE_DISASSEMBLER
40 #include "src/disasm.h"
41 #include "src/disassembler.h"
42 #endif
43
44 namespace v8 {
45 namespace internal {
46
OptimalType(Isolate * isolate,Representation representation)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);
62 }
63
64
ToObject(Isolate * isolate,Handle<Object> object,Handle<Context> native_context)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>();
80 }
81 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
82 Handle<JSValue>::cast(result)->set_value(*object);
83 return result;
84 }
85
86
BooleanValue()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;
95 }
96
97
IsCallable() const98 bool Object::IsCallable() const {
99 const Object* fun = this;
100 while (fun->IsJSFunctionProxy()) {
101 fun = JSFunctionProxy::cast(fun)->call_trap();
102 }
103 return fun->IsJSFunction() ||
104 (fun->IsHeapObject() &&
105 HeapObject::cast(fun)->map()->has_instance_call_handler());
106 }
107
108
GetProperty(LookupIterator * it)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();
134 }
135 }
136 return it->factory()->undefined_value();
137 }
138
139
GetDataProperty(Handle<JSObject> object,Handle<Name> key)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);
145 }
146
147
GetDataProperty(LookupIterator * it)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 }
170 }
171 return it->isolate()->factory()->undefined_value();
172 }
173
174
ToInt32(int32_t * value)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
ToUint32(uint32_t * value)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
IsTemplateFor(Object * object)210 bool FunctionTemplateInfo::IsTemplateFor(Object* object) {
211 if (!object->IsHeapObject()) return false;
212 return IsTemplateFor(HeapObject::cast(object)->map());
213 }
214
215
IsTemplateFor(Map * map)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>
CheckedCast(void * from)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
PerformCompare(const BitmaskCompareDescriptor & descriptor,char * ptr,Isolate * isolate)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
PerformCompare(const PointerCompareDescriptor & descriptor,char * ptr,Isolate * isolate)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
GetPrimitiveValue(const PrimitiveValueDescriptor & descriptor,char * ptr,Isolate * isolate)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
GetDeclaredAccessorProperty(Handle<Object> receiver,Handle<DeclaredAccessorInfo> info,Isolate * isolate)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
EnsureWritableFastElements(Handle<JSObject> object)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
GetPropertyWithHandler(Handle<JSProxy> proxy,Handle<Object> receiver,Handle<Name> name)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
GetPropertyWithAccessor(Handle<Object> receiver,Handle<Name> name,Handle<JSObject> holder,Handle<Object> structure)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());
417 // api style callbacks.
418 if (structure->IsAccessorInfo()) {
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);
426 }
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);
445 if (result.IsEmpty()) {
446 return isolate->factory()->undefined_value();
447 }
448 Handle<Object> return_value = v8::Utils::OpenHandle(*result);
449 return_value->VerifyApiCallResultType();
450 // Rebox handle before return.
451 return handle(*return_value, isolate);
452 }
453
454 // __defineGetter__ callback
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
IsCompatibleReceiverType(Isolate * isolate,Handle<AccessorInfo> info,Handle<HeapType> type)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
SetPropertyWithAccessor(Handle<Object> receiver,Handle<Name> name,Handle<Object> value,Handle<JSObject> holder,Handle<Object> structure,StrictMode strict_mode)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);
497 }
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;
529 }
530
531 UNREACHABLE();
532 return MaybeHandle<Object>();
533 }
534
535
GetPropertyWithDefinedGetter(Handle<Object> receiver,Handle<JSReceiver> getter)536 MaybeHandle<Object> Object::GetPropertyWithDefinedGetter(
537 Handle<Object> receiver,
538 Handle<JSReceiver> getter) {
539 Isolate* isolate = getter->GetIsolate();
540 Debug* debug = isolate->debug();
541 // Handle stepping into a getter if step into is active.
542 // TODO(rossberg): should this apply to getters that are function proxies?
543 if (debug->StepInActive() && getter->IsJSFunction()) {
544 debug->HandleStepIn(
545 Handle<JSFunction>::cast(getter), Handle<Object>::null(), 0, false);
546 }
547
548 return Execution::Call(isolate, getter, receiver, 0, NULL, true);
549 }
550
551
SetPropertyWithDefinedSetter(Handle<Object> receiver,Handle<JSReceiver> setter,Handle<Object> value)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);
564 }
565
566 Handle<Object> argv[] = { value };
567 RETURN_ON_EXCEPTION(isolate, Execution::Call(isolate, setter, receiver,
568 arraysize(argv), argv, true),
569 Object);
570 return value;
571 }
572
573
FindAllCanReadHolder(LookupIterator * it)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
GetPropertyWithFailedAccessCheck(LookupIterator * it)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
GetPropertyAttributesWithFailedAccessCheck(LookupIterator * it)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
FindAllCanWriteHolder(LookupIterator * it)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
SetPropertyWithFailedAccessCheck(LookupIterator * it,Handle<Object> value,StrictMode strict_mode)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
SetNormalizedProperty(Handle<JSObject> object,Handle<Name> name,Handle<Object> value,PropertyDetails details)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);
672 } else {
673 enumeration_index = original_details.dictionary_index();
674 DCHECK(enumeration_index > 0);
675 }
676
677 details = PropertyDetails(
678 details.attributes(), details.type(), enumeration_index);
679
680 if (object->IsGlobalObject()) {
681 Handle<PropertyCell> cell(
682 PropertyCell::cast(property_dictionary->ValueAt(entry)));
683 PropertyCell::SetValueInferType(cell, value);
684 // Please note we have to update the property details.
685 property_dictionary->DetailsAtPut(entry, details);
686 } else {
687 property_dictionary->SetEntry(entry, name, value, details);
688 }
689 }
690
691
DeleteNormalizedProperty(Handle<JSObject> object,Handle<Name> name,DeleteMode mode)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());
698 int entry = dictionary->FindEntry(name);
699 if (entry != NameDictionary::kNotFound) {
700 // If we have a global object set the cell to the hole.
701 if (object->IsGlobalObject()) {
702 PropertyDetails details = dictionary->DetailsAt(entry);
703 if (!details.IsConfigurable()) {
704 if (mode != FORCE_DELETION) return isolate->factory()->false_value();
705 // When forced to delete global properties, we have to make a
706 // map change to invalidate any ICs that think they can load
707 // from the non-configurable cell without checking if it contains
708 // the hole value.
709 Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
710 DCHECK(new_map->is_dictionary_map());
711 JSObject::MigrateToMap(object, new_map);
712 }
713 Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry)));
714 Handle<Object> value = isolate->factory()->the_hole_value();
715 PropertyCell::SetValueInferType(cell, value);
716 dictionary->DetailsAtPut(entry, details.AsDeleted());
717 } else {
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);
724 }
725 return deleted;
726 }
727 }
728 return isolate->factory()->true_value();
729 }
730
731
IsDirty()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);
737 if (!fun->shared()->IsApiFunction())
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()
742 || !HasFastObjectElements()
743 || !HasFastProperties();
744 }
745
746
GetElementWithReceiver(Isolate * isolate,Handle<Object> object,Handle<Object> receiver,uint32_t index)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();
755 }
756
757 // Iterate up the prototype chain until an element is found or the null
758 // prototype is encountered.
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);
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.
773 Handle<JSObject> js_object =
774 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
775
776 // Check access rights if needed.
777 if (js_object->IsAccessCheckNeeded()) {
778 if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) {
779 isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_GET);
780 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
781 return isolate->factory()->undefined_value();
782 }
783 }
784
785 if (js_object->HasIndexedInterceptor()) {
786 return JSObject::GetElementWithInterceptor(js_object, receiver, index);
787 }
788
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;
796 }
797 }
798
799 return isolate->factory()->undefined_value();
800 }
801
802
GetRootMap(Isolate * isolate)803 Map* Object::GetRootMap(Isolate* isolate) {
804 DisallowHeapAllocation no_alloc;
805 if (IsSmi()) {
806 Context* context = isolate->context()->native_context();
807 return context->number_function()->initial_map();
808 }
809
810 HeapObject* heap_object = HeapObject::cast(this);
811
812 // The object is either a number, a string, a boolean,
813 // a real JS object, or a Harmony proxy.
814 if (heap_object->IsJSReceiver()) {
815 return heap_object->map();
816 }
817 Context* context = isolate->context()->native_context();
818
819 if (heap_object->IsHeapNumber()) {
820 return context->number_function()->initial_map();
821 }
822 if (heap_object->IsString()) {
823 return context->string_function()->initial_map();
824 }
825 if (heap_object->IsSymbol()) {
826 return context->symbol_function()->initial_map();
827 }
828 if (heap_object->IsBoolean()) {
829 return context->boolean_function()->initial_map();
830 }
831 return isolate->heap()->null_value()->map();
832 }
833
834
GetHash()835 Object* Object::GetHash() {
836 // The object is either a number, a name, an odd-ball,
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 }
842 if (IsName()) {
843 uint32_t hash = Name::cast(this)->Hash();
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 }
850
851 DCHECK(IsJSReceiver());
852 return JSReceiver::cast(this)->GetIdentityHash();
853 }
854
855
GetOrCreateHash(Isolate * isolate,Handle<Object> object)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));
862 }
863
864
SameValue(Object * other)865 bool Object::SameValue(Object* other) {
866 if (other == this) return true;
867
868 // The object is either a number, a name, an odd-ball,
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();
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
SameValueZero(Object * other)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));
897 }
898 if (IsString() && other->IsString()) {
899 return String::cast(this)->Equals(String::cast(other));
900 }
901 return false;
902 }
903
904
ShortPrint(FILE * out)905 void Object::ShortPrint(FILE* out) {
906 OFStream os(out);
907 os << Brief(this);
908 }
909
910
ShortPrint(StringStream * accumulator)911 void Object::ShortPrint(StringStream* accumulator) {
912 OStringStream os;
913 os << Brief(this);
914 accumulator->Add(os.c_str());
915 }
916
917
operator <<(OStream & os,const Brief & v)918 OStream& operator<<(OStream& os, const Brief& v) {
919 if (v.value->IsSmi()) {
920 Smi::cast(v.value)->SmiPrint(os);
921 } else {
922 // TODO(svenpanne) Const-correct HeapObjectShortPrint!
923 HeapObject* obj = const_cast<HeapObject*>(HeapObject::cast(v.value));
924 obj->HeapObjectShortPrint(os);
925 }
926 return os;
927 }
928
929
SmiPrint(OStream & os) const930 void Smi::SmiPrint(OStream& os) const { // NOLINT
931 os << value();
932 }
933
934
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".
AnWord(String * str)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
SlowFlatten(Handle<ConsString> cons,PretenureFlag pretenure)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;
980 }
981 cons->set_first(*result);
982 cons->set_second(isolate->heap()->empty_string());
983 DCHECK(result->IsFlat());
984 return result;
985 }
986
987
988
MakeExternal(v8::String::ExternalStringResource * resource)989 bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
990 // Externalizing twice leaks the external resource, so it's
991 // prohibited by the API.
992 DCHECK(!this->IsExternalString());
993 #ifdef ENABLE_SLOW_DCHECKS
994 if (FLAG_enable_slow_asserts) {
995 // Assert that the resource and the string are equivalent.
996 DCHECK(static_cast<size_t>(this->length()) == resource->length());
997 ScopedVector<uc16> smart_chars(this->length());
998 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
999 DCHECK(memcmp(smart_chars.start(),
1000 resource->data(),
1001 resource->length() * sizeof(smart_chars[0])) == 0);
1002 }
1003 #endif // DEBUG
1004 int size = this->Size(); // Byte size of the original string.
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();
1010
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());
1025 } else {
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());
1032 }
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
1042 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
1043 self->set_resource(resource);
1044 if (is_internalized) self->Hash(); // Force regeneration of the hash value.
1045
1046 heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR);
1047 return true;
1048 }
1049
1050
MakeExternal(v8::String::ExternalOneByteStringResource * resource)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
1056 if (FLAG_enable_slow_asserts) {
1057 // Assert that the resource and the string are equivalent.
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 }
1064 ScopedVector<char> smart_chars(this->length());
1065 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
1066 DCHECK(memcmp(smart_chars.start(),
1067 resource->data(),
1068 resource->length() * sizeof(smart_chars[0])) == 0);
1069 }
1070 #endif // DEBUG
1071 int size = this->Size(); // Byte size of the original string.
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();
1076
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();
1088 } else {
1089 new_map = is_internalized
1090 ? heap->external_one_byte_internalized_string_map()
1091 : heap->external_one_byte_string_map();
1092 }
1093
1094 // Byte size of the external String object.
1095 int new_size = this->SizeFromMap(new_map);
1096 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
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);
1107 return true;
1108 }
1109
1110
StringShortPrint(StringStream * accumulator)1111 void String::StringShortPrint(StringStream* accumulator) {
1112 int len = length();
1113 if (len > kMaxShortPrintLength) {
1114 accumulator->Add("<Very long string[%u]>", len);
1115 return;
1116 }
1117
1118 if (!LooksValid()) {
1119 accumulator->Add("<Invalid String>");
1120 return;
1121 }
1122
1123 ConsStringIteratorOp op;
1124 StringCharacterStream stream(this, &op);
1125
1126 bool truncated = false;
1127 if (len > kMaxShortPrintLength) {
1128 len = kMaxShortPrintLength;
1129 truncated = true;
1130 }
1131 bool one_byte = true;
1132 for (int i = 0; i < len; i++) {
1133 uint16_t c = stream.GetNext();
1134
1135 if (c < 32 || c >= 127) {
1136 one_byte = false;
1137 }
1138 }
1139 stream.Reset(this);
1140 if (one_byte) {
1141 accumulator->Add("<String[%u]: ", length());
1142 for (int i = 0; i < len; i++) {
1143 accumulator->Put(static_cast<char>(stream.GetNext()));
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++) {
1151 uint16_t c = stream.GetNext();
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 {
1161 accumulator->Put(static_cast<char>(c));
1162 }
1163 }
1164 if (truncated) {
1165 accumulator->Put('.');
1166 accumulator->Put('.');
1167 accumulator->Put('.');
1168 }
1169 accumulator->Put('>');
1170 }
1171 return;
1172 }
1173
1174
PrintUC16(OStream & os,int start,int end)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
JSObjectShortPrint(StringStream * accumulator)1185 void JSObject::JSObjectShortPrint(StringStream* accumulator) {
1186 switch (map()->instance_type()) {
1187 case JS_ARRAY_TYPE: {
1188 double length = JSArray::cast(this)->length()->IsUndefined()
1189 ? 0
1190 : JSArray::cast(this)->length()->Number();
1191 accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length));
1192 break;
1193 }
1194 case JS_WEAK_MAP_TYPE: {
1195 accumulator->Add("<JS WeakMap>");
1196 break;
1197 }
1198 case JS_WEAK_SET_TYPE: {
1199 accumulator->Add("<JS WeakSet>");
1200 break;
1201 }
1202 case JS_REGEXP_TYPE: {
1203 accumulator->Add("<JS RegExp>");
1204 break;
1205 }
1206 case JS_FUNCTION_TYPE: {
1207 JSFunction* function = JSFunction::cast(this);
1208 Object* fun_name = function->shared()->DebugName();
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);
1215 printed = true;
1216 }
1217 }
1218 if (!printed) {
1219 accumulator->Add("<JS Function");
1220 }
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>");
1232 break;
1233 }
1234 // All other JSObjects are rather similar to each other (JSObject,
1235 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
1236 default: {
1237 Map* map_of_this = map();
1238 Heap* heap = GetHeap();
1239 Object* constructor = map_of_this->constructor();
1240 bool printed = false;
1241 if (constructor->IsHeapObject() &&
1242 !heap->Contains(HeapObject::cast(constructor))) {
1243 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
1244 } else {
1245 bool global_object = IsJSGlobalProxy();
1246 if (constructor->IsJSFunction()) {
1247 if (!heap->Contains(JSFunction::cast(constructor)->shared())) {
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);
1260 accumulator->Add(" with %smap %p",
1261 map_of_this->is_deprecated() ? "deprecated " : "",
1262 map_of_this);
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
PrintElementsTransition(FILE * file,Handle<JSObject> object,ElementsKind from_kind,Handle<FixedArrayBase> from_elements,ElementsKind to_kind,Handle<FixedArrayBase> to_elements)1283 void JSObject::PrintElementsTransition(
1284 FILE* file, Handle<JSObject> object,
1285 ElementsKind from_kind, Handle<FixedArrayBase> from_elements,
1286 ElementsKind to_kind, Handle<FixedArrayBase> to_elements) {
1287 if (from_kind != to_kind) {
1288 OFStream os(file);
1289 os << "elements transition [" << ElementsKindToString(from_kind) << " -> "
1290 << ElementsKindToString(to_kind) << "] in ";
1291 JavaScriptFrame::PrintTop(object->GetIsolate(), file, false, true);
1292 PrintF(file, " for ");
1293 object->ShortPrint(file);
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
PrintGeneralization(FILE * file,const char * reason,int modify_index,int split,int descriptors,bool constant_to_field,Representation old_representation,Representation new_representation,HeapType * old_field_type,HeapType * new_field_type)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
PrintInstanceMigration(FILE * file,Map * original_map,Map * new_map)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
HeapObjectShortPrint(OStream & os)1374 void HeapObject::HeapObjectShortPrint(OStream& os) { // NOLINT
1375 Heap* heap = GetHeap();
1376 if (!heap->Contains(this)) {
1377 os << "!!!INVALID POINTER!!!";
1378 return;
1379 }
1380 if (!heap->Contains(map())) {
1381 os << "!!!INVALID MAP!!!";
1382 return;
1383 }
1384
1385 os << this << " ";
1386
1387 if (IsString()) {
1388 HeapStringAllocator allocator;
1389 StringStream accumulator(&allocator);
1390 String::cast(this)->StringShortPrint(&accumulator);
1391 os << accumulator.ToCString().get();
1392 return;
1393 }
1394 if (IsJSObject()) {
1395 HeapStringAllocator allocator;
1396 StringStream accumulator(&allocator);
1397 JSObject::cast(this)->JSObjectShortPrint(&accumulator);
1398 os << accumulator.ToCString().get();
1399 return;
1400 }
1401 switch (map()->instance_type()) {
1402 case MAP_TYPE:
1403 os << "<Map(elements=" << Map::cast(this)->elements_kind() << ")>";
1404 break;
1405 case FIXED_ARRAY_TYPE:
1406 os << "<FixedArray[" << FixedArray::cast(this)->length() << "]>";
1407 break;
1408 case FIXED_DOUBLE_ARRAY_TYPE:
1409 os << "<FixedDoubleArray[" << FixedDoubleArray::cast(this)->length()
1410 << "]>";
1411 break;
1412 case BYTE_ARRAY_TYPE:
1413 os << "<ByteArray[" << ByteArray::cast(this)->length() << "]>";
1414 break;
1415 case FREE_SPACE_TYPE:
1416 os << "<FreeSpace[" << FreeSpace::cast(this)->Size() << "]>";
1417 break;
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 }
1440 break;
1441 }
1442 case JS_MESSAGE_OBJECT_TYPE:
1443 os << "<JSMessageObject>";
1444 break;
1445 #define MAKE_STRUCT_CASE(NAME, Name, name) \
1446 case NAME##_TYPE: \
1447 os << "<" #Name ">"; \
1448 break;
1449 STRUCT_LIST(MAKE_STRUCT_CASE)
1450 #undef MAKE_STRUCT_CASE
1451 case CODE_TYPE: {
1452 Code* code = Code::cast(this);
1453 os << "<Code: " << Code::Kind2String(code->kind()) << ">";
1454 break;
1455 }
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 }
1470 break;
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 }
1497 case JS_PROXY_TYPE:
1498 os << "<JSProxy>";
1499 break;
1500 case JS_FUNCTION_PROXY_TYPE:
1501 os << "<JSFunctionProxy>";
1502 break;
1503 case FOREIGN_TYPE:
1504 os << "<Foreign>";
1505 break;
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();
1512 break;
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 }
1522 default:
1523 os << "<Other heap object (" << map()->instance_type() << ")>";
1524 break;
1525 }
1526 }
1527
1528
Iterate(ObjectVisitor * v)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
IterateBody(InstanceType type,int object_size,ObjectVisitor * v)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:
1547 ConsString::BodyDescriptor::IterateBody(this, v);
1548 break;
1549 case kSlicedStringTag:
1550 SlicedString::BodyDescriptor::IterateBody(this, v);
1551 break;
1552 case kExternalStringTag:
1553 if ((type & kStringEncodingMask) == kOneByteStringTag) {
1554 reinterpret_cast<ExternalOneByteString*>(this)
1555 ->ExternalOneByteStringIterateBody(v);
1556 } else {
1557 reinterpret_cast<ExternalTwoByteString*>(this)->
1558 ExternalTwoByteStringIterateBody(v);
1559 }
1560 break;
1561 }
1562 return;
1563 }
1564
1565 switch (type) {
1566 case FIXED_ARRAY_TYPE:
1567 FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
1568 break;
1569 case CONSTANT_POOL_ARRAY_TYPE:
1570 reinterpret_cast<ConstantPoolArray*>(this)->ConstantPoolIterateBody(v);
1571 break;
1572 case FIXED_DOUBLE_ARRAY_TYPE:
1573 break;
1574 case JS_OBJECT_TYPE:
1575 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
1576 case JS_GENERATOR_OBJECT_TYPE:
1577 case JS_MODULE_TYPE:
1578 case JS_VALUE_TYPE:
1579 case JS_DATE_TYPE:
1580 case JS_ARRAY_TYPE:
1581 case JS_ARRAY_BUFFER_TYPE:
1582 case JS_TYPED_ARRAY_TYPE:
1583 case JS_DATA_VIEW_TYPE:
1584 case JS_SET_TYPE:
1585 case JS_MAP_TYPE:
1586 case JS_SET_ITERATOR_TYPE:
1587 case JS_MAP_ITERATOR_TYPE:
1588 case JS_WEAK_MAP_TYPE:
1589 case JS_WEAK_SET_TYPE:
1590 case JS_REGEXP_TYPE:
1591 case JS_GLOBAL_PROXY_TYPE:
1592 case JS_GLOBAL_OBJECT_TYPE:
1593 case JS_BUILTINS_OBJECT_TYPE:
1594 case JS_MESSAGE_OBJECT_TYPE:
1595 JSObject::BodyDescriptor::IterateBody(this, object_size, v);
1596 break;
1597 case JS_FUNCTION_TYPE:
1598 reinterpret_cast<JSFunction*>(this)
1599 ->JSFunctionIterateBody(object_size, v);
1600 break;
1601 case ODDBALL_TYPE:
1602 Oddball::BodyDescriptor::IterateBody(this, v);
1603 break;
1604 case JS_PROXY_TYPE:
1605 JSProxy::BodyDescriptor::IterateBody(this, v);
1606 break;
1607 case JS_FUNCTION_PROXY_TYPE:
1608 JSFunctionProxy::BodyDescriptor::IterateBody(this, v);
1609 break;
1610 case FOREIGN_TYPE:
1611 reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
1612 break;
1613 case MAP_TYPE:
1614 Map::BodyDescriptor::IterateBody(this, v);
1615 break;
1616 case CODE_TYPE:
1617 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1618 break;
1619 case CELL_TYPE:
1620 Cell::BodyDescriptor::IterateBody(this, v);
1621 break;
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
1629 case HEAP_NUMBER_TYPE:
1630 case MUTABLE_HEAP_NUMBER_TYPE:
1631 case FILLER_TYPE:
1632 case BYTE_ARRAY_TYPE:
1633 case FREE_SPACE_TYPE:
1634 break;
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
1644 case SHARED_FUNCTION_INFO_TYPE: {
1645 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
1646 break;
1647 }
1648
1649 #define MAKE_STRUCT_CASE(NAME, Name, name) \
1650 case NAME##_TYPE:
1651 STRUCT_LIST(MAKE_STRUCT_CASE)
1652 #undef MAKE_STRUCT_CASE
1653 if (type == ALLOCATION_SITE_TYPE) {
1654 AllocationSite::BodyDescriptor::IterateBody(this, v);
1655 } else {
1656 StructBodyDescriptor::IterateBody(this, object_size, v);
1657 }
1658 break;
1659 default:
1660 PrintF("Unknown type: %d\n", type);
1661 UNREACHABLE();
1662 }
1663 }
1664
1665
HeapNumberBooleanValue()1666 bool HeapNumber::HeapNumberBooleanValue() {
1667 return DoubleToBoolean(value());
1668 }
1669
1670
HeapNumberPrint(OStream & os)1671 void HeapNumber::HeapNumberPrint(OStream& os) { // NOLINT
1672 os << value();
1673 }
1674
1675
class_name()1676 String* JSReceiver::class_name() {
1677 if (IsJSFunction() || IsJSFunctionProxy()) {
1678 return GetHeap()->Function_string();
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".
1685 return GetHeap()->Object_string();
1686 }
1687
1688
constructor_name()1689 String* Map::constructor_name() {
1690 if (constructor()->IsJSFunction()) {
1691 JSFunction* constructor = JSFunction::cast(this->constructor());
1692 String* name = String::cast(constructor->shared()->name());
1693 if (name->length() > 0) return name;
1694 String* inferred_name = constructor->shared()->inferred_name();
1695 if (inferred_name->length() > 0) return inferred_name;
1696 Object* proto = prototype();
1697 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
1698 }
1699 // TODO(rossberg): what about proxies?
1700 // If the constructor is not present, return "Object".
1701 return GetHeap()->Object_string();
1702 }
1703
1704
constructor_name()1705 String* JSReceiver::constructor_name() {
1706 return map()->constructor_name();
1707 }
1708
1709
CopyWithField(Handle<Map> map,Handle<Name> name,Handle<HeapType> type,PropertyAttributes attributes,Representation representation,TransitionFlag flag)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()));
1719
1720 // Ensure the descriptor array does not get too big.
1721 if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors) {
1722 return MaybeHandle<Map>();
1723 }
1724
1725 Isolate* isolate = map->GetIsolate();
1726
1727 // Compute the new index for new field.
1728 int index = map->NextFreePropertyIndex();
1729
1730 if (map->instance_type() == JS_CONTEXT_EXTENSION_OBJECT_TYPE) {
1731 representation = Representation::Tagged();
1732 type = HeapType::Any(isolate);
1733 }
1734
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;
1740 }
1741 new_map->set_unused_property_fields(unused_property_fields);
1742 return new_map;
1743 }
1744
1745
CopyWithConstant(Handle<Map> map,Handle<Name> name,Handle<Object> constant,PropertyAttributes attributes,TransitionFlag flag)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>();
1754 }
1755
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);
1759 }
1760
1761
AddSlowProperty(Handle<JSObject> object,Handle<Name> name,Handle<Object> value,PropertyAttributes attributes)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()) {
1770 // In case name is an orphaned property reuse the cell.
1771 int entry = dict->FindEntry(name);
1772 if (entry != NameDictionary::kNotFound) {
1773 Handle<PropertyCell> cell(PropertyCell::cast(dict->ValueAt(entry)));
1774 PropertyCell::SetValueInferType(cell, value);
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);
1780 dict->SetEntry(entry, name, cell, details);
1781 return;
1782 }
1783 Handle<PropertyCell> cell = isolate->factory()->NewPropertyCell(value);
1784 PropertyCell::SetValueInferType(cell, value);
1785 value = cell;
1786 }
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);
1791 }
1792
1793
GetCreationContext()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
EnqueueChangeRecord(Handle<JSObject> object,const char * type_str,Handle<Name> name,Handle<Object> old_value)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
Mnemonic() const1828 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
InstancesNeedRewriting(Map * target,int target_number_of_fields,int target_inobject,int target_unused,int * old_number_of_fields)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;
1860 }
1861 }
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
ConnectElementsTransition(Handle<Map> parent,Handle<Map> child)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
MigrateToMap(Handle<JSObject> object,Handle<Map> new_map)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());
1900 }
1901 } else {
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.
MigrateFastToFast(Handle<JSObject> object,Handle<Map> new_map)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);
1959 }
1960 object->synchronized_set_map(*new_map);
1961 return;
1962 }
1963
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);
1970
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();
1977 }
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;
1990 }
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
GeneralizeFieldRepresentation(Handle<JSObject> object,int modify_index,Representation new_representation,Handle<HeapType> new_field_type)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
NumberOfFields()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 }
2101 return result;
2102 }
2103
2104
CopyGeneralizeAllRepresentations(Handle<Map> map,int modify_index,StoreMode store_mode,PropertyAttributes attributes,const char * reason)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);
2112
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());
2119 }
2120 }
2121
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;
2135 }
2136 new_map->set_unused_property_fields(unused_property_fields);
2137 }
2138 } else {
2139 DCHECK(details.attributes() == attributes);
2140 }
2141
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());
2152 }
2153 return new_map;
2154 }
2155
2156
2157 // static
CopyGeneralizeAllRepresentations(Handle<Map> map,int modify_index,StoreMode store_mode,const char * reason)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 }
2167
2168
DeprecateTransitionTree()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();
2175 }
2176 }
2177 deprecate();
2178 dependent_code()->DeoptimizeDependentCodeGroup(
2179 GetIsolate(), DependentCode::kTransitionGroup);
2180 NotifyLeafMapLayoutChange();
2181 }
2182
2183
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.
DeprecateTarget(Name * key,DescriptorArray * new_descriptors)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();
2193 }
2194 }
2195
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
FindRootMap()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
FindLastMatchMap(int verbatim,int length,DescriptorArray * descriptors)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;
2252 } else {
2253 if (descriptors->GetValue(i) != next_descriptors->GetValue(i)) break;
2254 }
2255
2256 current = next;
2257 }
2258 return current;
2259 }
2260
2261
FindFieldOwner(int descriptor)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;
2272 }
2273 return result;
2274 }
2275
2276
UpdateFieldType(int descriptor,Handle<Name> name,Handle<HeapType> new_type)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);
2286 }
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
GeneralizeFieldType(Handle<HeapType> type1,Handle<HeapType> type2,Isolate * isolate)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
GeneralizeFieldType(Handle<Map> map,int modify_index,Handle<HeapType> new_field_type)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.
GeneralizeRepresentation(Handle<Map> old_map,int modify_index,Representation new_representation,Handle<HeapType> new_field_type,StoreMode store_mode)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)) {
2457 break;
2458 }
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;
2480 }
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;
2672 }
2673
2674
2675 // Generalize the representation of all FIELD descriptors.
GeneralizeAllFieldRepresentations(Handle<Map> map)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);
2684 }
2685 }
2686 return map;
2687 }
2688
2689
2690 // static
TryUpdate(Handle<Map> map)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
Update(Handle<Map> map)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
TryUpdateInternal(Handle<Map> old_map)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
SetPropertyWithInterceptor(LookupIterator * it,Handle<Object> value)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
SetProperty(Handle<Object> object,Handle<Name> name,Handle<Object> value,StrictMode strict_mode,StoreFromKeyed store_mode)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
SetProperty(LookupIterator * it,Handle<Object> value,StrictMode strict_mode,StoreFromKeyed store_mode)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
WriteToReadOnlyProperty(LookupIterator * it,Handle<Object> value,StrictMode strict_mode)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
SetDataProperty(LookupIterator * it,Handle<Object> value)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
AddDataProperty(LookupIterator * it,Handle<Object> value,PropertyAttributes attributes,StrictMode strict_mode,StoreFromKeyed store_mode)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
SetElementWithCallbackSetterInPrototypes(Handle<JSObject> object,uint32_t index,Handle<Object> value,bool * found,StrictMode strict_mode)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()) {
3027 continue;
3028 }
3029 Handle<SeededNumberDictionary> dictionary(js_proto->element_dictionary());
3030 int entry = dictionary->FindEntry(index);
3031 if (entry != SeededNumberDictionary::kNotFound) {
3032 PropertyDetails details = dictionary->DetailsAt(entry);
3033 if (details.type() == CALLBACKS) {
3034 *found = true;
3035 Handle<Object> structure(dictionary->ValueAt(entry), isolate);
3036 return SetElementWithCallback(object, structure, index, value, js_proto,
3037 strict_mode);
3038 }
3039 }
3040 }
3041 *found = false;
3042 return isolate->factory()->the_hole_value();
3043 }
3044
3045
EnsureDescriptorSlack(Handle<Map> map,int slack)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>
AppendUniqueCallbacks(NeanderArray * callbacks,Handle<typename T::Array> array,int valid_descriptors)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++;
3116 }
3117 }
3118
3119 return valid_descriptors;
3120 }
3121
3122 struct DescriptorArrayAppender {
3123 typedef DescriptorArray Array;
Containsv8::internal::DescriptorArrayAppender3124 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 }
Insertv8::internal::DescriptorArrayAppender3131 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;
Containsv8::internal::FixedArrayAppender3144 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 }
Insertv8::internal::FixedArrayAppender3153 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
AppendCallbackDescriptors(Handle<Map> map,Handle<Object> descriptors)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);
3171 }
3172
3173
AppendUnique(Handle<Object> descriptors,Handle<FixedArray> array,int valid_descriptors)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);
3182 }
3183
3184
ContainsMap(MapHandleList * maps,Handle<Map> map)3185 static bool ContainsMap(MapHandleList* maps, Handle<Map> map) {
3186 DCHECK(!map.is_null());
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 }
3192
3193
3194 template <class T>
MaybeNull(T * p)3195 static Handle<T> MaybeNull(T* p) {
3196 if (p == NULL) return Handle<T>::null();
3197 return Handle<T>(p);
3198 }
3199
3200
FindTransitionedMap(MapHandleList * candidates)3201 Handle<Map> Map::FindTransitionedMap(MapHandleList* candidates) {
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;
3218 }
3219 }
3220 return transitioned_map;
3221 }
3222
3223
FindClosestElementsTransition(Map * map,ElementsKind to_kind)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;
3240 }
3241 return map;
3242 }
3243
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
LookupElementsTransitionMap(ElementsKind to_kind)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
IsMapInArrayPrototypeChain()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
AddMissingElementsTransitions(Handle<Map> map,ElementsKind to_kind)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
TransitionElementsTo(Handle<Map> map,ElementsKind to_kind)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));
3325 }
3326 }
3327 }
3328
3329 return TransitionElementsToSlow(map, to_kind);
3330 }
3331
3332
TransitionElementsToSlow(Handle<Map> map,ElementsKind to_kind)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;
3339 }
3340
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);
3352 }
3353
3354 if (!allow_store_transition) {
3355 return Map::CopyAsElementsKind(map, to_kind, OMIT_TRANSITION);
3356 }
3357
3358 return Map::AsElementsKind(map, to_kind);
3359 }
3360
3361
3362 // static
AsElementsKind(Handle<Map> map,ElementsKind kind)3363 Handle<Map> Map::AsElementsKind(Handle<Map> map, ElementsKind kind) {
3364 Handle<Map> closest_map(FindClosestElementsTransition(*map, kind));
3365
3366 if (closest_map->elements_kind() == kind) {
3367 return closest_map;
3368 }
3369
3370 return AddMissingElementsTransitions(closest_map, kind);
3371 }
3372
3373
GetElementsTransitionMap(Handle<JSObject> object,ElementsKind to_kind)3374 Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object,
3375 ElementsKind to_kind) {
3376 Handle<Map> map(object->map());
3377 return Map::TransitionElementsTo(map, to_kind);
3378 }
3379
3380
HasPropertyWithHandler(Handle<JSProxy> proxy,Handle<Name> name)3381 Maybe<bool> JSProxy::HasPropertyWithHandler(Handle<JSProxy> proxy,
3382 Handle<Name> name) {
3383 Isolate* isolate = proxy->GetIsolate();
3384
3385 // TODO(rossberg): adjust once there is a story for symbols vs proxies.
3386 if (name->IsSymbol()) return maybe(false);
3387
3388 Handle<Object> args[] = { name };
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>());
3394
3395 return maybe(result->BooleanValue());
3396 }
3397
3398
SetPropertyWithHandler(Handle<JSProxy> proxy,Handle<Object> receiver,Handle<Name> name,Handle<Object> value,StrictMode strict_mode)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;
3408
3409 Handle<Object> args[] = { receiver, name, value };
3410 RETURN_ON_EXCEPTION(
3411 isolate,
3412 CallTrap(proxy,
3413 "set",
3414 isolate->derived_set_trap(),
3415 arraysize(args),
3416 args),
3417 Object);
3418
3419 return value;
3420 }
3421
3422
SetPropertyViaPrototypesWithHandler(Handle<JSProxy> proxy,Handle<Object> receiver,Handle<Name> name,Handle<Object> value,StrictMode strict_mode,bool * done)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.
3428
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();
3433 }
3434
3435 *done = true; // except where redefined...
3436 Handle<Object> args[] = { name };
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);
3446
3447 if (result->IsUndefined()) {
3448 *done = false;
3449 return isolate->factory()->the_hole_value();
3450 }
3451
3452 // Emulate [[GetProperty]] semantics for proxies.
3453 Handle<Object> argv[] = { result };
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
DeletePropertyWithHandler(Handle<JSProxy> proxy,Handle<Name> name,DeleteMode mode)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
DeleteElementWithHandler(Handle<JSProxy> proxy,uint32_t index,DeleteMode mode)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
GetPropertyAttributesWithHandler(Handle<JSProxy> proxy,Handle<Object> receiver,Handle<Name> name)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>());
3586
3587 // Convert result to PropertyAttributes.
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 }
3615
3616 if (configurable->IsFalse()) {
3617 Handle<Object> handler(proxy->handler(), isolate);
3618 Handle<String> trap = isolate->factory()->InternalizeOneByteString(
3619 STATIC_CHAR_VECTOR("getPropertyDescriptor"));
3620 Handle<Object> args[] = { handler, trap, name };
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);
3626 }
3627
3628 int attributes = NONE;
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));
3633 }
3634
3635
GetElementAttributeWithHandler(Handle<JSProxy> proxy,Handle<JSReceiver> receiver,uint32_t index)3636 Maybe<PropertyAttributes> JSProxy::GetElementAttributeWithHandler(
3637 Handle<JSProxy> proxy, Handle<JSReceiver> receiver, uint32_t index) {
3638 Isolate* isolate = proxy->GetIsolate();
3639 Handle<String> name = isolate->factory()->Uint32ToString(index);
3640 return GetPropertyAttributesWithHandler(proxy, receiver, name);
3641 }
3642
3643
Fix(Handle<JSProxy> proxy)3644 void JSProxy::Fix(Handle<JSProxy> proxy) {
3645 Isolate* isolate = proxy->GetIsolate();
3646
3647 // Save identity hash.
3648 Handle<Object> hash(proxy->GetIdentityHash(), isolate);
3649
3650 if (proxy->IsJSFunctionProxy()) {
3651 isolate->factory()->BecomeJSFunction(proxy);
3652 // Code will be set on the JavaScript side.
3653 } else {
3654 isolate->factory()->BecomeJSObject(proxy);
3655 }
3656 DCHECK(proxy->IsJSObject());
3657
3658 // Inherit identity, if it was present.
3659 if (hash->IsSmi()) {
3660 JSObject::SetIdentityHash(Handle<JSObject>::cast(proxy),
3661 Handle<Smi>::cast(hash));
3662 }
3663 }
3664
3665
CallTrap(Handle<JSProxy> proxy,const char * name,Handle<Object> derived,int argc,Handle<Object> argv[])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);
3673
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);
3680
3681 if (trap->IsUndefined()) {
3682 if (derived.is_null()) {
3683 Handle<Object> args[] = { handler, trap_name };
3684 THROW_NEW_ERROR(isolate,
3685 NewTypeError("handler_trap_missing",
3686 HandleVector(args, arraysize(args))),
3687 Object);
3688 }
3689 trap = Handle<Object>(derived);
3690 }
3691
3692 return Execution::Call(isolate, trap, handler, argc, argv);
3693 }
3694
3695
AllocateStorageForMap(Handle<JSObject> object,Handle<Map> map)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;
3705 }
3706 if (IsDictionaryElementsKind(to_kind)) {
3707 NormalizeElements(object);
3708 } else {
3709 TransitionElementsKind(object, to_kind);
3710 }
3711 map = Map::AsElementsKind(map, to_kind);
3712 }
3713 JSObject::MigrateToMap(object, map);
3714 }
3715
3716
MigrateInstance(Handle<JSObject> object)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
TryMigrateInstance(Handle<JSObject> object)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
MigrateToNewProperty(Handle<JSObject> object,Handle<Map> map,Handle<Object> value)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
WriteToField(int descriptor,Object * value)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
AddProperty(Handle<JSObject> object,Handle<Name> name,Handle<Object> value,PropertyAttributes attributes)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.
SetOwnPropertyIgnoreAttributes(Handle<JSObject> object,Handle<Name> name,Handle<Object> value,PropertyAttributes attributes,ExecutableAccessorInfoHandling handling)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
3895 return value;
3896 }
3897
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();
3907
3908 it.ReconfigureDataProperty(value, attributes);
3909 it.PrepareForDataProperty(value);
3910 it.WriteDataValue(value);
3911
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 }
3918
3919 return value;
3920 }
3921 }
3922 }
3923
3924 return AddDataProperty(&it, value, attributes, STRICT,
3925 CERTAINLY_NOT_STORE_FROM_KEYED);
3926 }
3927
3928
GetPropertyAttributesWithInterceptor(Handle<JSObject> holder,Handle<Object> receiver,Handle<Name> name)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);
3938
3939 // Make sure that the top context does not change when doing
3940 // callbacks or interceptor calls.
3941 AssertNoContextChange ncc(isolate);
3942
3943 Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor());
3944 PropertyCallbackArguments args(
3945 isolate, interceptor->data(), *receiver, *holder);
3946 if (!interceptor->query()->IsUndefined()) {
3947 v8::NamedPropertyQueryCallback query =
3948 v8::ToCData<v8::NamedPropertyQueryCallback>(interceptor->query());
3949 LOG(isolate,
3950 ApiNamedPropertyAccess("interceptor-named-has", *holder, *name));
3951 v8::Handle<v8::Integer> result =
3952 args.Call(query, v8::Utils::ToLocal(Handle<String>::cast(name)));
3953 if (!result.IsEmpty()) {
3954 DCHECK(result->IsInt32());
3955 return maybe(static_cast<PropertyAttributes>(result->Int32Value()));
3956 }
3957 } else if (!interceptor->getter()->IsUndefined()) {
3958 v8::NamedPropertyGetterCallback getter =
3959 v8::ToCData<v8::NamedPropertyGetterCallback>(interceptor->getter());
3960 LOG(isolate,
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);
3965 }
3966
3967 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe<PropertyAttributes>());
3968 return maybe(ABSENT);
3969 }
3970
3971
GetOwnPropertyAttributes(Handle<JSReceiver> object,Handle<Name> name)3972 Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes(
3973 Handle<JSReceiver> object, Handle<Name> name) {
3974 // Check whether the name is an array index.
3975 uint32_t index = 0;
3976 if (object->IsJSObject() && name->AsArrayIndex(&index)) {
3977 return GetOwnElementAttribute(object, index);
3978 }
3979 LookupIterator it(object, name, LookupIterator::HIDDEN);
3980 return GetPropertyAttributes(&it);
3981 }
3982
3983
GetPropertyAttributes(LookupIterator * it)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;
4001 }
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());
4008 }
4009 }
4010 return maybe(ABSENT);
4011 }
4012
4013
GetElementAttributeWithReceiver(Handle<JSObject> object,Handle<JSReceiver> receiver,uint32_t index,bool check_prototype)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 }
4026 }
4027
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);
4035 }
4036
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
GetElementAttributeWithInterceptor(Handle<JSObject> object,Handle<JSReceiver> receiver,uint32_t index,bool check_prototype)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
GetElementAttributeWithoutInterceptor(Handle<JSObject> object,Handle<JSReceiver> receiver,uint32_t index,bool check_prototype)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
New(Isolate * isolate)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
Get(Handle<Map> fast_map,PropertyNormalizationMode mode)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
Set(Handle<Map> fast_map,Handle<Map> normalized_map)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);
4136 }
4137
4138
Clear()4139 void NormalizedMapCache::Clear() {
4140 int entries = length();
4141 for (int i = 0; i != entries; i++) {
4142 set_undefined(i);
4143 }
4144 }
4145
4146
UpdateMapCodeCache(Handle<HeapObject> object,Handle<Name> name,Handle<Code> code)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);
4152 }
4153
4154
NormalizeProperties(Handle<JSObject> object,PropertyNormalizationMode mode,int expected_additional_properties)4155 void JSObject::NormalizeProperties(Handle<JSObject> object,
4156 PropertyNormalizationMode mode,
4157 int expected_additional_properties) {
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);
4164 }
4165
4166
MigrateFastToSlow(Handle<JSObject> object,Handle<Map> new_map,int expected_additional_properties)4167 void JSObject::MigrateFastToSlow(Handle<JSObject> object,
4168 Handle<Map> new_map,
4169 int expected_additional_properties) {
4170 // The global object is always normalized.
4171 DCHECK(!object->IsGlobalObject());
4172 // JSGlobalProxy must never be normalized
4173 DCHECK(!object->IsJSGlobalProxy());
4174
4175 Isolate* isolate = object->GetIsolate();
4176 HandleScope scope(isolate);
4177 Handle<Map> map(object->map());
4178
4179 // Allocate new content.
4180 int real_size = map->NumberOfOwnDescriptors();
4181 int property_count = real_size;
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 }
4187 Handle<NameDictionary> dictionary =
4188 NameDictionary::New(isolate, property_count);
4189
4190 Handle<DescriptorArray> descs(map->instance_descriptors());
4191 for (int i = 0; i < real_size; i++) {
4192 PropertyDetails details = descs->GetDetails(i);
4193 switch (details.type()) {
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);
4200 break;
4201 }
4202 case FIELD: {
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 }
4212 PropertyDetails d =
4213 PropertyDetails(details.attributes(), NORMAL, i + 1);
4214 dictionary = NameDictionary::Add(dictionary, key, value, d);
4215 break;
4216 }
4217 case CALLBACKS: {
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);
4223 break;
4224 }
4225 case NORMAL:
4226 UNREACHABLE();
4227 break;
4228 }
4229 }
4230
4231 // Copy the next enumeration index from instance descriptor.
4232 dictionary->SetNextEnumerationIndex(real_size + 1);
4233
4234 // From here on we cannot fail and we shouldn't GC anymore.
4235 DisallowHeapAllocation no_allocation;
4236
4237 // Resize the object in the heap if necessary.
4238 int new_instance_size = new_map->instance_size();
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);
4248 }
4249
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);
4253
4254 object->set_properties(*dictionary);
4255
4256 isolate->counters()->props_to_dictionary()->Increment();
4257
4258 #ifdef DEBUG
4259 if (FLAG_trace_normalization) {
4260 OFStream os(stdout);
4261 os << "Object properties have been normalized:\n";
4262 object->Print(os);
4263 }
4264 #endif
4265 }
4266
4267
MigrateSlowToFast(Handle<JSObject> object,int unused_property_fields)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());
4402 }
4403
4404
ResetElements(Handle<JSObject> object)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
CopyFastElementsToDictionary(Handle<FixedArrayBase> array,int length,Handle<SeededNumberDictionary> dictionary)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;
4445 }
4446
4447
NormalizeElements(Handle<JSObject> object)4448 Handle<SeededNumberDictionary> JSObject::NormalizeElements(
4449 Handle<JSObject> object) {
4450 DCHECK(!object->HasExternalArrayElements() &&
4451 !object->HasFixedTypedArrayElements());
4452 Isolate* isolate = object->GetIsolate();
4453
4454 // Find the backing store.
4455 Handle<FixedArrayBase> array(FixedArrayBase::cast(object->elements()));
4456 bool is_arguments =
4457 (array->map() == isolate->heap()->sloppy_arguments_elements_map());
4458 if (is_arguments) {
4459 array = handle(FixedArrayBase::cast(
4460 Handle<FixedArray>::cast(array)->get(1)));
4461 }
4462 if (array->IsDictionary()) return Handle<SeededNumberDictionary>::cast(array);
4463
4464 DCHECK(object->HasFastSmiOrObjectElements() ||
4465 object->HasFastDoubleElements() ||
4466 object->HasFastArgumentsElements());
4467 // Compute the effective length and allocate a new backing store.
4468 int length = object->IsJSArray()
4469 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
4470 : array->length();
4471 int old_capacity = 0;
4472 int used_elements = 0;
4473 object->GetElementsCapacityAndUsage(&old_capacity, &used_elements);
4474 Handle<SeededNumberDictionary> dictionary =
4475 SeededNumberDictionary::New(isolate, used_elements);
4476
4477 dictionary = CopyFastElementsToDictionary(array, length, dictionary);
4478
4479 // Switch to using the dictionary as the backing storage for elements.
4480 if (is_arguments) {
4481 FixedArray::cast(object->elements())->set(1, *dictionary);
4482 } else {
4483 // Set the new map first to satify the elements type assert in
4484 // set_elements().
4485 Handle<Map> new_map =
4486 JSObject::GetElementsTransitionMap(object, DICTIONARY_ELEMENTS);
4487
4488 JSObject::MigrateToMap(object, new_map);
4489 object->set_elements(*dictionary);
4490 }
4491
4492 isolate->counters()->elements_to_dictionary()->Increment();
4493
4494 #ifdef DEBUG
4495 if (FLAG_trace_normalization) {
4496 OFStream os(stdout);
4497 os << "Object elements have been normalized:\n";
4498 object->Print(os);
4499 }
4500 #endif
4501
4502 DCHECK(object->HasDictionaryElements() ||
4503 object->HasDictionaryArgumentsElements());
4504 return dictionary;
4505 }
4506
4507
GenerateIdentityHash(Isolate * isolate)4508 static Smi* GenerateIdentityHash(Isolate* isolate) {
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.
4514 hash_value = isolate->random_number_generator()->NextInt() & Smi::kMaxValue;
4515 attempts++;
4516 } while (hash_value == 0 && attempts < 30);
4517 hash_value = hash_value != 0 ? hash_value : 1; // never return 0
4518
4519 return Smi::FromInt(hash_value);
4520 }
4521
4522
SetIdentityHash(Handle<JSObject> object,Handle<Smi> hash)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);
4527 }
4528
4529
4530 template<typename ProxyType>
GetOrCreateIdentityHashHelper(Handle<ProxyType> proxy)4531 static Handle<Smi> GetOrCreateIdentityHashHelper(Handle<ProxyType> proxy) {
4532 Isolate* isolate = proxy->GetIsolate();
4533
4534 Handle<Object> maybe_hash(proxy->hash(), isolate);
4535 if (maybe_hash->IsSmi()) return Handle<Smi>::cast(maybe_hash);
4536
4537 Handle<Smi> hash(GenerateIdentityHash(isolate), isolate);
4538 proxy->set_hash(*hash);
4539 return hash;
4540 }
4541
4542
GetIdentityHash()4543 Object* JSObject::GetIdentityHash() {
4544 DisallowHeapAllocation no_gc;
4545 Isolate* isolate = GetIsolate();
4546 if (IsJSGlobalProxy()) {
4547 return JSGlobalProxy::cast(this)->hash();
4548 }
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
GetOrCreateIdentityHash(Handle<JSObject> object)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);
4569 return hash;
4570 }
4571
4572
GetIdentityHash()4573 Object* JSProxy::GetIdentityHash() {
4574 return this->hash();
4575 }
4576
4577
GetOrCreateIdentityHash(Handle<JSProxy> proxy)4578 Handle<Smi> JSProxy::GetOrCreateIdentityHash(Handle<JSProxy> proxy) {
4579 return GetOrCreateIdentityHashHelper(proxy);
4580 }
4581
4582
GetHiddenProperty(Handle<Name> key)4583 Object* JSObject::GetHiddenProperty(Handle<Name> key) {
4584 DisallowHeapAllocation no_gc;
4585 DCHECK(key->IsUniqueName());
4586 if (IsJSGlobalProxy()) {
4587 // JSGlobalProxies store their hash internally.
4588 DCHECK(*key != GetHeap()->identity_hash_string());
4589 // For a proxy, use the prototype as target object.
4590 PrototypeIterator iter(GetIsolate(), this);
4591 // If the proxy is detached, return undefined.
4592 if (iter.IsAtEnd()) return GetHeap()->the_hole_value();
4593 DCHECK(iter.GetCurrent()->IsJSGlobalObject());
4594 return JSObject::cast(iter.GetCurrent())->GetHiddenProperty(key);
4595 }
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
SetHiddenProperty(Handle<JSObject> object,Handle<Name> key,Handle<Object> value)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);
4647
4648 // If it was found, check if the key is already in the dictionary.
4649 Handle<ObjectHashTable> new_table = ObjectHashTable::Put(hashtable, key,
4650 value);
4651 if (*new_table != *hashtable) {
4652 // If adding the key expanded the dictionary (i.e., Add returned a new
4653 // dictionary), store it back to the object.
4654 SetHiddenPropertiesHashTable(object, new_table);
4655 }
4656
4657 // Return this to mark success.
4658 return object;
4659 }
4660
4661
DeleteHiddenProperty(Handle<JSObject> object,Handle<Name> key)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);
4672 }
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);
4683 }
4684
4685
HasHiddenProperties(Handle<JSObject> object)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;
4693 }
4694
4695
GetHiddenPropertiesHashTable()4696 Object* JSObject::GetHiddenPropertiesHashTable() {
4697 DCHECK(!IsJSGlobalProxy());
4698 if (HasFastProperties()) {
4699 // If the object has fast properties, check whether the first slot
4700 // in the descriptor array matches the hidden string. Since the
4701 // hidden strings hash code is zero (and no other name has hash
4702 // code zero) it will always occupy the first entry if present.
4703 DescriptorArray* descriptors = this->map()->instance_descriptors();
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);
4714 } else {
4715 return GetHeap()->undefined_value();
4716 }
4717 } else {
4718 return GetHeap()->undefined_value();
4719 }
4720 } else {
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);
4726 }
4727 }
4728
GetOrCreateHiddenPropertiesHashtable(Handle<JSObject> object)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;
4752 }
4753
4754
SetHiddenPropertiesHashTable(Handle<JSObject> object,Handle<Object> value)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
DeletePropertyWithInterceptor(Handle<JSObject> holder,Handle<JSObject> receiver,Handle<Name> name)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
DeleteElementWithInterceptor(Handle<JSObject> object,uint32_t index)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
DeleteElement(Handle<JSObject> object,uint32_t index,DeleteMode mode)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());
4871 } else {
4872 old_value = Object::GetElement(
4873 isolate, object, index).ToHandleChecked();
4874 }
4875 }
4876 }
4877
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);
4884 }
4885 Handle<Object> result;
4886 ASSIGN_RETURN_ON_EXCEPTION(isolate, result, maybe_result, Object);
4887
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);
4894 }
4895 }
4896
4897 return result;
4898 }
4899
4900
DeleteProperty(Handle<JSObject> object,Handle<Name> name,DeleteMode delete_mode)4901 MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
4902 Handle<Name> name,
4903 DeleteMode delete_mode) {
4904 // ECMA-262, 3rd, 8.6.2.5
4905 DCHECK(name->IsName());
4906
4907 uint32_t index = 0;
4908 if (name->AsArrayIndex(&index)) {
4909 return DeleteElement(object, index, delete_mode);
4910 }
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();
4988 }
4989
4990
DeleteElement(Handle<JSReceiver> object,uint32_t index,DeleteMode mode)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);
4997 }
4998 return JSObject::DeleteElement(Handle<JSObject>::cast(object), index, mode);
4999 }
5000
5001
DeleteProperty(Handle<JSReceiver> object,Handle<Name> name,DeleteMode mode)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);
5008 }
5009 return JSObject::DeleteProperty(Handle<JSObject>::cast(object), name, mode);
5010 }
5011
5012
ReferencesObjectFromElements(FixedArray * elements,ElementsKind kind,Object * object)5013 bool JSObject::ReferencesObjectFromElements(FixedArray* elements,
5014 ElementsKind kind,
5015 Object* object) {
5016 DCHECK(IsFastObjectElementsKind(kind) ||
5017 kind == DICTIONARY_ELEMENTS);
5018 if (IsFastObjectElementsKind(kind)) {
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 {
5027 Object* key =
5028 SeededNumberDictionary::cast(elements)->SlowReverseLookup(object);
5029 if (!key->IsUndefined()) return true;
5030 }
5031 return false;
5032 }
5033
5034
5035 // Check whether this object references another object.
ReferencesObject(Object * obj)5036 bool JSObject::ReferencesObject(Object* obj) {
5037 Map* map_of_this = map();
5038 Heap* heap = GetHeap();
5039 DisallowHeapAllocation no_allocation;
5040
5041 // Is the object the constructor for this object?
5042 if (map_of_this->constructor() == obj) {
5043 return true;
5044 }
5045
5046 // Is the object the prototype for this object?
5047 if (map_of_this->prototype() == obj) {
5048 return true;
5049 }
5050
5051 // Check if the object is among the named properties.
5052 Object* key = SlowReverseLookup(obj);
5053 if (!key->IsUndefined()) {
5054 return true;
5055 }
5056
5057 // Check if the object is among the indexed properties.
5058 ElementsKind kind = GetElementsKind();
5059 switch (kind) {
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: \
5065 break;
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:
5075 break;
5076 case FAST_ELEMENTS:
5077 case FAST_HOLEY_ELEMENTS:
5078 case DICTIONARY_ELEMENTS: {
5079 FixedArray* elements = FixedArray::cast(this->elements());
5080 if (ReferencesObjectFromElements(elements, kind, obj)) return true;
5081 break;
5082 }
5083 case SLOPPY_ARGUMENTS_ELEMENTS: {
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));
5093 kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS :
5094 FAST_HOLEY_ELEMENTS;
5095 if (ReferencesObjectFromElements(arguments, kind, obj)) return true;
5096 break;
5097 }
5098 }
5099
5100 // For functions check the context.
5101 if (IsJSFunction()) {
5102 // Get the constructor function for arguments array.
5103 Map* arguments_map =
5104 heap->isolate()->context()->native_context()->sloppy_arguments_map();
5105 JSFunction* arguments_function =
5106 JSFunction::cast(arguments_map->constructor());
5107
5108 // Get the context and don't check if it is the native context.
5109 JSFunction* f = JSFunction::cast(this);
5110 Context* context = f->context();
5111 if (context->IsNativeContext()) {
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
5131 // Check the context extension (if any) if it can have references.
5132 if (context->has_extension() && !context->IsCatchContext()) {
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
5139 return JSObject::cast(context->extension())->ReferencesObject(obj);
5140 }
5141 }
5142
5143 // No references to object.
5144 return false;
5145 }
5146
5147
PreventExtensions(Handle<JSObject> object)5148 MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
5149 Isolate* isolate = object->GetIsolate();
5150
5151 if (!object->map()->is_extensible()) return object;
5152
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();
5159 }
5160
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)));
5167 }
5168
5169 // It's not possible to seal objects with external array elements
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);
5176 }
5177
5178 // If there are fast elements we normalize.
5179 Handle<SeededNumberDictionary> dictionary = NormalizeElements(object);
5180 DCHECK(object->HasDictionaryElements() ||
5181 object->HasDictionaryArgumentsElements());
5182
5183 // Make sure that we never go back to fast case.
5184 dictionary->set_requires_slow_elements();
5185
5186 // Do a map transition, other objects with this map may still
5187 // be extensible.
5188 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
5189 Handle<Map> new_map = Map::Copy(handle(object->map()));
5190
5191 new_map->set_is_extensible(false);
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>
FreezeDictionary(Dictionary * 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
Freeze(Handle<JSObject> object)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
SetObserved(Handle<JSObject> object)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
FastPropertyAt(Handle<JSObject> object,Representation representation,FieldIndex index)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:
JSObjectWalkVisitor(ContextObject * site_context,bool copying,JSObject::DeepCopyHints hints)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:
VisitElementOrProperty(Handle<JSObject> object,Handle<JSObject> value)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
site_context()5381 inline ContextObject* site_context() { return site_context_; }
isolate()5382 inline Isolate* isolate() { return site_context()->isolate(); }
5383
copying() const5384 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>
StructureWalk(Handle<JSObject> object)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
DeepWalk(Handle<JSObject> object,AllocationSiteCreationContext * site_context)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
DeepCopy(Handle<JSObject> object,AllocationSiteUsageContext * site_context,DeepCopyHints hints)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;
5591 }
5592
5593
5594 // Tests for the fast common case for property enumeration:
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).
5597 // - This object has no elements.
5598 // - No prototype has enumerable properties/elements.
IsSimpleEnum()5599 bool JSReceiver::IsSimpleEnum() {
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());
5610 if (curr->NumberOfEnumElements() > 0) return false;
5611 if (curr != this && enum_length != 0) return false;
5612 }
5613 return true;
5614 }
5615
5616
FilterKey(Object * key,PropertyAttributes filter)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
NumberOfDescribedProperties(DescriptorFlag which,PropertyAttributes filter)5635 int Map::NumberOfDescribedProperties(DescriptorFlag which,
5636 PropertyAttributes filter) {
5637 int result = 0;
5638 DescriptorArray* descs = instance_descriptors();
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)) {
5645 result++;
5646 }
5647 }
5648 return result;
5649 }
5650
5651
NextFreePropertyIndex()5652 int Map::NextFreePropertyIndex() {
5653 int max_index = -1;
5654 int number_of_own_descriptors = NumberOfOwnDescriptors();
5655 DescriptorArray* descs = instance_descriptors();
5656 for (int i = 0; i < number_of_own_descriptors; i++) {
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
ContainsOnlyValidKeys(Handle<FixedArray> array)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
ReduceFixedArrayTo(Handle<FixedArray> array,int length)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
GetEnumPropertyKeys(Handle<JSObject> object,bool cache_result)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));
5704 }
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;
5782 }
5783 }
5784
5785
GetKeys(Handle<JSReceiver> object,KeyCollectionType type)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()));
5793
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 }
5818
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;
5894 }
5895 return content;
5896 }
5897
5898
5899 // Try to update an accessor in an elements dictionary. Return true if the
5900 // update succeeded, and false otherwise.
UpdateGetterSetterInDictionary(SeededNumberDictionary * dictionary,uint32_t index,Object * getter,Object * setter,PropertyAttributes attributes)5901 static bool UpdateGetterSetterInDictionary(
5902 SeededNumberDictionary* dictionary,
5903 uint32_t index,
5904 Object* getter,
5905 Object* setter,
5906 PropertyAttributes attributes) {
5907 int entry = dictionary->FindEntry(index);
5908 if (entry != SeededNumberDictionary::kNotFound) {
5909 Object* result = dictionary->ValueAt(entry);
5910 PropertyDetails details = dictionary->DetailsAt(entry);
5911 if (details.type() == CALLBACKS && result->IsAccessorPair()) {
5912 DCHECK(details.IsConfigurable());
5913 if (details.attributes() != attributes) {
5914 dictionary->DetailsAtPut(
5915 entry,
5916 PropertyDetails(attributes, CALLBACKS, index));
5917 }
5918 AccessorPair::cast(result)->SetComponents(getter, setter);
5919 return true;
5920 }
5921 }
5922 return false;
5923 }
5924
5925
DefineElementAccessor(Handle<JSObject> object,uint32_t index,Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)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:
5933 case FAST_ELEMENTS:
5934 case FAST_DOUBLE_ELEMENTS:
5935 case FAST_HOLEY_SMI_ELEMENTS:
5936 case FAST_HOLEY_ELEMENTS:
5937 case FAST_HOLEY_DOUBLE_ELEMENTS:
5938 break;
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
5946 // Ignore getters and setters on pixel and external array elements.
5947 return;
5948
5949 case DICTIONARY_ELEMENTS:
5950 if (UpdateGetterSetterInDictionary(object->element_dictionary(),
5951 index,
5952 *getter,
5953 *setter,
5954 attributes)) {
5955 return;
5956 }
5957 break;
5958 case SLOPPY_ARGUMENTS_ELEMENTS: {
5959 // Ascertain whether we have read-only properties or an existing
5960 // getter/setter pair in an arguments elements dictionary backing
5961 // store.
5962 FixedArray* parameter_map = FixedArray::cast(object->elements());
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,
5973 *getter,
5974 *setter,
5975 attributes)) {
5976 return;
5977 }
5978 }
5979 }
5980 break;
5981 }
5982 }
5983
5984 Isolate* isolate = object->GetIsolate();
5985 Handle<AccessorPair> accessors = isolate->factory()->NewAccessorPair();
5986 accessors->SetComponents(*getter, *setter);
5987
5988 SetElementCallback(object, index, accessors, attributes);
5989 }
5990
5991
DictionaryElementsInPrototypeChainOnly()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;
6006 }
6007 }
6008
6009 return false;
6010 }
6011
6012
SetElementCallback(Handle<JSObject> object,uint32_t index,Handle<Object> structure,PropertyAttributes attributes)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);
6019
6020 // Normalize elements to make this operation simple.
6021 bool had_dictionary_elements = object->HasDictionaryElements();
6022 Handle<SeededNumberDictionary> dictionary = NormalizeElements(object);
6023 DCHECK(object->HasDictionaryElements() ||
6024 object->HasDictionaryArgumentsElements());
6025 // Update the dictionary with the new CALLBACKS property.
6026 dictionary = SeededNumberDictionary::Set(dictionary, index, structure,
6027 details);
6028 dictionary->set_requires_slow_elements();
6029
6030 // Update the dictionary backing store on the object.
6031 if (object->elements()->map() == heap->sloppy_arguments_elements_map()) {
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.
6037 FixedArray* parameter_map = FixedArray::cast(object->elements());
6038 if (index < static_cast<uint32_t>(parameter_map->length()) - 2) {
6039 parameter_map->set(index + 2, heap->the_hole_value());
6040 }
6041 parameter_map->set(1, *dictionary);
6042 } else {
6043 object->set_elements(*dictionary);
6044
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 }
6050 }
6051
6052
SetPropertyCallback(Handle<JSObject> object,Handle<Name> name,Handle<Object> structure,PropertyAttributes attributes)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;
6060 // Normalize object to make this operation simple.
6061 NormalizeProperties(object, mode, 0);
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.
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
6070 // When running crankshaft, changing the map is not enough. We
6071 // need to deoptimize all functions that rely on this global
6072 // object.
6073 Deoptimizer::DeoptimizeGlobalObject(*object);
6074 }
6075
6076 // Update the dictionary with the new CALLBACKS property.
6077 PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0);
6078 SetNormalizedProperty(object, name, structure, details);
6079
6080 ReoptimizeIfPrototype(object);
6081 }
6082
6083
DefineAccessor(Handle<JSObject> object,Handle<Name> name,Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)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();
6090 // Check access rights if needed.
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();
6096 }
6097
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();
6105 }
6106
6107 // Make sure that the top context does not change when doing callbacks or
6108 // interceptor calls.
6109 AssertNoContextChange ncc(isolate);
6110
6111 // Try to flatten before operating on the string.
6112 if (name->IsString()) name = String::Flatten(Handle<String>::cast(name));
6113
6114 uint32_t index = 0;
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();
6174 }
6175
6176
SetAccessor(Handle<JSObject> object,Handle<AccessorInfo> info)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
6183 // Check access rights if needed.
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();
6189 }
6190
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);
6197 }
6198
6199 // Make sure that the top context does not change when doing callbacks or
6200 // interceptor calls.
6201 AssertNoContextChange ncc(isolate);
6202
6203 // Try to flatten before operating on the string.
6204 if (name->IsString()) name = String::Flatten(Handle<String>::cast(name));
6205
6206 uint32_t index = 0;
6207 bool is_element = name->AsArrayIndex(&index);
6208
6209 if (is_element) {
6210 if (object->IsJSArray()) return factory->undefined_value();
6211
6212 // Accessors overwrite previous callbacks (cf. with getters/setters).
6213 switch (object->GetElementsKind()) {
6214 case FAST_SMI_ELEMENTS:
6215 case FAST_ELEMENTS:
6216 case FAST_DOUBLE_ELEMENTS:
6217 case FAST_HOLEY_SMI_ELEMENTS:
6218 case FAST_HOLEY_ELEMENTS:
6219 case FAST_HOLEY_DOUBLE_ELEMENTS:
6220 break;
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
6228 // Ignore getters and setters on pixel and external array
6229 // elements.
6230 return factory->undefined_value();
6231
6232 case DICTIONARY_ELEMENTS:
6233 break;
6234 case SLOPPY_ARGUMENTS_ELEMENTS:
6235 UNIMPLEMENTED();
6236 break;
6237 }
6238
6239 SetElementCallback(object, index, info, info->property_attributes());
6240 } else {
6241 // Lookup the name.
6242 LookupIterator it(object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
6243 CHECK(GetPropertyAttributes(&it).has_value);
6244 // ES5 forbids turning a property into an accessor if it's not
6245 // configurable. See 8.6.1 (Table 5).
6246 if (it.IsFound() && (it.IsReadOnly() || !it.IsConfigurable())) {
6247 return factory->undefined_value();
6248 }
6249
6250 SetPropertyCallback(object, name, info, info->property_attributes());
6251 }
6252
6253 return object;
6254 }
6255
6256
GetAccessor(Handle<JSObject> object,Handle<Name> name,AccessorComponent component)6257 MaybeHandle<Object> JSObject::GetAccessor(Handle<JSObject> object,
6258 Handle<Name> name,
6259 AccessorComponent component) {
6260 Isolate* isolate = object->GetIsolate();
6261
6262 // Make sure that the top context does not change when doing callbacks or
6263 // interceptor calls.
6264 AssertNoContextChange ncc(isolate);
6265
6266 // Make the lookup and include prototypes.
6267 uint32_t index = 0;
6268 if (name->AsArrayIndex(&index)) {
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);
6286 SeededNumberDictionary* dictionary = js_object->element_dictionary();
6287 int entry = dictionary->FindEntry(index);
6288 if (entry != SeededNumberDictionary::kNotFound) {
6289 Object* element = dictionary->ValueAt(entry);
6290 if (dictionary->DetailsAt(entry).type() == CALLBACKS &&
6291 element->IsAccessorPair()) {
6292 return handle(AccessorPair::cast(element)->GetComponent(component),
6293 isolate);
6294 }
6295 }
6296 }
6297 }
6298 } else {
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);
6326 }
6327 }
6328 }
6329 }
6330 }
6331 return isolate->factory()->undefined_value();
6332 }
6333
6334
SlowReverseLookup(Object * value)6335 Object* JSObject::SlowReverseLookup(Object* value) {
6336 if (HasFastProperties()) {
6337 int number_of_own_descriptors = map()->NumberOfOwnDescriptors();
6338 DescriptorArray* descs = map()->instance_descriptors();
6339 for (int i = 0; i < number_of_own_descriptors; i++) {
6340 if (descs->GetType(i) == FIELD) {
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) {
6349 return descs->GetKey(i);
6350 }
6351 } else if (descs->GetType(i) == CONSTANT) {
6352 if (descs->GetConstant(i) == value) {
6353 return descs->GetKey(i);
6354 }
6355 }
6356 }
6357 return GetHeap()->undefined_value();
6358 } else {
6359 return property_dictionary()->SlowReverseLookup(value);
6360 }
6361 }
6362
6363
RawCopy(Handle<Map> map,int instance_size)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);
6379 }
6380 new_bit_field3 = ConstructionCount::update(new_bit_field3,
6381 JSFunction::kNoSlackTracking);
6382 result->set_bit_field3(new_bit_field3);
6383 return result;
6384 }
6385
6386
Normalize(Handle<Map> fast_map,PropertyNormalizationMode mode)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
CopyNormalized(Handle<Map> map,PropertyNormalizationMode mode)6433 Handle<Map> Map::CopyNormalized(Handle<Map> map,
6434 PropertyNormalizationMode mode) {
6435 int new_instance_size = map->instance_size();
6436 if (mode == CLEAR_INOBJECT_PROPERTIES) {
6437 new_instance_size -= map->inobject_properties() * kPointerSize;
6438 }
6439
6440 Handle<Map> result = RawCopy(map, new_instance_size);
6441
6442 if (mode != CLEAR_INOBJECT_PROPERTIES) {
6443 result->set_inobject_properties(map->inobject_properties());
6444 }
6445
6446 result->set_dictionary_map(true);
6447 result->set_migration_target(false);
6448
6449 #ifdef VERIFY_HEAP
6450 if (FLAG_verify_heap) result->DictionaryMapVerify();
6451 #endif
6452
6453 return result;
6454 }
6455
6456
CopyDropDescriptors(Handle<Map> map)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
ShareDescriptor(Handle<Map> map,Handle<DescriptorArray> descriptors,Descriptor * descriptor)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 }
6493 }
6494
6495 {
6496 DisallowHeapAllocation no_gc;
6497 descriptors->Append(descriptor);
6498 result->InitializeDescriptors(*descriptors);
6499 }
6500
6501 DCHECK(result->NumberOfOwnDescriptors() == map->NumberOfOwnDescriptors() + 1);
6502 ConnectTransition(map, result, name, SIMPLE_TRANSITION);
6503
6504 return result;
6505 }
6506
6507
ConnectTransition(Handle<Map> parent,Handle<Map> child,Handle<Name> name,SimpleTransitionFlag flag)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
CopyReplaceDescriptors(Handle<Map> map,Handle<DescriptorArray> descriptors,TransitionFlag flag,MaybeHandle<Name> maybe_name,SimpleTransitionFlag simple_flag)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.
CopyInstallDescriptors(Handle<Map> map,int new_descriptor,Handle<DescriptorArray> descriptors)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
CopyAsElementsKind(Handle<Map> map,ElementsKind kind,TransitionFlag flag)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
6622 return new_map;
6623 }
6624
6625
CopyForObserved(Handle<Map> map)6626 Handle<Map> Map::CopyForObserved(Handle<Map> map) {
6627 DCHECK(!map->is_observed());
6628
6629 Isolate* isolate = map->GetIsolate();
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;
6650 }
6651
6652
Copy(Handle<Map> map)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
Create(Isolate * isolate,int inobject_properties)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
CopyForFreeze(Handle<Map> map)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
CanHoldValue(int descriptor,Object * value)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
PrepareForDataProperty(Handle<Map> map,int descriptor,Handle<Object> value)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
TransitionToDataProperty(Handle<Map> map,Handle<Name> name,Handle<Object> value,PropertyAttributes attributes,StoreFromKeyed store_mode)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);
6767 }
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
ReconfigureDataProperty(Handle<Map> map,int descriptor,PropertyAttributes attributes)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
TransitionToAccessorProperty(Handle<Map> map,Handle<Name> name,AccessorComponent component,Handle<Object> accessor,PropertyAttributes attributes)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
CopyAddDescriptor(Handle<Map> map,Descriptor * descriptor,TransitionFlag flag)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
CopyInsertDescriptor(Handle<Map> map,Descriptor * descriptor,TransitionFlag flag)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
CopyUpTo(Handle<DescriptorArray> desc,int enumeration_index,int slack)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
CopyUpToAddAttributes(Handle<DescriptorArray> desc,int enumeration_index,PropertyAttributes attributes,int slack)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
CopyReplaceDescriptor(Handle<Map> map,Handle<DescriptorArray> descriptors,Descriptor * descriptor,int insertion_index,TransitionFlag flag)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
UpdateCodeCache(Handle<Map> map,Handle<Name> name,Handle<Code> code)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);
7028 }
7029
7030 // Update the code cache.
7031 Handle<CodeCache> code_cache(CodeCache::cast(map->code_cache()), isolate);
7032 CodeCache::Update(code_cache, name, code);
7033 }
7034
7035
FindInCodeCache(Name * name,Code::Flags flags)7036 Object* Map::FindInCodeCache(Name* name, Code::Flags flags) {
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 {
7041 return GetHeap()->undefined_value();
7042 }
7043 }
7044
7045
IndexInCodeCache(Object * name,Code * code)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
RemoveFromCodeCache(Name * name,Code * code,int index)7055 void Map::RemoveFromCodeCache(Name* name, Code* code, int index) {
7056 // No GC is supposed to happen between a call to IndexInCodeCache and
7057 // RemoveFromCodeCache so the code cache must be there.
7058 DCHECK(!code_cache()->IsFixedArray());
7059 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
7060 }
7061
7062
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.
7067 class IntrusiveMapTransitionIterator {
7068 public:
IntrusiveMapTransitionIterator(Map * map,TransitionArray * transition_array,Object * constructor)7069 IntrusiveMapTransitionIterator(
7070 Map* map, TransitionArray* transition_array, Object* constructor)
7071 : map_(map),
7072 transition_array_(transition_array),
7073 constructor_(constructor) { }
7074
StartIfNotStarted()7075 void StartIfNotStarted() {
7076 DCHECK(!(*IteratorField())->IsSmi() || IsIterating());
7077 if (!(*IteratorField())->IsSmi()) {
7078 DCHECK(*IteratorField() == constructor_);
7079 *IteratorField() = Smi::FromInt(-1);
7080 }
7081 }
7082
IsIterating()7083 bool IsIterating() {
7084 return (*IteratorField())->IsSmi() &&
7085 Smi::cast(*IteratorField())->value() < 0;
7086 }
7087
Next()7088 Map* Next() {
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);
7096 }
7097
7098 *IteratorField() = constructor_;
7099 return NULL;
7100 }
7101
7102 private:
IteratorField()7103 Object** IteratorField() {
7104 return HeapObject::RawField(map_, Map::kConstructorOffset);
7105 }
7106
7107 Map* map_;
7108 TransitionArray* transition_array_;
7109 Object* constructor_;
7110 };
7111
7112
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.
7117 class IntrusivePrototypeTransitionIterator {
7118 public:
IntrusivePrototypeTransitionIterator(Map * map,HeapObject * proto_trans,Object * constructor)7119 IntrusivePrototypeTransitionIterator(
7120 Map* map, HeapObject* proto_trans, Object* constructor)
7121 : map_(map), proto_trans_(proto_trans), constructor_(constructor) { }
7122
StartIfNotStarted()7123 void StartIfNotStarted() {
7124 if (!(*IteratorField())->IsSmi()) {
7125 DCHECK(*IteratorField() == constructor_);
7126 *IteratorField() = Smi::FromInt(0);
7127 }
7128 }
7129
IsIterating()7130 bool IsIterating() {
7131 return (*IteratorField())->IsSmi() &&
7132 Smi::cast(*IteratorField())->value() >= 0;
7133 }
7134
Next()7135 Map* Next() {
7136 DCHECK(IsIterating());
7137 int transitionNumber = Smi::cast(*IteratorField())->value();
7138 if (transitionNumber < NumberOfTransitions()) {
7139 *IteratorField() = Smi::FromInt(transitionNumber + 1);
7140 return GetTransition(transitionNumber);
7141 }
7142 *IteratorField() = constructor_;
7143 return NULL;
7144 }
7145
7146 private:
IteratorField()7147 Object** IteratorField() {
7148 return HeapObject::RawField(map_, Map::kConstructorOffset);
7149 }
7150
NumberOfTransitions()7151 int NumberOfTransitions() {
7152 FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
7153 Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset);
7154 return Smi::cast(num)->value();
7155 }
7156
GetTransition(int transitionNumber)7157 Map* GetTransition(int transitionNumber) {
7158 FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
7159 return Map::cast(proto_trans->get(IndexFor(transitionNumber)));
7160 }
7161
IndexFor(int transitionNumber)7162 int IndexFor(int transitionNumber) {
7163 return Map::kProtoTransitionHeaderSize +
7164 Map::kProtoTransitionMapOffset +
7165 transitionNumber * Map::kProtoTransitionElementsPerEntry;
7166 }
7167
7168 Map* map_;
7169 HeapObject* proto_trans_;
7170 Object* constructor_;
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
7177 // temporarily reuse two fields with known values:
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
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.
7186 //
7187 // (a) If we currently follow normal map transitions, we temporarily store
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.
7191 //
7192 // (b) If we currently follow prototype transitions, we temporarily store
7193 // the current index in the constructor field, and restore it to the
7194 // original constructor afterwards.
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!
SetParent(TraversableMap * parent)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.
GetAndResetParent()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
7211 // If we have an unvisited child map, return that one and advance. If we have
7212 // none, return NULL and restore the overwritten constructor field.
ChildIteratorNext(Object * constructor)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();
7236 if (next != NULL) return static_cast<TraversableMap*>(next);
7237 }
7238
7239 return NULL;
7240 }
7241 };
7242
7243
7244 // Traverse the transition tree in postorder without using the C++ stack by
7245 // doing pointer reversal.
TraverseTransitionTree(TraverseCallback callback,void * data)7246 void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
7247 // Make sure that we do not allocate in the callback.
7248 DisallowHeapAllocation no_allocation;
7249
7250 TraversableMap* current = static_cast<TraversableMap*>(this);
7251 // Get the root constructor here to restore it later when finished iterating
7252 // over maps.
7253 Object* root_constructor = constructor();
7254 while (true) {
7255 TraversableMap* child = current->ChildIteratorNext(root_constructor);
7256 if (child != NULL) {
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 }
7265 }
7266 }
7267
7268
Update(Handle<CodeCache> code_cache,Handle<Name> name,Handle<Code> code)7269 void CodeCache::Update(
7270 Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) {
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.
7274 if (code->type() == Code::NORMAL) {
7275 // Make sure that a hash table is allocated for the normal load code cache.
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);
7281 }
7282 UpdateNormalTypeCache(code_cache, name, code);
7283 } else {
7284 DCHECK(code_cache->default_cache()->IsFixedArray());
7285 UpdateDefaultCache(code_cache, name, code);
7286 }
7287 }
7288
7289
UpdateDefaultCache(Handle<CodeCache> code_cache,Handle<Name> name,Handle<Code> code)7290 void CodeCache::UpdateDefaultCache(
7291 Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) {
7292 // When updating the default code cache we disregard the type encoded in the
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.
7299 Handle<FixedArray> cache = handle(code_cache->default_cache());
7300 int length = cache->length();
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 }
7323 }
7324 }
7325
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 }
7333 }
7334
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;
7339 DCHECK((new_length % kCodeCacheEntrySize) == 0);
7340 cache = FixedArray::CopySize(cache, new_length);
7341
7342 // Add the (name, code) pair to the new cache.
7343 cache->set(length + kCodeCacheEntryNameOffset, *name);
7344 cache->set(length + kCodeCacheEntryCodeOffset, *code);
7345 code_cache->set_default_cache(*cache);
7346 }
7347
7348
UpdateNormalTypeCache(Handle<CodeCache> code_cache,Handle<Name> name,Handle<Code> code)7349 void CodeCache::UpdateNormalTypeCache(
7350 Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) {
7351 // Adding a new entry can cause a new cache to be allocated.
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);
7356 }
7357
7358
Lookup(Name * name,Code::Flags flags)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();
7364 }
7365 return LookupNormalTypeCache(name, flags);
7366 }
7367
7368
LookupDefaultCache(Name * name,Code::Flags flags)7369 Object* CodeCache::LookupDefaultCache(Name* name, Code::Flags flags) {
7370 FixedArray* cache = default_cache();
7371 int length = cache->length();
7372 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
7373 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
7374 // Skip deleted elements.
7375 if (key->IsNull()) continue;
7376 if (key->IsUndefined()) return key;
7377 if (name->Equals(Name::cast(key))) {
7378 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
7379 if (Code::RemoveTypeFromFlags(code->flags()) == flags) {
7380 return code;
7381 }
7382 }
7383 }
7384 return GetHeap()->undefined_value();
7385 }
7386
7387
LookupNormalTypeCache(Name * name,Code::Flags flags)7388 Object* CodeCache::LookupNormalTypeCache(Name* name, Code::Flags flags) {
7389 if (!normal_type_cache()->IsUndefined()) {
7390 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
7391 return cache->Lookup(name, flags);
7392 } else {
7393 return GetHeap()->undefined_value();
7394 }
7395 }
7396
7397
GetIndex(Object * name,Code * code)7398 int CodeCache::GetIndex(Object* name, Code* code) {
7399 if (code->type() == Code::NORMAL) {
7400 if (normal_type_cache()->IsUndefined()) return -1;
7401 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
7402 return cache->GetIndex(Name::cast(name), code->flags());
7403 }
7404
7405 FixedArray* array = default_cache();
7406 int len = array->length();
7407 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
7408 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
7409 }
7410 return -1;
7411 }
7412
7413
RemoveByIndex(Object * name,Code * code,int index)7414 void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
7415 if (code->type() == Code::NORMAL) {
7416 DCHECK(!normal_type_cache()->IsUndefined());
7417 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
7418 DCHECK(cache->GetIndex(Name::cast(name), code->flags()) == index);
7419 cache->RemoveByIndex(index);
7420 } else {
7421 FixedArray* array = default_cache();
7422 DCHECK(array->length() >= index && array->get(index)->IsCode());
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.
7426 DCHECK_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
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:
CodeCacheHashTableKey(Handle<Name> name,Code::Flags flags)7439 CodeCacheHashTableKey(Handle<Name> name, Code::Flags flags)
7440 : name_(name), flags_(flags), code_() { }
7441
CodeCacheHashTableKey(Handle<Name> name,Handle<Code> code)7442 CodeCacheHashTableKey(Handle<Name> name, Handle<Code> code)
7443 : name_(name), flags_(code->flags()), code_(code) { }
7444
IsMatch(Object * other)7445 bool IsMatch(Object* other) OVERRIDE {
7446 if (!other->IsFixedArray()) return false;
7447 FixedArray* pair = FixedArray::cast(other);
7448 Name* name = Name::cast(pair->get(0));
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
NameFlagsHashHelper(Name * name,Code::Flags flags)7456 static uint32_t NameFlagsHashHelper(Name* name, Code::Flags flags) {
7457 return name->Hash() ^ flags;
7458 }
7459
Hash()7460 uint32_t Hash() OVERRIDE { return NameFlagsHashHelper(*name_, flags_); }
7461
HashForObject(Object * obj)7462 uint32_t HashForObject(Object* obj) OVERRIDE {
7463 FixedArray* pair = FixedArray::cast(obj);
7464 Name* name = Name::cast(pair->get(0));
7465 Code* code = Code::cast(pair->get(1));
7466 return NameFlagsHashHelper(name, code->flags());
7467 }
7468
AsHandle(Isolate * isolate)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);
7474 return pair;
7475 }
7476
7477 private:
7478 Handle<Name> name_;
7479 Code::Flags flags_;
7480 // TODO(jkummerow): We should be able to get by without this.
7481 MaybeHandle<Code> code_;
7482 };
7483
7484
Lookup(Name * name,Code::Flags flags)7485 Object* CodeCacheHashTable::Lookup(Name* name, Code::Flags flags) {
7486 DisallowHeapAllocation no_alloc;
7487 CodeCacheHashTableKey key(handle(name), flags);
7488 int entry = FindEntry(&key);
7489 if (entry == kNotFound) return GetHeap()->undefined_value();
7490 return get(EntryToIndex(entry) + 1);
7491 }
7492
7493
Put(Handle<CodeCacheHashTable> cache,Handle<Name> name,Handle<Code> code)7494 Handle<CodeCacheHashTable> CodeCacheHashTable::Put(
7495 Handle<CodeCacheHashTable> cache, Handle<Name> name, Handle<Code> code) {
7496 CodeCacheHashTableKey key(name, code);
7497
7498 Handle<CodeCacheHashTable> new_cache = EnsureCapacity(cache, 1, &key);
7499
7500 int entry = new_cache->FindInsertionEntry(key.Hash());
7501 Handle<Object> k = key.AsHandle(cache->GetIsolate());
7502
7503 new_cache->set(EntryToIndex(entry), *k);
7504 new_cache->set(EntryToIndex(entry) + 1, *code);
7505 new_cache->ElementAdded();
7506 return new_cache;
7507 }
7508
7509
GetIndex(Name * name,Code::Flags flags)7510 int CodeCacheHashTable::GetIndex(Name* name, Code::Flags flags) {
7511 DisallowHeapAllocation no_alloc;
7512 CodeCacheHashTableKey key(handle(name), flags);
7513 int entry = FindEntry(&key);
7514 return (entry == kNotFound) ? -1 : entry;
7515 }
7516
7517
RemoveByIndex(int index)7518 void CodeCacheHashTable::RemoveByIndex(int index) {
7519 DCHECK(index >= 0);
7520 Heap* heap = GetHeap();
7521 set(EntryToIndex(index), heap->the_hole_value());
7522 set(EntryToIndex(index) + 1, heap->the_hole_value());
7523 ElementRemoved();
7524 }
7525
7526
Update(Handle<PolymorphicCodeCache> code_cache,MapHandleList * maps,Code::Flags flags,Handle<Code> code)7527 void PolymorphicCodeCache::Update(Handle<PolymorphicCodeCache> code_cache,
7528 MapHandleList* maps,
7529 Code::Flags flags,
7530 Handle<Code> code) {
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);
7538 } else {
7539 // This entry shouldn't be contained in the cache yet.
7540 DCHECK(PolymorphicCodeCacheHashTable::cast(code_cache->cache())
7541 ->Lookup(maps, flags)->IsUndefined());
7542 }
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);
7548 }
7549
7550
Lookup(MapHandleList * maps,Code::Flags flags)7551 Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps,
7552 Code::Flags flags) {
7553 if (!cache()->IsUndefined()) {
7554 PolymorphicCodeCacheHashTable* hash_table =
7555 PolymorphicCodeCacheHashTable::cast(cache());
7556 return Handle<Object>(hash_table->Lookup(maps, flags), GetIsolate());
7557 } else {
7558 return GetIsolate()->factory()->undefined_value();
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.
PolymorphicCodeCacheHashTableKey(MapHandleList * maps,int code_flags)7569 PolymorphicCodeCacheHashTableKey(MapHandleList* maps, int code_flags)
7570 : maps_(maps),
7571 code_flags_(code_flags) {}
7572
IsMatch(Object * other)7573 bool IsMatch(Object* other) OVERRIDE {
7574 MapHandleList other_maps(kDefaultListAllocationSize);
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) {
7590 if (*(maps_->at(i)) == *(other_maps.at(j))) {
7591 match_found = true;
7592 break;
7593 }
7594 }
7595 if (!match_found) return false;
7596 }
7597 return true;
7598 }
7599
MapsHashHelper(MapHandleList * maps,int code_flags)7600 static uint32_t MapsHashHelper(MapHandleList* maps, int code_flags) {
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
Hash()7608 uint32_t Hash() OVERRIDE {
7609 return MapsHashHelper(maps_, code_flags_);
7610 }
7611
HashForObject(Object * obj)7612 uint32_t HashForObject(Object* obj) OVERRIDE {
7613 MapHandleList other_maps(kDefaultListAllocationSize);
7614 int other_flags;
7615 FromObject(obj, &other_flags, &other_maps);
7616 return MapsHashHelper(&other_maps, other_flags);
7617 }
7618
AsHandle(Isolate * isolate)7619 MUST_USE_RESULT Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
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.
7623 Handle<FixedArray> list =
7624 isolate->factory()->NewUninitializedFixedArray(maps_->length() + 1);
7625 list->set(0, Smi::FromInt(code_flags_));
7626 for (int i = 0; i < maps_->length(); ++i) {
7627 list->set(i + 1, *maps_->at(i));
7628 }
7629 return list;
7630 }
7631
7632 private:
FromObject(Object * obj,int * code_flags,MapHandleList * maps)7633 static MapHandleList* FromObject(Object* obj,
7634 int* code_flags,
7635 MapHandleList* maps) {
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) {
7640 maps->Add(Handle<Map>(Map::cast(list->get(i))));
7641 }
7642 return maps;
7643 }
7644
7645 MapHandleList* maps_; // weak.
7646 int code_flags_;
7647 static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1;
7648 };
7649
7650
Lookup(MapHandleList * maps,int code_kind)7651 Object* PolymorphicCodeCacheHashTable::Lookup(MapHandleList* maps,
7652 int code_kind) {
7653 DisallowHeapAllocation no_alloc;
7654 PolymorphicCodeCacheHashTableKey key(maps, code_kind);
7655 int entry = FindEntry(&key);
7656 if (entry == kNotFound) return GetHeap()->undefined_value();
7657 return get(EntryToIndex(entry) + 1);
7658 }
7659
7660
Put(Handle<PolymorphicCodeCacheHashTable> hash_table,MapHandleList * maps,int code_kind,Handle<Code> code)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);
7669 int entry = cache->FindInsertionEntry(key.Hash());
7670
7671 Handle<Object> obj = key.AsHandle(hash_table->GetIsolate());
7672 cache->set(EntryToIndex(entry), *obj);
7673 cache->set(EntryToIndex(entry) + 1, *code);
7674 cache->ElementAdded();
7675 return cache;
7676 }
7677
7678
Shrink(int new_length)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
AddKeysFromArrayLike(Handle<FixedArray> content,Handle<JSObject> array)7688 MaybeHandle<FixedArray> FixedArray::AddKeysFromArrayLike(
7689 Handle<FixedArray> content,
7690 Handle<JSObject> array) {
7691 DCHECK(array->IsJSArray() || array->HasSloppyArgumentsElements());
7692 ElementsAccessor* accessor = array->GetElementsAccessor();
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
7700 if (FLAG_enable_slow_asserts) {
7701 DisallowHeapAllocation no_allocation;
7702 for (int i = 0; i < result->length(); i++) {
7703 Object* current = result->get(i);
7704 DCHECK(current->IsNumber() || current->IsName());
7705 }
7706 }
7707 #endif
7708 return result;
7709 }
7710
7711
UnionOfKeys(Handle<FixedArray> first,Handle<FixedArray> second)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
7726 if (FLAG_enable_slow_asserts) {
7727 DisallowHeapAllocation no_allocation;
7728 for (int i = 0; i < result->length(); i++) {
7729 Object* current = result->get(i);
7730 DCHECK(current->IsNumber() || current->IsName());
7731 }
7732 }
7733 #endif
7734 return result;
7735 }
7736
7737
CopySize(Handle<FixedArray> array,int new_length,PretenureFlag pretenure)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);
7744 // Copy the content
7745 DisallowHeapAllocation no_gc;
7746 int len = array->length();
7747 if (new_length < len) len = new_length;
7748 // We are taking the map from the old fixed array so the map is sure to
7749 // be an immortal immutable object.
7750 result->set_map_no_write_barrier(array->map());
7751 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
7752 for (int i = 0; i < len; i++) {
7753 result->set(i, array->get(i), mode);
7754 }
7755 return result;
7756 }
7757
7758
CopyTo(int pos,FixedArray * dest,int dest_pos,int len)7759 void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
7760 DisallowHeapAllocation no_gc;
7761 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
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
IsEqualTo(FixedArray * other)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
Allocate(Isolate * isolate,int number_of_descriptors,int slack)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();
7784 // Do not use DescriptorArray::cast on incomplete object.
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));
7789
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
ClearEnumCache()7796 void DescriptorArray::ClearEnumCache() {
7797 set(kEnumCacheIndex, Smi::FromInt(0));
7798 }
7799
7800
Replace(int index,Descriptor * descriptor)7801 void DescriptorArray::Replace(int index, Descriptor* descriptor) {
7802 descriptor->SetSortedKeyIndex(GetSortedKeyIndex(index));
7803 Set(index, descriptor);
7804 }
7805
7806
SetEnumCache(FixedArray * bridge_storage,FixedArray * new_cache,Object * new_index_cache)7807 void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
7808 FixedArray* new_cache,
7809 Object* new_index_cache) {
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);
7819 }
7820
7821
CopyFrom(int index,DescriptorArray * src,const WhitenessWitness & witness)7822 void DescriptorArray::CopyFrom(int index,
7823 DescriptorArray* src,
7824 const WhitenessWitness& witness) {
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);
7831 }
7832
7833
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.
Sort()7838 void DescriptorArray::Sort() {
7839 // In-place heap sort.
7840 int len = number_of_descriptors();
7841 // Reset sorting since the descriptor array might contain invalid pointers.
7842 for (int i = 0; i < len; ++i) SetSortedKey(i, i);
7843 // Bottom-up max-heap construction.
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;
7848 const uint32_t parent_hash = GetSortedKey(i)->Hash();
7849 while (parent_index <= max_parent_index) {
7850 int child_index = 2 * parent_index + 1;
7851 uint32_t child_hash = GetSortedKey(child_index)->Hash();
7852 if (child_index + 1 < len) {
7853 uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash();
7854 if (right_child_hash > child_hash) {
7855 child_index++;
7856 child_hash = right_child_hash;
7857 }
7858 }
7859 if (child_hash <= parent_hash) break;
7860 SwapSortedKeys(parent_index, child_index);
7861 // Now element at child_index could be < its children.
7862 parent_index = child_index; // parent_hash remains correct.
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.
7869 SwapSortedKeys(0, i);
7870 // Shift down the new top element.
7871 int parent_index = 0;
7872 const uint32_t parent_hash = GetSortedKey(parent_index)->Hash();
7873 const int max_parent_index = (i / 2) - 1;
7874 while (parent_index <= max_parent_index) {
7875 int child_index = parent_index * 2 + 1;
7876 uint32_t child_hash = GetSortedKey(child_index)->Hash();
7877 if (child_index + 1 < i) {
7878 uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash();
7879 if (right_child_hash > child_hash) {
7880 child_index++;
7881 child_hash = right_child_hash;
7882 }
7883 }
7884 if (child_hash <= parent_hash) break;
7885 SwapSortedKeys(parent_index, child_index);
7886 parent_index = child_index;
7887 }
7888 }
7889 DCHECK(IsSortedNoDuplicates());
7890 }
7891
7892
Copy(Handle<AccessorPair> pair)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());
7897 return copy;
7898 }
7899
7900
GetComponent(AccessorComponent component)7901 Object* AccessorPair::GetComponent(AccessorComponent component) {
7902 Object* accessor = get(component);
7903 return accessor->IsTheHole() ? GetHeap()->undefined_value() : accessor;
7904 }
7905
7906
New(Isolate * isolate,int deopt_entry_count,PretenureFlag pretenure)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));
7913 }
7914
7915
New(Isolate * isolate,int number_of_deopt_points,PretenureFlag pretenure)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);
7928 }
7929
7930
7931 #ifdef DEBUG
IsEqualTo(DescriptorArray * other)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) {
7937 if (get(i) != other->get(i)) return false;
7938 }
7939 return true;
7940 }
7941 #endif
7942
7943
LooksValid()7944 bool String::LooksValid() {
7945 if (!GetIsolate()->heap()->Contains(this)) return false;
7946 return true;
7947 }
7948
7949
GetFlatContent()7950 String::FlatContent String::GetFlatContent() {
7951 DCHECK(!AllowHeapAllocation::IsAllowed());
7952 int length = this->length();
7953 StringShape shape(this);
7954 String* string = this;
7955 int offset = 0;
7956 if (shape.representation_tag() == kConsStringTag) {
7957 ConsString* cons = ConsString::cast(string);
7958 if (cons->second()->length() != 0) {
7959 return FlatContent();
7960 }
7961 string = cons->first();
7962 shape = StringShape(string);
7963 }
7964 if (shape.representation_tag() == kSlicedStringTag) {
7965 SlicedString* slice = SlicedString::cast(string);
7966 offset = slice->offset();
7967 string = slice->parent();
7968 shape = StringShape(string);
7969 DCHECK(shape.representation_tag() != kConsStringTag &&
7970 shape.representation_tag() != kSlicedStringTag);
7971 }
7972 if (shape.encoding_tag() == kOneByteStringTag) {
7973 const uint8_t* start;
7974 if (shape.representation_tag() == kSeqStringTag) {
7975 start = SeqOneByteString::cast(string)->GetChars();
7976 } else {
7977 start = ExternalOneByteString::cast(string)->GetChars();
7978 }
7979 return FlatContent(start + offset, length);
7980 } else {
7981 DCHECK(shape.encoding_tag() == kTwoByteStringTag);
7982 const uc16* start;
7983 if (shape.representation_tag() == kSeqStringTag) {
7984 start = SeqTwoByteString::cast(string)->GetChars();
7985 } else {
7986 start = ExternalTwoByteString::cast(string)->GetChars();
7987 }
7988 return FlatContent(start + offset, length);
7989 }
7990 }
7991
7992
ToCString(AllowNullsFlag allow_nulls,RobustnessFlag robust_flag,int offset,int length,int * length_return)7993 SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
7994 RobustnessFlag robust_flag,
7995 int offset,
7996 int length,
7997 int* length_return) {
7998 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
7999 return SmartArrayPointer<char>(NULL);
8000 }
8001 Heap* heap = GetHeap();
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.
8007 Access<ConsStringIteratorOp> op(
8008 heap->isolate()->objects_string_iterator());
8009 StringCharacterStream stream(this, op.value(), offset);
8010 int character_position = offset;
8011 int utf8_bytes = 0;
8012 int last = unibrow::Utf16::kNoPreviousCharacter;
8013 while (stream.HasMore() && character_position++ < offset + length) {
8014 uint16_t character = stream.GetNext();
8015 utf8_bytes += unibrow::Utf8::Length(character, last);
8016 last = character;
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.
8026 stream.Reset(this, offset);
8027 character_position = offset;
8028 int utf8_byte_position = 0;
8029 last = unibrow::Utf16::kNoPreviousCharacter;
8030 while (stream.HasMore() && character_position++ < offset + length) {
8031 uint16_t character = stream.GetNext();
8032 if (allow_nulls == DISALLOW_NULLS && character == 0) {
8033 character = ' ';
8034 }
8035 utf8_byte_position +=
8036 unibrow::Utf8::Encode(result + utf8_byte_position, character, last);
8037 last = character;
8038 }
8039 result[utf8_byte_position] = 0;
8040 return SmartArrayPointer<char>(result);
8041 }
8042
8043
ToCString(AllowNullsFlag allow_nulls,RobustnessFlag robust_flag,int * length_return)8044 SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
8045 RobustnessFlag robust_flag,
8046 int* length_return) {
8047 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
8048 }
8049
8050
GetTwoByteData(unsigned start)8051 const uc16* String::GetTwoByteData(unsigned start) {
8052 DCHECK(!IsOneByteRepresentationUnderneath());
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);
8059 case kSlicedStringTag: {
8060 SlicedString* slice = SlicedString::cast(this);
8061 return slice->parent()->GetTwoByteData(start + slice->offset());
8062 }
8063 case kConsStringTag:
8064 UNREACHABLE();
8065 return NULL;
8066 }
8067 UNREACHABLE();
8068 return NULL;
8069 }
8070
8071
ToWideCString(RobustnessFlag robust_flag)8072 SmartArrayPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
8073 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
8074 return SmartArrayPointer<uc16>();
8075 }
8076 Heap* heap = GetHeap();
8077
8078 Access<ConsStringIteratorOp> op(
8079 heap->isolate()->objects_string_iterator());
8080 StringCharacterStream stream(this, op.value());
8081
8082 uc16* result = NewArray<uc16>(length() + 1);
8083
8084 int i = 0;
8085 while (stream.HasMore()) {
8086 uint16_t character = stream.GetNext();
8087 result[i++] = character;
8088 }
8089 result[i] = 0;
8090 return SmartArrayPointer<uc16>(result);
8091 }
8092
8093
SeqTwoByteStringGetData(unsigned start)8094 const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
8095 return reinterpret_cast<uc16*>(
8096 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
8097 }
8098
8099
PostGarbageCollectionProcessing(Isolate * isolate)8100 void Relocatable::PostGarbageCollectionProcessing(Isolate* isolate) {
8101 Relocatable* current = isolate->relocatable_top();
8102 while (current != NULL) {
8103 current->PostGarbageCollection();
8104 current = current->prev_;
8105 }
8106 }
8107
8108
8109 // Reserve space for statics needing saving and restoring.
ArchiveSpacePerThread()8110 int Relocatable::ArchiveSpacePerThread() {
8111 return sizeof(Relocatable*); // NOLINT
8112 }
8113
8114
8115 // Archive statics that are thread-local.
ArchiveState(Isolate * isolate,char * to)8116 char* Relocatable::ArchiveState(Isolate* isolate, char* to) {
8117 *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top();
8118 isolate->set_relocatable_top(NULL);
8119 return to + ArchiveSpacePerThread();
8120 }
8121
8122
8123 // Restore statics that are thread-local.
RestoreState(Isolate * isolate,char * from)8124 char* Relocatable::RestoreState(Isolate* isolate, char* from) {
8125 isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from));
8126 return from + ArchiveSpacePerThread();
8127 }
8128
8129
Iterate(ObjectVisitor * v,char * thread_storage)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
Iterate(Isolate * isolate,ObjectVisitor * v)8137 void Relocatable::Iterate(Isolate* isolate, ObjectVisitor* v) {
8138 Iterate(v, isolate->relocatable_top());
8139 }
8140
8141
Iterate(ObjectVisitor * v,Relocatable * top)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
FlatStringReader(Isolate * isolate,Handle<String> str)8151 FlatStringReader::FlatStringReader(Isolate* isolate, Handle<String> str)
8152 : Relocatable(isolate),
8153 str_(str.location()),
8154 length_(str->length()) {
8155 PostGarbageCollection();
8156 }
8157
8158
FlatStringReader(Isolate * isolate,Vector<const char> input)8159 FlatStringReader::FlatStringReader(Isolate* isolate, Vector<const char> input)
8160 : Relocatable(isolate),
8161 str_(0),
8162 is_one_byte_(true),
8163 length_(input.length()),
8164 start_(input.start()) {}
8165
8166
PostGarbageCollection()8167 void FlatStringReader::PostGarbageCollection() {
8168 if (str_ == NULL) return;
8169 Handle<String> str(str_);
8170 DCHECK(str->IsFlat());
8171 DisallowHeapAllocation no_gc;
8172 // This does not actually prevent the vector from being relocated later.
8173 String::FlatContent content = str->GetFlatContent();
8174 DCHECK(content.IsFlat());
8175 is_one_byte_ = content.IsOneByte();
8176 if (is_one_byte_) {
8177 start_ = content.ToOneByteVector().start();
8178 } else {
8179 start_ = content.ToUC16Vector().start();
8180 }
8181 }
8182
8183
Initialize(ConsString * cons_string,int offset)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());
8192 }
8193
8194
Continue(int * offset_out)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);
8206 }
8207 // Ensure future calls return null immediately.
8208 if (string == NULL) Reset(NULL);
8209 return string;
8210 }
8211
8212
Search(int * offset_out)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;
8221 while (true) {
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);
8233 continue;
8234 }
8235 // Tell the stack we're done descending.
8236 AdjustMaximumDepth();
8237 } else {
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;
8248 }
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
NextLeaf(bool * blew_stack)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);
8315 }
8316 }
8317 UNREACHABLE();
8318 return NULL;
8319 }
8320
8321
ConsStringGet(int index)8322 uint16_t ConsString::ConsStringGet(int index) {
8323 DCHECK(index >= 0 && index < this->length());
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
SlicedStringGet(int index)8353 uint16_t SlicedString::SlicedStringGet(int index) {
8354 return parent()->Get(offset() + index);
8355 }
8356
8357
8358 template <typename sinkchar>
WriteToFlat(String * src,sinkchar * sink,int f,int t)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) {
8367 DCHECK(0 <= from && from <= to && to <= source->length());
8368 switch (StringShape(source).full_representation_tag()) {
8369 case kOneByteStringTag | kExternalStringTag: {
8370 CopyChars(sink, ExternalOneByteString::cast(source)->GetChars() + from,
8371 to - from);
8372 return;
8373 }
8374 case kTwoByteStringTag | kExternalStringTag: {
8375 const uc16* data =
8376 ExternalTwoByteString::cast(source)->GetChars();
8377 CopyChars(sink,
8378 data + from,
8379 to - from);
8380 return;
8381 }
8382 case kOneByteStringTag | kSeqStringTag: {
8383 CopyChars(sink,
8384 SeqOneByteString::cast(source)->GetChars() + from,
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 }
8394 case kOneByteStringTag | kConsStringTag:
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();
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
8416 // common case of sequential one-byte right child.
8417 if (to - boundary == 1) {
8418 sink[boundary - from] = static_cast<sinkchar>(second->Get(0));
8419 } else if (second->IsSeqOneByteString()) {
8420 CopyChars(sink + boundary - from,
8421 SeqOneByteString::cast(second)->GetChars(),
8422 to - boundary);
8423 } else {
8424 WriteToFlat(second,
8425 sink + boundary - from,
8426 0,
8427 to - boundary);
8428 }
8429 to = boundary;
8430 }
8431 source = first;
8432 }
8433 break;
8434 }
8435 case kOneByteStringTag | kSlicedStringTag:
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 }
8442 }
8443 }
8444 }
8445
8446
8447
8448 template <typename SourceChar>
CalculateLineEndsImpl(Isolate * isolate,List<int> * line_ends,Vector<const SourceChar> src,bool include_ending_line)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 }
8468 }
8469 }
8470
8471
CalculateLineEnds(Handle<String> src,bool include_ending_line)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;
8502 }
8503
8504
8505 // Compares the contents of two strings by reading and comparing
8506 // int-sized blocks of characters.
8507 template <typename Char>
CompareRawStringContents(const Char * const a,const Char * const b,int length)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:
compare(const Chars1 * a,const Chars2 * b,int len)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]) {
8522 return false;
8523 }
8524 }
8525 return true;
8526 }
8527 };
8528
8529
8530 template<>
8531 class RawStringComparator<uint16_t, uint16_t> {
8532 public:
compare(const uint16_t * a,const uint16_t * b,int len)8533 static inline bool compare(const uint16_t* a, const uint16_t* b, int len) {
8534 return CompareRawStringContents(a, b, len);
8535 }
8536 };
8537
8538
8539 template<>
8540 class RawStringComparator<uint8_t, uint8_t> {
8541 public:
compare(const uint8_t * a,const uint8_t * b,int len)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:
State(ConsStringIteratorOp * op)8551 explicit inline State(ConsStringIteratorOp* op)
8552 : op_(op), is_one_byte_(true), length_(0), buffer8_(NULL) {}
8553
Init(String * string)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
VisitOneByteString(const uint8_t * chars,int length)8564 inline void VisitOneByteString(const uint8_t* chars, int length) {
8565 is_one_byte_ = true;
8566 buffer8_ = chars;
8567 length_ = length;
8568 }
8569
VisitTwoByteString(const uint16_t * chars,int length)8570 inline void VisitTwoByteString(const uint16_t* chars, int length) {
8571 is_one_byte_ = false;
8572 buffer16_ = chars;
8573 length_ = length;
8574 }
8575
Advance(int consumed)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:
StringComparator(ConsStringIteratorOp * op_1,ConsStringIteratorOp * op_2)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>
Equals(State * state_1,State * state_2,int to_check)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
Equals(String * string_1,String * string_2)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 };
8658
8659
SlowEquals(String * other)8660 bool String::SlowEquals(String* other) {
8661 DisallowHeapAllocation no_gc;
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()) {
8670 #ifdef ENABLE_SLOW_DCHECKS
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 }
8680 DCHECK(found_difference);
8681 }
8682 }
8683 #endif
8684 if (Hash() != other->Hash()) return false;
8685 }
8686
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
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);
8695 }
8696
8697 Isolate* isolate = GetIsolate();
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
SlowEquals(Handle<String> one,Handle<String> two)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 }
8723 }
8724 DCHECK(found_difference);
8725 }
8726 }
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);
8746 } else {
8747 for (int i = 0; i < one_length; i++) {
8748 if (flat1.Get(i) != flat2.Get(i)) return false;
8749 }
8750 return true;
8751 }
8752 }
8753
8754
MarkAsUndetectable()8755 bool String::MarkAsUndetectable() {
8756 if (StringShape(this).IsInternalized()) return false;
8757
8758 Map* map = this->map();
8759 Heap* heap = GetHeap();
8760 if (map == heap->string_map()) {
8761 this->set_map(heap->undetectable_string_map());
8762 return true;
8763 } else if (map == heap->one_byte_string_map()) {
8764 this->set_map(heap->undetectable_one_byte_string_map());
8765 return true;
8766 }
8767 // Rest cannot be marked as undetectable
8768 return false;
8769 }
8770
8771
IsUtf8EqualTo(Vector<const char> str,bool allow_prefix_match)8772 bool String::IsUtf8EqualTo(Vector<const char> str, bool allow_prefix_match) {
8773 int slen = length();
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 }
8781 int i;
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);
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 }
8795 utf8_data += cursor;
8796 remaining_in_str -= cursor;
8797 }
8798 return (allow_prefix_match || i == slen) && remaining_in_str == 0;
8799 }
8800
8801
IsOneByteEqualTo(Vector<const uint8_t> str)8802 bool String::IsOneByteEqualTo(Vector<const uint8_t> str) {
8803 int slen = length();
8804 if (str.length() != slen) return false;
8805 DisallowHeapAllocation no_gc;
8806 FlatContent content = GetFlatContent();
8807 if (content.IsOneByte()) {
8808 return CompareChars(content.ToOneByteVector().start(),
8809 str.start(), slen) == 0;
8810 }
8811 for (int i = 0; i < slen; i++) {
8812 if (Get(i) != static_cast<uint16_t>(str[i])) return false;
8813 }
8814 return true;
8815 }
8816
8817
IsTwoByteEqualTo(Vector<const uc16> str)8818 bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
8819 int slen = length();
8820 if (str.length() != slen) return false;
8821 DisallowHeapAllocation no_gc;
8822 FlatContent content = GetFlatContent();
8823 if (content.IsTwoByte()) {
8824 return CompareChars(content.ToUC16Vector().start(), str.start(), slen) == 0;
8825 }
8826 for (int i = 0; i < slen; i++) {
8827 if (Get(i) != str[i]) return false;
8828 }
8829 return true;
8830 }
8831
8832
ComputeAndSetHash()8833 uint32_t String::ComputeAndSetHash() {
8834 // Should only be called if hash code has not yet been computed.
8835 DCHECK(!HasHashCode());
8836
8837 // Store the hash code in the object.
8838 uint32_t field = IteratingStringHasher::Hash(this, GetHeap()->HashSeed());
8839 set_hash_field(field);
8840
8841 // Check the hash code is there.
8842 DCHECK(HasHashCode());
8843 uint32_t result = field >> kHashShift;
8844 DCHECK(result != 0); // Ensure that the hash value of 0 is never computed.
8845 return result;
8846 }
8847
8848
ComputeArrayIndex(uint32_t * index)8849 bool String::ComputeArrayIndex(uint32_t* index) {
8850 int length = this->length();
8851 if (length == 0 || length > kMaxArrayIndexSize) return false;
8852 ConsStringIteratorOp op;
8853 StringCharacterStream stream(this, &op);
8854 return StringToArrayIndex(&stream, index);
8855 }
8856
8857
SlowAsArrayIndex(uint32_t * index)8858 bool String::SlowAsArrayIndex(uint32_t* index) {
8859 if (length() <= kMaxCachedArrayIndexLength) {
8860 Hash(); // force computation of hash code
8861 uint32_t field = hash_field();
8862 if ((field & kIsNotArrayIndexMask) != 0) return false;
8863 // Isolate the array index form the full hash field.
8864 *index = ArrayIndexValueBits::decode(field);
8865 return true;
8866 } else {
8867 return ComputeArrayIndex(index);
8868 }
8869 }
8870
8871
Truncate(Handle<SeqString> string,int new_length)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
MakeArrayIndexHash(uint32_t value,int length)8914 uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
8915 // For array indexes mix the length into the hash as an array index could
8916 // be zero.
8917 DCHECK(length > 0);
8918 DCHECK(length <= String::kMaxArrayIndexSize);
8919 DCHECK(TenToThe(String::kMaxCachedArrayIndexLength) <
8920 (1 << String::kArrayIndexValueBits));
8921
8922 value <<= String::ArrayIndexValueBits::kShift;
8923 value |= length << String::ArrayIndexLengthBits::kShift;
8924
8925 DCHECK((value & String::kIsNotArrayIndexMask) == 0);
8926 DCHECK((length > String::kMaxCachedArrayIndexLength) ||
8927 (value & String::kContainsCachedArrayIndexMask) == 0);
8928 return value;
8929 }
8930
8931
GetHashField()8932 uint32_t StringHasher::GetHashField() {
8933 if (length_ <= String::kMaxHashCalcLength) {
8934 if (is_array_index_) {
8935 return MakeArrayIndexHash(array_index_, length_);
8936 }
8937 return (GetHashCore(raw_running_hash_) << String::kHashShift) |
8938 String::kIsNotArrayIndexMask;
8939 } else {
8940 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
8941 }
8942 }
8943
8944
ComputeUtf8Hash(Vector<const char> chars,uint32_t seed,int * utf16_length_out)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);
8956 }
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 }
8986 }
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;
8990 return hasher.GetHashField();
8991 }
8992
8993
PrintOn(FILE * file)8994 void String::PrintOn(FILE* file) {
8995 int length = this->length();
8996 for (int i = 0; i < length; i++) {
8997 PrintF(file, "%c", Get(i));
8998 }
8999 }
9000
9001
Hash()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
CheckEquivalent(Map * first,Map * second)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
EquivalentToForTransition(Map * other)9032 bool Map::EquivalentToForTransition(Map* other) {
9033 return CheckEquivalent(this, other);
9034 }
9035
9036
EquivalentToForNormalization(Map * other,PropertyNormalizationMode mode)9037 bool Map::EquivalentToForNormalization(Map* other,
9038 PropertyNormalizationMode mode) {
9039 int properties = mode == CLEAR_INOBJECT_PROPERTIES
9040 ? 0 : other->inobject_properties();
9041 return CheckEquivalent(this, other) && inobject_properties() == properties;
9042 }
9043
9044
ConstantPoolIterateBody(ObjectVisitor * v)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
ClearPtrEntries(Isolate * isolate)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 }
9085 }
9086
9087
JSFunctionIterateBody(int object_size,ObjectVisitor * v)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
MarkForOptimization()9097 void JSFunction::MarkForOptimization() {
9098 DCHECK(!IsOptimized());
9099 DCHECK(shared()->allows_lazy_compilation() ||
9100 code()->optimizable());
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.
9105 }
9106
9107
MarkForConcurrentOptimization()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");
9118 }
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.
9122 }
9123
9124
MarkInOptimizationQueue()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.
9140 }
9141
9142
CloneClosure(Handle<JSFunction> function)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
AddToOptimizedCodeMap(Handle<SharedFunctionInfo> shared,Handle<Context> native_context,Handle<Code> code,Handle<FixedArray> literals,BailoutId osr_ast_id)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;
9186 } else {
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 }
9198 }
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);
9216 }
9217
9218
GetLiteralsFromOptimizedCodeMap(int index)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;
9228 }
9229
9230
GetCodeFromOptimizedCodeMap(int index)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;
9237 }
9238
9239
ClearOptimizedCodeMap()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
EvictFromOptimizedCodeMap(Code * optimized_code,const char * reason)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
TrimOptimizedCodeMap(int shrink_by)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
OptimizeAsPrototype(Handle<JSObject> object,PrototypeOptimizationMode mode)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
ReoptimizeIfPrototype(Handle<JSObject> object)9332 void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
9333 if (!object->map()->is_prototype_map()) return;
9334 OptimizeAsPrototype(object, FAST_PROTOTYPE);
9335 }
9336
9337
CacheInitialJSArrayMaps(Handle<Context> native_context,Handle<Map> initial_map)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
SetInstancePrototype(Handle<JSFunction> function,Handle<Object> value)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);
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.
9416 function->set_prototype_or_initial_map(*value);
9417 }
9418 isolate->heap()->ClearInstanceofCache();
9419 }
9420
9421
SetPrototype(Handle<JSFunction> function,Handle<Object> value)9422 void JSFunction::SetPrototype(Handle<JSFunction> function,
9423 Handle<Object> value) {
9424 DCHECK(function->should_have_prototype());
9425 Handle<Object> construct_prototype = value;
9426
9427 // If the value is not a JSReceiver, store the value in the map's
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.
9431 if (!value->IsJSReceiver()) {
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.
9435 Handle<Map> new_map = Map::Copy(handle(function->map()));
9436
9437 JSObject::MigrateToMap(function, new_map);
9438 new_map->set_constructor(*value);
9439 new_map->set_non_instance_prototype(true);
9440 Isolate* isolate = new_map->GetIsolate();
9441 construct_prototype = handle(
9442 isolate->context()->native_context()->initial_object_prototype(),
9443 isolate);
9444 } else {
9445 function->map()->set_non_instance_prototype(false);
9446 }
9447
9448 return SetInstancePrototype(function, construct_prototype);
9449 }
9450
9451
RemovePrototype()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();
9457
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;
9465 }
9466 #endif
9467
9468 set_map(no_prototype_map);
9469 set_prototype_or_initial_map(no_prototype_map->GetHeap()->the_hole_value());
9470 return true;
9471 }
9472
9473
SetInitialMap(Handle<JSFunction> function,Handle<Map> map,Handle<Object> prototype)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
EnsureHasInitialMap(Handle<JSFunction> function)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
SetInstanceClassName(String * name)9526 void JSFunction::SetInstanceClassName(String* name) {
9527 shared()->set_instance_class_name(name);
9528 }
9529
9530
PrintName(FILE * out)9531 void JSFunction::PrintName(FILE* out) {
9532 SmartArrayPointer<char> name = shared()->DebugName()->ToCString();
9533 PrintF(out, "%s", name.get());
9534 }
9535
9536
NativeContextFromLiterals(FixedArray * literals)9537 Context* JSFunction::NativeContextFromLiterals(FixedArray* literals) {
9538 return Context::cast(literals->get(JSFunction::kLiteralNativeContextIndex));
9539 }
9540
9541
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
PassesFilter(const char * raw_filter)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;
9570 }
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
Initialize(Isolate * isolate,Handle<Oddball> oddball,const char * to_string,Handle<Object> to_number,byte kind)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
InitLineEnds(Handle<Script> script)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
GetColumnNumber(Handle<Script> script,int code_pos)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
GetLineNumberWithArray(int code_pos)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
GetLineNumber(Handle<Script> script,int code_pos)9656 int Script::GetLineNumber(Handle<Script> script, int code_pos) {
9657 InitLineEnds(script);
9658 return script->GetLineNumberWithArray(code_pos);
9659 }
9660
9661
GetLineNumber(int code_pos)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
GetNameOrSourceURL(Handle<Script> script)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.
ClearWrapperCacheWeakCallback(const v8::WeakCallbackData<v8::Value,void> & data)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
ClearWrapperCache()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
GetWrapper(Handle<Script> script)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;
9749 }
9750
9751
DebugName()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
HasSourceCode() const9759 bool SharedFunctionInfo::HasSourceCode() const {
9760 return !script()->IsUndefined() &&
9761 !reinterpret_cast<Script*>(script())->source()->IsUndefined();
9762 }
9763
9764
GetSourceCode()9765 Handle<Object> SharedFunctionInfo::GetSourceCode() {
9766 if (!HasSourceCode()) return GetIsolate()->factory()->undefined_value();
9767 Handle<String> source(String::cast(Script::cast(script())->source()));
9768 return GetIsolate()->factory()->NewSubString(
9769 source, start_position(), end_position());
9770 }
9771
9772
IsInlineable()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();
9780 }
9781
9782
SourceSize()9783 int SharedFunctionInfo::SourceSize() {
9784 return end_position() - start_position();
9785 }
9786
9787
CalculateInstanceSize()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
CalculateInObjectProperties()9799 int SharedFunctionInfo::CalculateInObjectProperties() {
9800 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
9801 }
9802
9803
9804 // Output the source code without any allocation in the heap.
operator <<(OStream & os,const SourceCodeOf & v)9805 OStream& operator<<(OStream& os, const SourceCodeOf& v) {
9806 const SharedFunctionInfo* s = v.value;
9807 // For some native functions there is no source.
9808 if (!s->HasSourceCode()) return os << "<No Source>";
9809
9810 // Get the source for the script which this function came from.
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 =
9814 reinterpret_cast<String*>(Script::cast(s->script())->source());
9815
9816 if (!script_source->LooksValid()) return os << "<Invalid Source>";
9817
9818 if (!s->is_toplevel()) {
9819 os << "function ";
9820 Object* name = s->name();
9821 if (name->IsString() && String::cast(name)->length() > 0) {
9822 String::cast(name)->PrintUC16(os);
9823 }
9824 }
9825
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;
9830 } else {
9831 script_source->PrintUC16(os, s->start_position(),
9832 s->start_position() + v.max_length);
9833 return os << "...\n";
9834 }
9835 }
9836
9837
IsCodeEquivalent(Code * code,Code * recompiled)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
EnableDeoptimizationSupport(Code * recompiled)9851 void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
9852 DCHECK(!has_deoptimization_support());
9853 DisallowHeapAllocation no_allocation;
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.
9864 ReplaceCode(recompiled);
9865 }
9866 DCHECK(has_deoptimization_support());
9867 }
9868
9869
DisableOptimization(BailoutReason reason)9870 void SharedFunctionInfo::DisableOptimization(BailoutReason reason) {
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);
9879 set_bailout_reason(reason);
9880 // Code should be the lazy compilation stub or else unoptimized. If the
9881 // latter, disable optimization for the code too.
9882 DCHECK(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN);
9883 if (code()->kind() == Code::FUNCTION) {
9884 code()->set_optimizable(false);
9885 }
9886 PROFILE(GetIsolate(), CodeDisableOptEvent(code(), this));
9887 if (FLAG_trace_opt) {
9888 PrintF("[disabled optimization for ");
9889 ShortPrint();
9890 PrintF(", reason: %s]\n", GetBailoutReason(reason));
9891 }
9892 }
9893
9894
VerifyBailoutId(BailoutId id)9895 bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) {
9896 DCHECK(!id.IsNone());
9897 Code* unoptimized = code();
9898 DeoptimizationOutputData* data =
9899 DeoptimizationOutputData::cast(unoptimized->deoptimization_data());
9900 unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this);
9901 USE(ignore);
9902 return true; // Return true if there was no DCHECK.
9903 }
9904
9905
StartInobjectSlackTracking()9906 void JSFunction::StartInobjectSlackTracking() {
9907 DCHECK(has_initial_map() && !IsInobjectSlackTrackingInProgress());
9908
9909 if (!FLAG_clever_optimizations) return;
9910 Map* map = initial_map();
9911
9912 // Only initiate the tracking the first time.
9913 if (map->done_inobject_slack_tracking()) return;
9914 map->set_done_inobject_slack_tracking(true);
9915
9916 // No tracking during the snapshot construction phase.
9917 Isolate* isolate = GetIsolate();
9918 if (isolate->serializer_enabled()) return;
9919
9920 if (map->unused_property_fields() == 0) return;
9921
9922 map->set_construction_count(kGenerousAllocationCount);
9923 }
9924
9925
ResetForNewContext(int new_ic_age)9926 void SharedFunctionInfo::ResetForNewContext(int new_ic_age) {
9927 code()->ClearInlineCaches();
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();
9931 set_ic_age(new_ic_age);
9932 if (code()->kind() == Code::FUNCTION) {
9933 code()->set_profiler_ticks(0);
9934 if (optimization_disabled() &&
9935 opt_count() >= FLAG_max_opt_count) {
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);
9941 set_deopt_count(0);
9942 }
9943 }
9944
9945
GetMinInobjectSlack(Map * map,void * data)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
ShrinkInstanceSize(Map * map,void * data)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
CompleteInobjectSlackTracking()9965 void JSFunction::CompleteInobjectSlackTracking() {
9966 DCHECK(has_initial_map());
9967 Map* map = initial_map();
9968
9969 DCHECK(map->done_inobject_slack_tracking());
9970 map->set_construction_count(kNoSlackTracking);
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);
9977 }
9978 }
9979
9980
SearchOptimizedCodeMap(Context * native_context,BailoutId osr_ast_id)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;
10004 }
10005
10006
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
VisitCodeTarget(RelocInfo * rinfo)10023 void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
10024 DCHECK(RelocInfo::IsCodeTarget(rinfo->rmode()));
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
VisitCodeAgeSequence(RelocInfo * rinfo)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
VisitCodeEntry(Address entry_address)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
VisitCell(RelocInfo * rinfo)10051 void ObjectVisitor::VisitCell(RelocInfo* rinfo) {
10052 DCHECK(rinfo->rmode() == RelocInfo::CELL);
10053 Object* cell = rinfo->target_cell();
10054 Object* old_cell = cell;
10055 VisitPointer(&cell);
10056 if (cell != old_cell) {
10057 rinfo->set_target_cell(reinterpret_cast<Cell*>(cell));
10058 }
10059 }
10060
10061
VisitDebugTarget(RelocInfo * rinfo)10062 void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
10063 DCHECK((RelocInfo::IsJSReturn(rinfo->rmode()) &&
10064 rinfo->IsPatchedReturnSequence()) ||
10065 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
10066 rinfo->IsPatchedDebugBreakSlotSequence()));
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
10073
VisitEmbeddedPointer(RelocInfo * rinfo)10074 void ObjectVisitor::VisitEmbeddedPointer(RelocInfo* rinfo) {
10075 DCHECK(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT);
10076 Object* p = rinfo->target_object();
10077 VisitPointer(&p);
10078 }
10079
10080
VisitExternalReference(RelocInfo * rinfo)10081 void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) {
10082 Address p = rinfo->target_reference();
10083 VisitExternalReference(&p);
10084 }
10085
10086
InvalidateRelocation()10087 void Code::InvalidateRelocation() {
10088 InvalidateEmbeddedObjects();
10089 set_relocation_info(GetHeap()->empty_byte_array());
10090 }
10091
10092
InvalidateEmbeddedObjects()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
Relocate(intptr_t delta)10109 void Code::Relocate(intptr_t delta) {
10110 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
10111 it.rinfo()->apply(delta, SKIP_ICACHE_FLUSH);
10112 }
10113 CpuFeatures::FlushICache(instruction_start(), instruction_size());
10114 }
10115
10116
CopyFrom(const CodeDesc & desc)10117 void Code::CopyFrom(const CodeDesc& desc) {
10118 DCHECK(Marking::Color(this) == Marking::WHITE_OBJECT);
10119
10120 // copy code
10121 CopyBytes(instruction_start(), desc.buffer,
10122 static_cast<size_t>(desc.instr_size));
10123
10124 // copy reloc info
10125 CopyBytes(relocation_start(),
10126 desc.buffer + desc.buffer_size - desc.reloc_size,
10127 static_cast<size_t>(desc.reloc_size));
10128
10129 // unbox handles and relocate
10130 intptr_t delta = instruction_start() - desc.buffer;
10131 int mode_mask = RelocInfo::kCodeTargetMask |
10132 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
10133 RelocInfo::ModeMask(RelocInfo::CELL) |
10134 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) |
10135 RelocInfo::kApplyMask;
10136 // Needed to find target_object and runtime_entry on X64
10137 Assembler* origin = desc.origin;
10138 AllowDeferredHandleDereference embedding_raw_address;
10139 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
10140 RelocInfo::Mode mode = it.rinfo()->rmode();
10141 if (mode == RelocInfo::EMBEDDED_OBJECT) {
10142 Handle<Object> p = it.rinfo()->target_object_handle(origin);
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);
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
10150 Handle<Object> p = it.rinfo()->target_object_handle(origin);
10151 Code* code = Code::cast(*p);
10152 it.rinfo()->set_target_address(code->instruction_start(),
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);
10163 } else {
10164 it.rinfo()->apply(delta, SKIP_ICACHE_FLUSH);
10165 }
10166 }
10167 CpuFeatures::FlushICache(instruction_start(), instruction_size());
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.
SourcePosition(Address pc)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.
10187
10188 int dist = static_cast<int>(pc - it.rinfo()->pc());
10189 int pos = static_cast<int>(it.rinfo()->data());
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.
SourceStatementPosition(Address pc)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())) {
10216 int p = static_cast<int>(it.rinfo()->data());
10217 if (statement_position < p && p <= position) {
10218 statement_position = p;
10219 }
10220 }
10221 it.next();
10222 }
10223 return statement_position;
10224 }
10225
10226
GetSafepointEntry(Address pc)10227 SafepointEntry Code::GetSafepointEntry(Address pc) {
10228 SafepointTable table(this);
10229 return table.FindEntry(pc);
10230 }
10231
10232
FindNthObject(int n,Map * match_map)10233 Object* Code::FindNthObject(int n, Map* match_map) {
10234 DCHECK(is_inline_cache_stub());
10235 DisallowHeapAllocation no_allocation;
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();
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
FindFirstAllocationSite()10250 AllocationSite* Code::FindFirstAllocationSite() {
10251 Object* result = FindNthObject(1, GetHeap()->allocation_site_map());
10252 return (result != NULL) ? AllocationSite::cast(result) : NULL;
10253 }
10254
10255
FindFirstMap()10256 Map* Code::FindFirstMap() {
10257 Object* result = FindNthObject(1, GetHeap()->meta_map());
10258 return (result != NULL) ? Map::cast(result) : NULL;
10259 }
10260
10261
FindAndReplace(const FindAndReplacePattern & pattern)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
FindAllMaps(MapHandleList * maps)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
FindFirstHandler()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
FindHandlers(CodeHandleList * code_list,int length)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
FindHandlerForMap(Map * map)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
FindFirstName()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);
10355 }
10356 return NULL;
10357 }
10358
10359
ClearInlineCaches()10360 void Code::ClearInlineCaches() {
10361 ClearInlineCaches(NULL);
10362 }
10363
10364
ClearInlineCaches(Code::Kind kind)10365 void Code::ClearInlineCaches(Code::Kind kind) {
10366 ClearInlineCaches(&kind);
10367 }
10368
10369
ClearInlineCaches(Code::Kind * kind)10370 void Code::ClearInlineCaches(Code::Kind* kind) {
10371 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
10372 RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) |
10373 RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
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()) {
10378 if (kind == NULL || *kind == target->kind()) {
10379 IC::Clear(this->GetIsolate(), info->pc(),
10380 info->host()->constant_pool());
10381 }
10382 }
10383 }
10384 }
10385
10386
ClearTypeFeedbackInfo()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
TranslatePcOffsetToAstId(uint32_t pc_offset)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
TranslateAstIdToPcOffset(BailoutId ast_id)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
MakeCodeAgeSequenceYoung(byte * sequence,Isolate * isolate)10435 void Code::MakeCodeAgeSequenceYoung(byte* sequence, Isolate* isolate) {
10436 PatchPlatformCodeAge(isolate, sequence, kNoAgeCodeAge, NO_MARKING_PARITY);
10437 }
10438
10439
MarkCodeAsExecuted(byte * sequence,Isolate * isolate)10440 void Code::MarkCodeAsExecuted(byte* sequence, Isolate* isolate) {
10441 PatchPlatformCodeAge(isolate, sequence, kExecutedOnceCodeAge,
10442 NO_MARKING_PARITY);
10443 }
10444
10445
EffectiveAge(Code::Age age)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
MakeOlder(MarkingParity current_parity)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
IsOld()10476 bool Code::IsOld() {
10477 return GetAge() >= kIsOldCodeAge;
10478 }
10479
10480
FindCodeAgeSequence()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
GetAge()10491 Code::Age Code::GetAge() {
10492 return EffectiveAge(GetRawAge());
10493 }
10494
10495
GetRawAge()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
GetCodeAgeAndParity(Code * code,Age * age,MarkingParity * parity)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
GetCodeAgeStub(Isolate * isolate,Age age,MarkingParity parity)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
PrintDeoptLocation(FILE * out,int bailout_id)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
CanDeoptAt(Address pc)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.
Kind2String(Kind kind)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
10622 #ifdef ENABLE_DISASSEMBLER
10623
DeoptimizationInputDataPrint(OStream & os)10624 void DeoptimizationInputData::DeoptimizationInputDataPrint(
10625 OStream& os) { // NOLINT
10626 disasm::NameConverter converter;
10627 int deopt_count = DeoptCount();
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 }
10634 for (int i = 0; i < deopt_count; i++) {
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();
10640
10641 if (!FLAG_print_code_verbose) {
10642 os << "\n";
10643 continue;
10644 }
10645 // Print details of the frame translation.
10646 int translation_index = TranslationIndex(i)->value();
10647 TranslationIterator iterator(TranslationByteArray(), translation_index);
10648 Translation::Opcode opcode =
10649 static_cast<Translation::Opcode>(iterator.Next());
10650 DCHECK(Translation::BEGIN == opcode);
10651 int frame_count = iterator.Next();
10652 int jsframe_count = iterator.Next();
10653 os << " " << Translation::StringFor(opcode)
10654 << " {frame count=" << frame_count
10655 << ", js frame count=" << jsframe_count << "}\n";
10656
10657 while (iterator.HasNext() &&
10658 Translation::BEGIN !=
10659 (opcode = static_cast<Translation::Opcode>(iterator.Next()))) {
10660 Vector<char> buf2 = Vector<char>::New(128);
10661 SNPrintF(buf2, "%27s %s ", "", Translation::StringFor(opcode));
10662 os << buf2.start();
10663
10664 switch (opcode) {
10665 case Translation::BEGIN:
10666 UNREACHABLE();
10667 break;
10668
10669 case Translation::JS_FRAME: {
10670 int ast_id = iterator.Next();
10671 int function_id = iterator.Next();
10672 unsigned height = iterator.Next();
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 << "}";
10687 break;
10688 }
10689
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();
10696 os << "{function=" << Brief(function->shared()->DebugName())
10697 << ", height=" << height << "}";
10698 break;
10699 }
10700
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()) << "}";
10707 break;
10708 }
10709
10710 case Translation::REGISTER: {
10711 int reg_code = iterator.Next();
10712 os << "{input=" << converter.NameOfCPURegister(reg_code) << "}";
10713 break;
10714 }
10715
10716 case Translation::INT32_REGISTER: {
10717 int reg_code = iterator.Next();
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)}";
10726 break;
10727 }
10728
10729 case Translation::DOUBLE_REGISTER: {
10730 int reg_code = iterator.Next();
10731 os << "{input=" << DoubleRegister::AllocationIndexToString(reg_code)
10732 << "}";
10733 break;
10734 }
10735
10736 case Translation::STACK_SLOT: {
10737 int input_slot_index = iterator.Next();
10738 os << "{input=" << input_slot_index << "}";
10739 break;
10740 }
10741
10742 case Translation::INT32_STACK_SLOT: {
10743 int input_slot_index = iterator.Next();
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)}";
10751 break;
10752 }
10753
10754 case Translation::DOUBLE_STACK_SLOT: {
10755 int input_slot_index = iterator.Next();
10756 os << "{input=" << input_slot_index << "}";
10757 break;
10758 }
10759
10760 case Translation::LITERAL: {
10761 unsigned literal_index = iterator.Next();
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 << "}";
10769 break;
10770 }
10771
10772 case Translation::ARGUMENTS_OBJECT:
10773 case Translation::CAPTURED_OBJECT: {
10774 int args_length = iterator.Next();
10775 os << "{length=" << args_length << "}";
10776 break;
10777 }
10778 }
10779 os << "\n";
10780 }
10781 }
10782 }
10783
10784
DeoptimizationOutputDataPrint(OStream & os)10785 void DeoptimizationOutputData::DeoptimizationOutputDataPrint(
10786 OStream& os) { // NOLINT
10787 os << "Deoptimization Output Data (deopt points = " << this->DeoptPoints()
10788 << ")\n";
10789 if (this->DeoptPoints() == 0) return;
10790
10791 os << "ast id pc state\n";
10792 for (int i = 0; i < this->DeoptPoints(); i++) {
10793 int pc_and_state = this->PcAndState(i)->value();
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();
10801 }
10802 }
10803
10804
ICState2String(InlineCacheState state)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";
10810 case PROTOTYPE_FAILURE:
10811 return "PROTOTYPE_FAILURE";
10812 case POLYMORPHIC: return "POLYMORPHIC";
10813 case MEGAMORPHIC: return "MEGAMORPHIC";
10814 case GENERIC: return "GENERIC";
10815 case DEBUG_STUB: return "DEBUG_STUB";
10816 case DEFAULT:
10817 return "DEFAULT";
10818 }
10819 UNREACHABLE();
10820 return NULL;
10821 }
10822
10823
StubType2String(StubType type)10824 const char* Code::StubType2String(StubType type) {
10825 switch (type) {
10826 case NORMAL: return "NORMAL";
10827 case FAST: return "FAST";
10828 }
10829 UNREACHABLE(); // keep the compiler happy
10830 return NULL;
10831 }
10832
10833
PrintExtraICState(OStream & os,Kind kind,ExtraICState extra)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";
10839 } else {
10840 os << extra << "\n";
10841 }
10842 }
10843
10844
Disassemble(const char * name,OStream & os)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 }
10851 if (is_inline_cache_stub()) {
10852 os << "ic_state = " << ICState2String(ic_state()) << "\n";
10853 PrintExtraICState(os, kind(), extra_ic_state());
10854 if (ic_state() == MONOMORPHIC) {
10855 os << "type = " << StubType2String(type()) << "\n";
10856 }
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";
10864 }
10865 }
10866 if ((name != NULL) && (name[0] != '\0')) {
10867 os << "name = " << name << "\n";
10868 }
10869 if (kind() == OPTIMIZED_FUNCTION) {
10870 os << "stack_slots = " << stack_slots() << "\n";
10871 }
10872
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";
10880
10881 if (kind() == FUNCTION) {
10882 DeoptimizationOutputData* data =
10883 DeoptimizationOutputData::cast(this->deoptimization_data());
10884 data->DeoptimizationOutputDataPrint(os);
10885 } else if (kind() == OPTIMIZED_FUNCTION) {
10886 DeoptimizationInputData* data =
10887 DeoptimizationInputData::cast(this->deoptimization_data());
10888 data->DeoptimizationInputDataPrint(os);
10889 }
10890 os << "\n";
10891
10892 if (is_crankshafted()) {
10893 SafepointTable table(this);
10894 os << "Safepoints (size = " << table.size() << ")\n";
10895 for (unsigned i = 0; i < table.length(); i++) {
10896 unsigned pc_offset = table.GetPcOffset(i);
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) ";
10904 SafepointEntry entry = table.GetEntry(i);
10905 if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
10906 Vector<char> buf2 = Vector<char>::New(30);
10907 SNPrintF(buf2, "%6d", entry.deoptimization_index());
10908 os << buf2.start();
10909 } else {
10910 os << "<none>";
10911 }
10912 if (entry.argument_count() > 0) {
10913 os << " argc: " << entry.argument_count();
10914 }
10915 os << "\n";
10916 }
10917 os << "\n";
10918 } else if (kind() == FUNCTION) {
10919 unsigned offset = back_edge_table_offset();
10920 // If there is no back edge table, the "table start" will be at or after
10921 // (due to alignment) the end of the instruction stream.
10922 if (static_cast<int>(offset) < instruction_size()) {
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();
10934 }
10935
10936 os << "\n";
10937 }
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
10945 }
10946
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";
10952 }
10953 #endif // ENABLE_DISASSEMBLER
10954
10955
SetFastElementsCapacityAndLength(Handle<JSObject> object,int capacity,int length,SetFastElementsCapacitySmiMode smi_mode)10956 Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
10957 Handle<JSObject> object,
10958 int capacity,
10959 int length,
10960 SetFastElementsCapacitySmiMode smi_mode) {
10961 // We should never end in here with a pixel or external array.
10962 DCHECK(!object->HasExternalArrayElements());
10963
10964 // Allocate a new fast elements backing store.
10965 Handle<FixedArray> new_elements =
10966 object->GetIsolate()->factory()->NewUninitializedFixedArray(capacity);
10967
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 }
10981 } else {
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);
11004 }
11005
11006 if (FLAG_trace_elements_transitions) {
11007 PrintElementsTransition(stdout, object, elements_kind, old_elements,
11008 object->GetElementsKind(), new_elements);
11009 }
11010
11011 if (object->IsJSArray()) {
11012 Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
11013 }
11014 return new_elements;
11015 }
11016
11017
SetFastDoubleElementsCapacityAndLength(Handle<JSObject> object,int capacity,int length)11018 void JSObject::SetFastDoubleElementsCapacityAndLength(Handle<JSObject> object,
11019 int capacity,
11020 int length) {
11021 // We should never end in here with a pixel or external array.
11022 DCHECK(!object->HasExternalArrayElements());
11023
11024 Handle<FixedArrayBase> elems =
11025 object->GetIsolate()->factory()->NewFixedDoubleArray(capacity);
11026
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;
11032 } else {
11033 new_elements_kind = FAST_DOUBLE_ELEMENTS;
11034 }
11035
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
11045 if (FLAG_trace_elements_transitions) {
11046 PrintElementsTransition(stdout, object, elements_kind, old_elements,
11047 object->GetElementsKind(), elems);
11048 }
11049
11050 if (object->IsJSArray()) {
11051 Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
11052 }
11053 }
11054
11055
11056 // static
Initialize(Handle<JSArray> array,int capacity,int length)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
Expand(Handle<JSArray> array,int required_size)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.
GetOldValue(Isolate * isolate,Handle<JSObject> object,uint32_t index,List<Handle<Object>> * old_values,List<uint32_t> * indices)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());
11086 } else {
11087 value = Object::GetElement(isolate, object, index).ToHandleChecked();
11088 }
11089 old_values->Add(value);
11090 indices->Add(index);
11091 return true;
11092 }
11093
EnqueueSpliceRecord(Handle<JSArray> object,uint32_t index,Handle<JSArray> deleted,uint32_t add_count)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();
11112 }
11113
11114
BeginPerformSplice(Handle<JSArray> object)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();
11125 }
11126
11127
EndPerformSplice(Handle<JSArray> object)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
SetElementsLength(Handle<JSArray> array,Handle<Object> new_length_handle)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
11155 // We should never end in here with a pixel or external array.
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;
11238 }
11239
11240
GetPrototypeTransition(Handle<Map> map,Handle<Object> prototype)11241 Handle<Map> Map::GetPrototypeTransition(Handle<Map> map,
11242 Handle<Object> prototype) {
11243 FixedArray* cache = map->GetPrototypeTransitions();
11244 int number_of_transitions = map->NumberOfProtoTransitions();
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++) {
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));
11253 }
11254 }
11255 return Handle<Map>();
11256 }
11257
11258
PutPrototypeTransition(Handle<Map> map,Handle<Object> prototype,Handle<Map> target_map)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;
11268
11269 const int step = kProtoTransitionElementsPerEntry;
11270 const int header = kProtoTransitionHeaderSize;
11271
11272 Handle<FixedArray> cache(map->GetPrototypeTransitions());
11273 int capacity = (cache->length() - header) / step;
11274 int transitions = map->NumberOfProtoTransitions() + 1;
11275
11276 if (transitions > capacity) {
11277 if (capacity > kMaxCachedPrototypeTransitions) return map;
11278
11279 // Grow array by factor 2 over and above what we need.
11280 cache = FixedArray::CopySize(cache, transitions * 2 * step + header);
11281
11282 SetPrototypeTransitions(map, cache);
11283 }
11284
11285 // Reload number of transitions as GC might shrink them.
11286 int last = map->NumberOfProtoTransitions();
11287 int entry = header + last * step;
11288
11289 cache->set(entry + kProtoTransitionPrototypeOffset, *prototype);
11290 cache->set(entry + kProtoTransitionMapOffset, *target_map);
11291 map->SetNumberOfProtoTransitions(last + 1);
11292
11293 return map;
11294 }
11295
11296
ZapTransitions()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
ZapPrototypeTransitions()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
AddDependentCompilationInfo(Handle<Map> map,DependentCode::DependencyGroup group,CompilationInfo * info)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
AddDependentCode(Handle<Map> map,DependentCode::DependencyGroup group,Handle<Code> code)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
AddDependentIC(Handle<Map> map,Handle<Code> stub)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
GroupStartIndexes(DependentCode * entries)11357 DependentCode::GroupStartIndexes::GroupStartIndexes(DependentCode* entries) {
11358 Recompute(entries);
11359 }
11360
11361
Recompute(DependentCode * entries)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
ForObject(Handle<HeapObject> object,DependencyGroup group)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
Insert(Handle<DependentCode> entries,DependencyGroup group,Handle<Object> object)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
UpdateToFinishedCode(DependencyGroup group,CompilationInfo * info,Code * code)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
11440 #ifdef DEBUG
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
RemoveCompilationInfo(DependentCode::DependencyGroup group,CompilationInfo * info)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
CodeListContains(Object * head,Code * code)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
Contains(DependencyGroup group,Code * code)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
MarkCodeForDeoptimization(Isolate * isolate,DependentCode::DependencyGroup group)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
DeoptimizeDependentCodeGroup(Isolate * isolate,DependentCode::DependencyGroup group)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
AddToDependentICList(Handle<Code> stub)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
SetMarkedForDeoptimization(Code * code,DependencyGroup group)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
DependencyGroupName(DependencyGroup group)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
TransitionToPrototype(Handle<Map> map,Handle<Object> prototype)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
SetPrototype(Handle<JSObject> object,Handle<Object> value,bool from_javascript)11632 MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> object,
11633 Handle<Object> value,
11634 bool from_javascript) {
11635 #ifdef DEBUG
11636 int size = object->Size();
11637 #endif
11638
11639 Isolate* isolate = object->GetIsolate();
11640 Heap* heap = isolate->heap();
11641 // Silently ignore the change if value is not a JSObject or null.
11642 // SpiderMonkey behaves this way.
11643 if (!value->IsJSReceiver() && !value->IsNull()) return value;
11644
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.
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);
11658 }
11659
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.
11664 for (PrototypeIterator iter(isolate, *value,
11665 PrototypeIterator::START_AT_RECEIVER);
11666 !iter.IsAtEnd(); iter.Advance()) {
11667 if (JSReceiver::cast(iter.GetCurrent()) == *object) {
11668 // Cycle detected.
11669 THROW_NEW_ERROR(isolate,
11670 NewError("cyclic_proto", HandleVector<Object>(NULL, 0)),
11671 Object);
11672 }
11673 }
11674
11675 bool dictionary_elements_in_chain =
11676 object->map()->DictionaryElementsInPrototypeChainOnly();
11677 Handle<JSObject> real_receiver = object;
11678
11679 if (from_javascript) {
11680 // Find the first object in the chain whose prototype object is not
11681 // hidden and set the new prototype on that object.
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();
11687 }
11688 }
11689
11690 // Set the new prototype of the object.
11691 Handle<Map> map(real_receiver->map());
11692
11693 // Nothing to do if prototype is already set.
11694 if (map->prototype() == *value) return value;
11695
11696 if (value->IsJSObject()) {
11697 PrototypeOptimizationMode mode =
11698 from_javascript ? REGULAR_PROTOTYPE : FAST_PROTOTYPE;
11699 JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value), mode);
11700 }
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 }
11713
11714 heap->ClearInstanceofCache();
11715 DCHECK(size == object->Size());
11716 return value;
11717 }
11718
11719
EnsureCanContainElements(Handle<JSObject> object,Arguments * args,uint32_t first_arg,uint32_t arg_count,EnsureElementsMode mode)11720 void JSObject::EnsureCanContainElements(Handle<JSObject> object,
11721 Arguments* args,
11722 uint32_t first_arg,
11723 uint32_t arg_count,
11724 EnsureElementsMode mode) {
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(
11729 object, args->arguments() - first_arg - (arg_count - 1), arg_count, mode);
11730 }
11731
11732
GetOwnElementAccessorPair(Handle<JSObject> object,uint32_t index)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
SetElementWithInterceptor(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,StrictMode strict_mode,bool check_prototype,SetPropertyMode set_mode)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
11761 // Make sure that the top context does not change when doing
11762 // callbacks or interceptor calls.
11763 AssertNoContextChange ncc(isolate);
11764
11765 Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor());
11766 if (!interceptor->setter()->IsUndefined()) {
11767 v8::IndexedPropertySetterCallback setter =
11768 v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter());
11769 LOG(isolate,
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;
11777 }
11778
11779 return SetElementWithoutInterceptor(object, index, value, attributes,
11780 strict_mode,
11781 check_prototype,
11782 set_mode);
11783 }
11784
11785
GetElementWithCallback(Handle<JSObject> object,Handle<Object> receiver,Handle<Object> structure,uint32_t index,Handle<Object> holder)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());
11794 // api style callbacks.
11795 if (structure->IsExecutableAccessorInfo()) {
11796 Handle<ExecutableAccessorInfo> data =
11797 Handle<ExecutableAccessorInfo>::cast(structure);
11798 Object* fun_obj = data->getter();
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);
11803 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
11804 Handle<String> key = isolate->factory()->NumberToString(number);
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);
11815 }
11816
11817 // __defineGetter__ callback
11818 if (structure->IsAccessorPair()) {
11819 Handle<Object> getter(Handle<AccessorPair>::cast(structure)->getter(),
11820 isolate);
11821 if (getter->IsSpecFunction()) {
11822 // TODO(rossberg): nicer would be to cast to some JSCallable here...
11823 return GetPropertyWithDefinedGetter(
11824 receiver, Handle<JSReceiver>::cast(getter));
11825 }
11826 // Getter is not a function.
11827 return isolate->factory()->undefined_value();
11828 }
11829
11830 if (structure->IsDeclaredAccessorInfo()) {
11831 return GetDeclaredAccessorProperty(
11832 receiver, Handle<DeclaredAccessorInfo>::cast(structure), isolate);
11833 }
11834
11835 UNREACHABLE();
11836 return MaybeHandle<Object>();
11837 }
11838
11839
SetElementWithCallback(Handle<JSObject> object,Handle<Object> structure,uint32_t index,Handle<Object> value,Handle<JSObject> holder,StrictMode strict_mode)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();
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.
11850 DCHECK(!value->IsTheHole());
11851 DCHECK(!structure->IsForeign());
11852 if (structure->IsExecutableAccessorInfo()) {
11853 // api style callbacks
11854 Handle<ExecutableAccessorInfo> data =
11855 Handle<ExecutableAccessorInfo>::cast(structure);
11856 Object* call_obj = data->setter();
11857 v8::AccessorNameSetterCallback call_fun =
11858 v8::ToCData<v8::AccessorNameSetterCallback>(call_obj);
11859 if (call_fun == NULL) return value;
11860 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
11861 Handle<String> key(isolate->factory()->NumberToString(number));
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;
11870 }
11871
11872 if (structure->IsAccessorPair()) {
11873 Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate);
11874 if (setter->IsSpecFunction()) {
11875 // TODO(rossberg): nicer would be to cast to some JSCallable here...
11876 return SetPropertyWithDefinedSetter(
11877 object, Handle<JSReceiver>::cast(setter), value);
11878 } else {
11879 if (strict_mode == SLOPPY) return value;
11880 Handle<Object> key(isolate->factory()->NewNumberFromUint(index));
11881 Handle<Object> args[2] = { key, holder };
11882 THROW_NEW_ERROR(
11883 isolate, NewTypeError("no_setter_in_callback", HandleVector(args, 2)),
11884 Object);
11885 }
11886 }
11887
11888 // TODO(dcarney): Handle correctly.
11889 if (structure->IsDeclaredAccessorInfo()) return value;
11890
11891 UNREACHABLE();
11892 return MaybeHandle<Object>();
11893 }
11894
11895
HasFastArgumentsElements()11896 bool JSObject::HasFastArgumentsElements() {
11897 Heap* heap = GetHeap();
11898 if (!elements()->IsFixedArray()) return false;
11899 FixedArray* elements = FixedArray::cast(this->elements());
11900 if (elements->map() != heap->sloppy_arguments_elements_map()) {
11901 return false;
11902 }
11903 FixedArray* arguments = FixedArray::cast(elements->get(1));
11904 return !arguments->IsDictionary();
11905 }
11906
11907
HasDictionaryArgumentsElements()11908 bool JSObject::HasDictionaryArgumentsElements() {
11909 Heap* heap = GetHeap();
11910 if (!elements()->IsFixedArray()) return false;
11911 FixedArray* elements = FixedArray::cast(this->elements());
11912 if (elements->map() != heap->sloppy_arguments_elements_map()) {
11913 return false;
11914 }
11915 FixedArray* arguments = FixedArray::cast(elements->get(1));
11916 return arguments->IsDictionary();
11917 }
11918
11919
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.
SetFastElement(Handle<JSObject> object,uint32_t index,Handle<Object> value,StrictMode strict_mode,bool check_prototype)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());
11930
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)));
11946 } else {
11947 backing_store = EnsureWritableFastElements(object);
11948 }
11949 uint32_t capacity = static_cast<uint32_t>(backing_store->length());
11950
11951 if (check_prototype &&
11952 (index >= capacity || backing_store->get(index)->IsTheHole())) {
11953 bool found;
11954 MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes(
11955 object, index, value, &found, strict_mode);
11956 if (found) return result;
11957 }
11958
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;
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;
11967 if (index >= array_length) {
11968 must_update_array_length = true;
11969 array_length = index + 1;
11970 }
11971 } else {
11972 introduces_holes = index >= capacity;
11973 }
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
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);
11991 DCHECK(new_capacity > index);
11992 if (!object->ShouldConvertToSlowElements(new_capacity)) {
11993 convert_to_slow = false;
11994 }
11995 }
11996 if (convert_to_slow) {
11997 NormalizeElements(object);
11998 return SetDictionaryElement(object, index, value, NONE, strict_mode,
11999 check_prototype);
12000 }
12001 }
12002 // Convert to fast double elements if appropriate.
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);
12014 return value;
12015 }
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()));
12026 }
12027 // Increase backing store capacity if that's been decided previously.
12028 if (new_capacity != capacity) {
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);
12038 return value;
12039 }
12040
12041 // Finally, set the new element and length.
12042 DCHECK(object->elements()->IsFixedArray());
12043 backing_store->set(index, *value);
12044 if (must_update_array_length) {
12045 Handle<JSArray>::cast(object)->set_length(Smi::FromInt(array_length));
12046 }
12047 return value;
12048 }
12049
12050
SetDictionaryElement(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,StrictMode strict_mode,bool check_prototype,SetPropertyMode set_mode)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();
12062
12063 // Insert element in the dictionary.
12064 Handle<FixedArray> elements(FixedArray::cast(object->elements()));
12065 bool is_arguments =
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));
12070
12071 int entry = dictionary->FindEntry(index);
12072 if (entry != SeededNumberDictionary::kNotFound) {
12073 Handle<Object> element(dictionary->ValueAt(entry), isolate);
12074 PropertyDetails details = dictionary->DetailsAt(entry);
12075 if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) {
12076 return SetElementWithCallback(object, element, index, value, object,
12077 strict_mode);
12078 } else {
12079 dictionary->UpdateMaxNumberKey(index);
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) {
12084 details = PropertyDetails(
12085 attributes, NORMAL, details.dictionary_index());
12086 dictionary->DetailsAtPut(entry, details);
12087 } else if (details.IsReadOnly() && !element->IsTheHole()) {
12088 if (strict_mode == SLOPPY) {
12089 return isolate->factory()->undefined_value();
12090 } else {
12091 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
12092 Handle<Object> args[2] = { number, object };
12093 THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
12094 HandleVector(args, 2)),
12095 Object);
12096 }
12097 }
12098 // Elements of the arguments object in slow mode might be slow aliases.
12099 if (is_arguments && element->IsAliasedArgumentsEntry()) {
12100 Handle<AliasedArgumentsEntry> entry =
12101 Handle<AliasedArgumentsEntry>::cast(element);
12102 Handle<Context> context(Context::cast(elements->get(0)));
12103 int context_index = entry->aliased_context_slot();
12104 DCHECK(!context->get(context_index)->IsTheHole());
12105 context->set(context_index, *value);
12106 // For elements that are still writable we keep slow aliasing.
12107 if (!details.IsReadOnly()) value = element;
12108 }
12109 dictionary->ValueAtPut(entry, *value);
12110 }
12111 } else {
12112 // Index not already used. Look for an accessor in the prototype chain.
12113 // Can cause GC!
12114 if (check_prototype) {
12115 bool found;
12116 MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes(
12117 object, index, value, &found, strict_mode);
12118 if (found) return result;
12119 }
12120
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).
12123 if (!object->map()->is_extensible()) {
12124 if (strict_mode == SLOPPY) {
12125 return isolate->factory()->undefined_value();
12126 } else {
12127 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
12128 Handle<String> name = isolate->factory()->NumberToString(number);
12129 Handle<Object> args[1] = { name };
12130 THROW_NEW_ERROR(isolate, NewTypeError("object_not_extensible",
12131 HandleVector(args, 1)),
12132 Object);
12133 }
12134 }
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) {
12141 if (is_arguments) {
12142 elements->set(1, *new_dictionary);
12143 } else {
12144 object->set_elements(*new_dictionary);
12145 }
12146 dictionary = new_dictionary;
12147 }
12148 }
12149
12150 // Update the array length if this JSObject is an array.
12151 if (object->IsJSArray()) {
12152 JSArray::JSArrayUpdateLengthFromIndex(Handle<JSArray>::cast(object), index,
12153 value);
12154 }
12155
12156 // Attempt to put this object back in fast case.
12157 if (object->ShouldConvertToFastElements()) {
12158 uint32_t new_length = 0;
12159 if (object->IsJSArray()) {
12160 CHECK(Handle<JSArray>::cast(object)->length()->ToArrayIndex(&new_length));
12161 } else {
12162 new_length = dictionary->max_number_key() + 1;
12163 }
12164 bool has_smi_only_elements = false;
12165 bool should_convert_to_fast_double_elements =
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);
12175 }
12176 JSObject::ValidateElements(object);
12177 #ifdef DEBUG
12178 if (FLAG_trace_normalization) {
12179 OFStream os(stdout);
12180 os << "Object elements are fast case again:\n";
12181 object->Print(os);
12182 }
12183 #endif
12184 }
12185 return value;
12186 }
12187
SetFastDoubleElement(Handle<JSObject> object,uint32_t index,Handle<Object> value,StrictMode strict_mode,bool check_prototype)12188 MaybeHandle<Object> JSObject::SetFastDoubleElement(
12189 Handle<JSObject> object,
12190 uint32_t index,
12191 Handle<Object> value,
12192 StrictMode strict_mode,
12193 bool check_prototype) {
12194 DCHECK(object->HasFastDoubleElements());
12195
12196 Handle<FixedArrayBase> base_elms(FixedArrayBase::cast(object->elements()));
12197 uint32_t elms_length = static_cast<uint32_t>(base_elms->length());
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 &&
12202 (index >= elms_length ||
12203 Handle<FixedDoubleArray>::cast(base_elms)->is_the_hole(index))) {
12204 bool found;
12205 MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes(
12206 object, index, value, &found, strict_mode);
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();
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
12222 if (!value->IsNumber()) {
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;
12232 }
12233
12234 double double_value = value_is_smi
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 }
12245
12246 // Check whether there is extra space in the fixed array.
12247 if (index < elms_length) {
12248 Handle<FixedDoubleArray> elms(FixedDoubleArray::cast(object->elements()));
12249 elms->set(index, double_value);
12250 if (object->IsJSArray()) {
12251 // Update the length of the array if needed.
12252 uint32_t array_length = 0;
12253 CHECK(
12254 Handle<JSArray>::cast(object)->length()->ToArrayIndex(&array_length));
12255 if (index >= array_length) {
12256 Handle<JSArray>::cast(object)->set_length(Smi::FromInt(index + 1));
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);
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);
12271 return value;
12272 }
12273 }
12274
12275 // Otherwise default to slow case.
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
SetElement(Handle<JSReceiver> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,StrictMode strict_mode)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);
12295 }
12296 return JSObject::SetElement(
12297 Handle<JSObject>::cast(object), index, value, attributes, strict_mode);
12298 }
12299
12300
SetOwnElement(Handle<JSObject> object,uint32_t index,Handle<Object> value,StrictMode strict_mode)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);
12307 }
12308
12309
SetElement(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,StrictMode strict_mode,bool check_prototype,SetPropertyMode set_mode)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();
12318
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);
12325 }
12326 }
12327
12328 // Check access rights if needed.
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;
12334 }
12335 }
12336
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);
12344 }
12345
12346 // Don't allow element properties to be redefined for external arrays.
12347 if ((object->HasExternalArrayElements() ||
12348 object->HasFixedTypedArrayElements()) &&
12349 set_mode == DEFINE_PROPERTY) {
12350 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
12351 Handle<Object> args[] = { object, number };
12352 THROW_NEW_ERROR(isolate, NewTypeError("redef_external_array_element",
12353 HandleVector(args, arraysize(args))),
12354 Object);
12355 }
12356
12357 // Normalize the elements to enable attributes on the property.
12358 if ((attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) {
12359 Handle<SeededNumberDictionary> dictionary = NormalizeElements(object);
12360 // Make sure that we never go back to fast case.
12361 dictionary->set_requires_slow_elements();
12362 }
12363
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);
12370 }
12371
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;
12446 }
12447
12448
SetElementWithoutInterceptor(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,StrictMode strict_mode,bool check_prototype,SetPropertyMode set_mode)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:
12481 case FAST_ELEMENTS:
12482 case FAST_HOLEY_SMI_ELEMENTS:
12483 case FAST_HOLEY_ELEMENTS:
12484 return SetFastElement(object, index, value, strict_mode, check_prototype);
12485 case FAST_DOUBLE_ELEMENTS:
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); \
12500 }
12501
12502 TYPED_ARRAYS(TYPED_ARRAY_CASE)
12503
12504 #undef TYPED_ARRAY_CASE
12505
12506 case DICTIONARY_ELEMENTS:
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()));
12512 uint32_t length = parameter_map->length();
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);
12521 // Redefining attributes of an aliased element destroys fast aliasing.
12522 if (set_mode == SET_PROPERTY || attributes == NONE) return value;
12523 parameter_map->set_the_hole(index + 2);
12524 // For elements that are still writable we re-establish slow aliasing.
12525 if ((attributes & READ_ONLY) == 0) {
12526 value = Handle<Object>::cast(
12527 isolate->factory()->NewAliasedArgumentsEntry(context_index));
12528 }
12529 }
12530 Handle<FixedArray> arguments(FixedArray::cast(parameter_map->get(1)));
12531 if (arguments->IsDictionary()) {
12532 return SetDictionaryElement(object, index, value, attributes,
12533 strict_mode,
12534 check_prototype,
12535 set_mode);
12536 } else {
12537 return SetFastElement(object, index, value, strict_mode,
12538 check_prototype);
12539 }
12540 }
12541 }
12542 // All possible cases have been handled above. Add a return to avoid the
12543 // complaints from the compiler.
12544 UNREACHABLE();
12545 return isolate->factory()->null_value();
12546 }
12547
12548
12549 const double AllocationSite::kPretenureRatio = 0.85;
12550
12551
ResetPretenureDecision()12552 void AllocationSite::ResetPretenureDecision() {
12553 set_pretenure_decision(kUndecided);
12554 set_memento_found_count(0);
12555 set_memento_create_count(0);
12556 }
12557
12558
GetPretenureMode()12559 PretenureFlag AllocationSite::GetPretenureMode() {
12560 PretenureDecision mode = pretenure_decision();
12561 // Zombie objects "decide" to be untenured.
12562 return mode == kTenure ? TENURED : NOT_TENURED;
12563 }
12564
12565
IsNestedSite()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;
12573 }
12574 current = current_site->weak_next();
12575 }
12576 return false;
12577 }
12578
12579
DigestTransitionFeedback(Handle<AllocationSite> site,ElementsKind to_kind)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
AddDependentCompilationInfo(Handle<AllocationSite> site,Reason reason,CompilationInfo * info)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
PretenureDecisionName(PretenureDecision decision)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
UpdateAllocationSite(Handle<JSObject> object,ElementsKind to_kind)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
TransitionElementsKind(Handle<JSObject> object,ElementsKind to_kind)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);
12686 }
12687
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());
12713 uint32_t capacity = static_cast<uint32_t>(elms->length());
12714 uint32_t length = capacity;
12715
12716 if (object->IsJSArray()) {
12717 Object* raw_length = Handle<JSArray>::cast(object)->length();
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 {
12723 CHECK(raw_length->ToArrayIndex(&length));
12724 }
12725 }
12726
12727 if (IsFastSmiElementsKind(from_kind) &&
12728 IsFastDoubleElementsKind(to_kind)) {
12729 SetFastDoubleElementsCapacityAndLength(object, capacity, length);
12730 JSObject::ValidateElements(object);
12731 return;
12732 }
12733
12734 if (IsFastDoubleElementsKind(from_kind) &&
12735 IsFastObjectElementsKind(to_kind)) {
12736 SetFastElementsCapacityAndLength(object, capacity, length,
12737 kDontAllowSmiElements);
12738 JSObject::ValidateElements(object);
12739 return;
12740 }
12741
12742 // This method should never be called for any other case than the ones
12743 // handled above.
12744 UNREACHABLE();
12745 }
12746
12747
12748 // static
IsValidElementsTransition(ElementsKind from_kind,ElementsKind to_kind)12749 bool Map::IsValidElementsTransition(ElementsKind from_kind,
12750 ElementsKind to_kind) {
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);
12759 }
12760
12761
JSArrayUpdateLengthFromIndex(Handle<JSArray> array,uint32_t index,Handle<Object> value)12762 void JSArray::JSArrayUpdateLengthFromIndex(Handle<JSArray> array,
12763 uint32_t index,
12764 Handle<Object> value) {
12765 uint32_t old_len = 0;
12766 CHECK(array->length()->ToArrayIndex(&old_len));
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) {
12770 Handle<Object> len = array->GetIsolate()->factory()->NewNumber(
12771 static_cast<double>(index) + 1);
12772 array->set_length(*len);
12773 }
12774 }
12775
12776
IsReadOnlyLengthDescriptor(Handle<Map> jsarray_map)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
WouldChangeReadOnlyLength(Handle<JSArray> array,uint32_t index)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
ReadOnlyLengthError(Handle<JSArray> array)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
GetElementWithInterceptor(Handle<JSObject> object,Handle<Object> receiver,uint32_t index)12813 MaybeHandle<Object> JSObject::GetElementWithInterceptor(
12814 Handle<JSObject> object,
12815 Handle<Object> receiver,
12816 uint32_t index) {
12817 Isolate* isolate = object->GetIsolate();
12818
12819 // Make sure that the top context does not change when doing
12820 // callbacks or interceptor calls.
12821 AssertNoContextChange ncc(isolate);
12822
12823 Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor(), isolate);
12824 if (!interceptor->getter()->IsUndefined()) {
12825 v8::IndexedPropertyGetterCallback getter =
12826 v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter());
12827 LOG(isolate,
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);
12838 }
12839 }
12840
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;
12847
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);
12852 }
12853
12854
HasDenseElements()12855 bool JSObject::HasDenseElements() {
12856 int capacity = 0;
12857 int used = 0;
12858 GetElementsCapacityAndUsage(&capacity, &used);
12859 return (capacity == 0) || (used > (capacity / 2));
12860 }
12861
12862
GetElementsCapacityAndUsage(int * capacity,int * used)12863 void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
12864 *capacity = 0;
12865 *used = 0;
12866
12867 FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements());
12868 FixedArray* backing_store = NULL;
12869 switch (GetElementsKind()) {
12870 case SLOPPY_ARGUMENTS_ELEMENTS:
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()) {
12875 SeededNumberDictionary* dictionary =
12876 SeededNumberDictionary::cast(backing_store);
12877 *capacity = dictionary->Capacity();
12878 *used = dictionary->NumberOfElements();
12879 break;
12880 }
12881 // Fall through.
12882 case FAST_SMI_ELEMENTS:
12883 case FAST_ELEMENTS:
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:
12892 backing_store = FixedArray::cast(backing_store_base);
12893 *capacity = backing_store->length();
12894 for (int i = 0; i < *capacity; ++i) {
12895 if (!backing_store->get(i)->IsTheHole()) ++(*used);
12896 }
12897 break;
12898 case DICTIONARY_ELEMENTS: {
12899 SeededNumberDictionary* dictionary = element_dictionary();
12900 *capacity = dictionary->Capacity();
12901 *used = dictionary->NumberOfElements();
12902 break;
12903 }
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());
12915 for (int i = 0; i < *capacity; i++) {
12916 if (!elms->is_the_hole(i)) ++(*used);
12917 }
12918 break;
12919 }
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 {
12928 // External arrays are considered 100% used.
12929 FixedArrayBase* external_array = FixedArrayBase::cast(elements());
12930 *capacity = external_array->length();
12931 *used = external_array->length();
12932 break;
12933 }
12934 }
12935 }
12936
12937
WouldConvertToSlowElements(Handle<Object> key)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
ShouldConvertToSlowElements(int new_capacity)12953 bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
12954 STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
12955 kMaxUncheckedFastElementsLength);
12956 if (new_capacity <= kMaxUncheckedOldFastElementsLength ||
12957 (new_capacity <= kMaxUncheckedFastElementsLength &&
12958 GetHeap()->InNewSpace(this))) {
12959 return false;
12960 }
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);
12967 int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) *
12968 SeededNumberDictionary::kEntrySize;
12969 return 3 * dictionary_size <= new_capacity;
12970 }
12971
12972
ShouldConvertToFastElements()12973 bool JSObject::ShouldConvertToFastElements() {
12974 DCHECK(HasDictionaryElements() || HasDictionaryArgumentsElements());
12975 // If the elements are sparse, we should not go back to fast case.
12976 if (!HasDenseElements()) return false;
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;
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;
12983
12984 FixedArray* elements = FixedArray::cast(this->elements());
12985 SeededNumberDictionary* dictionary = NULL;
12986 if (elements->map() == GetHeap()->sloppy_arguments_elements_map()) {
12987 dictionary = SeededNumberDictionary::cast(elements->get(1));
12988 } else {
12989 dictionary = SeededNumberDictionary::cast(elements);
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;
12994 // If the dictionary backing storage takes up roughly half as much
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;
12998 if (IsJSArray()) {
12999 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size));
13000 } else {
13001 array_size = dictionary->max_number_key();
13002 }
13003 uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
13004 SeededNumberDictionary::kEntrySize;
13005 return 2 * dictionary_size >= array_size;
13006 }
13007
13008
ShouldConvertToFastDoubleElements(bool * has_smi_only_elements)13009 bool JSObject::ShouldConvertToFastDoubleElements(
13010 bool* has_smi_only_elements) {
13011 *has_smi_only_elements = false;
13012 if (HasSloppyArgumentsElements()) return false;
13013 if (FLAG_unbox_double_arrays) {
13014 DCHECK(HasDictionaryElements());
13015 SeededNumberDictionary* dictionary = element_dictionary();
13016 bool found_double = false;
13017 for (int i = 0; i < dictionary->Capacity(); i++) {
13018 Object* key = dictionary->KeyAt(i);
13019 if (key->IsNumber()) {
13020 Object* value = dictionary->ValueAt(i);
13021 if (!value->IsNumber()) return false;
13022 if (!value->IsSmi()) {
13023 found_double = true;
13024 }
13025 }
13026 }
13027 *has_smi_only_elements = !found_double;
13028 return found_double;
13029 } else {
13030 return false;
13031 }
13032 }
13033
13034
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.
13040 #ifdef OBJECT_PRINT
13041 template <typename Derived, typename Shape, typename Key>
Print(OStream & os)13042 void Dictionary<Derived, Shape, Key>::Print(OStream& os) { // NOLINT
13043 int capacity = DerivedHashTable::Capacity();
13044 for (int i = 0; i < capacity; i++) {
13045 Object* k = DerivedHashTable::KeyAt(i);
13046 if (DerivedHashTable::IsKey(k)) {
13047 os << " ";
13048 if (k->IsString()) {
13049 String::cast(k)->StringPrint(os);
13050 } else {
13051 os << Brief(k);
13052 }
13053 os << ": " << Brief(ValueAt(i)) << "\n";
13054 }
13055 }
13056 }
13057 #endif
13058
13059
13060 template<typename Derived, typename Shape, typename Key>
CopyValuesTo(FixedArray * elements)13061 void Dictionary<Derived, Shape, Key>::CopyValuesTo(FixedArray* elements) {
13062 int pos = 0;
13063 int capacity = DerivedHashTable::Capacity();
13064 DisallowHeapAllocation no_gc;
13065 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
13066 for (int i = 0; i < capacity; i++) {
13067 Object* k = Dictionary::KeyAt(i);
13068 if (Dictionary::IsKey(k)) {
13069 elements->set(pos++, ValueAt(i), mode);
13070 }
13071 }
13072 DCHECK(pos == elements->length());
13073 }
13074
13075
GetNamedInterceptor()13076 InterceptorInfo* JSObject::GetNamedInterceptor() {
13077 DCHECK(map()->has_named_interceptor());
13078 JSFunction* constructor = JSFunction::cast(map()->constructor());
13079 DCHECK(constructor->shared()->IsApiFunction());
13080 Object* result =
13081 constructor->shared()->get_api_func_data()->named_property_handler();
13082 return InterceptorInfo::cast(result);
13083 }
13084
13085
GetIndexedInterceptor()13086 InterceptorInfo* JSObject::GetIndexedInterceptor() {
13087 DCHECK(map()->has_indexed_interceptor());
13088 JSFunction* constructor = JSFunction::cast(map()->constructor());
13089 DCHECK(constructor->shared()->IsApiFunction());
13090 Object* result =
13091 constructor->shared()->get_api_func_data()->indexed_property_handler();
13092 return InterceptorInfo::cast(result);
13093 }
13094
13095
GetPropertyWithInterceptor(Handle<JSObject> holder,Handle<Object> receiver,Handle<Name> name)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);
13125 }
13126
13127
13128 // Compute the property keys from the interceptor.
13129 // TODO(rossberg): support symbols in API, and filter here if needed.
GetKeysForNamedInterceptor(Handle<JSObject> object,Handle<JSReceiver> receiver)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);
13143 }
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);
13151 }
13152
13153
13154 // Compute the element keys from the interceptor.
GetKeysForIndexedInterceptor(Handle<JSObject> object,Handle<JSReceiver> receiver)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
HasRealNamedProperty(Handle<JSObject> object,Handle<Name> key)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
HasRealElementProperty(Handle<JSObject> object,uint32_t index)13188 Maybe<bool> JSObject::HasRealElementProperty(Handle<JSObject> object,
13189 uint32_t index) {
13190 Isolate* isolate = object->GetIsolate();
13191 HandleScope scope(isolate);
13192 // Check access rights if needed.
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);
13198 }
13199 }
13200
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);
13214 }
13215
13216
HasRealNamedCallbackProperty(Handle<JSObject> object,Handle<Name> key)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);
13223 }
13224
13225
NumberOfOwnProperties(PropertyAttributes filter)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;
13233 }
13234 return map->NumberOfDescribedProperties(OWN_DESCRIPTORS, filter);
13235 }
13236 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
13237 }
13238
13239
SwapPairs(FixedArray * numbers,int i,int j)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);
13246 numbers->set(i, Smi::cast(numbers->get(j)));
13247 numbers->set(j, Smi::cast(temp));
13248 }
13249 }
13250
13251
InsertionSortPairs(FixedArray * content,FixedArray * numbers,int len)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
HeapSortPairs(FixedArray * content,FixedArray * numbers,int len)13267 void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
13268 // In-place heap sort.
13269 DCHECK(content->length() == numbers->length());
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.
SortPairs(FixedArray * numbers,uint32_t len)13314 void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
13315 DCHECK(this->length() == numbers->length());
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
13353 // Fill in the names of own properties into the supplied storage. The main
13354 // purpose of this function is to provide reflection information for the object
13355 // mirrors.
GetOwnPropertyNames(FixedArray * storage,int index,PropertyAttributes filter)13356 void JSObject::GetOwnPropertyNames(
13357 FixedArray* storage, int index, PropertyAttributes filter) {
13358 DCHECK(storage->length() >= (NumberOfOwnProperties(filter) - index));
13359 if (HasFastProperties()) {
13360 int real_size = map()->NumberOfOwnDescriptors();
13361 DescriptorArray* descs = map()->instance_descriptors();
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 }
13367 }
13368 } else {
13369 property_dictionary()->CopyKeysTo(storage,
13370 index,
13371 filter,
13372 NameDictionary::UNSORTED);
13373 }
13374 }
13375
13376
NumberOfOwnElements(PropertyAttributes filter)13377 int JSObject::NumberOfOwnElements(PropertyAttributes filter) {
13378 return GetOwnElementKeys(NULL, filter);
13379 }
13380
13381
NumberOfEnumElements()13382 int JSObject::NumberOfEnumElements() {
13383 // Fast case for objects with no elements.
13384 if (!IsJSValue() && HasFastObjectElements()) {
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.
13392 return NumberOfOwnElements(static_cast<PropertyAttributes>(DONT_ENUM));
13393 }
13394
13395
GetOwnElementKeys(FixedArray * storage,PropertyAttributes filter)13396 int JSObject::GetOwnElementKeys(FixedArray* storage,
13397 PropertyAttributes filter) {
13398 int counter = 0;
13399 switch (GetElementsKind()) {
13400 case FAST_SMI_ELEMENTS:
13401 case FAST_ELEMENTS:
13402 case FAST_HOLEY_SMI_ELEMENTS:
13403 case FAST_HOLEY_ELEMENTS: {
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) {
13410 storage->set(counter, Smi::FromInt(i));
13411 }
13412 counter++;
13413 }
13414 }
13415 DCHECK(!storage || storage->length() >= counter);
13416 break;
13417 }
13418 case FAST_DOUBLE_ELEMENTS:
13419 case FAST_HOLEY_DOUBLE_ELEMENTS: {
13420 int length = IsJSArray() ?
13421 Smi::cast(JSArray::cast(this)->length())->value() :
13422 FixedArrayBase::cast(elements())->length();
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 }
13431 DCHECK(!storage || storage->length() >= counter);
13432 break;
13433 }
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();
13443 while (counter < length) {
13444 if (storage != NULL) {
13445 storage->set(counter, Smi::FromInt(counter));
13446 }
13447 counter++;
13448 }
13449 DCHECK(!storage || storage->length() >= counter);
13450 break;
13451 }
13452
13453 case DICTIONARY_ELEMENTS: {
13454 if (storage != NULL) {
13455 element_dictionary()->CopyKeysTo(storage,
13456 filter,
13457 SeededNumberDictionary::SORTED);
13458 }
13459 counter += element_dictionary()->NumberOfElementsFilterAttributes(filter);
13460 break;
13461 }
13462 case SLOPPY_ARGUMENTS_ELEMENTS: {
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.
13469 SeededNumberDictionary* dictionary =
13470 SeededNumberDictionary::cast(arguments);
13471 if (storage != NULL) {
13472 dictionary->CopyKeysTo(
13473 storage, filter, SeededNumberDictionary::UNSORTED);
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 }
13501 break;
13502 }
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++) {
13511 storage->set(counter + i, Smi::FromInt(i));
13512 }
13513 }
13514 counter += str->length();
13515 }
13516 }
13517 DCHECK(!storage || storage->length() == counter);
13518 return counter;
13519 }
13520
13521
GetEnumElementKeys(FixedArray * storage)13522 int JSObject::GetEnumElementKeys(FixedArray* storage) {
13523 return GetOwnElementKeys(storage, static_cast<PropertyAttributes>(DONT_ENUM));
13524 }
13525
13526
13527 // StringSharedKeys are used as keys in the eval cache.
13528 class StringSharedKey : public HashTableKey {
13529 public:
StringSharedKey(Handle<String> source,Handle<SharedFunctionInfo> shared,StrictMode strict_mode,int scope_position)13530 StringSharedKey(Handle<String> source,
13531 Handle<SharedFunctionInfo> shared,
13532 StrictMode strict_mode,
13533 int scope_position)
13534 : source_(source),
13535 shared_(shared),
13536 strict_mode_(strict_mode),
13537 scope_position_(scope_position) { }
13538
IsMatch(Object * other)13539 bool IsMatch(Object* other) OVERRIDE {
13540 DisallowHeapAllocation no_allocation;
13541 if (!other->IsFixedArray()) return false;
13542 FixedArray* other_array = FixedArray::cast(other);
13543 SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0));
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;
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));
13552 return source->Equals(*source_);
13553 }
13554
StringSharedHashHelper(String * source,SharedFunctionInfo * shared,StrictMode strict_mode,int scope_position)13555 static uint32_t StringSharedHashHelper(String* source,
13556 SharedFunctionInfo* shared,
13557 StrictMode strict_mode,
13558 int scope_position) {
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
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
13565 // collection.
13566 Script* script(Script::cast(shared->script()));
13567 hash ^= String::cast(script->source())->Hash();
13568 if (strict_mode == STRICT) hash ^= 0x8000;
13569 hash += scope_position;
13570 }
13571 return hash;
13572 }
13573
Hash()13574 uint32_t Hash() OVERRIDE {
13575 return StringSharedHashHelper(*source_, *shared_, strict_mode_,
13576 scope_position_);
13577 }
13578
HashForObject(Object * obj)13579 uint32_t HashForObject(Object* obj) OVERRIDE {
13580 DisallowHeapAllocation no_allocation;
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));
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);
13587 int scope_position = Smi::cast(other_array->get(3))->value();
13588 return StringSharedHashHelper(
13589 source, shared, strict_mode, scope_position);
13590 }
13591
13592
AsHandle(Isolate * isolate)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;
13600 }
13601
13602 private:
13603 Handle<String> source_;
13604 Handle<SharedFunctionInfo> shared_;
13605 StrictMode strict_mode_;
13606 int scope_position_;
13607 };
13608
13609
13610 // RegExpKey carries the source and flags of a regular expression as key.
13611 class RegExpKey : public HashTableKey {
13612 public:
RegExpKey(Handle<String> string,JSRegExp::Flags flags)13613 RegExpKey(Handle<String> string, JSRegExp::Flags flags)
13614 : string_(string),
13615 flags_(Smi::FromInt(flags.value())) { }
13616
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.
IsMatch(Object * obj)13621 bool IsMatch(Object* obj) OVERRIDE {
13622 FixedArray* val = FixedArray::cast(obj);
13623 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
13624 && (flags_ == val->get(JSRegExp::kFlagsIndex));
13625 }
13626
Hash()13627 uint32_t Hash() OVERRIDE { return RegExpHash(*string_, flags_); }
13628
AsHandle(Isolate * isolate)13629 Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
13630 // Plain hash maps, which is where regexp keys are used, don't
13631 // use this function.
13632 UNREACHABLE();
13633 return MaybeHandle<Object>().ToHandleChecked();
13634 }
13635
HashForObject(Object * obj)13636 uint32_t HashForObject(Object* obj) OVERRIDE {
13637 FixedArray* val = FixedArray::cast(obj);
13638 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
13639 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
13640 }
13641
RegExpHash(String * string,Smi * flags)13642 static uint32_t RegExpHash(String* string, Smi* flags) {
13643 return string->Hash() + flags->value();
13644 }
13645
13646 Handle<String> string_;
13647 Smi* flags_;
13648 };
13649
13650
AsHandle(Isolate * isolate)13651 Handle<Object> OneByteStringKey::AsHandle(Isolate* isolate) {
13652 if (hash_field_ == 0) Hash();
13653 return isolate->factory()->NewOneByteInternalizedString(string_, hash_field_);
13654 }
13655
13656
AsHandle(Isolate * isolate)13657 Handle<Object> TwoByteStringKey::AsHandle(Isolate* isolate) {
13658 if (hash_field_ == 0) Hash();
13659 return isolate->factory()->NewTwoByteInternalizedString(string_, hash_field_);
13660 }
13661
13662
AsHandle(Isolate * isolate)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
IsMatch(Object * string)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 {
13678 public:
InternalizedStringKey(Handle<String> string)13679 explicit InternalizedStringKey(Handle<String> string)
13680 : string_(string) { }
13681
IsMatch(Object * string)13682 virtual bool IsMatch(Object* string) OVERRIDE {
13683 return String::cast(string)->Equals(*string_);
13684 }
13685
Hash()13686 virtual uint32_t Hash() OVERRIDE { return string_->Hash(); }
13687
HashForObject(Object * other)13688 virtual uint32_t HashForObject(Object* other) OVERRIDE {
13689 return String::cast(other)->Hash();
13690 }
13691
AsHandle(Isolate * isolate)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());
13700 return string_;
13701 }
13702 // Otherwise allocate a new internalized string.
13703 return isolate->factory()->NewInternalizedStringImpl(
13704 string_, string_->length(), string_->hash_field());
13705 }
13706
StringHash(Object * obj)13707 static uint32_t StringHash(Object* obj) {
13708 return String::cast(obj)->Hash();
13709 }
13710
13711 Handle<String> string_;
13712 };
13713
13714
13715 template<typename Derived, typename Shape, typename Key>
IteratePrefix(ObjectVisitor * v)13716 void HashTable<Derived, Shape, Key>::IteratePrefix(ObjectVisitor* v) {
13717 IteratePointers(v, 0, kElementsStartOffset);
13718 }
13719
13720
13721 template<typename Derived, typename Shape, typename Key>
IterateElements(ObjectVisitor * v)13722 void HashTable<Derived, Shape, Key>::IterateElements(ObjectVisitor* v) {
13723 IteratePointers(v,
13724 kElementsStartOffset,
13725 kHeaderSize + length() * kPointerSize);
13726 }
13727
13728
13729 template<typename Derived, typename Shape, typename Key>
New(Isolate * isolate,int at_least_space_for,MinimumCapacity capacity_option,PretenureFlag pretenure)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);
13740 if (capacity > HashTable::kMaxCapacity) {
13741 v8::internal::Heap::FatalProcessOutOfMemory("invalid table size", true);
13742 }
13743
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;
13754 }
13755
13756
13757 // Find entry for key otherwise return kNotFound.
FindEntry(Handle<Name> key)13758 int NameDictionary::FindEntry(Handle<Name> key) {
13759 if (!key->IsUniqueName()) {
13760 return DerivedHashTable::FindEntry(key);
13761 }
13762
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).
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.
13781 if (*key == element) return entry;
13782 if (!element->IsUniqueName() &&
13783 !element->IsTheHole() &&
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);
13788 return entry;
13789 }
13790 DCHECK(element->IsTheHole() || !Name::cast(element)->Equals(*key));
13791 entry = NextProbe(entry, count++, capacity);
13792 }
13793 return kNotFound;
13794 }
13795
13796
13797 template<typename Derived, typename Shape, typename Key>
Rehash(Handle<Derived> new_table,Key key)13798 void HashTable<Derived, Shape, Key>::Rehash(
13799 Handle<Derived> new_table,
13800 Key key) {
13801 DCHECK(NumberOfElements() < new_table->Capacity());
13802
13803 DisallowHeapAllocation no_gc;
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)) {
13819 uint32_t hash = HashTable::HashForObject(key, k);
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);
13829 }
13830
13831
13832 template<typename Derived, typename Shape, typename Key>
EntryForProbe(Key key,Object * k,int probe,uint32_t expected)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>
Swap(uint32_t entry1,uint32_t entry2,WriteBarrierMode mode)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>
Rehash(Key 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>
EnsureCapacity(Handle<Derived> table,int n,Key key,PretenureFlag pretenure)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();
13911 // Return if:
13912 // 50% is still free after adding n elements and
13913 // at most 50% of the free elements are deleted elements.
13914 if (nod <= (capacity - nof) >> 1) {
13915 int needed_free = nof >> 1;
13916 if (nof + needed_free <= capacity) return table;
13917 }
13918
13919 const int kMinCapacityForPretenure = 256;
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);
13928
13929 table->Rehash(new_table, key);
13930 return new_table;
13931 }
13932
13933
13934 template<typename Derived, typename Shape, typename Key>
Shrink(Handle<Derived> table,Key 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();
13939
13940 // Shrink to fit the number of elements if only a quarter of the
13941 // capacity is filled with elements.
13942 if (nof > (capacity >> 2)) return table;
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;
13948 if (at_least_room_for < 16) return table;
13949
13950 Isolate* isolate = table->GetIsolate();
13951 const int kMinCapacityForPretenure = 256;
13952 bool pretenure =
13953 (at_least_room_for > kMinCapacityForPretenure) &&
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);
13960
13961 table->Rehash(new_table, key);
13962 return new_table;
13963 }
13964
13965
13966 template<typename Derived, typename Shape, typename Key>
FindInsertionEntry(uint32_t hash)13967 uint32_t HashTable<Derived, Shape, Key>::FindInsertionEntry(uint32_t hash) {
13968 uint32_t capacity = Capacity();
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);
13974 if (element->IsUndefined() || element->IsTheHole()) break;
13975 entry = NextProbe(entry, count++, capacity);
13976 }
13977 return entry;
13978 }
13979
13980
13981 // Force instantiation of template instances class.
13982 // Please note this list is compiler dependent.
13983
13984 template class HashTable<StringTable, StringTableShape, HashTableKey*>;
13985
13986 template class HashTable<CompilationCacheTable,
13987 CompilationCacheShape,
13988 HashTableKey*>;
13989
13990 template class HashTable<MapCache, MapCacheShape, HashTableKey*>;
13991
13992 template class HashTable<ObjectHashTable,
13993 ObjectHashTableShape,
13994 Handle<Object> >;
13995
13996 template class HashTable<WeakHashTable, WeakHashTableShape<2>, Handle<Object> >;
13997
13998 template class Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >;
13999
14000 template class Dictionary<SeededNumberDictionary,
14001 SeededNumberDictionaryShape,
14002 uint32_t>;
14003
14004 template class Dictionary<UnseededNumberDictionary,
14005 UnseededNumberDictionaryShape,
14006 uint32_t>;
14007
14008 template Handle<SeededNumberDictionary>
14009 Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14010 New(Isolate*, int at_least_space_for, PretenureFlag pretenure);
14011
14012 template Handle<UnseededNumberDictionary>
14013 Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>::
14014 New(Isolate*, int at_least_space_for, PretenureFlag pretenure);
14015
14016 template Handle<NameDictionary>
14017 Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
14018 New(Isolate*, int n, PretenureFlag pretenure);
14019
14020 template Handle<SeededNumberDictionary>
14021 Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14022 AtPut(Handle<SeededNumberDictionary>, uint32_t, Handle<Object>);
14023
14024 template Handle<UnseededNumberDictionary>
14025 Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>::
14026 AtPut(Handle<UnseededNumberDictionary>, uint32_t, Handle<Object>);
14027
14028 template Object*
14029 Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14030 SlowReverseLookup(Object* value);
14031
14032 template Object*
14033 Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
14034 SlowReverseLookup(Object* value);
14035
14036 template void
14037 Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14038 CopyKeysTo(
14039 FixedArray*,
14040 PropertyAttributes,
14041 Dictionary<SeededNumberDictionary,
14042 SeededNumberDictionaryShape,
14043 uint32_t>::SortMode);
14044
14045 template Handle<Object>
14046 Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::DeleteProperty(
14047 Handle<NameDictionary>, int, JSObject::DeleteMode);
14048
14049 template Handle<Object>
14050 Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14051 DeleteProperty(Handle<SeededNumberDictionary>, int, JSObject::DeleteMode);
14052
14053 template Handle<NameDictionary>
14054 HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >::
14055 New(Isolate*, int, MinimumCapacity, PretenureFlag);
14056
14057 template Handle<NameDictionary>
14058 HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >::
14059 Shrink(Handle<NameDictionary>, Handle<Name>);
14060
14061 template Handle<SeededNumberDictionary>
14062 HashTable<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14063 Shrink(Handle<SeededNumberDictionary>, uint32_t);
14064
14065 template void Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
14066 CopyKeysTo(
14067 FixedArray*,
14068 int,
14069 PropertyAttributes,
14070 Dictionary<
14071 NameDictionary, NameDictionaryShape, Handle<Name> >::SortMode);
14072
14073 template int
14074 Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
14075 NumberOfElementsFilterAttributes(PropertyAttributes);
14076
14077 template Handle<NameDictionary>
14078 Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::Add(
14079 Handle<NameDictionary>, Handle<Name>, Handle<Object>, PropertyDetails);
14080
14081 template void
14082 Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
14083 GenerateNewEnumerationIndices(Handle<NameDictionary>);
14084
14085 template int
14086 Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14087 NumberOfElementsFilterAttributes(PropertyAttributes);
14088
14089 template Handle<SeededNumberDictionary>
14090 Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14091 Add(Handle<SeededNumberDictionary>,
14092 uint32_t,
14093 Handle<Object>,
14094 PropertyDetails);
14095
14096 template Handle<UnseededNumberDictionary>
14097 Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>::
14098 Add(Handle<UnseededNumberDictionary>,
14099 uint32_t,
14100 Handle<Object>,
14101 PropertyDetails);
14102
14103 template Handle<SeededNumberDictionary>
14104 Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14105 EnsureCapacity(Handle<SeededNumberDictionary>, int, uint32_t);
14106
14107 template Handle<UnseededNumberDictionary>
14108 Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>::
14109 EnsureCapacity(Handle<UnseededNumberDictionary>, int, uint32_t);
14110
14111 template Handle<NameDictionary>
14112 Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
14113 EnsureCapacity(Handle<NameDictionary>, int, Handle<Name>);
14114
14115 template
14116 int Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14117 NumberOfEnumElements();
14118
14119 template
14120 int Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::
14121 NumberOfEnumElements();
14122
14123 template
14124 int HashTable<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::
14125 FindEntry(uint32_t);
14126
14127
PrepareSlowElementsForSort(Handle<JSObject> object,uint32_t limit)14128 Handle<Object> JSObject::PrepareSlowElementsForSort(
14129 Handle<JSObject> object, uint32_t limit) {
14130 DCHECK(object->HasDictionaryElements());
14131 Isolate* isolate = object->GetIsolate();
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.
14135 Handle<SeededNumberDictionary> dict(object->element_dictionary(), isolate);
14136 Handle<SeededNumberDictionary> new_dict =
14137 SeededNumberDictionary::New(isolate, dict->NumberOfElements());
14138
14139 uint32_t pos = 0;
14140 uint32_t undefs = 0;
14141 int capacity = dict->Capacity();
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;
14146 for (int i = 0; i < capacity; i++) {
14147 Object* k = dict->KeyAt(i);
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;
14172 } else {
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++;
14178 }
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);
14188 }
14189 }
14190
14191 uint32_t result = pos;
14192 PropertyDetails no_details = PropertyDetails(NONE, NORMAL, 0);
14193 while (undefs > 0) {
14194 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
14195 // Adding an entry with the key beyond smi-range requires
14196 // allocation. Bailout.
14197 return bailout;
14198 }
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);
14204 pos++;
14205 undefs--;
14206 }
14207
14208 object->set_elements(*new_dict);
14209
14210 AllowHeapAllocation allocate_return_value;
14211 return isolate->factory()->NewNumberFromUint(result);
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.
PrepareElementsForSort(Handle<JSObject> object,uint32_t limit)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 }
14226
14227 if (object->HasDictionaryElements()) {
14228 // Convert to fast elements containing only the existing properties.
14229 // Ordering is irrelevant, since we are going to sort anyway.
14230 Handle<SeededNumberDictionary> dict(object->element_dictionary());
14231 if (object->IsJSArray() || dict->requires_slow_elements() ||
14232 dict->max_number_key() >= limit) {
14233 return JSObject::PrepareSlowElementsForSort(object, limit);
14234 }
14235 // Convert to fast elements.
14236
14237 Handle<Map> new_map =
14238 JSObject::GetElementsTransitionMap(object, FAST_HOLEY_ELEMENTS);
14239
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);
14246
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);
14255 }
14256 DCHECK(object->HasFastSmiOrObjectElements() ||
14257 object->HasFastDoubleElements());
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
14262 Handle<FixedArrayBase> elements_base(object->elements());
14263 uint32_t elements_length = static_cast<uint32_t>(elements_base->length());
14264 if (limit > elements_length) {
14265 limit = elements_length ;
14266 }
14267 if (limit == 0) {
14268 return handle(Smi::FromInt(0), isolate);
14269 }
14270
14271 uint32_t result = 0;
14272 if (elements_base->map() == isolate->heap()->fixed_double_array_map()) {
14273 FixedDoubleArray* elements = FixedDoubleArray::cast(*elements_base);
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 }
14293 }
14294 result = holes;
14295 while (holes < limit) {
14296 elements->set_the_hole(holes);
14297 holes++;
14298 }
14299 } else {
14300 FixedArray* elements = FixedArray::cast(*elements_base);
14301 DisallowHeapAllocation no_gc;
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.
14305 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_gc);
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);
14312 if (current->IsTheHole()) {
14313 holes--;
14314 undefs--;
14315 } else if (current->IsUndefined()) {
14316 undefs--;
14317 } else {
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 }
14332 }
14333 }
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 }
14343 }
14344
14345 return isolate->factory()->NewNumberFromUint(result);
14346 }
14347
14348
type()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
element_size()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
SetValue(Handle<ExternalUint8ClampedArray> array,uint32_t index,Handle<Object> value)14382 Handle<Object> ExternalUint8ClampedArray::SetValue(
14383 Handle<ExternalUint8ClampedArray> array,
14384 uint32_t index,
14385 Handle<Object> value) {
14386 uint8_t clamped_value = 0;
14387 if (index < static_cast<uint32_t>(array->length())) {
14388 if (value->IsSmi()) {
14389 int int_value = Handle<Smi>::cast(value)->value();
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()) {
14398 double double_value = Handle<HeapNumber>::cast(value)->value();
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.
14407 clamped_value = static_cast<uint8_t>(lrint(double_value));
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.
14412 DCHECK(value->IsUndefined());
14413 }
14414 array->set(index, clamped_value);
14415 }
14416 return handle(Smi::FromInt(clamped_value), array->GetIsolate());
14417 }
14418
14419
14420 template<typename ExternalArrayClass, typename ValueType>
ExternalArrayIntSetter(Isolate * isolate,Handle<ExternalArrayClass> receiver,uint32_t index,Handle<Object> value)14421 static Handle<Object> ExternalArrayIntSetter(
14422 Isolate* isolate,
14423 Handle<ExternalArrayClass> receiver,
14424 uint32_t index,
14425 Handle<Object> value) {
14426 ValueType cast_value = 0;
14427 if (index < static_cast<uint32_t>(receiver->length())) {
14428 if (value->IsSmi()) {
14429 int int_value = Handle<Smi>::cast(value)->value();
14430 cast_value = static_cast<ValueType>(int_value);
14431 } else if (value->IsHeapNumber()) {
14432 double double_value = Handle<HeapNumber>::cast(value)->value();
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.
14437 DCHECK(value->IsUndefined());
14438 }
14439 receiver->set(index, cast_value);
14440 }
14441 return isolate->factory()->NewNumberFromInt(cast_value);
14442 }
14443
14444
SetValue(Handle<ExternalInt8Array> array,uint32_t index,Handle<Object> value)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);
14450 }
14451
14452
SetValue(Handle<ExternalUint8Array> array,uint32_t index,Handle<Object> value)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);
14458 }
14459
14460
SetValue(Handle<ExternalInt16Array> array,uint32_t index,Handle<Object> value)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);
14466 }
14467
14468
SetValue(Handle<ExternalUint16Array> array,uint32_t index,Handle<Object> value)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);
14474 }
14475
14476
SetValue(Handle<ExternalInt32Array> array,uint32_t index,Handle<Object> value)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);
14482 }
14483
14484
SetValue(Handle<ExternalUint32Array> array,uint32_t index,Handle<Object> value)14485 Handle<Object> ExternalUint32Array::SetValue(
14486 Handle<ExternalUint32Array> array,
14487 uint32_t index,
14488 Handle<Object> value) {
14489 uint32_t cast_value = 0;
14490 if (index < static_cast<uint32_t>(array->length())) {
14491 if (value->IsSmi()) {
14492 int int_value = Handle<Smi>::cast(value)->value();
14493 cast_value = static_cast<uint32_t>(int_value);
14494 } else if (value->IsHeapNumber()) {
14495 double double_value = Handle<HeapNumber>::cast(value)->value();
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.
14500 DCHECK(value->IsUndefined());
14501 }
14502 array->set(index, cast_value);
14503 }
14504 return array->GetIsolate()->factory()->NewNumberFromUint(cast_value);
14505 }
14506
14507
SetValue(Handle<ExternalFloat32Array> array,uint32_t index,Handle<Object> value)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())) {
14514 if (value->IsSmi()) {
14515 int int_value = Handle<Smi>::cast(value)->value();
14516 cast_value = static_cast<float>(int_value);
14517 } else if (value->IsHeapNumber()) {
14518 double double_value = Handle<HeapNumber>::cast(value)->value();
14519 cast_value = static_cast<float>(double_value);
14520 } else {
14521 // Clamp undefined to NaN (default). All other types have been
14522 // converted to a number type further up in the call chain.
14523 DCHECK(value->IsUndefined());
14524 }
14525 array->set(index, cast_value);
14526 }
14527 return array->GetIsolate()->factory()->NewNumber(cast_value);
14528 }
14529
14530
SetValue(Handle<ExternalFloat64Array> array,uint32_t index,Handle<Object> value)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();
14539 } else {
14540 // Clamp undefined to NaN (default). All other types have been
14541 // converted to a number type further up in the call chain.
14542 DCHECK(value->IsUndefined());
14543 }
14544 array->set(index, double_value);
14545 }
14546 return array->GetIsolate()->factory()->NewNumber(double_value);
14547 }
14548
14549
EnsurePropertyCell(Handle<JSGlobalObject> global,Handle<Name> name)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);
14560 details = details.AsDeleted();
14561 Handle<NameDictionary> dictionary = NameDictionary::Add(
14562 handle(global->property_dictionary()), name, cell, details);
14563 global->set_properties(*dictionary);
14564 return cell;
14565 } else {
14566 Object* value = global->property_dictionary()->ValueAt(entry);
14567 DCHECK(value->IsPropertyCell());
14568 return handle(PropertyCell::cast(value));
14569 }
14570 }
14571
14572
14573 // This class is used for looking up two character strings in the string table.
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:
TwoCharHashTableKey(uint16_t c1,uint16_t c2,uint32_t seed)14580 TwoCharHashTableKey(uint16_t c1, uint16_t c2, uint32_t seed)
14581 : c1_(c1), c2_(c2) {
14582 // Char 1.
14583 uint32_t hash = seed;
14584 hash += c1;
14585 hash += hash << 10;
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;
14595 if ((hash & String::kHashBitMask) == 0) hash = StringHasher::kZeroHash;
14596 hash_ = hash;
14597 #ifdef DEBUG
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.
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));
14606 #endif
14607 }
14608
IsMatch(Object * o)14609 bool IsMatch(Object* o) OVERRIDE {
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
Hash()14617 uint32_t Hash() OVERRIDE { return hash_; }
HashForObject(Object * key)14618 uint32_t HashForObject(Object* key) OVERRIDE {
14619 if (!key->IsString()) return 0;
14620 return String::cast(key)->Hash();
14621 }
14622
AsHandle(Isolate * isolate)14623 Handle<Object> AsHandle(Isolate* isolate) OVERRIDE {
14624 // The TwoCharHashTableKey is only used for looking in the string
14625 // table, not for adding to it.
14626 UNREACHABLE();
14627 return MaybeHandle<Object>().ToHandleChecked();
14628 }
14629
14630 private:
14631 uint16_t c1_;
14632 uint16_t c2_;
14633 uint32_t hash_;
14634 };
14635
14636
InternalizeStringIfExists(Isolate * isolate,Handle<String> string)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
LookupStringIfExists(Isolate * isolate,Handle<String> string)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);
14653 if (entry == kNotFound) {
14654 return MaybeHandle<String>();
14655 } else {
14656 Handle<String> result(String::cast(string_table->KeyAt(entry)), isolate);
14657 DCHECK(StringShape(*result).IsInternalized());
14658 return result;
14659 }
14660 }
14661
14662
LookupTwoCharsStringIfExists(Isolate * isolate,uint16_t c1,uint16_t c2)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);
14670 if (entry == kNotFound) {
14671 return MaybeHandle<String>();
14672 } else {
14673 Handle<String> result(String::cast(string_table->KeyAt(entry)), isolate);
14674 DCHECK(StringShape(*result).IsInternalized());
14675 return result;
14676 }
14677 }
14678
14679
LookupString(Isolate * isolate,Handle<String> string)14680 Handle<String> StringTable::LookupString(Isolate* isolate,
14681 Handle<String> string) {
14682 InternalizedStringKey key(string);
14683 return LookupKey(isolate, &key);
14684 }
14685
14686
LookupKey(Isolate * isolate,HashTableKey * key)14687 Handle<String> StringTable::LookupKey(Isolate* isolate, HashTableKey* key) {
14688 Handle<StringTable> table = isolate->factory()->string_table();
14689 int entry = table->FindEntry(key);
14690
14691 // String already in table.
14692 if (entry != kNotFound) {
14693 return handle(String::cast(table->KeyAt(entry)), isolate);
14694 }
14695
14696 // Adding new string. Grow table if needed.
14697 table = StringTable::EnsureCapacity(table, 1, key);
14698
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());
14704
14705 // Add the new string and return it along with the string table.
14706 entry = table->FindInsertionEntry(key->Hash());
14707 table->set(EntryToIndex(entry), *string);
14708 table->ElementAdded();
14709
14710 isolate->factory()->set_string_table(table);
14711 return Handle<String>::cast(string);
14712 }
14713
14714
Lookup(Handle<String> src,Handle<Context> context)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);
14721 int entry = FindEntry(&key);
14722 if (entry == kNotFound) return isolate->factory()->undefined_value();
14723 return Handle<Object>(get(EntryToIndex(entry) + 1), isolate);
14724 }
14725
14726
LookupEval(Handle<String> src,Handle<SharedFunctionInfo> outer_info,StrictMode strict_mode,int scope_position)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);
14734 int entry = FindEntry(&key);
14735 if (entry == kNotFound) return isolate->factory()->undefined_value();
14736 return Handle<Object>(get(EntryToIndex(entry) + 1), isolate);
14737 }
14738
14739
LookupRegExp(Handle<String> src,JSRegExp::Flags flags)14740 Handle<Object> CompilationCacheTable::LookupRegExp(Handle<String> src,
14741 JSRegExp::Flags flags) {
14742 Isolate* isolate = GetIsolate();
14743 DisallowHeapAllocation no_allocation;
14744 RegExpKey key(src, flags);
14745 int entry = FindEntry(&key);
14746 if (entry == kNotFound) return isolate->factory()->undefined_value();
14747 return Handle<Object>(get(EntryToIndex(entry) + 1), isolate);
14748 }
14749
14750
Put(Handle<CompilationCacheTable> cache,Handle<String> src,Handle<Context> context,Handle<Object> value)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);
14760 int entry = cache->FindInsertionEntry(key.Hash());
14761 cache->set(EntryToIndex(entry), *k);
14762 cache->set(EntryToIndex(entry) + 1, *value);
14763 cache->ElementAdded();
14764 return cache;
14765 }
14766
14767
PutEval(Handle<CompilationCacheTable> cache,Handle<String> src,Handle<SharedFunctionInfo> outer_info,Handle<SharedFunctionInfo> value,int scope_position)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);
14776 int entry = cache->FindInsertionEntry(key.Hash());
14777 cache->set(EntryToIndex(entry), *k);
14778 cache->set(EntryToIndex(entry) + 1, *value);
14779 cache->ElementAdded();
14780 return cache;
14781 }
14782
14783
PutRegExp(Handle<CompilationCacheTable> cache,Handle<String> src,JSRegExp::Flags flags,Handle<FixedArray> value)14784 Handle<CompilationCacheTable> CompilationCacheTable::PutRegExp(
14785 Handle<CompilationCacheTable> cache, Handle<String> src,
14786 JSRegExp::Flags flags, Handle<FixedArray> value) {
14787 RegExpKey key(src, flags);
14788 cache = EnsureCapacity(cache, 1, &key);
14789 int entry = cache->FindInsertionEntry(key.Hash());
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.
14792 cache->set(EntryToIndex(entry), *value);
14793 cache->set(EntryToIndex(entry) + 1, *value);
14794 cache->ElementAdded();
14795 return cache;
14796 }
14797
14798
Remove(Object * value)14799 void CompilationCacheTable::Remove(Object* value) {
14800 DisallowHeapAllocation no_allocation;
14801 Object* the_hole_value = GetHeap()->the_hole_value();
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) {
14806 NoWriteBarrierSet(this, entry_index, the_hole_value);
14807 NoWriteBarrierSet(this, value_index, the_hole_value);
14808 ElementRemoved();
14809 }
14810 }
14811 return;
14812 }
14813
14814
14815 // StringsKey used for HashTable where key is array of internalized strings.
14816 class StringsKey : public HashTableKey {
14817 public:
StringsKey(Handle<FixedArray> strings)14818 explicit StringsKey(Handle<FixedArray> strings) : strings_(strings) { }
14819
IsMatch(Object * strings)14820 bool IsMatch(Object* strings) OVERRIDE {
14821 FixedArray* o = FixedArray::cast(strings);
14822 int len = strings_->length();
14823 if (o->length() != len) return false;
14824 for (int i = 0; i < len; i++) {
14825 if (o->get(i) != strings_->get(i)) return false;
14826 }
14827 return true;
14828 }
14829
Hash()14830 uint32_t Hash() OVERRIDE { return HashForObject(*strings_); }
14831
HashForObject(Object * obj)14832 uint32_t HashForObject(Object* obj) OVERRIDE {
14833 FixedArray* strings = FixedArray::cast(obj);
14834 int len = strings->length();
14835 uint32_t hash = 0;
14836 for (int i = 0; i < len; i++) {
14837 hash ^= String::cast(strings->get(i))->Hash();
14838 }
14839 return hash;
14840 }
14841
AsHandle(Isolate * isolate)14842 Handle<Object> AsHandle(Isolate* isolate) OVERRIDE { return strings_; }
14843
14844 private:
14845 Handle<FixedArray> strings_;
14846 };
14847
14848
Lookup(FixedArray * array)14849 Object* MapCache::Lookup(FixedArray* array) {
14850 DisallowHeapAllocation no_alloc;
14851 StringsKey key(handle(array));
14852 int entry = FindEntry(&key);
14853 if (entry == kNotFound) return GetHeap()->undefined_value();
14854 return get(EntryToIndex(entry) + 1);
14855 }
14856
14857
Put(Handle<MapCache> map_cache,Handle<FixedArray> array,Handle<Map> value)14858 Handle<MapCache> MapCache::Put(
14859 Handle<MapCache> map_cache, Handle<FixedArray> array, Handle<Map> value) {
14860 StringsKey key(array);
14861
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;
14868 }
14869
14870
14871 template<typename Derived, typename Shape, typename Key>
New(Isolate * isolate,int at_least_space_for,PretenureFlag pretenure)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
14882 // Initialize the next enumeration index.
14883 dict->SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
14884 return dict;
14885 }
14886
14887
14888 template<typename Derived, typename Shape, typename Key>
GenerateNewEnumerationIndices(Handle<Derived> dictionary)14889 void Dictionary<Derived, Shape, Key>::GenerateNewEnumerationIndices(
14890 Handle<Derived> dictionary) {
14891 Factory* factory = dictionary->GetIsolate()->factory();
14892 int length = dictionary->NumberOfElements();
14893
14894 // Allocate and initialize iteration order array.
14895 Handle<FixedArray> iteration_order = factory->NewFixedArray(length);
14896 for (int i = 0; i < length; i++) {
14897 iteration_order->set(i, Smi::FromInt(i));
14898 }
14899
14900 // Allocate array with enumeration order.
14901 Handle<FixedArray> enumeration_order = factory->NewFixedArray(length);
14902
14903 // Fill the enumeration order array with property details.
14904 int capacity = dictionary->Capacity();
14905 int pos = 0;
14906 for (int i = 0; i < capacity; i++) {
14907 if (dictionary->IsKey(dictionary->KeyAt(i))) {
14908 int index = dictionary->DetailsAt(i).dictionary_index();
14909 enumeration_order->set(pos++, Smi::FromInt(index));
14910 }
14911 }
14912
14913 // Sort the arrays wrt. enumeration order.
14914 iteration_order->SortPairs(*enumeration_order, enumeration_order->length());
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;
14920 enumeration_order->set(index, Smi::FromInt(enum_index));
14921 }
14922
14923 // Update the dictionary with new indices.
14924 capacity = dictionary->Capacity();
14925 pos = 0;
14926 for (int i = 0; i < capacity; i++) {
14927 if (dictionary->IsKey(dictionary->KeyAt(i))) {
14928 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
14929 PropertyDetails details = dictionary->DetailsAt(i);
14930 PropertyDetails new_details = PropertyDetails(
14931 details.attributes(), details.type(), enum_index);
14932 dictionary->DetailsAtPut(i, new_details);
14933 }
14934 }
14935
14936 // Set the next enumeration index.
14937 dictionary->SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
14938 }
14939
14940
14941 template<typename Derived, typename Shape, typename Key>
EnsureCapacity(Handle<Derived> dictionary,int n,Key key)14942 Handle<Derived> Dictionary<Derived, Shape, Key>::EnsureCapacity(
14943 Handle<Derived> dictionary, int n, Key key) {
14944 // Check whether there are enough enumeration indices to add n elements.
14945 if (Shape::kIsEnumerable &&
14946 !PropertyDetails::IsValidIndex(dictionary->NextEnumerationIndex() + n)) {
14947 // If not, we generate new indices for the properties.
14948 GenerateNewEnumerationIndices(dictionary);
14949 }
14950 return DerivedHashTable::EnsureCapacity(dictionary, n, key);
14951 }
14952
14953
14954 template<typename Derived, typename Shape, typename Key>
DeleteProperty(Handle<Derived> dictionary,int entry,JSObject::DeleteMode mode)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);
14961 // Ignore attributes if forcing a deletion.
14962 if (!details.IsConfigurable() && mode != JSReceiver::FORCE_DELETION) {
14963 return factory->false_value();
14964 }
14965
14966 dictionary->SetEntry(
14967 entry, factory->the_hole_value(), factory->the_hole_value());
14968 dictionary->ElementRemoved();
14969 return factory->true_value();
14970 }
14971
14972
14973 template<typename Derived, typename Shape, typename Key>
AtPut(Handle<Derived> dictionary,Key key,Handle<Object> value)14974 Handle<Derived> Dictionary<Derived, Shape, Key>::AtPut(
14975 Handle<Derived> dictionary, Key key, Handle<Object> value) {
14976 int entry = dictionary->FindEntry(key);
14977
14978 // If the entry is present set the value;
14979 if (entry != Dictionary::kNotFound) {
14980 dictionary->ValueAtPut(entry, *value);
14981 return dictionary;
14982 }
14983
14984 // Check whether the dictionary should be extended.
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);
14990
14991 AddEntry(dictionary, key, value, details, dictionary->Hash(key));
14992 return dictionary;
14993 }
14994
14995
14996 template<typename Derived, typename Shape, typename Key>
Add(Handle<Derived> dictionary,Key key,Handle<Object> value,PropertyDetails details)14997 Handle<Derived> Dictionary<Derived, Shape, Key>::Add(
14998 Handle<Derived> dictionary,
14999 Key key,
15000 Handle<Object> value,
15001 PropertyDetails details) {
15002 // Valdate key is absent.
15003 SLOW_DCHECK((dictionary->FindEntry(key) == Dictionary::kNotFound));
15004 // Check whether the dictionary should be extended.
15005 dictionary = EnsureCapacity(dictionary, 1, key);
15006
15007 AddEntry(dictionary, key, value, details, dictionary->Hash(key));
15008 return dictionary;
15009 }
15010
15011
15012 // Add a key, value pair to the dictionary.
15013 template<typename Derived, typename Shape, typename Key>
AddEntry(Handle<Derived> dictionary,Key key,Handle<Object> value,PropertyDetails details,uint32_t hash)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) {
15020 // Compute the key object.
15021 Handle<Object> k = Shape::AsHandle(dictionary->GetIsolate(), key);
15022
15023 uint32_t entry = dictionary->FindInsertionEntry(hash);
15024 // Insert element at empty or deleted entry
15025 if (!details.IsDeleted() &&
15026 details.dictionary_index() == 0 &&
15027 Shape::kIsEnumerable) {
15028 // Assign an enumeration index to the property and update
15029 // SetNextEnumerationIndex.
15030 int index = dictionary->NextEnumerationIndex();
15031 details = PropertyDetails(details.attributes(), details.type(), index);
15032 dictionary->SetNextEnumerationIndex(index + 1);
15033 }
15034 dictionary->SetEntry(entry, k, value, details);
15035 DCHECK((dictionary->KeyAt(entry)->IsNumber() ||
15036 dictionary->KeyAt(entry)->IsName()));
15037 dictionary->ElementAdded();
15038 }
15039
15040
UpdateMaxNumberKey(uint32_t key)15041 void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) {
15042 DisallowHeapAllocation no_allocation;
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,
15056 Smi::FromInt(key << kRequiresSlowElementsTagSize));
15057 }
15058 }
15059
15060
AddNumberEntry(Handle<SeededNumberDictionary> dictionary,uint32_t key,Handle<Object> value,PropertyDetails details)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);
15069 }
15070
15071
AddNumberEntry(Handle<UnseededNumberDictionary> dictionary,uint32_t key,Handle<Object> value)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));
15078 }
15079
15080
AtNumberPut(Handle<SeededNumberDictionary> dictionary,uint32_t key,Handle<Object> value)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);
15087 }
15088
15089
AtNumberPut(Handle<UnseededNumberDictionary> dictionary,uint32_t key,Handle<Object> value)15090 Handle<UnseededNumberDictionary> UnseededNumberDictionary::AtNumberPut(
15091 Handle<UnseededNumberDictionary> dictionary,
15092 uint32_t key,
15093 Handle<Object> value) {
15094 return AtPut(dictionary, key, value);
15095 }
15096
15097
Set(Handle<SeededNumberDictionary> dictionary,uint32_t key,Handle<Object> value,PropertyDetails details)15098 Handle<SeededNumberDictionary> SeededNumberDictionary::Set(
15099 Handle<SeededNumberDictionary> dictionary,
15100 uint32_t key,
15101 Handle<Object> value,
15102 PropertyDetails details) {
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;
15115 }
15116
15117
Set(Handle<UnseededNumberDictionary> dictionary,uint32_t key,Handle<Object> value)15118 Handle<UnseededNumberDictionary> UnseededNumberDictionary::Set(
15119 Handle<UnseededNumberDictionary> dictionary,
15120 uint32_t key,
15121 Handle<Object> value) {
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;
15128 }
15129
15130
15131
15132 template<typename Derived, typename Shape, typename Key>
NumberOfElementsFilterAttributes(PropertyAttributes filter)15133 int Dictionary<Derived, Shape, Key>::NumberOfElementsFilterAttributes(
15134 PropertyAttributes filter) {
15135 int capacity = DerivedHashTable::Capacity();
15136 int result = 0;
15137 for (int i = 0; i < capacity; i++) {
15138 Object* k = DerivedHashTable::KeyAt(i);
15139 if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) {
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
15150 template<typename Derived, typename Shape, typename Key>
NumberOfEnumElements()15151 int Dictionary<Derived, Shape, Key>::NumberOfEnumElements() {
15152 return NumberOfElementsFilterAttributes(
15153 static_cast<PropertyAttributes>(DONT_ENUM | SYMBOLIC));
15154 }
15155
15156
15157 template<typename Derived, typename Shape, typename Key>
CopyKeysTo(FixedArray * storage,PropertyAttributes filter,typename Dictionary<Derived,Shape,Key>::SortMode sort_mode)15158 void Dictionary<Derived, Shape, Key>::CopyKeysTo(
15159 FixedArray* storage,
15160 PropertyAttributes filter,
15161 typename Dictionary<Derived, Shape, Key>::SortMode sort_mode) {
15162 DCHECK(storage->length() >= NumberOfElementsFilterAttributes(filter));
15163 int capacity = DerivedHashTable::Capacity();
15164 int index = 0;
15165 for (int i = 0; i < capacity; i++) {
15166 Object* k = DerivedHashTable::KeyAt(i);
15167 if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) {
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 }
15174 if (sort_mode == Dictionary::SORTED) {
15175 storage->SortPairs(storage, index);
15176 }
15177 DCHECK(storage->length() >= index);
15178 }
15179
15180
15181 struct EnumIndexComparator {
EnumIndexComparatorv8::internal::EnumIndexComparator15182 explicit EnumIndexComparator(NameDictionary* dict) : dict(dict) { }
operator ()v8::internal::EnumIndexComparator15183 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
CopyEnumKeysTo(FixedArray * storage)15192 void NameDictionary::CopyEnumKeysTo(FixedArray* storage) {
15193 int length = storage->length();
15194 int capacity = Capacity();
15195 int properties = 0;
15196 for (int i = 0; i < capacity; i++) {
15197 Object* k = KeyAt(i);
15198 if (IsKey(k) && !k->IsSymbol()) {
15199 PropertyDetails details = DetailsAt(i);
15200 if (details.IsDeleted() || details.IsDontEnum()) continue;
15201 storage->set(properties, Smi::FromInt(i));
15202 properties++;
15203 if (properties == length) break;
15204 }
15205 }
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 }
15214 }
15215
15216
15217 template<typename Derived, typename Shape, typename Key>
CopyKeysTo(FixedArray * storage,int index,PropertyAttributes filter,typename Dictionary<Derived,Shape,Key>::SortMode sort_mode)15218 void Dictionary<Derived, Shape, Key>::CopyKeysTo(
15219 FixedArray* storage,
15220 int index,
15221 PropertyAttributes filter,
15222 typename Dictionary<Derived, Shape, Key>::SortMode sort_mode) {
15223 DCHECK(storage->length() >= NumberOfElementsFilterAttributes(filter));
15224 int capacity = DerivedHashTable::Capacity();
15225 for (int i = 0; i < capacity; i++) {
15226 Object* k = DerivedHashTable::KeyAt(i);
15227 if (DerivedHashTable::IsKey(k) && !FilterKey(k, filter)) {
15228 PropertyDetails details = DetailsAt(i);
15229 if (details.IsDeleted()) continue;
15230 PropertyAttributes attr = details.attributes();
15231 if ((attr & filter) == 0) storage->set(index++, k);
15232 }
15233 }
15234 if (sort_mode == Dictionary::SORTED) {
15235 storage->SortPairs(storage, index);
15236 }
15237 DCHECK(storage->length() >= index);
15238 }
15239
15240
15241 // Backwards lookup (slow).
15242 template<typename Derived, typename Shape, typename Key>
SlowReverseLookup(Object * value)15243 Object* Dictionary<Derived, Shape, Key>::SlowReverseLookup(Object* value) {
15244 int capacity = DerivedHashTable::Capacity();
15245 for (int i = 0; i < capacity; i++) {
15246 Object* k = DerivedHashTable::KeyAt(i);
15247 if (Dictionary::IsKey(k)) {
15248 Object* e = ValueAt(i);
15249 if (e->IsPropertyCell()) {
15250 e = PropertyCell::cast(e)->value();
15251 }
15252 if (e == value) return k;
15253 }
15254 }
15255 Heap* heap = Dictionary::GetHeap();
15256 return heap->undefined_value();
15257 }
15258
15259
Lookup(Handle<Object> key)15260 Object* ObjectHashTable::Lookup(Handle<Object> key) {
15261 DisallowHeapAllocation no_gc;
15262 DCHECK(IsKey(*key));
15263
15264 // If the object does not have an identity hash, it was never used as a key.
15265 Object* hash = key->GetHash();
15266 if (hash->IsUndefined()) {
15267 return GetHeap()->the_hole_value();
15268 }
15269 int entry = FindEntry(key);
15270 if (entry == kNotFound) return GetHeap()->the_hole_value();
15271 return get(EntryToIndex(entry) + 1);
15272 }
15273
15274
Put(Handle<ObjectHashTable> table,Handle<Object> key,Handle<Object> value)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();
15282
15283 // Make sure the key object has an identity hash code.
15284 Handle<Smi> hash = Object::GetOrCreateHash(isolate, key);
15285
15286 int entry = table->FindEntry(key);
15287
15288 // Key is already in table, just overwrite value.
15289 if (entry != kNotFound) {
15290 table->set(EntryToIndex(entry) + 1, *value);
15291 return table;
15292 }
15293
15294 // Check whether the hash table should be extended.
15295 table = EnsureCapacity(table, 1, key);
15296 table->AddEntry(table->FindInsertionEntry(hash->value()),
15297 *key,
15298 *value);
15299 return table;
15300 }
15301
15302
Remove(Handle<ObjectHashTable> table,Handle<Object> key,bool * was_present)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
AddEntry(int entry,Object * key,Object * value)15326 void ObjectHashTable::AddEntry(int entry, Object* key, Object* value) {
15327 set(EntryToIndex(entry), key);
15328 set(EntryToIndex(entry) + 1, value);
15329 ElementAdded();
15330 }
15331
15332
RemoveEntry(int entry)15333 void ObjectHashTable::RemoveEntry(int entry) {
15334 set_the_hole(EntryToIndex(entry));
15335 set_the_hole(EntryToIndex(entry) + 1);
15336 ElementRemoved();
15337 }
15338
15339
Lookup(Handle<Object> key)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
Put(Handle<WeakHashTable> table,Handle<Object> key,Handle<Object> value)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
AddEntry(int entry,Handle<Object> key,Handle<Object> value)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>
Allocate(Isolate * isolate,int capacity,PretenureFlag pretenure)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>
EnsureGrowable(Handle<Derived> table)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>
Shrink(Handle<Derived> table)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>
Clear(Handle<Derived> table)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>
Remove(Handle<Derived> table,Handle<Object> key,bool * was_present)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>
Rehash(Handle<Derived> table,int new_capacity)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>
FindEntry(Handle<Object> key,int hash)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>
FindEntry(Handle<Object> key)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>
AddEntry(int hash)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>
RemoveEntry(int entry)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
Contains(Handle<Object> key)15635 bool OrderedHashSet::Contains(Handle<Object> key) {
15636 return FindEntry(key) != kNotFound;
15637 }
15638
15639
Add(Handle<OrderedHashSet> table,Handle<Object> key)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
Lookup(Handle<Object> key)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
Put(Handle<OrderedHashMap> table,Handle<Object> key,Handle<Object> value)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>
Transition()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>
HasMore()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>
Next(JSArray * value_array)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
DeclaredAccessorDescriptorIterator(DeclaredAccessorDescriptor * descriptor)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*
Next()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
Create(Isolate * isolate,const DeclaredAccessorDescriptorData & descriptor,Handle<DeclaredAccessorDescriptor> previous)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
15840 // Check if there is a break point at this code position.
HasBreakPoint(int 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.
GetBreakPointInfo(int 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.
15858 if (index == kNoBreakPointInfo) return GetHeap()->undefined_value();
15859 return BreakPointInfo::cast(break_points()->get(index));
15860 }
15861
15862
15863 // Clear a break point at the specified code position.
ClearBreakPoint(Handle<DebugInfo> debug_info,int code_position,Handle<Object> break_point_object)15864 void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
15865 int code_position,
15866 Handle<Object> break_point_object) {
15867 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position),
15868 debug_info->GetIsolate());
15869 if (break_point_info->IsUndefined()) return;
15870 BreakPointInfo::ClearBreakPoint(
15871 Handle<BreakPointInfo>::cast(break_point_info),
15872 break_point_object);
15873 }
15874
15875
SetBreakPoint(Handle<DebugInfo> debug_info,int code_position,int source_position,int statement_position,Handle<Object> break_point_object)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) {
15881 Isolate* isolate = debug_info->GetIsolate();
15882 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position),
15883 isolate);
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()));
15904 Handle<FixedArray> new_break_points =
15905 isolate->factory()->NewFixedArray(
15906 old_break_points->length() +
15907 DebugInfo::kEstimatedNofBreakPointsInFunction);
15908
15909 debug_info->set_break_points(*new_break_points);
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 }
15915 DCHECK(index != kNoBreakPointInfo);
15916
15917 // Allocate new BreakPointInfo object and set the break point.
15918 Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast(
15919 isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE));
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));
15924 new_break_point_info->set_break_point_objects(
15925 isolate->heap()->undefined_value());
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.
GetBreakPointObjects(int code_position)15932 Object* DebugInfo::GetBreakPointObjects(int code_position) {
15933 Object* break_point_info = GetBreakPointInfo(code_position);
15934 if (break_point_info->IsUndefined()) {
15935 return GetHeap()->undefined_value();
15936 }
15937 return BreakPointInfo::cast(break_point_info)->break_point_objects();
15938 }
15939
15940
15941 // Get the total number of break points.
GetBreakPointCount()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
FindBreakPointInfo(Handle<DebugInfo> debug_info,Handle<Object> break_point_object)15956 Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
15957 Handle<Object> break_point_object) {
15958 Heap* heap = debug_info->GetHeap();
15959 if (debug_info->break_points()->IsUndefined()) return heap->undefined_value();
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 }
15971 return heap->undefined_value();
15972 }
15973
15974
15975 // Find the index of the break point info object for the specified code
15976 // position.
GetBreakPointInfoIndex(int code_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.
ClearBreakPoint(Handle<BreakPointInfo> break_point_info,Handle<Object> break_point_object)15993 void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
15994 Handle<Object> break_point_object) {
15995 Isolate* isolate = break_point_info->GetIsolate();
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) {
16001 break_point_info->set_break_point_objects(
16002 isolate->heap()->undefined_value());
16003 }
16004 return;
16005 }
16006 // If there are multiple break points shrink the array
16007 DCHECK(break_point_info->break_point_objects()->IsFixedArray());
16008 Handle<FixedArray> old_array =
16009 Handle<FixedArray>(
16010 FixedArray::cast(break_point_info->break_point_objects()));
16011 Handle<FixedArray> new_array =
16012 isolate->factory()->NewFixedArray(old_array->length() - 1);
16013 int found_count = 0;
16014 for (int i = 0; i < old_array->length(); i++) {
16015 if (old_array->get(i) == *break_point_object) {
16016 DCHECK(found_count == 0);
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.
SetBreakPoint(Handle<BreakPointInfo> break_point_info,Handle<Object> break_point_object)16028 void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
16029 Handle<Object> break_point_object) {
16030 Isolate* isolate = break_point_info->GetIsolate();
16031
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()) {
16041 Handle<FixedArray> array = isolate->factory()->NewFixedArray(2);
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 =
16052 isolate->factory()->NewFixedArray(old_array->length() + 1);
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
HasBreakPointObject(Handle<BreakPointInfo> break_point_info,Handle<Object> break_point_object)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;
16069 // Single break point.
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.
GetBreakPointCount()16085 int BreakPointInfo::GetBreakPointCount() {
16086 // No break point.
16087 if (break_point_objects()->IsUndefined()) return 0;
16088 // Single break point.
16089 if (!break_point_objects()->IsFixedArray()) return 1;
16090 // Multiple break points.
16091 return FixedArray::cast(break_point_objects())->length();
16092 }
16093
16094
GetField(Object * object,Smi * index)16095 Object* JSDate::GetField(Object* object, Smi* index) {
16096 return JSDate::cast(object)->DoGetField(
16097 static_cast<FieldIndex>(index->value()));
16098 }
16099
16100
DoGetField(FieldIndex index)16101 Object* JSDate::DoGetField(FieldIndex index) {
16102 DCHECK(index != kDateValue);
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()));
16112 SetCachedFields(local_time_ms, date_cache);
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();
16131 if (std::isnan(time)) return GetIsolate()->heap()->nan_value();
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);
16140 DCHECK(index == kTimeInDay);
16141 return Smi::FromInt(time_in_day_ms);
16142 }
16143
16144
GetUTCField(FieldIndex index,double value,DateCache * date_cache)16145 Object* JSDate::GetUTCField(FieldIndex index,
16146 double value,
16147 DateCache* date_cache) {
16148 DCHECK(index >= kFirstUTCField);
16149
16150 if (std::isnan(value)) return GetIsolate()->heap()->nan_value();
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);
16167 DCHECK(index == kDayUTC);
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
SetValue(Object * value,bool is_value_nan)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
SetCachedFields(int64_t local_time_ms,DateCache * date_cache)16205 void JSDate::SetCachedFields(int64_t local_time_ms, DateCache* date_cache) {
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
16224
Neuter()16225 void JSArrayBuffer::Neuter() {
16226 DCHECK(is_external());
16227 set_backing_store(NULL);
16228 set_byte_length(Smi::FromInt(0));
16229 }
16230
16231
NeuterView()16232 void JSArrayBufferView::NeuterView() {
16233 set_byte_offset(Smi::FromInt(0));
16234 set_byte_length(Smi::FromInt(0));
16235 }
16236
16237
Neuter()16238 void JSDataView::Neuter() {
16239 NeuterView();
16240 }
16241
16242
Neuter()16243 void JSTypedArray::Neuter() {
16244 NeuterView();
16245 set_length(Smi::FromInt(0));
16246 set_elements(GetHeap()->EmptyExternalArrayForMap(map()));
16247 }
16248
16249
FixedToExternalElementsKind(ElementsKind elements_kind)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
MaterializeArrayBuffer(Handle<JSTypedArray> typed_array)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
GetBuffer()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
type()16310 HeapType* PropertyCell::type() {
16311 return static_cast<HeapType*>(type_raw());
16312 }
16313
16314
set_type(HeapType * type,WriteBarrierMode ignored)16315 void PropertyCell::set_type(HeapType* type, WriteBarrierMode ignored) {
16316 DCHECK(IsPropertyCell());
16317 set_type_raw(type, ignored);
16318 }
16319
16320
UpdatedType(Handle<PropertyCell> cell,Handle<Object> value)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
SetValueInferType(Handle<PropertyCell> cell,Handle<Object> value)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
AddDependentCompilationInfo(Handle<PropertyCell> cell,CompilationInfo * info)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
16362 } } // namespace v8::internal
16363