1 // Copyright 2006-2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "v8.h"
29
30 #include "api.h"
31 #include "bootstrapper.h"
32 #include "debug.h"
33 #include "execution.h"
34 #include "objects-inl.h"
35 #include "macro-assembler.h"
36 #include "scanner.h"
37 #include "scopeinfo.h"
38 #include "string-stream.h"
39
40 #ifdef ENABLE_DISASSEMBLER
41 #include "disassembler.h"
42 #endif
43
44
45 namespace v8 {
46 namespace internal {
47
48 // Getters and setters are stored in a fixed array property. These are
49 // constants for their indices.
50 const int kGetterIndex = 0;
51 const int kSetterIndex = 1;
52
53
CreateJSValue(JSFunction * constructor,Object * value)54 static Object* CreateJSValue(JSFunction* constructor, Object* value) {
55 Object* result = Heap::AllocateJSObject(constructor);
56 if (result->IsFailure()) return result;
57 JSValue::cast(result)->set_value(value);
58 return result;
59 }
60
61
ToObject(Context * global_context)62 Object* Object::ToObject(Context* global_context) {
63 if (IsNumber()) {
64 return CreateJSValue(global_context->number_function(), this);
65 } else if (IsBoolean()) {
66 return CreateJSValue(global_context->boolean_function(), this);
67 } else if (IsString()) {
68 return CreateJSValue(global_context->string_function(), this);
69 }
70 ASSERT(IsJSObject());
71 return this;
72 }
73
74
ToObject()75 Object* Object::ToObject() {
76 Context* global_context = Top::context()->global_context();
77 if (IsJSObject()) {
78 return this;
79 } else if (IsNumber()) {
80 return CreateJSValue(global_context->number_function(), this);
81 } else if (IsBoolean()) {
82 return CreateJSValue(global_context->boolean_function(), this);
83 } else if (IsString()) {
84 return CreateJSValue(global_context->string_function(), this);
85 }
86
87 // Throw a type error.
88 return Failure::InternalError();
89 }
90
91
ToBoolean()92 Object* Object::ToBoolean() {
93 if (IsTrue()) return Heap::true_value();
94 if (IsFalse()) return Heap::false_value();
95 if (IsSmi()) {
96 return Heap::ToBoolean(Smi::cast(this)->value() != 0);
97 }
98 if (IsUndefined() || IsNull()) return Heap::false_value();
99 // Undetectable object is false
100 if (IsUndetectableObject()) {
101 return Heap::false_value();
102 }
103 if (IsString()) {
104 return Heap::ToBoolean(String::cast(this)->length() != 0);
105 }
106 if (IsHeapNumber()) {
107 return HeapNumber::cast(this)->HeapNumberToBoolean();
108 }
109 return Heap::true_value();
110 }
111
112
Lookup(String * name,LookupResult * result)113 void Object::Lookup(String* name, LookupResult* result) {
114 if (IsJSObject()) return JSObject::cast(this)->Lookup(name, result);
115 Object* holder = NULL;
116 Context* global_context = Top::context()->global_context();
117 if (IsString()) {
118 holder = global_context->string_function()->instance_prototype();
119 } else if (IsNumber()) {
120 holder = global_context->number_function()->instance_prototype();
121 } else if (IsBoolean()) {
122 holder = global_context->boolean_function()->instance_prototype();
123 }
124 ASSERT(holder != NULL); // Cannot handle null or undefined.
125 JSObject::cast(holder)->Lookup(name, result);
126 }
127
128
GetPropertyWithReceiver(Object * receiver,String * name,PropertyAttributes * attributes)129 Object* Object::GetPropertyWithReceiver(Object* receiver,
130 String* name,
131 PropertyAttributes* attributes) {
132 LookupResult result;
133 Lookup(name, &result);
134 Object* value = GetProperty(receiver, &result, name, attributes);
135 ASSERT(*attributes <= ABSENT);
136 return value;
137 }
138
139
GetPropertyWithCallback(Object * receiver,Object * structure,String * name,Object * holder)140 Object* Object::GetPropertyWithCallback(Object* receiver,
141 Object* structure,
142 String* name,
143 Object* holder) {
144 // To accommodate both the old and the new api we switch on the
145 // data structure used to store the callbacks. Eventually proxy
146 // callbacks should be phased out.
147 if (structure->IsProxy()) {
148 AccessorDescriptor* callback =
149 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
150 Object* value = (callback->getter)(receiver, callback->data);
151 RETURN_IF_SCHEDULED_EXCEPTION();
152 return value;
153 }
154
155 // api style callbacks.
156 if (structure->IsAccessorInfo()) {
157 AccessorInfo* data = AccessorInfo::cast(structure);
158 Object* fun_obj = data->getter();
159 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
160 HandleScope scope;
161 Handle<JSObject> self(JSObject::cast(receiver));
162 Handle<JSObject> holder_handle(JSObject::cast(holder));
163 Handle<String> key(name);
164 Handle<Object> fun_data(data->data());
165 LOG(ApiNamedPropertyAccess("load", *self, name));
166 v8::AccessorInfo info(v8::Utils::ToLocal(self),
167 v8::Utils::ToLocal(fun_data),
168 v8::Utils::ToLocal(holder_handle));
169 v8::Handle<v8::Value> result;
170 {
171 // Leaving JavaScript.
172 VMState state(EXTERNAL);
173 result = call_fun(v8::Utils::ToLocal(key), info);
174 }
175 RETURN_IF_SCHEDULED_EXCEPTION();
176 if (result.IsEmpty()) return Heap::undefined_value();
177 return *v8::Utils::OpenHandle(*result);
178 }
179
180 // __defineGetter__ callback
181 if (structure->IsFixedArray()) {
182 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
183 if (getter->IsJSFunction()) {
184 return Object::GetPropertyWithDefinedGetter(receiver,
185 JSFunction::cast(getter));
186 }
187 // Getter is not a function.
188 return Heap::undefined_value();
189 }
190
191 UNREACHABLE();
192 return 0;
193 }
194
195
GetPropertyWithDefinedGetter(Object * receiver,JSFunction * getter)196 Object* Object::GetPropertyWithDefinedGetter(Object* receiver,
197 JSFunction* getter) {
198 HandleScope scope;
199 Handle<JSFunction> fun(JSFunction::cast(getter));
200 Handle<Object> self(receiver);
201 #ifdef ENABLE_DEBUGGER_SUPPORT
202 // Handle stepping into a getter if step into is active.
203 if (Debug::StepInActive()) {
204 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
205 }
206 #endif
207 bool has_pending_exception;
208 Handle<Object> result =
209 Execution::Call(fun, self, 0, NULL, &has_pending_exception);
210 // Check for pending exception and return the result.
211 if (has_pending_exception) return Failure::Exception();
212 return *result;
213 }
214
215
216 // Only deal with CALLBACKS and INTERCEPTOR
GetPropertyWithFailedAccessCheck(Object * receiver,LookupResult * result,String * name,PropertyAttributes * attributes)217 Object* JSObject::GetPropertyWithFailedAccessCheck(
218 Object* receiver,
219 LookupResult* result,
220 String* name,
221 PropertyAttributes* attributes) {
222 if (result->IsValid()) {
223 switch (result->type()) {
224 case CALLBACKS: {
225 // Only allow API accessors.
226 Object* obj = result->GetCallbackObject();
227 if (obj->IsAccessorInfo()) {
228 AccessorInfo* info = AccessorInfo::cast(obj);
229 if (info->all_can_read()) {
230 *attributes = result->GetAttributes();
231 return GetPropertyWithCallback(receiver,
232 result->GetCallbackObject(),
233 name,
234 result->holder());
235 }
236 }
237 break;
238 }
239 case NORMAL:
240 case FIELD:
241 case CONSTANT_FUNCTION: {
242 // Search ALL_CAN_READ accessors in prototype chain.
243 LookupResult r;
244 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
245 if (r.IsValid()) {
246 return GetPropertyWithFailedAccessCheck(receiver,
247 &r,
248 name,
249 attributes);
250 }
251 break;
252 }
253 case INTERCEPTOR: {
254 // If the object has an interceptor, try real named properties.
255 // No access check in GetPropertyAttributeWithInterceptor.
256 LookupResult r;
257 result->holder()->LookupRealNamedProperty(name, &r);
258 if (r.IsValid()) {
259 return GetPropertyWithFailedAccessCheck(receiver,
260 &r,
261 name,
262 attributes);
263 }
264 }
265 default: {
266 break;
267 }
268 }
269 }
270
271 // No accessible property found.
272 *attributes = ABSENT;
273 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
274 return Heap::undefined_value();
275 }
276
277
GetPropertyAttributeWithFailedAccessCheck(Object * receiver,LookupResult * result,String * name,bool continue_search)278 PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
279 Object* receiver,
280 LookupResult* result,
281 String* name,
282 bool continue_search) {
283 if (result->IsValid()) {
284 switch (result->type()) {
285 case CALLBACKS: {
286 // Only allow API accessors.
287 Object* obj = result->GetCallbackObject();
288 if (obj->IsAccessorInfo()) {
289 AccessorInfo* info = AccessorInfo::cast(obj);
290 if (info->all_can_read()) {
291 return result->GetAttributes();
292 }
293 }
294 break;
295 }
296
297 case NORMAL:
298 case FIELD:
299 case CONSTANT_FUNCTION: {
300 if (!continue_search) break;
301 // Search ALL_CAN_READ accessors in prototype chain.
302 LookupResult r;
303 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
304 if (r.IsValid()) {
305 return GetPropertyAttributeWithFailedAccessCheck(receiver,
306 &r,
307 name,
308 continue_search);
309 }
310 break;
311 }
312
313 case INTERCEPTOR: {
314 // If the object has an interceptor, try real named properties.
315 // No access check in GetPropertyAttributeWithInterceptor.
316 LookupResult r;
317 if (continue_search) {
318 result->holder()->LookupRealNamedProperty(name, &r);
319 } else {
320 result->holder()->LocalLookupRealNamedProperty(name, &r);
321 }
322 if (r.IsValid()) {
323 return GetPropertyAttributeWithFailedAccessCheck(receiver,
324 &r,
325 name,
326 continue_search);
327 }
328 break;
329 }
330
331 default: {
332 break;
333 }
334 }
335 }
336
337 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
338 return ABSENT;
339 }
340
341
GetLazyProperty(Object * receiver,LookupResult * result,String * name,PropertyAttributes * attributes)342 Object* JSObject::GetLazyProperty(Object* receiver,
343 LookupResult* result,
344 String* name,
345 PropertyAttributes* attributes) {
346 HandleScope scope;
347 Handle<Object> this_handle(this);
348 Handle<Object> receiver_handle(receiver);
349 Handle<String> name_handle(name);
350 bool pending_exception;
351 LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
352 &pending_exception);
353 if (pending_exception) return Failure::Exception();
354 return this_handle->GetPropertyWithReceiver(*receiver_handle,
355 *name_handle,
356 attributes);
357 }
358
359
SetLazyProperty(LookupResult * result,String * name,Object * value,PropertyAttributes attributes)360 Object* JSObject::SetLazyProperty(LookupResult* result,
361 String* name,
362 Object* value,
363 PropertyAttributes attributes) {
364 ASSERT(!IsJSGlobalProxy());
365 HandleScope scope;
366 Handle<JSObject> this_handle(this);
367 Handle<String> name_handle(name);
368 Handle<Object> value_handle(value);
369 bool pending_exception;
370 LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
371 &pending_exception);
372 if (pending_exception) return Failure::Exception();
373 return this_handle->SetProperty(*name_handle, *value_handle, attributes);
374 }
375
376
DeleteLazyProperty(LookupResult * result,String * name,DeleteMode mode)377 Object* JSObject::DeleteLazyProperty(LookupResult* result,
378 String* name,
379 DeleteMode mode) {
380 HandleScope scope;
381 Handle<JSObject> this_handle(this);
382 Handle<String> name_handle(name);
383 bool pending_exception;
384 LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
385 &pending_exception);
386 if (pending_exception) return Failure::Exception();
387 return this_handle->DeleteProperty(*name_handle, mode);
388 }
389
390
GetNormalizedProperty(LookupResult * result)391 Object* JSObject::GetNormalizedProperty(LookupResult* result) {
392 ASSERT(!HasFastProperties());
393 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
394 if (IsGlobalObject()) {
395 value = JSGlobalPropertyCell::cast(value)->value();
396 }
397 ASSERT(!value->IsJSGlobalPropertyCell());
398 return value;
399 }
400
401
SetNormalizedProperty(LookupResult * result,Object * value)402 Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) {
403 ASSERT(!HasFastProperties());
404 if (IsGlobalObject()) {
405 JSGlobalPropertyCell* cell =
406 JSGlobalPropertyCell::cast(
407 property_dictionary()->ValueAt(result->GetDictionaryEntry()));
408 cell->set_value(value);
409 } else {
410 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
411 }
412 return value;
413 }
414
415
SetNormalizedProperty(String * name,Object * value,PropertyDetails details)416 Object* JSObject::SetNormalizedProperty(String* name,
417 Object* value,
418 PropertyDetails details) {
419 ASSERT(!HasFastProperties());
420 int entry = property_dictionary()->FindEntry(name);
421 if (entry == StringDictionary::kNotFound) {
422 Object* store_value = value;
423 if (IsGlobalObject()) {
424 store_value = Heap::AllocateJSGlobalPropertyCell(value);
425 if (store_value->IsFailure()) return store_value;
426 }
427 Object* dict = property_dictionary()->Add(name, store_value, details);
428 if (dict->IsFailure()) return dict;
429 set_properties(StringDictionary::cast(dict));
430 return value;
431 }
432 // Preserve enumeration index.
433 details = PropertyDetails(details.attributes(),
434 details.type(),
435 property_dictionary()->DetailsAt(entry).index());
436 if (IsGlobalObject()) {
437 JSGlobalPropertyCell* cell =
438 JSGlobalPropertyCell::cast(property_dictionary()->ValueAt(entry));
439 cell->set_value(value);
440 // Please note we have to update the property details.
441 property_dictionary()->DetailsAtPut(entry, details);
442 } else {
443 property_dictionary()->SetEntry(entry, name, value, details);
444 }
445 return value;
446 }
447
448
DeleteNormalizedProperty(String * name,DeleteMode mode)449 Object* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) {
450 ASSERT(!HasFastProperties());
451 StringDictionary* dictionary = property_dictionary();
452 int entry = dictionary->FindEntry(name);
453 if (entry != StringDictionary::kNotFound) {
454 // If we have a global object set the cell to the hole.
455 if (IsGlobalObject()) {
456 PropertyDetails details = dictionary->DetailsAt(entry);
457 if (details.IsDontDelete()) {
458 if (mode != FORCE_DELETION) return Heap::false_value();
459 // When forced to delete global properties, we have to make a
460 // map change to invalidate any ICs that think they can load
461 // from the DontDelete cell without checking if it contains
462 // the hole value.
463 Object* new_map = map()->CopyDropDescriptors();
464 if (new_map->IsFailure()) return new_map;
465 set_map(Map::cast(new_map));
466 }
467 JSGlobalPropertyCell* cell =
468 JSGlobalPropertyCell::cast(dictionary->ValueAt(entry));
469 cell->set_value(Heap::the_hole_value());
470 dictionary->DetailsAtPut(entry, details.AsDeleted());
471 } else {
472 return dictionary->DeleteProperty(entry, mode);
473 }
474 }
475 return Heap::true_value();
476 }
477
478
GetProperty(Object * receiver,LookupResult * result,String * name,PropertyAttributes * attributes)479 Object* Object::GetProperty(Object* receiver,
480 LookupResult* result,
481 String* name,
482 PropertyAttributes* attributes) {
483 // Make sure that the top context does not change when doing
484 // callbacks or interceptor calls.
485 AssertNoContextChange ncc;
486
487 // Traverse the prototype chain from the current object (this) to
488 // the holder and check for access rights. This avoid traversing the
489 // objects more than once in case of interceptors, because the
490 // holder will always be the interceptor holder and the search may
491 // only continue with a current object just after the interceptor
492 // holder in the prototype chain.
493 Object* last = result->IsValid() ? result->holder() : Heap::null_value();
494 for (Object* current = this; true; current = current->GetPrototype()) {
495 if (current->IsAccessCheckNeeded()) {
496 // Check if we're allowed to read from the current object. Note
497 // that even though we may not actually end up loading the named
498 // property from the current object, we still check that we have
499 // access to it.
500 JSObject* checked = JSObject::cast(current);
501 if (!Top::MayNamedAccess(checked, name, v8::ACCESS_GET)) {
502 return checked->GetPropertyWithFailedAccessCheck(receiver,
503 result,
504 name,
505 attributes);
506 }
507 }
508 // Stop traversing the chain once we reach the last object in the
509 // chain; either the holder of the result or null in case of an
510 // absent property.
511 if (current == last) break;
512 }
513
514 if (!result->IsProperty()) {
515 *attributes = ABSENT;
516 return Heap::undefined_value();
517 }
518 *attributes = result->GetAttributes();
519 if (!result->IsLoaded()) {
520 return JSObject::cast(this)->GetLazyProperty(receiver,
521 result,
522 name,
523 attributes);
524 }
525 Object* value;
526 JSObject* holder = result->holder();
527 switch (result->type()) {
528 case NORMAL:
529 value = holder->GetNormalizedProperty(result);
530 ASSERT(!value->IsTheHole() || result->IsReadOnly());
531 return value->IsTheHole() ? Heap::undefined_value() : value;
532 case FIELD:
533 value = holder->FastPropertyAt(result->GetFieldIndex());
534 ASSERT(!value->IsTheHole() || result->IsReadOnly());
535 return value->IsTheHole() ? Heap::undefined_value() : value;
536 case CONSTANT_FUNCTION:
537 return result->GetConstantFunction();
538 case CALLBACKS:
539 return GetPropertyWithCallback(receiver,
540 result->GetCallbackObject(),
541 name,
542 holder);
543 case INTERCEPTOR: {
544 JSObject* recvr = JSObject::cast(receiver);
545 return holder->GetPropertyWithInterceptor(recvr, name, attributes);
546 }
547 default:
548 UNREACHABLE();
549 return NULL;
550 }
551 }
552
553
GetElementWithReceiver(Object * receiver,uint32_t index)554 Object* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
555 // Non-JS objects do not have integer indexed properties.
556 if (!IsJSObject()) return Heap::undefined_value();
557 return JSObject::cast(this)->GetElementWithReceiver(JSObject::cast(receiver),
558 index);
559 }
560
561
GetPrototype()562 Object* Object::GetPrototype() {
563 // The object is either a number, a string, a boolean, or a real JS object.
564 if (IsJSObject()) return JSObject::cast(this)->map()->prototype();
565 Context* context = Top::context()->global_context();
566
567 if (IsNumber()) return context->number_function()->instance_prototype();
568 if (IsString()) return context->string_function()->instance_prototype();
569 if (IsBoolean()) {
570 return context->boolean_function()->instance_prototype();
571 } else {
572 return Heap::null_value();
573 }
574 }
575
576
ShortPrint()577 void Object::ShortPrint() {
578 HeapStringAllocator allocator;
579 StringStream accumulator(&allocator);
580 ShortPrint(&accumulator);
581 accumulator.OutputToStdOut();
582 }
583
584
ShortPrint(StringStream * accumulator)585 void Object::ShortPrint(StringStream* accumulator) {
586 if (IsSmi()) {
587 Smi::cast(this)->SmiPrint(accumulator);
588 } else if (IsFailure()) {
589 Failure::cast(this)->FailurePrint(accumulator);
590 } else {
591 HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
592 }
593 }
594
595
SmiPrint()596 void Smi::SmiPrint() {
597 PrintF("%d", value());
598 }
599
600
SmiPrint(StringStream * accumulator)601 void Smi::SmiPrint(StringStream* accumulator) {
602 accumulator->Add("%d", value());
603 }
604
605
FailurePrint(StringStream * accumulator)606 void Failure::FailurePrint(StringStream* accumulator) {
607 accumulator->Add("Failure(%d)", value());
608 }
609
610
FailurePrint()611 void Failure::FailurePrint() {
612 PrintF("Failure(%d)", value());
613 }
614
615
RetryAfterGC(int requested_bytes,AllocationSpace space)616 Failure* Failure::RetryAfterGC(int requested_bytes, AllocationSpace space) {
617 ASSERT((space & ~kSpaceTagMask) == 0);
618 // TODO(X64): Stop using Smi validation for non-smi checks, even if they
619 // happen to be identical at the moment.
620
621 int requested = requested_bytes >> kObjectAlignmentBits;
622 int value = (requested << kSpaceTagSize) | space;
623 // We can't very well allocate a heap number in this situation, and if the
624 // requested memory is so large it seems reasonable to say that this is an
625 // out of memory situation. This fixes a crash in
626 // js1_5/Regress/regress-303213.js.
627 if (value >> kSpaceTagSize != requested ||
628 !Smi::IsValid(value) ||
629 value != ((value << kFailureTypeTagSize) >> kFailureTypeTagSize) ||
630 !Smi::IsValid(value << kFailureTypeTagSize)) {
631 Top::context()->mark_out_of_memory();
632 return Failure::OutOfMemoryException();
633 }
634 return Construct(RETRY_AFTER_GC, value);
635 }
636
637
638 // Should a word be prefixed by 'a' or 'an' in order to read naturally in
639 // English? Returns false for non-ASCII or words that don't start with
640 // a capital letter. The a/an rule follows pronunciation in English.
641 // We don't use the BBC's overcorrect "an historic occasion" though if
642 // you speak a dialect you may well say "an 'istoric occasion".
AnWord(String * str)643 static bool AnWord(String* str) {
644 if (str->length() == 0) return false; // A nothing.
645 int c0 = str->Get(0);
646 int c1 = str->length() > 1 ? str->Get(1) : 0;
647 if (c0 == 'U') {
648 if (c1 > 'Z') {
649 return true; // An Umpire, but a UTF8String, a U.
650 }
651 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
652 return true; // An Ape, an ABCBook.
653 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
654 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
655 c0 == 'S' || c0 == 'X')) {
656 return true; // An MP3File, an M.
657 }
658 return false;
659 }
660
661
TryFlatten()662 Object* String::TryFlatten() {
663 #ifdef DEBUG
664 // Do not attempt to flatten in debug mode when allocation is not
665 // allowed. This is to avoid an assertion failure when allocating.
666 // Flattening strings is the only case where we always allow
667 // allocation because no GC is performed if the allocation fails.
668 if (!Heap::IsAllocationAllowed()) return this;
669 #endif
670
671 switch (StringShape(this).representation_tag()) {
672 case kSlicedStringTag: {
673 SlicedString* ss = SlicedString::cast(this);
674 // The SlicedString constructor should ensure that there are no
675 // SlicedStrings that are constructed directly on top of other
676 // SlicedStrings.
677 String* buf = ss->buffer();
678 ASSERT(!buf->IsSlicedString());
679 Object* ok = buf->TryFlatten();
680 if (ok->IsFailure()) return ok;
681 // Under certain circumstances (TryFlattenIfNotFlat fails in
682 // String::Slice) we can have a cons string under a slice.
683 // In this case we need to get the flat string out of the cons!
684 if (StringShape(String::cast(ok)).IsCons()) {
685 ss->set_buffer(ConsString::cast(ok)->first());
686 }
687 return this;
688 }
689 case kConsStringTag: {
690 ConsString* cs = ConsString::cast(this);
691 if (cs->second()->length() == 0) {
692 return this;
693 }
694 // There's little point in putting the flat string in new space if the
695 // cons string is in old space. It can never get GCed until there is
696 // an old space GC.
697 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED : TENURED;
698 int len = length();
699 Object* object;
700 String* result;
701 if (IsAsciiRepresentation()) {
702 object = Heap::AllocateRawAsciiString(len, tenure);
703 if (object->IsFailure()) return object;
704 result = String::cast(object);
705 String* first = cs->first();
706 int first_length = first->length();
707 char* dest = SeqAsciiString::cast(result)->GetChars();
708 WriteToFlat(first, dest, 0, first_length);
709 String* second = cs->second();
710 WriteToFlat(second,
711 dest + first_length,
712 0,
713 len - first_length);
714 } else {
715 object = Heap::AllocateRawTwoByteString(len, tenure);
716 if (object->IsFailure()) return object;
717 result = String::cast(object);
718 uc16* dest = SeqTwoByteString::cast(result)->GetChars();
719 String* first = cs->first();
720 int first_length = first->length();
721 WriteToFlat(first, dest, 0, first_length);
722 String* second = cs->second();
723 WriteToFlat(second,
724 dest + first_length,
725 0,
726 len - first_length);
727 }
728 cs->set_first(result);
729 cs->set_second(Heap::empty_string());
730 return this;
731 }
732 default:
733 return this;
734 }
735 }
736
737
MakeExternal(v8::String::ExternalStringResource * resource)738 bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
739 #ifdef DEBUG
740 { // NOLINT (presubmit.py gets confused about if and braces)
741 // Assert that the resource and the string are equivalent.
742 ASSERT(static_cast<size_t>(this->length()) == resource->length());
743 SmartPointer<uc16> smart_chars = this->ToWideCString();
744 ASSERT(memcmp(*smart_chars,
745 resource->data(),
746 resource->length() * sizeof(**smart_chars)) == 0);
747 }
748 #endif // DEBUG
749
750 int size = this->Size(); // Byte size of the original string.
751 if (size < ExternalString::kSize) {
752 // The string is too small to fit an external String in its place. This can
753 // only happen for zero length strings.
754 return false;
755 }
756 ASSERT(size >= ExternalString::kSize);
757 bool is_symbol = this->IsSymbol();
758 int length = this->length();
759
760 // Morph the object to an external string by adjusting the map and
761 // reinitializing the fields.
762 this->set_map(ExternalTwoByteString::StringMap(length));
763 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
764 self->set_length(length);
765 self->set_resource(resource);
766 // Additionally make the object into an external symbol if the original string
767 // was a symbol to start with.
768 if (is_symbol) {
769 self->Hash(); // Force regeneration of the hash value.
770 // Now morph this external string into a external symbol.
771 self->set_map(ExternalTwoByteString::SymbolMap(length));
772 }
773
774 // Fill the remainder of the string with dead wood.
775 int new_size = this->Size(); // Byte size of the external String object.
776 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
777 return true;
778 }
779
780
MakeExternal(v8::String::ExternalAsciiStringResource * resource)781 bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
782 #ifdef DEBUG
783 { // NOLINT (presubmit.py gets confused about if and braces)
784 // Assert that the resource and the string are equivalent.
785 ASSERT(static_cast<size_t>(this->length()) == resource->length());
786 SmartPointer<char> smart_chars = this->ToCString();
787 ASSERT(memcmp(*smart_chars,
788 resource->data(),
789 resource->length()*sizeof(**smart_chars)) == 0);
790 }
791 #endif // DEBUG
792
793 int size = this->Size(); // Byte size of the original string.
794 if (size < ExternalString::kSize) {
795 // The string is too small to fit an external String in its place. This can
796 // only happen for zero length strings.
797 return false;
798 }
799 ASSERT(size >= ExternalString::kSize);
800 bool is_symbol = this->IsSymbol();
801 int length = this->length();
802
803 // Morph the object to an external string by adjusting the map and
804 // reinitializing the fields.
805 this->set_map(ExternalAsciiString::StringMap(length));
806 ExternalAsciiString* self = ExternalAsciiString::cast(this);
807 self->set_length(length);
808 self->set_resource(resource);
809 // Additionally make the object into an external symbol if the original string
810 // was a symbol to start with.
811 if (is_symbol) {
812 self->Hash(); // Force regeneration of the hash value.
813 // Now morph this external string into a external symbol.
814 self->set_map(ExternalAsciiString::SymbolMap(length));
815 }
816
817 // Fill the remainder of the string with dead wood.
818 int new_size = this->Size(); // Byte size of the external String object.
819 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
820 return true;
821 }
822
823
StringShortPrint(StringStream * accumulator)824 void String::StringShortPrint(StringStream* accumulator) {
825 int len = length();
826 if (len > kMaxMediumStringSize) {
827 accumulator->Add("<Very long string[%u]>", len);
828 return;
829 }
830
831 if (!LooksValid()) {
832 accumulator->Add("<Invalid String>");
833 return;
834 }
835
836 StringInputBuffer buf(this);
837
838 bool truncated = false;
839 if (len > kMaxShortPrintLength) {
840 len = kMaxShortPrintLength;
841 truncated = true;
842 }
843 bool ascii = true;
844 for (int i = 0; i < len; i++) {
845 int c = buf.GetNext();
846
847 if (c < 32 || c >= 127) {
848 ascii = false;
849 }
850 }
851 buf.Reset(this);
852 if (ascii) {
853 accumulator->Add("<String[%u]: ", length());
854 for (int i = 0; i < len; i++) {
855 accumulator->Put(buf.GetNext());
856 }
857 accumulator->Put('>');
858 } else {
859 // Backslash indicates that the string contains control
860 // characters and that backslashes are therefore escaped.
861 accumulator->Add("<String[%u]\\: ", length());
862 for (int i = 0; i < len; i++) {
863 int c = buf.GetNext();
864 if (c == '\n') {
865 accumulator->Add("\\n");
866 } else if (c == '\r') {
867 accumulator->Add("\\r");
868 } else if (c == '\\') {
869 accumulator->Add("\\\\");
870 } else if (c < 32 || c > 126) {
871 accumulator->Add("\\x%02x", c);
872 } else {
873 accumulator->Put(c);
874 }
875 }
876 if (truncated) {
877 accumulator->Put('.');
878 accumulator->Put('.');
879 accumulator->Put('.');
880 }
881 accumulator->Put('>');
882 }
883 return;
884 }
885
886
JSObjectShortPrint(StringStream * accumulator)887 void JSObject::JSObjectShortPrint(StringStream* accumulator) {
888 switch (map()->instance_type()) {
889 case JS_ARRAY_TYPE: {
890 double length = JSArray::cast(this)->length()->Number();
891 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
892 break;
893 }
894 case JS_REGEXP_TYPE: {
895 accumulator->Add("<JS RegExp>");
896 break;
897 }
898 case JS_FUNCTION_TYPE: {
899 Object* fun_name = JSFunction::cast(this)->shared()->name();
900 bool printed = false;
901 if (fun_name->IsString()) {
902 String* str = String::cast(fun_name);
903 if (str->length() > 0) {
904 accumulator->Add("<JS Function ");
905 accumulator->Put(str);
906 accumulator->Put('>');
907 printed = true;
908 }
909 }
910 if (!printed) {
911 accumulator->Add("<JS Function>");
912 }
913 break;
914 }
915 // All other JSObjects are rather similar to each other (JSObject,
916 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
917 default: {
918 Object* constructor = map()->constructor();
919 bool printed = false;
920 if (constructor->IsHeapObject() &&
921 !Heap::Contains(HeapObject::cast(constructor))) {
922 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
923 } else {
924 bool global_object = IsJSGlobalProxy();
925 if (constructor->IsJSFunction()) {
926 if (!Heap::Contains(JSFunction::cast(constructor)->shared())) {
927 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
928 } else {
929 Object* constructor_name =
930 JSFunction::cast(constructor)->shared()->name();
931 if (constructor_name->IsString()) {
932 String* str = String::cast(constructor_name);
933 if (str->length() > 0) {
934 bool vowel = AnWord(str);
935 accumulator->Add("<%sa%s ",
936 global_object ? "Global Object: " : "",
937 vowel ? "n" : "");
938 accumulator->Put(str);
939 accumulator->Put('>');
940 printed = true;
941 }
942 }
943 }
944 }
945 if (!printed) {
946 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
947 }
948 }
949 if (IsJSValue()) {
950 accumulator->Add(" value = ");
951 JSValue::cast(this)->value()->ShortPrint(accumulator);
952 }
953 accumulator->Put('>');
954 break;
955 }
956 }
957 }
958
959
HeapObjectShortPrint(StringStream * accumulator)960 void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
961 // if (!Heap::InNewSpace(this)) PrintF("*", this);
962 if (!Heap::Contains(this)) {
963 accumulator->Add("!!!INVALID POINTER!!!");
964 return;
965 }
966 if (!Heap::Contains(map())) {
967 accumulator->Add("!!!INVALID MAP!!!");
968 return;
969 }
970
971 accumulator->Add("%p ", this);
972
973 if (IsString()) {
974 String::cast(this)->StringShortPrint(accumulator);
975 return;
976 }
977 if (IsJSObject()) {
978 JSObject::cast(this)->JSObjectShortPrint(accumulator);
979 return;
980 }
981 switch (map()->instance_type()) {
982 case MAP_TYPE:
983 accumulator->Add("<Map>");
984 break;
985 case FIXED_ARRAY_TYPE:
986 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
987 break;
988 case BYTE_ARRAY_TYPE:
989 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
990 break;
991 case PIXEL_ARRAY_TYPE:
992 accumulator->Add("<PixelArray[%u]>", PixelArray::cast(this)->length());
993 break;
994 case SHARED_FUNCTION_INFO_TYPE:
995 accumulator->Add("<SharedFunctionInfo>");
996 break;
997 #define MAKE_STRUCT_CASE(NAME, Name, name) \
998 case NAME##_TYPE: \
999 accumulator->Put('<'); \
1000 accumulator->Add(#Name); \
1001 accumulator->Put('>'); \
1002 break;
1003 STRUCT_LIST(MAKE_STRUCT_CASE)
1004 #undef MAKE_STRUCT_CASE
1005 case CODE_TYPE:
1006 accumulator->Add("<Code>");
1007 break;
1008 case ODDBALL_TYPE: {
1009 if (IsUndefined())
1010 accumulator->Add("<undefined>");
1011 else if (IsTheHole())
1012 accumulator->Add("<the hole>");
1013 else if (IsNull())
1014 accumulator->Add("<null>");
1015 else if (IsTrue())
1016 accumulator->Add("<true>");
1017 else if (IsFalse())
1018 accumulator->Add("<false>");
1019 else
1020 accumulator->Add("<Odd Oddball>");
1021 break;
1022 }
1023 case HEAP_NUMBER_TYPE:
1024 accumulator->Add("<Number: ");
1025 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
1026 accumulator->Put('>');
1027 break;
1028 case PROXY_TYPE:
1029 accumulator->Add("<Proxy>");
1030 break;
1031 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1032 accumulator->Add("Cell for ");
1033 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator);
1034 break;
1035 default:
1036 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
1037 break;
1038 }
1039 }
1040
1041
SlowSizeFromMap(Map * map)1042 int HeapObject::SlowSizeFromMap(Map* map) {
1043 // Avoid calling functions such as FixedArray::cast during GC, which
1044 // read map pointer of this object again.
1045 InstanceType instance_type = map->instance_type();
1046 uint32_t type = static_cast<uint32_t>(instance_type);
1047
1048 if (instance_type < FIRST_NONSTRING_TYPE
1049 && (StringShape(instance_type).IsSequential())) {
1050 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1051 SeqAsciiString* seq_ascii_this = reinterpret_cast<SeqAsciiString*>(this);
1052 return seq_ascii_this->SeqAsciiStringSize(instance_type);
1053 } else {
1054 SeqTwoByteString* self = reinterpret_cast<SeqTwoByteString*>(this);
1055 return self->SeqTwoByteStringSize(instance_type);
1056 }
1057 }
1058
1059 switch (instance_type) {
1060 case FIXED_ARRAY_TYPE:
1061 return reinterpret_cast<FixedArray*>(this)->FixedArraySize();
1062 case BYTE_ARRAY_TYPE:
1063 return reinterpret_cast<ByteArray*>(this)->ByteArraySize();
1064 case CODE_TYPE:
1065 return reinterpret_cast<Code*>(this)->CodeSize();
1066 case MAP_TYPE:
1067 return Map::kSize;
1068 default:
1069 return map->instance_size();
1070 }
1071 }
1072
1073
Iterate(ObjectVisitor * v)1074 void HeapObject::Iterate(ObjectVisitor* v) {
1075 // Handle header
1076 IteratePointer(v, kMapOffset);
1077 // Handle object body
1078 Map* m = map();
1079 IterateBody(m->instance_type(), SizeFromMap(m), v);
1080 }
1081
1082
IterateBody(InstanceType type,int object_size,ObjectVisitor * v)1083 void HeapObject::IterateBody(InstanceType type, int object_size,
1084 ObjectVisitor* v) {
1085 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
1086 // During GC, the map pointer field is encoded.
1087 if (type < FIRST_NONSTRING_TYPE) {
1088 switch (type & kStringRepresentationMask) {
1089 case kSeqStringTag:
1090 break;
1091 case kConsStringTag:
1092 reinterpret_cast<ConsString*>(this)->ConsStringIterateBody(v);
1093 break;
1094 case kSlicedStringTag:
1095 reinterpret_cast<SlicedString*>(this)->SlicedStringIterateBody(v);
1096 break;
1097 }
1098 return;
1099 }
1100
1101 switch (type) {
1102 case FIXED_ARRAY_TYPE:
1103 reinterpret_cast<FixedArray*>(this)->FixedArrayIterateBody(v);
1104 break;
1105 case JS_OBJECT_TYPE:
1106 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
1107 case JS_VALUE_TYPE:
1108 case JS_ARRAY_TYPE:
1109 case JS_REGEXP_TYPE:
1110 case JS_FUNCTION_TYPE:
1111 case JS_GLOBAL_PROXY_TYPE:
1112 case JS_GLOBAL_OBJECT_TYPE:
1113 case JS_BUILTINS_OBJECT_TYPE:
1114 reinterpret_cast<JSObject*>(this)->JSObjectIterateBody(object_size, v);
1115 break;
1116 case ODDBALL_TYPE:
1117 reinterpret_cast<Oddball*>(this)->OddballIterateBody(v);
1118 break;
1119 case PROXY_TYPE:
1120 reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
1121 break;
1122 case MAP_TYPE:
1123 reinterpret_cast<Map*>(this)->MapIterateBody(v);
1124 break;
1125 case CODE_TYPE:
1126 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1127 break;
1128 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1129 reinterpret_cast<JSGlobalPropertyCell*>(this)
1130 ->JSGlobalPropertyCellIterateBody(v);
1131 break;
1132 case HEAP_NUMBER_TYPE:
1133 case FILLER_TYPE:
1134 case BYTE_ARRAY_TYPE:
1135 case PIXEL_ARRAY_TYPE:
1136 break;
1137 case SHARED_FUNCTION_INFO_TYPE: {
1138 SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(this);
1139 shared->SharedFunctionInfoIterateBody(v);
1140 break;
1141 }
1142 #define MAKE_STRUCT_CASE(NAME, Name, name) \
1143 case NAME##_TYPE:
1144 STRUCT_LIST(MAKE_STRUCT_CASE)
1145 #undef MAKE_STRUCT_CASE
1146 IterateStructBody(object_size, v);
1147 break;
1148 default:
1149 PrintF("Unknown type: %d\n", type);
1150 UNREACHABLE();
1151 }
1152 }
1153
1154
IterateStructBody(int object_size,ObjectVisitor * v)1155 void HeapObject::IterateStructBody(int object_size, ObjectVisitor* v) {
1156 IteratePointers(v, HeapObject::kHeaderSize, object_size);
1157 }
1158
1159
HeapNumberToBoolean()1160 Object* HeapNumber::HeapNumberToBoolean() {
1161 // NaN, +0, and -0 should return the false object
1162 switch (fpclassify(value())) {
1163 case FP_NAN: // fall through
1164 case FP_ZERO: return Heap::false_value();
1165 default: return Heap::true_value();
1166 }
1167 }
1168
1169
HeapNumberPrint()1170 void HeapNumber::HeapNumberPrint() {
1171 PrintF("%.16g", Number());
1172 }
1173
1174
HeapNumberPrint(StringStream * accumulator)1175 void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1176 // The Windows version of vsnprintf can allocate when printing a %g string
1177 // into a buffer that may not be big enough. We don't want random memory
1178 // allocation when producing post-crash stack traces, so we print into a
1179 // buffer that is plenty big enough for any floating point number, then
1180 // print that using vsnprintf (which may truncate but never allocate if
1181 // there is no more space in the buffer).
1182 EmbeddedVector<char, 100> buffer;
1183 OS::SNPrintF(buffer, "%.16g", Number());
1184 accumulator->Add("%s", buffer.start());
1185 }
1186
1187
class_name()1188 String* JSObject::class_name() {
1189 if (IsJSFunction()) return Heap::function_class_symbol();
1190 if (map()->constructor()->IsJSFunction()) {
1191 JSFunction* constructor = JSFunction::cast(map()->constructor());
1192 return String::cast(constructor->shared()->instance_class_name());
1193 }
1194 // If the constructor is not present, return "Object".
1195 return Heap::Object_symbol();
1196 }
1197
1198
JSObjectIterateBody(int object_size,ObjectVisitor * v)1199 void JSObject::JSObjectIterateBody(int object_size, ObjectVisitor* v) {
1200 // Iterate over all fields in the body. Assumes all are Object*.
1201 IteratePointers(v, kPropertiesOffset, object_size);
1202 }
1203
1204
AddFastPropertyUsingMap(Map * new_map,String * name,Object * value)1205 Object* JSObject::AddFastPropertyUsingMap(Map* new_map,
1206 String* name,
1207 Object* value) {
1208 int index = new_map->PropertyIndexFor(name);
1209 if (map()->unused_property_fields() == 0) {
1210 ASSERT(map()->unused_property_fields() == 0);
1211 int new_unused = new_map->unused_property_fields();
1212 Object* values =
1213 properties()->CopySize(properties()->length() + new_unused + 1);
1214 if (values->IsFailure()) return values;
1215 set_properties(FixedArray::cast(values));
1216 }
1217 set_map(new_map);
1218 return FastPropertyAtPut(index, value);
1219 }
1220
1221
AddFastProperty(String * name,Object * value,PropertyAttributes attributes)1222 Object* JSObject::AddFastProperty(String* name,
1223 Object* value,
1224 PropertyAttributes attributes) {
1225 // Normalize the object if the name is an actual string (not the
1226 // hidden symbols) and is not a real identifier.
1227 StringInputBuffer buffer(name);
1228 if (!Scanner::IsIdentifier(&buffer) && name != Heap::hidden_symbol()) {
1229 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1230 if (obj->IsFailure()) return obj;
1231 return AddSlowProperty(name, value, attributes);
1232 }
1233
1234 DescriptorArray* old_descriptors = map()->instance_descriptors();
1235 // Compute the new index for new field.
1236 int index = map()->NextFreePropertyIndex();
1237
1238 // Allocate new instance descriptors with (name, index) added
1239 FieldDescriptor new_field(name, index, attributes);
1240 Object* new_descriptors =
1241 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1242 if (new_descriptors->IsFailure()) return new_descriptors;
1243
1244 // Only allow map transition if the object's map is NOT equal to the
1245 // global object_function's map and there is not a transition for name.
1246 bool allow_map_transition =
1247 !old_descriptors->Contains(name) &&
1248 (Top::context()->global_context()->object_function()->map() != map());
1249
1250 ASSERT(index < map()->inobject_properties() ||
1251 (index - map()->inobject_properties()) < properties()->length() ||
1252 map()->unused_property_fields() == 0);
1253 // Allocate a new map for the object.
1254 Object* r = map()->CopyDropDescriptors();
1255 if (r->IsFailure()) return r;
1256 Map* new_map = Map::cast(r);
1257 if (allow_map_transition) {
1258 // Allocate new instance descriptors for the old map with map transition.
1259 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
1260 Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1261 if (r->IsFailure()) return r;
1262 old_descriptors = DescriptorArray::cast(r);
1263 }
1264
1265 if (map()->unused_property_fields() == 0) {
1266 if (properties()->length() > kMaxFastProperties) {
1267 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1268 if (obj->IsFailure()) return obj;
1269 return AddSlowProperty(name, value, attributes);
1270 }
1271 // Make room for the new value
1272 Object* values =
1273 properties()->CopySize(properties()->length() + kFieldsAdded);
1274 if (values->IsFailure()) return values;
1275 set_properties(FixedArray::cast(values));
1276 new_map->set_unused_property_fields(kFieldsAdded - 1);
1277 } else {
1278 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
1279 }
1280 // We have now allocated all the necessary objects.
1281 // All the changes can be applied at once, so they are atomic.
1282 map()->set_instance_descriptors(old_descriptors);
1283 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1284 set_map(new_map);
1285 return FastPropertyAtPut(index, value);
1286 }
1287
1288
AddConstantFunctionProperty(String * name,JSFunction * function,PropertyAttributes attributes)1289 Object* JSObject::AddConstantFunctionProperty(String* name,
1290 JSFunction* function,
1291 PropertyAttributes attributes) {
1292 // Allocate new instance descriptors with (name, function) added
1293 ConstantFunctionDescriptor d(name, function, attributes);
1294 Object* new_descriptors =
1295 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1296 if (new_descriptors->IsFailure()) return new_descriptors;
1297
1298 // Allocate a new map for the object.
1299 Object* new_map = map()->CopyDropDescriptors();
1300 if (new_map->IsFailure()) return new_map;
1301
1302 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1303 Map::cast(new_map)->set_instance_descriptors(descriptors);
1304 Map* old_map = map();
1305 set_map(Map::cast(new_map));
1306
1307 // If the old map is the global object map (from new Object()),
1308 // then transitions are not added to it, so we are done.
1309 if (old_map == Top::context()->global_context()->object_function()->map()) {
1310 return function;
1311 }
1312
1313 // Do not add CONSTANT_TRANSITIONS to global objects
1314 if (IsGlobalObject()) {
1315 return function;
1316 }
1317
1318 // Add a CONSTANT_TRANSITION descriptor to the old map,
1319 // so future assignments to this property on other objects
1320 // of the same type will create a normal field, not a constant function.
1321 // Don't do this for special properties, with non-trival attributes.
1322 if (attributes != NONE) {
1323 return function;
1324 }
1325 ConstTransitionDescriptor mark(name);
1326 new_descriptors =
1327 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1328 if (new_descriptors->IsFailure()) {
1329 return function; // We have accomplished the main goal, so return success.
1330 }
1331 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1332
1333 return function;
1334 }
1335
1336
1337 // Add property in slow mode
AddSlowProperty(String * name,Object * value,PropertyAttributes attributes)1338 Object* JSObject::AddSlowProperty(String* name,
1339 Object* value,
1340 PropertyAttributes attributes) {
1341 ASSERT(!HasFastProperties());
1342 StringDictionary* dict = property_dictionary();
1343 Object* store_value = value;
1344 if (IsGlobalObject()) {
1345 // In case name is an orphaned property reuse the cell.
1346 int entry = dict->FindEntry(name);
1347 if (entry != StringDictionary::kNotFound) {
1348 store_value = dict->ValueAt(entry);
1349 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1350 // Assign an enumeration index to the property and update
1351 // SetNextEnumerationIndex.
1352 int index = dict->NextEnumerationIndex();
1353 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1354 dict->SetNextEnumerationIndex(index + 1);
1355 dict->SetEntry(entry, name, store_value, details);
1356 return value;
1357 }
1358 store_value = Heap::AllocateJSGlobalPropertyCell(value);
1359 if (store_value->IsFailure()) return store_value;
1360 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1361 }
1362 PropertyDetails details = PropertyDetails(attributes, NORMAL);
1363 Object* result = dict->Add(name, store_value, details);
1364 if (result->IsFailure()) return result;
1365 if (dict != result) set_properties(StringDictionary::cast(result));
1366 return value;
1367 }
1368
1369
AddProperty(String * name,Object * value,PropertyAttributes attributes)1370 Object* JSObject::AddProperty(String* name,
1371 Object* value,
1372 PropertyAttributes attributes) {
1373 ASSERT(!IsJSGlobalProxy());
1374 if (HasFastProperties()) {
1375 // Ensure the descriptor array does not get too big.
1376 if (map()->instance_descriptors()->number_of_descriptors() <
1377 DescriptorArray::kMaxNumberOfDescriptors) {
1378 if (value->IsJSFunction()) {
1379 return AddConstantFunctionProperty(name,
1380 JSFunction::cast(value),
1381 attributes);
1382 } else {
1383 return AddFastProperty(name, value, attributes);
1384 }
1385 } else {
1386 // Normalize the object to prevent very large instance descriptors.
1387 // This eliminates unwanted N^2 allocation and lookup behavior.
1388 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1389 if (obj->IsFailure()) return obj;
1390 }
1391 }
1392 return AddSlowProperty(name, value, attributes);
1393 }
1394
1395
SetPropertyPostInterceptor(String * name,Object * value,PropertyAttributes attributes)1396 Object* JSObject::SetPropertyPostInterceptor(String* name,
1397 Object* value,
1398 PropertyAttributes attributes) {
1399 // Check local property, ignore interceptor.
1400 LookupResult result;
1401 LocalLookupRealNamedProperty(name, &result);
1402 if (result.IsValid()) return SetProperty(&result, name, value, attributes);
1403 // Add real property.
1404 return AddProperty(name, value, attributes);
1405 }
1406
1407
ReplaceSlowProperty(String * name,Object * value,PropertyAttributes attributes)1408 Object* JSObject::ReplaceSlowProperty(String* name,
1409 Object* value,
1410 PropertyAttributes attributes) {
1411 StringDictionary* dictionary = property_dictionary();
1412 int old_index = dictionary->FindEntry(name);
1413 int new_enumeration_index = 0; // 0 means "Use the next available index."
1414 if (old_index != -1) {
1415 // All calls to ReplaceSlowProperty have had all transitions removed.
1416 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1417 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1418 }
1419
1420 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
1421 return SetNormalizedProperty(name, value, new_details);
1422 }
1423
ConvertDescriptorToFieldAndMapTransition(String * name,Object * new_value,PropertyAttributes attributes)1424 Object* JSObject::ConvertDescriptorToFieldAndMapTransition(
1425 String* name,
1426 Object* new_value,
1427 PropertyAttributes attributes) {
1428 Map* old_map = map();
1429 Object* result = ConvertDescriptorToField(name, new_value, attributes);
1430 if (result->IsFailure()) return result;
1431 // If we get to this point we have succeeded - do not return failure
1432 // after this point. Later stuff is optional.
1433 if (!HasFastProperties()) {
1434 return result;
1435 }
1436 // Do not add transitions to the map of "new Object()".
1437 if (map() == Top::context()->global_context()->object_function()->map()) {
1438 return result;
1439 }
1440
1441 MapTransitionDescriptor transition(name,
1442 map(),
1443 attributes);
1444 Object* new_descriptors =
1445 old_map->instance_descriptors()->
1446 CopyInsert(&transition, KEEP_TRANSITIONS);
1447 if (new_descriptors->IsFailure()) return result; // Yes, return _result_.
1448 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1449 return result;
1450 }
1451
1452
ConvertDescriptorToField(String * name,Object * new_value,PropertyAttributes attributes)1453 Object* JSObject::ConvertDescriptorToField(String* name,
1454 Object* new_value,
1455 PropertyAttributes attributes) {
1456 if (map()->unused_property_fields() == 0 &&
1457 properties()->length() > kMaxFastProperties) {
1458 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1459 if (obj->IsFailure()) return obj;
1460 return ReplaceSlowProperty(name, new_value, attributes);
1461 }
1462
1463 int index = map()->NextFreePropertyIndex();
1464 FieldDescriptor new_field(name, index, attributes);
1465 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
1466 Object* descriptors_unchecked = map()->instance_descriptors()->
1467 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1468 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
1469 DescriptorArray* new_descriptors =
1470 DescriptorArray::cast(descriptors_unchecked);
1471
1472 // Make a new map for the object.
1473 Object* new_map_unchecked = map()->CopyDropDescriptors();
1474 if (new_map_unchecked->IsFailure()) return new_map_unchecked;
1475 Map* new_map = Map::cast(new_map_unchecked);
1476 new_map->set_instance_descriptors(new_descriptors);
1477
1478 // Make new properties array if necessary.
1479 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1480 int new_unused_property_fields = map()->unused_property_fields() - 1;
1481 if (map()->unused_property_fields() == 0) {
1482 new_unused_property_fields = kFieldsAdded - 1;
1483 Object* new_properties_unchecked =
1484 properties()->CopySize(properties()->length() + kFieldsAdded);
1485 if (new_properties_unchecked->IsFailure()) return new_properties_unchecked;
1486 new_properties = FixedArray::cast(new_properties_unchecked);
1487 }
1488
1489 // Update pointers to commit changes.
1490 // Object points to the new map.
1491 new_map->set_unused_property_fields(new_unused_property_fields);
1492 set_map(new_map);
1493 if (new_properties) {
1494 set_properties(FixedArray::cast(new_properties));
1495 }
1496 return FastPropertyAtPut(index, new_value);
1497 }
1498
1499
1500
SetPropertyWithInterceptor(String * name,Object * value,PropertyAttributes attributes)1501 Object* JSObject::SetPropertyWithInterceptor(String* name,
1502 Object* value,
1503 PropertyAttributes attributes) {
1504 HandleScope scope;
1505 Handle<JSObject> this_handle(this);
1506 Handle<String> name_handle(name);
1507 Handle<Object> value_handle(value);
1508 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1509 if (!interceptor->setter()->IsUndefined()) {
1510 Handle<Object> data_handle(interceptor->data());
1511 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name));
1512 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
1513 v8::Utils::ToLocal(data_handle),
1514 v8::Utils::ToLocal(this_handle));
1515 v8::NamedPropertySetter setter =
1516 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1517 v8::Handle<v8::Value> result;
1518 {
1519 // Leaving JavaScript.
1520 VMState state(EXTERNAL);
1521 Handle<Object> value_unhole(value->IsTheHole() ?
1522 Heap::undefined_value() :
1523 value);
1524 result = setter(v8::Utils::ToLocal(name_handle),
1525 v8::Utils::ToLocal(value_unhole),
1526 info);
1527 }
1528 RETURN_IF_SCHEDULED_EXCEPTION();
1529 if (!result.IsEmpty()) return *value_handle;
1530 }
1531 Object* raw_result = this_handle->SetPropertyPostInterceptor(*name_handle,
1532 *value_handle,
1533 attributes);
1534 RETURN_IF_SCHEDULED_EXCEPTION();
1535 return raw_result;
1536 }
1537
1538
SetProperty(String * name,Object * value,PropertyAttributes attributes)1539 Object* JSObject::SetProperty(String* name,
1540 Object* value,
1541 PropertyAttributes attributes) {
1542 LookupResult result;
1543 LocalLookup(name, &result);
1544 return SetProperty(&result, name, value, attributes);
1545 }
1546
1547
SetPropertyWithCallback(Object * structure,String * name,Object * value,JSObject * holder)1548 Object* JSObject::SetPropertyWithCallback(Object* structure,
1549 String* name,
1550 Object* value,
1551 JSObject* holder) {
1552 HandleScope scope;
1553
1554 // We should never get here to initialize a const with the hole
1555 // value since a const declaration would conflict with the setter.
1556 ASSERT(!value->IsTheHole());
1557 Handle<Object> value_handle(value);
1558
1559 // To accommodate both the old and the new api we switch on the
1560 // data structure used to store the callbacks. Eventually proxy
1561 // callbacks should be phased out.
1562 if (structure->IsProxy()) {
1563 AccessorDescriptor* callback =
1564 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
1565 Object* obj = (callback->setter)(this, value, callback->data);
1566 RETURN_IF_SCHEDULED_EXCEPTION();
1567 if (obj->IsFailure()) return obj;
1568 return *value_handle;
1569 }
1570
1571 if (structure->IsAccessorInfo()) {
1572 // api style callbacks
1573 AccessorInfo* data = AccessorInfo::cast(structure);
1574 Object* call_obj = data->setter();
1575 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1576 if (call_fun == NULL) return value;
1577 Handle<JSObject> self(this);
1578 Handle<JSObject> holder_handle(JSObject::cast(holder));
1579 Handle<String> key(name);
1580 Handle<Object> fun_data(data->data());
1581 LOG(ApiNamedPropertyAccess("store", this, name));
1582 v8::AccessorInfo info(v8::Utils::ToLocal(self),
1583 v8::Utils::ToLocal(fun_data),
1584 v8::Utils::ToLocal(holder_handle));
1585 {
1586 // Leaving JavaScript.
1587 VMState state(EXTERNAL);
1588 call_fun(v8::Utils::ToLocal(key),
1589 v8::Utils::ToLocal(value_handle),
1590 info);
1591 }
1592 RETURN_IF_SCHEDULED_EXCEPTION();
1593 return *value_handle;
1594 }
1595
1596 if (structure->IsFixedArray()) {
1597 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1598 if (setter->IsJSFunction()) {
1599 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
1600 } else {
1601 Handle<String> key(name);
1602 Handle<Object> holder_handle(holder);
1603 Handle<Object> args[2] = { key, holder_handle };
1604 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
1605 HandleVector(args, 2)));
1606 }
1607 }
1608
1609 UNREACHABLE();
1610 return 0;
1611 }
1612
1613
SetPropertyWithDefinedSetter(JSFunction * setter,Object * value)1614 Object* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1615 Object* value) {
1616 Handle<Object> value_handle(value);
1617 Handle<JSFunction> fun(JSFunction::cast(setter));
1618 Handle<JSObject> self(this);
1619 #ifdef ENABLE_DEBUGGER_SUPPORT
1620 // Handle stepping into a setter if step into is active.
1621 if (Debug::StepInActive()) {
1622 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
1623 }
1624 #endif
1625 bool has_pending_exception;
1626 Object** argv[] = { value_handle.location() };
1627 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1628 // Check for pending exception and return the result.
1629 if (has_pending_exception) return Failure::Exception();
1630 return *value_handle;
1631 }
1632
1633
LookupCallbackSetterInPrototypes(String * name,LookupResult * result)1634 void JSObject::LookupCallbackSetterInPrototypes(String* name,
1635 LookupResult* result) {
1636 for (Object* pt = GetPrototype();
1637 pt != Heap::null_value();
1638 pt = pt->GetPrototype()) {
1639 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
1640 if (result->IsValid()) {
1641 if (!result->IsTransitionType() && result->IsReadOnly()) {
1642 result->NotFound();
1643 return;
1644 }
1645 if (result->type() == CALLBACKS) {
1646 return;
1647 }
1648 }
1649 }
1650 result->NotFound();
1651 }
1652
1653
LookupCallbackSetterInPrototypes(uint32_t index)1654 Object* JSObject::LookupCallbackSetterInPrototypes(uint32_t index) {
1655 for (Object* pt = GetPrototype();
1656 pt != Heap::null_value();
1657 pt = pt->GetPrototype()) {
1658 if (!JSObject::cast(pt)->HasDictionaryElements()) {
1659 continue;
1660 }
1661 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1662 int entry = dictionary->FindEntry(index);
1663 if (entry != NumberDictionary::kNotFound) {
1664 Object* element = dictionary->ValueAt(entry);
1665 PropertyDetails details = dictionary->DetailsAt(entry);
1666 if (details.type() == CALLBACKS) {
1667 // Only accessors allowed as elements.
1668 return FixedArray::cast(element)->get(kSetterIndex);
1669 }
1670 }
1671 }
1672 return Heap::undefined_value();
1673 }
1674
1675
LookupInDescriptor(String * name,LookupResult * result)1676 void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1677 DescriptorArray* descriptors = map()->instance_descriptors();
1678 int number = DescriptorLookupCache::Lookup(descriptors, name);
1679 if (number == DescriptorLookupCache::kAbsent) {
1680 number = descriptors->Search(name);
1681 DescriptorLookupCache::Update(descriptors, name, number);
1682 }
1683 if (number != DescriptorArray::kNotFound) {
1684 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1685 } else {
1686 result->NotFound();
1687 }
1688 }
1689
1690
LocalLookupRealNamedProperty(String * name,LookupResult * result)1691 void JSObject::LocalLookupRealNamedProperty(String* name,
1692 LookupResult* result) {
1693 if (IsJSGlobalProxy()) {
1694 Object* proto = GetPrototype();
1695 if (proto->IsNull()) return result->NotFound();
1696 ASSERT(proto->IsJSGlobalObject());
1697 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
1698 }
1699
1700 if (HasFastProperties()) {
1701 LookupInDescriptor(name, result);
1702 if (result->IsValid()) {
1703 ASSERT(result->holder() == this && result->type() != NORMAL);
1704 // Disallow caching for uninitialized constants. These can only
1705 // occur as fields.
1706 if (result->IsReadOnly() && result->type() == FIELD &&
1707 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
1708 result->DisallowCaching();
1709 }
1710 return;
1711 }
1712 } else {
1713 int entry = property_dictionary()->FindEntry(name);
1714 if (entry != StringDictionary::kNotFound) {
1715 Object* value = property_dictionary()->ValueAt(entry);
1716 if (IsGlobalObject()) {
1717 PropertyDetails d = property_dictionary()->DetailsAt(entry);
1718 if (d.IsDeleted()) {
1719 result->NotFound();
1720 return;
1721 }
1722 value = JSGlobalPropertyCell::cast(value)->value();
1723 ASSERT(result->IsLoaded());
1724 }
1725 // Make sure to disallow caching for uninitialized constants
1726 // found in the dictionary-mode objects.
1727 if (value->IsTheHole()) result->DisallowCaching();
1728 result->DictionaryResult(this, entry);
1729 return;
1730 }
1731 // Slow case object skipped during lookup. Do not use inline caching.
1732 if (!IsGlobalObject()) result->DisallowCaching();
1733 }
1734 result->NotFound();
1735 }
1736
1737
LookupRealNamedProperty(String * name,LookupResult * result)1738 void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
1739 LocalLookupRealNamedProperty(name, result);
1740 if (result->IsProperty()) return;
1741
1742 LookupRealNamedPropertyInPrototypes(name, result);
1743 }
1744
1745
LookupRealNamedPropertyInPrototypes(String * name,LookupResult * result)1746 void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
1747 LookupResult* result) {
1748 for (Object* pt = GetPrototype();
1749 pt != Heap::null_value();
1750 pt = JSObject::cast(pt)->GetPrototype()) {
1751 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
1752 if (result->IsValid()) {
1753 switch (result->type()) {
1754 case NORMAL:
1755 case FIELD:
1756 case CONSTANT_FUNCTION:
1757 case CALLBACKS:
1758 return;
1759 default: break;
1760 }
1761 }
1762 }
1763 result->NotFound();
1764 }
1765
1766
1767 // We only need to deal with CALLBACKS and INTERCEPTORS
SetPropertyWithFailedAccessCheck(LookupResult * result,String * name,Object * value)1768 Object* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
1769 String* name,
1770 Object* value) {
1771 if (!result->IsProperty()) {
1772 LookupCallbackSetterInPrototypes(name, result);
1773 }
1774
1775 if (result->IsProperty()) {
1776 if (!result->IsReadOnly()) {
1777 switch (result->type()) {
1778 case CALLBACKS: {
1779 Object* obj = result->GetCallbackObject();
1780 if (obj->IsAccessorInfo()) {
1781 AccessorInfo* info = AccessorInfo::cast(obj);
1782 if (info->all_can_write()) {
1783 return SetPropertyWithCallback(result->GetCallbackObject(),
1784 name,
1785 value,
1786 result->holder());
1787 }
1788 }
1789 break;
1790 }
1791 case INTERCEPTOR: {
1792 // Try lookup real named properties. Note that only property can be
1793 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
1794 LookupResult r;
1795 LookupRealNamedProperty(name, &r);
1796 if (r.IsProperty()) {
1797 return SetPropertyWithFailedAccessCheck(&r, name, value);
1798 }
1799 break;
1800 }
1801 default: {
1802 break;
1803 }
1804 }
1805 }
1806 }
1807
1808 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
1809 return value;
1810 }
1811
1812
SetProperty(LookupResult * result,String * name,Object * value,PropertyAttributes attributes)1813 Object* JSObject::SetProperty(LookupResult* result,
1814 String* name,
1815 Object* value,
1816 PropertyAttributes attributes) {
1817 // Make sure that the top context does not change when doing callbacks or
1818 // interceptor calls.
1819 AssertNoContextChange ncc;
1820
1821 // Check access rights if needed.
1822 if (IsAccessCheckNeeded()
1823 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1824 return SetPropertyWithFailedAccessCheck(result, name, value);
1825 }
1826
1827 if (IsJSGlobalProxy()) {
1828 Object* proto = GetPrototype();
1829 if (proto->IsNull()) return value;
1830 ASSERT(proto->IsJSGlobalObject());
1831 return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
1832 }
1833
1834 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
1835 // We could not find a local property so let's check whether there is an
1836 // accessor that wants to handle the property.
1837 LookupResult accessor_result;
1838 LookupCallbackSetterInPrototypes(name, &accessor_result);
1839 if (accessor_result.IsValid()) {
1840 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
1841 name,
1842 value,
1843 accessor_result.holder());
1844 }
1845 }
1846 if (result->IsNotFound()) {
1847 return AddProperty(name, value, attributes);
1848 }
1849 if (!result->IsLoaded()) {
1850 return SetLazyProperty(result, name, value, attributes);
1851 }
1852 if (result->IsReadOnly() && result->IsProperty()) return value;
1853 // This is a real property that is not read-only, or it is a
1854 // transition or null descriptor and there are no setters in the prototypes.
1855 switch (result->type()) {
1856 case NORMAL:
1857 return SetNormalizedProperty(result, value);
1858 case FIELD:
1859 return FastPropertyAtPut(result->GetFieldIndex(), value);
1860 case MAP_TRANSITION:
1861 if (attributes == result->GetAttributes()) {
1862 // Only use map transition if the attributes match.
1863 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1864 name,
1865 value);
1866 }
1867 return ConvertDescriptorToField(name, value, attributes);
1868 case CONSTANT_FUNCTION:
1869 // Only replace the function if necessary.
1870 if (value == result->GetConstantFunction()) return value;
1871 // Preserve the attributes of this existing property.
1872 attributes = result->GetAttributes();
1873 return ConvertDescriptorToField(name, value, attributes);
1874 case CALLBACKS:
1875 return SetPropertyWithCallback(result->GetCallbackObject(),
1876 name,
1877 value,
1878 result->holder());
1879 case INTERCEPTOR:
1880 return SetPropertyWithInterceptor(name, value, attributes);
1881 case CONSTANT_TRANSITION:
1882 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
1883 // if the value is a function.
1884 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1885 case NULL_DESCRIPTOR:
1886 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1887 default:
1888 UNREACHABLE();
1889 }
1890 UNREACHABLE();
1891 return value;
1892 }
1893
1894
1895 // Set a real local property, even if it is READ_ONLY. If the property is not
1896 // present, add it with attributes NONE. This code is an exact clone of
1897 // SetProperty, with the check for IsReadOnly and the check for a
1898 // callback setter removed. The two lines looking up the LookupResult
1899 // result are also added. If one of the functions is changed, the other
1900 // should be.
IgnoreAttributesAndSetLocalProperty(String * name,Object * value,PropertyAttributes attributes)1901 Object* JSObject::IgnoreAttributesAndSetLocalProperty(
1902 String* name,
1903 Object* value,
1904 PropertyAttributes attributes) {
1905 // Make sure that the top context does not change when doing callbacks or
1906 // interceptor calls.
1907 AssertNoContextChange ncc;
1908 // ADDED TO CLONE
1909 LookupResult result_struct;
1910 LocalLookup(name, &result_struct);
1911 LookupResult* result = &result_struct;
1912 // END ADDED TO CLONE
1913 // Check access rights if needed.
1914 if (IsAccessCheckNeeded()
1915 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1916 return SetPropertyWithFailedAccessCheck(result, name, value);
1917 }
1918
1919 if (IsJSGlobalProxy()) {
1920 Object* proto = GetPrototype();
1921 if (proto->IsNull()) return value;
1922 ASSERT(proto->IsJSGlobalObject());
1923 return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
1924 name,
1925 value,
1926 attributes);
1927 }
1928
1929 // Check for accessor in prototype chain removed here in clone.
1930 if (result->IsNotFound()) {
1931 return AddProperty(name, value, attributes);
1932 }
1933 if (!result->IsLoaded()) {
1934 return SetLazyProperty(result, name, value, attributes);
1935 }
1936 // Check of IsReadOnly removed from here in clone.
1937 switch (result->type()) {
1938 case NORMAL:
1939 return SetNormalizedProperty(result, value);
1940 case FIELD:
1941 return FastPropertyAtPut(result->GetFieldIndex(), value);
1942 case MAP_TRANSITION:
1943 if (attributes == result->GetAttributes()) {
1944 // Only use map transition if the attributes match.
1945 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1946 name,
1947 value);
1948 }
1949 return ConvertDescriptorToField(name, value, attributes);
1950 case CONSTANT_FUNCTION:
1951 // Only replace the function if necessary.
1952 if (value == result->GetConstantFunction()) return value;
1953 // Preserve the attributes of this existing property.
1954 attributes = result->GetAttributes();
1955 return ConvertDescriptorToField(name, value, attributes);
1956 case CALLBACKS:
1957 case INTERCEPTOR:
1958 // Override callback in clone
1959 return ConvertDescriptorToField(name, value, attributes);
1960 case CONSTANT_TRANSITION:
1961 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
1962 // if the value is a function.
1963 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1964 case NULL_DESCRIPTOR:
1965 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1966 default:
1967 UNREACHABLE();
1968 }
1969 UNREACHABLE();
1970 return value;
1971 }
1972
1973
GetPropertyAttributePostInterceptor(JSObject * receiver,String * name,bool continue_search)1974 PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
1975 JSObject* receiver,
1976 String* name,
1977 bool continue_search) {
1978 // Check local property, ignore interceptor.
1979 LookupResult result;
1980 LocalLookupRealNamedProperty(name, &result);
1981 if (result.IsProperty()) return result.GetAttributes();
1982
1983 if (continue_search) {
1984 // Continue searching via the prototype chain.
1985 Object* pt = GetPrototype();
1986 if (pt != Heap::null_value()) {
1987 return JSObject::cast(pt)->
1988 GetPropertyAttributeWithReceiver(receiver, name);
1989 }
1990 }
1991 return ABSENT;
1992 }
1993
1994
GetPropertyAttributeWithInterceptor(JSObject * receiver,String * name,bool continue_search)1995 PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
1996 JSObject* receiver,
1997 String* name,
1998 bool continue_search) {
1999 // Make sure that the top context does not change when doing
2000 // callbacks or interceptor calls.
2001 AssertNoContextChange ncc;
2002
2003 HandleScope scope;
2004 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2005 Handle<JSObject> receiver_handle(receiver);
2006 Handle<JSObject> holder_handle(this);
2007 Handle<String> name_handle(name);
2008 Handle<Object> data_handle(interceptor->data());
2009 v8::AccessorInfo info(v8::Utils::ToLocal(receiver_handle),
2010 v8::Utils::ToLocal(data_handle),
2011 v8::Utils::ToLocal(holder_handle));
2012 if (!interceptor->query()->IsUndefined()) {
2013 v8::NamedPropertyQuery query =
2014 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
2015 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
2016 v8::Handle<v8::Boolean> result;
2017 {
2018 // Leaving JavaScript.
2019 VMState state(EXTERNAL);
2020 result = query(v8::Utils::ToLocal(name_handle), info);
2021 }
2022 if (!result.IsEmpty()) {
2023 // Convert the boolean result to a property attribute
2024 // specification.
2025 return result->IsTrue() ? NONE : ABSENT;
2026 }
2027 } else if (!interceptor->getter()->IsUndefined()) {
2028 v8::NamedPropertyGetter getter =
2029 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
2030 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
2031 v8::Handle<v8::Value> result;
2032 {
2033 // Leaving JavaScript.
2034 VMState state(EXTERNAL);
2035 result = getter(v8::Utils::ToLocal(name_handle), info);
2036 }
2037 if (!result.IsEmpty()) return NONE;
2038 }
2039 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2040 *name_handle,
2041 continue_search);
2042 }
2043
2044
GetPropertyAttributeWithReceiver(JSObject * receiver,String * key)2045 PropertyAttributes JSObject::GetPropertyAttributeWithReceiver(
2046 JSObject* receiver,
2047 String* key) {
2048 uint32_t index = 0;
2049 if (key->AsArrayIndex(&index)) {
2050 if (HasElementWithReceiver(receiver, index)) return NONE;
2051 return ABSENT;
2052 }
2053 // Named property.
2054 LookupResult result;
2055 Lookup(key, &result);
2056 return GetPropertyAttribute(receiver, &result, key, true);
2057 }
2058
2059
GetPropertyAttribute(JSObject * receiver,LookupResult * result,String * name,bool continue_search)2060 PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
2061 LookupResult* result,
2062 String* name,
2063 bool continue_search) {
2064 // Check access rights if needed.
2065 if (IsAccessCheckNeeded() &&
2066 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2067 return GetPropertyAttributeWithFailedAccessCheck(receiver,
2068 result,
2069 name,
2070 continue_search);
2071 }
2072 if (result->IsValid()) {
2073 switch (result->type()) {
2074 case NORMAL: // fall through
2075 case FIELD:
2076 case CONSTANT_FUNCTION:
2077 case CALLBACKS:
2078 return result->GetAttributes();
2079 case INTERCEPTOR:
2080 return result->holder()->
2081 GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
2082 case MAP_TRANSITION:
2083 case CONSTANT_TRANSITION:
2084 case NULL_DESCRIPTOR:
2085 return ABSENT;
2086 default:
2087 UNREACHABLE();
2088 break;
2089 }
2090 }
2091 return ABSENT;
2092 }
2093
2094
GetLocalPropertyAttribute(String * name)2095 PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) {
2096 // Check whether the name is an array index.
2097 uint32_t index = 0;
2098 if (name->AsArrayIndex(&index)) {
2099 if (HasLocalElement(index)) return NONE;
2100 return ABSENT;
2101 }
2102 // Named property.
2103 LookupResult result;
2104 LocalLookup(name, &result);
2105 return GetPropertyAttribute(this, &result, name, false);
2106 }
2107
2108
NormalizeProperties(PropertyNormalizationMode mode,int expected_additional_properties)2109 Object* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2110 int expected_additional_properties) {
2111 if (!HasFastProperties()) return this;
2112
2113 // The global object is always normalized.
2114 ASSERT(!IsGlobalObject());
2115
2116 // Allocate new content.
2117 int property_count = map()->NumberOfDescribedProperties();
2118 if (expected_additional_properties > 0) {
2119 property_count += expected_additional_properties;
2120 } else {
2121 property_count += 2; // Make space for two more properties.
2122 }
2123 Object* obj =
2124 StringDictionary::Allocate(property_count * 2);
2125 if (obj->IsFailure()) return obj;
2126 StringDictionary* dictionary = StringDictionary::cast(obj);
2127
2128 DescriptorArray* descs = map()->instance_descriptors();
2129 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2130 PropertyDetails details = descs->GetDetails(i);
2131 switch (details.type()) {
2132 case CONSTANT_FUNCTION: {
2133 PropertyDetails d =
2134 PropertyDetails(details.attributes(), NORMAL, details.index());
2135 Object* value = descs->GetConstantFunction(i);
2136 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2137 if (result->IsFailure()) return result;
2138 dictionary = StringDictionary::cast(result);
2139 break;
2140 }
2141 case FIELD: {
2142 PropertyDetails d =
2143 PropertyDetails(details.attributes(), NORMAL, details.index());
2144 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
2145 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2146 if (result->IsFailure()) return result;
2147 dictionary = StringDictionary::cast(result);
2148 break;
2149 }
2150 case CALLBACKS: {
2151 PropertyDetails d =
2152 PropertyDetails(details.attributes(), CALLBACKS, details.index());
2153 Object* value = descs->GetCallbacksObject(i);
2154 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2155 if (result->IsFailure()) return result;
2156 dictionary = StringDictionary::cast(result);
2157 break;
2158 }
2159 case MAP_TRANSITION:
2160 case CONSTANT_TRANSITION:
2161 case NULL_DESCRIPTOR:
2162 case INTERCEPTOR:
2163 break;
2164 default:
2165 UNREACHABLE();
2166 }
2167 }
2168
2169 // Copy the next enumeration index from instance descriptor.
2170 int index = map()->instance_descriptors()->NextEnumerationIndex();
2171 dictionary->SetNextEnumerationIndex(index);
2172
2173 // Allocate new map.
2174 obj = map()->CopyDropDescriptors();
2175 if (obj->IsFailure()) return obj;
2176 Map* new_map = Map::cast(obj);
2177
2178 // Clear inobject properties if needed by adjusting the instance size and
2179 // putting in a filler object instead of the inobject properties.
2180 if (mode == CLEAR_INOBJECT_PROPERTIES && map()->inobject_properties() > 0) {
2181 int instance_size_delta = map()->inobject_properties() * kPointerSize;
2182 int new_instance_size = map()->instance_size() - instance_size_delta;
2183 new_map->set_inobject_properties(0);
2184 new_map->set_instance_size(new_instance_size);
2185 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2186 instance_size_delta);
2187 }
2188 new_map->set_unused_property_fields(0);
2189
2190 // We have now successfully allocated all the necessary objects.
2191 // Changes can now be made with the guarantee that all of them take effect.
2192 set_map(new_map);
2193 map()->set_instance_descriptors(Heap::empty_descriptor_array());
2194
2195 set_properties(dictionary);
2196
2197 Counters::props_to_dictionary.Increment();
2198
2199 #ifdef DEBUG
2200 if (FLAG_trace_normalization) {
2201 PrintF("Object properties have been normalized:\n");
2202 Print();
2203 }
2204 #endif
2205 return this;
2206 }
2207
2208
TransformToFastProperties(int unused_property_fields)2209 Object* JSObject::TransformToFastProperties(int unused_property_fields) {
2210 if (HasFastProperties()) return this;
2211 ASSERT(!IsGlobalObject());
2212 return property_dictionary()->
2213 TransformPropertiesToFastFor(this, unused_property_fields);
2214 }
2215
2216
NormalizeElements()2217 Object* JSObject::NormalizeElements() {
2218 ASSERT(!HasPixelElements());
2219 if (HasDictionaryElements()) return this;
2220
2221 // Get number of entries.
2222 FixedArray* array = FixedArray::cast(elements());
2223
2224 // Compute the effective length.
2225 int length = IsJSArray() ?
2226 Smi::cast(JSArray::cast(this)->length())->value() :
2227 array->length();
2228 Object* obj = NumberDictionary::Allocate(length);
2229 if (obj->IsFailure()) return obj;
2230 NumberDictionary* dictionary = NumberDictionary::cast(obj);
2231 // Copy entries.
2232 for (int i = 0; i < length; i++) {
2233 Object* value = array->get(i);
2234 if (!value->IsTheHole()) {
2235 PropertyDetails details = PropertyDetails(NONE, NORMAL);
2236 Object* result = dictionary->AddNumberEntry(i, array->get(i), details);
2237 if (result->IsFailure()) return result;
2238 dictionary = NumberDictionary::cast(result);
2239 }
2240 }
2241 // Switch to using the dictionary as the backing storage for elements.
2242 set_elements(dictionary);
2243
2244 Counters::elements_to_dictionary.Increment();
2245
2246 #ifdef DEBUG
2247 if (FLAG_trace_normalization) {
2248 PrintF("Object elements have been normalized:\n");
2249 Print();
2250 }
2251 #endif
2252
2253 return this;
2254 }
2255
2256
DeletePropertyPostInterceptor(String * name,DeleteMode mode)2257 Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) {
2258 // Check local property, ignore interceptor.
2259 LookupResult result;
2260 LocalLookupRealNamedProperty(name, &result);
2261 if (!result.IsValid()) return Heap::true_value();
2262
2263 // Normalize object if needed.
2264 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2265 if (obj->IsFailure()) return obj;
2266
2267 return DeleteNormalizedProperty(name, mode);
2268 }
2269
2270
DeletePropertyWithInterceptor(String * name)2271 Object* JSObject::DeletePropertyWithInterceptor(String* name) {
2272 HandleScope scope;
2273 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2274 Handle<String> name_handle(name);
2275 Handle<JSObject> this_handle(this);
2276 if (!interceptor->deleter()->IsUndefined()) {
2277 v8::NamedPropertyDeleter deleter =
2278 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2279 Handle<Object> data_handle(interceptor->data());
2280 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2281 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
2282 v8::Utils::ToLocal(data_handle),
2283 v8::Utils::ToLocal(this_handle));
2284 v8::Handle<v8::Boolean> result;
2285 {
2286 // Leaving JavaScript.
2287 VMState state(EXTERNAL);
2288 result = deleter(v8::Utils::ToLocal(name_handle), info);
2289 }
2290 RETURN_IF_SCHEDULED_EXCEPTION();
2291 if (!result.IsEmpty()) {
2292 ASSERT(result->IsBoolean());
2293 return *v8::Utils::OpenHandle(*result);
2294 }
2295 }
2296 Object* raw_result =
2297 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
2298 RETURN_IF_SCHEDULED_EXCEPTION();
2299 return raw_result;
2300 }
2301
2302
DeleteElementPostInterceptor(uint32_t index,DeleteMode mode)2303 Object* JSObject::DeleteElementPostInterceptor(uint32_t index,
2304 DeleteMode mode) {
2305 ASSERT(!HasPixelElements());
2306 switch (GetElementsKind()) {
2307 case FAST_ELEMENTS: {
2308 uint32_t length = IsJSArray() ?
2309 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2310 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2311 if (index < length) {
2312 FixedArray::cast(elements())->set_the_hole(index);
2313 }
2314 break;
2315 }
2316 case DICTIONARY_ELEMENTS: {
2317 NumberDictionary* dictionary = element_dictionary();
2318 int entry = dictionary->FindEntry(index);
2319 if (entry != NumberDictionary::kNotFound) {
2320 return dictionary->DeleteProperty(entry, mode);
2321 }
2322 break;
2323 }
2324 default:
2325 UNREACHABLE();
2326 break;
2327 }
2328 return Heap::true_value();
2329 }
2330
2331
DeleteElementWithInterceptor(uint32_t index)2332 Object* JSObject::DeleteElementWithInterceptor(uint32_t index) {
2333 // Make sure that the top context does not change when doing
2334 // callbacks or interceptor calls.
2335 AssertNoContextChange ncc;
2336 HandleScope scope;
2337 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2338 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2339 v8::IndexedPropertyDeleter deleter =
2340 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2341 Handle<JSObject> this_handle(this);
2342 Handle<Object> data_handle(interceptor->data());
2343 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2344 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
2345 v8::Utils::ToLocal(data_handle),
2346 v8::Utils::ToLocal(this_handle));
2347 v8::Handle<v8::Boolean> result;
2348 {
2349 // Leaving JavaScript.
2350 VMState state(EXTERNAL);
2351 result = deleter(index, info);
2352 }
2353 RETURN_IF_SCHEDULED_EXCEPTION();
2354 if (!result.IsEmpty()) {
2355 ASSERT(result->IsBoolean());
2356 return *v8::Utils::OpenHandle(*result);
2357 }
2358 Object* raw_result =
2359 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
2360 RETURN_IF_SCHEDULED_EXCEPTION();
2361 return raw_result;
2362 }
2363
2364
DeleteElement(uint32_t index,DeleteMode mode)2365 Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
2366 // Check access rights if needed.
2367 if (IsAccessCheckNeeded() &&
2368 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
2369 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2370 return Heap::false_value();
2371 }
2372
2373 if (IsJSGlobalProxy()) {
2374 Object* proto = GetPrototype();
2375 if (proto->IsNull()) return Heap::false_value();
2376 ASSERT(proto->IsJSGlobalObject());
2377 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
2378 }
2379
2380 if (HasIndexedInterceptor()) {
2381 // Skip interceptor if forcing deletion.
2382 if (mode == FORCE_DELETION) {
2383 return DeleteElementPostInterceptor(index, mode);
2384 }
2385 return DeleteElementWithInterceptor(index);
2386 }
2387
2388 switch (GetElementsKind()) {
2389 case FAST_ELEMENTS: {
2390 uint32_t length = IsJSArray() ?
2391 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2392 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2393 if (index < length) {
2394 FixedArray::cast(elements())->set_the_hole(index);
2395 }
2396 break;
2397 }
2398 case PIXEL_ELEMENTS: {
2399 // Pixel elements cannot be deleted. Just silently ignore here.
2400 break;
2401 }
2402 case DICTIONARY_ELEMENTS: {
2403 NumberDictionary* dictionary = element_dictionary();
2404 int entry = dictionary->FindEntry(index);
2405 if (entry != NumberDictionary::kNotFound) {
2406 return dictionary->DeleteProperty(entry, mode);
2407 }
2408 break;
2409 }
2410 default:
2411 UNREACHABLE();
2412 break;
2413 }
2414 return Heap::true_value();
2415 }
2416
2417
DeleteProperty(String * name,DeleteMode mode)2418 Object* JSObject::DeleteProperty(String* name, DeleteMode mode) {
2419 // ECMA-262, 3rd, 8.6.2.5
2420 ASSERT(name->IsString());
2421
2422 // Check access rights if needed.
2423 if (IsAccessCheckNeeded() &&
2424 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2425 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2426 return Heap::false_value();
2427 }
2428
2429 if (IsJSGlobalProxy()) {
2430 Object* proto = GetPrototype();
2431 if (proto->IsNull()) return Heap::false_value();
2432 ASSERT(proto->IsJSGlobalObject());
2433 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
2434 }
2435
2436 uint32_t index = 0;
2437 if (name->AsArrayIndex(&index)) {
2438 return DeleteElement(index, mode);
2439 } else {
2440 LookupResult result;
2441 LocalLookup(name, &result);
2442 if (!result.IsValid()) return Heap::true_value();
2443 // Ignore attributes if forcing a deletion.
2444 if (result.IsDontDelete() && mode != FORCE_DELETION) {
2445 return Heap::false_value();
2446 }
2447 // Check for interceptor.
2448 if (result.type() == INTERCEPTOR) {
2449 // Skip interceptor if forcing a deletion.
2450 if (mode == FORCE_DELETION) {
2451 return DeletePropertyPostInterceptor(name, mode);
2452 }
2453 return DeletePropertyWithInterceptor(name);
2454 }
2455 if (!result.IsLoaded()) {
2456 return JSObject::cast(this)->DeleteLazyProperty(&result,
2457 name,
2458 mode);
2459 }
2460 // Normalize object if needed.
2461 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2462 if (obj->IsFailure()) return obj;
2463 // Make sure the properties are normalized before removing the entry.
2464 return DeleteNormalizedProperty(name, mode);
2465 }
2466 }
2467
2468
2469 // Check whether this object references another object.
ReferencesObject(Object * obj)2470 bool JSObject::ReferencesObject(Object* obj) {
2471 AssertNoAllocation no_alloc;
2472
2473 // Is the object the constructor for this object?
2474 if (map()->constructor() == obj) {
2475 return true;
2476 }
2477
2478 // Is the object the prototype for this object?
2479 if (map()->prototype() == obj) {
2480 return true;
2481 }
2482
2483 // Check if the object is among the named properties.
2484 Object* key = SlowReverseLookup(obj);
2485 if (key != Heap::undefined_value()) {
2486 return true;
2487 }
2488
2489 // Check if the object is among the indexed properties.
2490 switch (GetElementsKind()) {
2491 case PIXEL_ELEMENTS:
2492 // Raw pixels do not reference other objects.
2493 break;
2494 case FAST_ELEMENTS: {
2495 int length = IsJSArray() ?
2496 Smi::cast(JSArray::cast(this)->length())->value() :
2497 FixedArray::cast(elements())->length();
2498 for (int i = 0; i < length; i++) {
2499 Object* element = FixedArray::cast(elements())->get(i);
2500 if (!element->IsTheHole() && element == obj) {
2501 return true;
2502 }
2503 }
2504 break;
2505 }
2506 case DICTIONARY_ELEMENTS: {
2507 key = element_dictionary()->SlowReverseLookup(obj);
2508 if (key != Heap::undefined_value()) {
2509 return true;
2510 }
2511 break;
2512 }
2513 default:
2514 UNREACHABLE();
2515 break;
2516 }
2517
2518 // For functions check the context. Boilerplate functions do
2519 // not have to be traversed since they have no real context.
2520 if (IsJSFunction() && !JSFunction::cast(this)->IsBoilerplate()) {
2521 // Get the constructor function for arguments array.
2522 JSObject* arguments_boilerplate =
2523 Top::context()->global_context()->arguments_boilerplate();
2524 JSFunction* arguments_function =
2525 JSFunction::cast(arguments_boilerplate->map()->constructor());
2526
2527 // Get the context and don't check if it is the global context.
2528 JSFunction* f = JSFunction::cast(this);
2529 Context* context = f->context();
2530 if (context->IsGlobalContext()) {
2531 return false;
2532 }
2533
2534 // Check the non-special context slots.
2535 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2536 // Only check JS objects.
2537 if (context->get(i)->IsJSObject()) {
2538 JSObject* ctxobj = JSObject::cast(context->get(i));
2539 // If it is an arguments array check the content.
2540 if (ctxobj->map()->constructor() == arguments_function) {
2541 if (ctxobj->ReferencesObject(obj)) {
2542 return true;
2543 }
2544 } else if (ctxobj == obj) {
2545 return true;
2546 }
2547 }
2548 }
2549
2550 // Check the context extension if any.
2551 if (context->has_extension()) {
2552 return context->extension()->ReferencesObject(obj);
2553 }
2554 }
2555
2556 // No references to object.
2557 return false;
2558 }
2559
2560
2561 // Tests for the fast common case for property enumeration:
2562 // - this object has an enum cache
2563 // - this object has no elements
2564 // - no prototype has enumerable properties/elements
2565 // - neither this object nor any prototype has interceptors
IsSimpleEnum()2566 bool JSObject::IsSimpleEnum() {
2567 JSObject* arguments_boilerplate =
2568 Top::context()->global_context()->arguments_boilerplate();
2569 JSFunction* arguments_function =
2570 JSFunction::cast(arguments_boilerplate->map()->constructor());
2571 if (IsAccessCheckNeeded()) return false;
2572 if (map()->constructor() == arguments_function) return false;
2573
2574 for (Object* o = this;
2575 o != Heap::null_value();
2576 o = JSObject::cast(o)->GetPrototype()) {
2577 JSObject* curr = JSObject::cast(o);
2578 if (!curr->HasFastProperties()) return false;
2579 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
2580 if (curr->NumberOfEnumElements() > 0) return false;
2581 if (curr->HasNamedInterceptor()) return false;
2582 if (curr->HasIndexedInterceptor()) return false;
2583 if (curr != this) {
2584 FixedArray* curr_fixed_array =
2585 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
2586 if (curr_fixed_array->length() > 0) {
2587 return false;
2588 }
2589 }
2590 }
2591 return true;
2592 }
2593
2594
NumberOfDescribedProperties()2595 int Map::NumberOfDescribedProperties() {
2596 int result = 0;
2597 DescriptorArray* descs = instance_descriptors();
2598 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2599 if (descs->IsProperty(i)) result++;
2600 }
2601 return result;
2602 }
2603
2604
PropertyIndexFor(String * name)2605 int Map::PropertyIndexFor(String* name) {
2606 DescriptorArray* descs = instance_descriptors();
2607 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2608 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
2609 return descs->GetFieldIndex(i);
2610 }
2611 }
2612 return -1;
2613 }
2614
2615
NextFreePropertyIndex()2616 int Map::NextFreePropertyIndex() {
2617 int max_index = -1;
2618 DescriptorArray* descs = instance_descriptors();
2619 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2620 if (descs->GetType(i) == FIELD) {
2621 int current_index = descs->GetFieldIndex(i);
2622 if (current_index > max_index) max_index = current_index;
2623 }
2624 }
2625 return max_index + 1;
2626 }
2627
2628
FindAccessor(String * name)2629 AccessorDescriptor* Map::FindAccessor(String* name) {
2630 DescriptorArray* descs = instance_descriptors();
2631 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2632 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
2633 return descs->GetCallbacks(i);
2634 }
2635 }
2636 return NULL;
2637 }
2638
2639
LocalLookup(String * name,LookupResult * result)2640 void JSObject::LocalLookup(String* name, LookupResult* result) {
2641 ASSERT(name->IsString());
2642
2643 if (IsJSGlobalProxy()) {
2644 Object* proto = GetPrototype();
2645 if (proto->IsNull()) return result->NotFound();
2646 ASSERT(proto->IsJSGlobalObject());
2647 return JSObject::cast(proto)->LocalLookup(name, result);
2648 }
2649
2650 // Do not use inline caching if the object is a non-global object
2651 // that requires access checks.
2652 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
2653 result->DisallowCaching();
2654 }
2655
2656 // Check __proto__ before interceptor.
2657 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
2658 result->ConstantResult(this);
2659 return;
2660 }
2661
2662 // Check for lookup interceptor except when bootstrapping.
2663 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2664 result->InterceptorResult(this);
2665 return;
2666 }
2667
2668 LocalLookupRealNamedProperty(name, result);
2669 }
2670
2671
Lookup(String * name,LookupResult * result)2672 void JSObject::Lookup(String* name, LookupResult* result) {
2673 // Ecma-262 3rd 8.6.2.4
2674 for (Object* current = this;
2675 current != Heap::null_value();
2676 current = JSObject::cast(current)->GetPrototype()) {
2677 JSObject::cast(current)->LocalLookup(name, result);
2678 if (result->IsValid() && !result->IsTransitionType()) return;
2679 }
2680 result->NotFound();
2681 }
2682
2683
2684 // Search object and it's prototype chain for callback properties.
LookupCallback(String * name,LookupResult * result)2685 void JSObject::LookupCallback(String* name, LookupResult* result) {
2686 for (Object* current = this;
2687 current != Heap::null_value();
2688 current = JSObject::cast(current)->GetPrototype()) {
2689 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
2690 if (result->IsValid() && result->type() == CALLBACKS) return;
2691 }
2692 result->NotFound();
2693 }
2694
2695
DefineGetterSetter(String * name,PropertyAttributes attributes)2696 Object* JSObject::DefineGetterSetter(String* name,
2697 PropertyAttributes attributes) {
2698 // Make sure that the top context does not change when doing callbacks or
2699 // interceptor calls.
2700 AssertNoContextChange ncc;
2701
2702 // Check access rights if needed.
2703 if (IsAccessCheckNeeded() &&
2704 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
2705 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
2706 return Heap::undefined_value();
2707 }
2708
2709 // Try to flatten before operating on the string.
2710 name->TryFlattenIfNotFlat();
2711
2712 // Check if there is an API defined callback object which prohibits
2713 // callback overwriting in this object or it's prototype chain.
2714 // This mechanism is needed for instance in a browser setting, where
2715 // certain accessors such as window.location should not be allowed
2716 // to be overwritten because allowing overwriting could potentially
2717 // cause security problems.
2718 LookupResult callback_result;
2719 LookupCallback(name, &callback_result);
2720 if (callback_result.IsValid()) {
2721 Object* obj = callback_result.GetCallbackObject();
2722 if (obj->IsAccessorInfo() &&
2723 AccessorInfo::cast(obj)->prohibits_overwriting()) {
2724 return Heap::undefined_value();
2725 }
2726 }
2727
2728 uint32_t index;
2729 bool is_element = name->AsArrayIndex(&index);
2730 if (is_element && IsJSArray()) return Heap::undefined_value();
2731
2732 if (is_element) {
2733 switch (GetElementsKind()) {
2734 case FAST_ELEMENTS:
2735 break;
2736 case PIXEL_ELEMENTS:
2737 // Ignore getters and setters on pixel elements.
2738 return Heap::undefined_value();
2739 case DICTIONARY_ELEMENTS: {
2740 // Lookup the index.
2741 NumberDictionary* dictionary = element_dictionary();
2742 int entry = dictionary->FindEntry(index);
2743 if (entry != NumberDictionary::kNotFound) {
2744 Object* result = dictionary->ValueAt(entry);
2745 PropertyDetails details = dictionary->DetailsAt(entry);
2746 if (details.IsReadOnly()) return Heap::undefined_value();
2747 if (details.type() == CALLBACKS) {
2748 // Only accessors allowed as elements.
2749 ASSERT(result->IsFixedArray());
2750 return result;
2751 }
2752 }
2753 break;
2754 }
2755 default:
2756 UNREACHABLE();
2757 break;
2758 }
2759 } else {
2760 // Lookup the name.
2761 LookupResult result;
2762 LocalLookup(name, &result);
2763 if (result.IsValid()) {
2764 if (result.IsReadOnly()) return Heap::undefined_value();
2765 if (result.type() == CALLBACKS) {
2766 Object* obj = result.GetCallbackObject();
2767 if (obj->IsFixedArray()) return obj;
2768 }
2769 }
2770 }
2771
2772 // Allocate the fixed array to hold getter and setter.
2773 Object* structure = Heap::AllocateFixedArray(2, TENURED);
2774 if (structure->IsFailure()) return structure;
2775 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
2776
2777 if (is_element) {
2778 // Normalize object to make this operation simple.
2779 Object* ok = NormalizeElements();
2780 if (ok->IsFailure()) return ok;
2781
2782 // Update the dictionary with the new CALLBACKS property.
2783 Object* dict =
2784 element_dictionary()->Set(index, structure, details);
2785 if (dict->IsFailure()) return dict;
2786
2787 // If name is an index we need to stay in slow case.
2788 NumberDictionary* elements = NumberDictionary::cast(dict);
2789 elements->set_requires_slow_elements();
2790 // Set the potential new dictionary on the object.
2791 set_elements(NumberDictionary::cast(dict));
2792 } else {
2793 // Normalize object to make this operation simple.
2794 Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2795 if (ok->IsFailure()) return ok;
2796
2797 // For the global object allocate a new map to invalidate the global inline
2798 // caches which have a global property cell reference directly in the code.
2799 if (IsGlobalObject()) {
2800 Object* new_map = map()->CopyDropDescriptors();
2801 if (new_map->IsFailure()) return new_map;
2802 set_map(Map::cast(new_map));
2803 }
2804
2805 // Update the dictionary with the new CALLBACKS property.
2806 return SetNormalizedProperty(name, structure, details);
2807 }
2808
2809 return structure;
2810 }
2811
2812
DefineAccessor(String * name,bool is_getter,JSFunction * fun,PropertyAttributes attributes)2813 Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
2814 PropertyAttributes attributes) {
2815 // Check access rights if needed.
2816 if (IsAccessCheckNeeded() &&
2817 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2818 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2819 return Heap::undefined_value();
2820 }
2821
2822 if (IsJSGlobalProxy()) {
2823 Object* proto = GetPrototype();
2824 if (proto->IsNull()) return this;
2825 ASSERT(proto->IsJSGlobalObject());
2826 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
2827 fun, attributes);
2828 }
2829
2830 Object* array = DefineGetterSetter(name, attributes);
2831 if (array->IsFailure() || array->IsUndefined()) return array;
2832 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
2833 return this;
2834 }
2835
2836
LookupAccessor(String * name,bool is_getter)2837 Object* JSObject::LookupAccessor(String* name, bool is_getter) {
2838 // Make sure that the top context does not change when doing callbacks or
2839 // interceptor calls.
2840 AssertNoContextChange ncc;
2841
2842 // Check access rights if needed.
2843 if (IsAccessCheckNeeded() &&
2844 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2845 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2846 return Heap::undefined_value();
2847 }
2848
2849 // Make the lookup and include prototypes.
2850 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
2851 uint32_t index;
2852 if (name->AsArrayIndex(&index)) {
2853 for (Object* obj = this;
2854 obj != Heap::null_value();
2855 obj = JSObject::cast(obj)->GetPrototype()) {
2856 JSObject* js_object = JSObject::cast(obj);
2857 if (js_object->HasDictionaryElements()) {
2858 NumberDictionary* dictionary = js_object->element_dictionary();
2859 int entry = dictionary->FindEntry(index);
2860 if (entry != NumberDictionary::kNotFound) {
2861 Object* element = dictionary->ValueAt(entry);
2862 PropertyDetails details = dictionary->DetailsAt(entry);
2863 if (details.type() == CALLBACKS) {
2864 // Only accessors allowed as elements.
2865 return FixedArray::cast(element)->get(accessor_index);
2866 }
2867 }
2868 }
2869 }
2870 } else {
2871 for (Object* obj = this;
2872 obj != Heap::null_value();
2873 obj = JSObject::cast(obj)->GetPrototype()) {
2874 LookupResult result;
2875 JSObject::cast(obj)->LocalLookup(name, &result);
2876 if (result.IsValid()) {
2877 if (result.IsReadOnly()) return Heap::undefined_value();
2878 if (result.type() == CALLBACKS) {
2879 Object* obj = result.GetCallbackObject();
2880 if (obj->IsFixedArray()) {
2881 return FixedArray::cast(obj)->get(accessor_index);
2882 }
2883 }
2884 }
2885 }
2886 }
2887 return Heap::undefined_value();
2888 }
2889
2890
SlowReverseLookup(Object * value)2891 Object* JSObject::SlowReverseLookup(Object* value) {
2892 if (HasFastProperties()) {
2893 DescriptorArray* descs = map()->instance_descriptors();
2894 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2895 if (descs->GetType(i) == FIELD) {
2896 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
2897 return descs->GetKey(i);
2898 }
2899 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
2900 if (descs->GetConstantFunction(i) == value) {
2901 return descs->GetKey(i);
2902 }
2903 }
2904 }
2905 return Heap::undefined_value();
2906 } else {
2907 return property_dictionary()->SlowReverseLookup(value);
2908 }
2909 }
2910
2911
CopyDropDescriptors()2912 Object* Map::CopyDropDescriptors() {
2913 Object* result = Heap::AllocateMap(instance_type(), instance_size());
2914 if (result->IsFailure()) return result;
2915 Map::cast(result)->set_prototype(prototype());
2916 Map::cast(result)->set_constructor(constructor());
2917 // Don't copy descriptors, so map transitions always remain a forest.
2918 // If we retained the same descriptors we would have two maps
2919 // pointing to the same transition which is bad because the garbage
2920 // collector relies on being able to reverse pointers from transitions
2921 // to maps. If properties need to be retained use CopyDropTransitions.
2922 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
2923 // Please note instance_type and instance_size are set when allocated.
2924 Map::cast(result)->set_inobject_properties(inobject_properties());
2925 Map::cast(result)->set_unused_property_fields(unused_property_fields());
2926
2927 // If the map has pre-allocated properties always start out with a descriptor
2928 // array describing these properties.
2929 if (pre_allocated_property_fields() > 0) {
2930 ASSERT(constructor()->IsJSFunction());
2931 JSFunction* ctor = JSFunction::cast(constructor());
2932 Object* descriptors =
2933 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
2934 if (descriptors->IsFailure()) return descriptors;
2935 Map::cast(result)->set_instance_descriptors(
2936 DescriptorArray::cast(descriptors));
2937 Map::cast(result)->set_pre_allocated_property_fields(
2938 pre_allocated_property_fields());
2939 }
2940 Map::cast(result)->set_bit_field(bit_field());
2941 Map::cast(result)->set_bit_field2(bit_field2());
2942 Map::cast(result)->ClearCodeCache();
2943 return result;
2944 }
2945
2946
CopyDropTransitions()2947 Object* Map::CopyDropTransitions() {
2948 Object* new_map = CopyDropDescriptors();
2949 if (new_map->IsFailure()) return new_map;
2950 Object* descriptors = instance_descriptors()->RemoveTransitions();
2951 if (descriptors->IsFailure()) return descriptors;
2952 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
2953 return cast(new_map);
2954 }
2955
2956
UpdateCodeCache(String * name,Code * code)2957 Object* Map::UpdateCodeCache(String* name, Code* code) {
2958 ASSERT(code->ic_state() == MONOMORPHIC);
2959 FixedArray* cache = code_cache();
2960
2961 // When updating the code cache we disregard the type encoded in the
2962 // flags. This allows call constant stubs to overwrite call field
2963 // stubs, etc.
2964 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
2965
2966 // First check whether we can update existing code cache without
2967 // extending it.
2968 int length = cache->length();
2969 int deleted_index = -1;
2970 for (int i = 0; i < length; i += 2) {
2971 Object* key = cache->get(i);
2972 if (key->IsNull()) {
2973 if (deleted_index < 0) deleted_index = i;
2974 continue;
2975 }
2976 if (key->IsUndefined()) {
2977 if (deleted_index >= 0) i = deleted_index;
2978 cache->set(i + 0, name);
2979 cache->set(i + 1, code);
2980 return this;
2981 }
2982 if (name->Equals(String::cast(key))) {
2983 Code::Flags found = Code::cast(cache->get(i + 1))->flags();
2984 if (Code::RemoveTypeFromFlags(found) == flags) {
2985 cache->set(i + 1, code);
2986 return this;
2987 }
2988 }
2989 }
2990
2991 // Reached the end of the code cache. If there were deleted
2992 // elements, reuse the space for the first of them.
2993 if (deleted_index >= 0) {
2994 cache->set(deleted_index + 0, name);
2995 cache->set(deleted_index + 1, code);
2996 return this;
2997 }
2998
2999 // Extend the code cache with some new entries (at least one).
3000 int new_length = length + ((length >> 1) & ~1) + 2;
3001 ASSERT((new_length & 1) == 0); // must be a multiple of two
3002 Object* result = cache->CopySize(new_length);
3003 if (result->IsFailure()) return result;
3004
3005 // Add the (name, code) pair to the new cache.
3006 cache = FixedArray::cast(result);
3007 cache->set(length + 0, name);
3008 cache->set(length + 1, code);
3009 set_code_cache(cache);
3010 return this;
3011 }
3012
3013
FindInCodeCache(String * name,Code::Flags flags)3014 Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3015 FixedArray* cache = code_cache();
3016 int length = cache->length();
3017 for (int i = 0; i < length; i += 2) {
3018 Object* key = cache->get(i);
3019 // Skip deleted elements.
3020 if (key->IsNull()) continue;
3021 if (key->IsUndefined()) return key;
3022 if (name->Equals(String::cast(key))) {
3023 Code* code = Code::cast(cache->get(i + 1));
3024 if (code->flags() == flags) return code;
3025 }
3026 }
3027 return Heap::undefined_value();
3028 }
3029
3030
IndexInCodeCache(Code * code)3031 int Map::IndexInCodeCache(Code* code) {
3032 FixedArray* array = code_cache();
3033 int len = array->length();
3034 for (int i = 0; i < len; i += 2) {
3035 if (array->get(i + 1) == code) return i + 1;
3036 }
3037 return -1;
3038 }
3039
3040
RemoveFromCodeCache(int index)3041 void Map::RemoveFromCodeCache(int index) {
3042 FixedArray* array = code_cache();
3043 ASSERT(array->length() >= index && array->get(index)->IsCode());
3044 // Use null instead of undefined for deleted elements to distinguish
3045 // deleted elements from unused elements. This distinction is used
3046 // when looking up in the cache and when updating the cache.
3047 array->set_null(index - 1); // key
3048 array->set_null(index); // code
3049 }
3050
3051
FixedArrayIterateBody(ObjectVisitor * v)3052 void FixedArray::FixedArrayIterateBody(ObjectVisitor* v) {
3053 IteratePointers(v, kHeaderSize, kHeaderSize + length() * kPointerSize);
3054 }
3055
3056
HasKey(FixedArray * array,Object * key)3057 static bool HasKey(FixedArray* array, Object* key) {
3058 int len0 = array->length();
3059 for (int i = 0; i < len0; i++) {
3060 Object* element = array->get(i);
3061 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3062 if (element->IsString() &&
3063 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3064 return true;
3065 }
3066 }
3067 return false;
3068 }
3069
3070
AddKeysFromJSArray(JSArray * array)3071 Object* FixedArray::AddKeysFromJSArray(JSArray* array) {
3072 ASSERT(!array->HasPixelElements());
3073 switch (array->GetElementsKind()) {
3074 case JSObject::FAST_ELEMENTS:
3075 return UnionOfKeys(FixedArray::cast(array->elements()));
3076 case JSObject::DICTIONARY_ELEMENTS: {
3077 NumberDictionary* dict = array->element_dictionary();
3078 int size = dict->NumberOfElements();
3079
3080 // Allocate a temporary fixed array.
3081 Object* object = Heap::AllocateFixedArray(size);
3082 if (object->IsFailure()) return object;
3083 FixedArray* key_array = FixedArray::cast(object);
3084
3085 int capacity = dict->Capacity();
3086 int pos = 0;
3087 // Copy the elements from the JSArray to the temporary fixed array.
3088 for (int i = 0; i < capacity; i++) {
3089 if (dict->IsKey(dict->KeyAt(i))) {
3090 key_array->set(pos++, dict->ValueAt(i));
3091 }
3092 }
3093 // Compute the union of this and the temporary fixed array.
3094 return UnionOfKeys(key_array);
3095 }
3096 default:
3097 UNREACHABLE();
3098 }
3099 UNREACHABLE();
3100 return Heap::null_value(); // Failure case needs to "return" a value.
3101 }
3102
3103
UnionOfKeys(FixedArray * other)3104 Object* FixedArray::UnionOfKeys(FixedArray* other) {
3105 int len0 = length();
3106 int len1 = other->length();
3107 // Optimize if either is empty.
3108 if (len0 == 0) return other;
3109 if (len1 == 0) return this;
3110
3111 // Compute how many elements are not in this.
3112 int extra = 0;
3113 for (int y = 0; y < len1; y++) {
3114 Object* value = other->get(y);
3115 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3116 }
3117
3118 if (extra == 0) return this;
3119
3120 // Allocate the result
3121 Object* obj = Heap::AllocateFixedArray(len0 + extra);
3122 if (obj->IsFailure()) return obj;
3123 // Fill in the content
3124 FixedArray* result = FixedArray::cast(obj);
3125 WriteBarrierMode mode = result->GetWriteBarrierMode();
3126 for (int i = 0; i < len0; i++) {
3127 result->set(i, get(i), mode);
3128 }
3129 // Fill in the extra keys.
3130 int index = 0;
3131 for (int y = 0; y < len1; y++) {
3132 Object* value = other->get(y);
3133 if (!value->IsTheHole() && !HasKey(this, value)) {
3134 result->set(len0 + index, other->get(y), mode);
3135 index++;
3136 }
3137 }
3138 ASSERT(extra == index);
3139 return result;
3140 }
3141
3142
CopySize(int new_length)3143 Object* FixedArray::CopySize(int new_length) {
3144 if (new_length == 0) return Heap::empty_fixed_array();
3145 Object* obj = Heap::AllocateFixedArray(new_length);
3146 if (obj->IsFailure()) return obj;
3147 FixedArray* result = FixedArray::cast(obj);
3148 // Copy the content
3149 int len = length();
3150 if (new_length < len) len = new_length;
3151 result->set_map(map());
3152 WriteBarrierMode mode = result->GetWriteBarrierMode();
3153 for (int i = 0; i < len; i++) {
3154 result->set(i, get(i), mode);
3155 }
3156 return result;
3157 }
3158
3159
CopyTo(int pos,FixedArray * dest,int dest_pos,int len)3160 void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
3161 WriteBarrierMode mode = dest->GetWriteBarrierMode();
3162 for (int index = 0; index < len; index++) {
3163 dest->set(dest_pos+index, get(pos+index), mode);
3164 }
3165 }
3166
3167
3168 #ifdef DEBUG
IsEqualTo(FixedArray * other)3169 bool FixedArray::IsEqualTo(FixedArray* other) {
3170 if (length() != other->length()) return false;
3171 for (int i = 0 ; i < length(); ++i) {
3172 if (get(i) != other->get(i)) return false;
3173 }
3174 return true;
3175 }
3176 #endif
3177
3178
Allocate(int number_of_descriptors)3179 Object* DescriptorArray::Allocate(int number_of_descriptors) {
3180 if (number_of_descriptors == 0) {
3181 return Heap::empty_descriptor_array();
3182 }
3183 // Allocate the array of keys.
3184 Object* array = Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
3185 if (array->IsFailure()) return array;
3186 // Do not use DescriptorArray::cast on incomplete object.
3187 FixedArray* result = FixedArray::cast(array);
3188
3189 // Allocate the content array and set it in the descriptor array.
3190 array = Heap::AllocateFixedArray(number_of_descriptors << 1);
3191 if (array->IsFailure()) return array;
3192 result->set(kContentArrayIndex, array);
3193 result->set(kEnumerationIndexIndex,
3194 Smi::FromInt(PropertyDetails::kInitialIndex),
3195 SKIP_WRITE_BARRIER);
3196 return result;
3197 }
3198
3199
SetEnumCache(FixedArray * bridge_storage,FixedArray * new_cache)3200 void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3201 FixedArray* new_cache) {
3202 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3203 if (HasEnumCache()) {
3204 FixedArray::cast(get(kEnumerationIndexIndex))->
3205 set(kEnumCacheBridgeCacheIndex, new_cache);
3206 } else {
3207 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3208 FixedArray::cast(bridge_storage)->
3209 set(kEnumCacheBridgeCacheIndex, new_cache);
3210 fast_set(FixedArray::cast(bridge_storage),
3211 kEnumCacheBridgeEnumIndex,
3212 get(kEnumerationIndexIndex));
3213 set(kEnumerationIndexIndex, bridge_storage);
3214 }
3215 }
3216
3217
CopyInsert(Descriptor * descriptor,TransitionFlag transition_flag)3218 Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
3219 TransitionFlag transition_flag) {
3220 // Transitions are only kept when inserting another transition.
3221 // This precondition is not required by this function's implementation, but
3222 // is currently required by the semantics of maps, so we check it.
3223 // Conversely, we filter after replacing, so replacing a transition and
3224 // removing all other transitions is not supported.
3225 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3226 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
3227 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
3228
3229 // Ensure the key is a symbol.
3230 Object* result = descriptor->KeyToSymbol();
3231 if (result->IsFailure()) return result;
3232
3233 int transitions = 0;
3234 int null_descriptors = 0;
3235 if (remove_transitions) {
3236 for (int i = 0; i < number_of_descriptors(); i++) {
3237 if (IsTransition(i)) transitions++;
3238 if (IsNullDescriptor(i)) null_descriptors++;
3239 }
3240 } else {
3241 for (int i = 0; i < number_of_descriptors(); i++) {
3242 if (IsNullDescriptor(i)) null_descriptors++;
3243 }
3244 }
3245 int new_size = number_of_descriptors() - transitions - null_descriptors;
3246
3247 // If key is in descriptor, we replace it in-place when filtering.
3248 // Count a null descriptor for key as inserted, not replaced.
3249 int index = Search(descriptor->GetKey());
3250 const bool inserting = (index == kNotFound);
3251 const bool replacing = !inserting;
3252 bool keep_enumeration_index = false;
3253 if (inserting) {
3254 ++new_size;
3255 }
3256 if (replacing) {
3257 // We are replacing an existing descriptor. We keep the enumeration
3258 // index of a visible property.
3259 PropertyType t = PropertyDetails(GetDetails(index)).type();
3260 if (t == CONSTANT_FUNCTION ||
3261 t == FIELD ||
3262 t == CALLBACKS ||
3263 t == INTERCEPTOR) {
3264 keep_enumeration_index = true;
3265 } else if (remove_transitions) {
3266 // Replaced descriptor has been counted as removed if it is
3267 // a transition that will be replaced. Adjust count in this case.
3268 ++new_size;
3269 }
3270 }
3271 result = Allocate(new_size);
3272 if (result->IsFailure()) return result;
3273 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3274 // Set the enumeration index in the descriptors and set the enumeration index
3275 // in the result.
3276 int enumeration_index = NextEnumerationIndex();
3277 if (!descriptor->GetDetails().IsTransition()) {
3278 if (keep_enumeration_index) {
3279 descriptor->SetEnumerationIndex(
3280 PropertyDetails(GetDetails(index)).index());
3281 } else {
3282 descriptor->SetEnumerationIndex(enumeration_index);
3283 ++enumeration_index;
3284 }
3285 }
3286 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3287
3288 // Copy the descriptors, filtering out transitions and null descriptors,
3289 // and inserting or replacing a descriptor.
3290 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
3291 int from_index = 0;
3292 int to_index = 0;
3293
3294 for (; from_index < number_of_descriptors(); from_index++) {
3295 String* key = GetKey(from_index);
3296 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
3297 break;
3298 }
3299 if (IsNullDescriptor(from_index)) continue;
3300 if (remove_transitions && IsTransition(from_index)) continue;
3301 new_descriptors->CopyFrom(to_index++, this, from_index);
3302 }
3303
3304 new_descriptors->Set(to_index++, descriptor);
3305 if (replacing) from_index++;
3306
3307 for (; from_index < number_of_descriptors(); from_index++) {
3308 if (IsNullDescriptor(from_index)) continue;
3309 if (remove_transitions && IsTransition(from_index)) continue;
3310 new_descriptors->CopyFrom(to_index++, this, from_index);
3311 }
3312
3313 ASSERT(to_index == new_descriptors->number_of_descriptors());
3314 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
3315
3316 return new_descriptors;
3317 }
3318
3319
RemoveTransitions()3320 Object* DescriptorArray::RemoveTransitions() {
3321 // Remove all transitions and null descriptors. Return a copy of the array
3322 // with all transitions removed, or a Failure object if the new array could
3323 // not be allocated.
3324
3325 // Compute the size of the map transition entries to be removed.
3326 int num_removed = 0;
3327 for (int i = 0; i < number_of_descriptors(); i++) {
3328 if (!IsProperty(i)) num_removed++;
3329 }
3330
3331 // Allocate the new descriptor array.
3332 Object* result = Allocate(number_of_descriptors() - num_removed);
3333 if (result->IsFailure()) return result;
3334 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3335
3336 // Copy the content.
3337 int next_descriptor = 0;
3338 for (int i = 0; i < number_of_descriptors(); i++) {
3339 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
3340 }
3341 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
3342
3343 return new_descriptors;
3344 }
3345
3346
Sort()3347 void DescriptorArray::Sort() {
3348 // In-place heap sort.
3349 int len = number_of_descriptors();
3350
3351 // Bottom-up max-heap construction.
3352 for (int i = 1; i < len; ++i) {
3353 int child_index = i;
3354 while (child_index > 0) {
3355 int parent_index = ((child_index + 1) >> 1) - 1;
3356 uint32_t parent_hash = GetKey(parent_index)->Hash();
3357 uint32_t child_hash = GetKey(child_index)->Hash();
3358 if (parent_hash < child_hash) {
3359 Swap(parent_index, child_index);
3360 } else {
3361 break;
3362 }
3363 child_index = parent_index;
3364 }
3365 }
3366
3367 // Extract elements and create sorted array.
3368 for (int i = len - 1; i > 0; --i) {
3369 // Put max element at the back of the array.
3370 Swap(0, i);
3371 // Sift down the new top element.
3372 int parent_index = 0;
3373 while (true) {
3374 int child_index = ((parent_index + 1) << 1) - 1;
3375 if (child_index >= i) break;
3376 uint32_t child1_hash = GetKey(child_index)->Hash();
3377 uint32_t child2_hash = GetKey(child_index + 1)->Hash();
3378 uint32_t parent_hash = GetKey(parent_index)->Hash();
3379 if (child_index + 1 >= i || child1_hash > child2_hash) {
3380 if (parent_hash > child1_hash) break;
3381 Swap(parent_index, child_index);
3382 parent_index = child_index;
3383 } else {
3384 if (parent_hash > child2_hash) break;
3385 Swap(parent_index, child_index + 1);
3386 parent_index = child_index + 1;
3387 }
3388 }
3389 }
3390
3391 SLOW_ASSERT(IsSortedNoDuplicates());
3392 }
3393
3394
BinarySearch(String * name,int low,int high)3395 int DescriptorArray::BinarySearch(String* name, int low, int high) {
3396 uint32_t hash = name->Hash();
3397
3398 while (low <= high) {
3399 int mid = (low + high) / 2;
3400 String* mid_name = GetKey(mid);
3401 uint32_t mid_hash = mid_name->Hash();
3402
3403 if (mid_hash > hash) {
3404 high = mid - 1;
3405 continue;
3406 }
3407 if (mid_hash < hash) {
3408 low = mid + 1;
3409 continue;
3410 }
3411 // Found an element with the same hash-code.
3412 ASSERT(hash == mid_hash);
3413 // There might be more, so we find the first one and
3414 // check them all to see if we have a match.
3415 if (name == mid_name && !is_null_descriptor(mid)) return mid;
3416 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
3417 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
3418 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
3419 }
3420 break;
3421 }
3422 return kNotFound;
3423 }
3424
3425
LinearSearch(String * name,int len)3426 int DescriptorArray::LinearSearch(String* name, int len) {
3427 uint32_t hash = name->Hash();
3428 for (int number = 0; number < len; number++) {
3429 String* entry = GetKey(number);
3430 if ((entry->Hash() == hash) &&
3431 name->Equals(entry) &&
3432 !is_null_descriptor(number)) {
3433 return number;
3434 }
3435 }
3436 return kNotFound;
3437 }
3438
3439
3440 #ifdef DEBUG
IsEqualTo(DescriptorArray * other)3441 bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
3442 if (IsEmpty()) return other->IsEmpty();
3443 if (other->IsEmpty()) return false;
3444 if (length() != other->length()) return false;
3445 for (int i = 0; i < length(); ++i) {
3446 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
3447 }
3448 return GetContentArray()->IsEqualTo(other->GetContentArray());
3449 }
3450 #endif
3451
3452
3453 static StaticResource<StringInputBuffer> string_input_buffer;
3454
3455
LooksValid()3456 bool String::LooksValid() {
3457 if (!Heap::Contains(this)) return false;
3458 return true;
3459 }
3460
3461
Utf8Length()3462 int String::Utf8Length() {
3463 if (IsAsciiRepresentation()) return length();
3464 // Attempt to flatten before accessing the string. It probably
3465 // doesn't make Utf8Length faster, but it is very likely that
3466 // the string will be accessed later (for example by WriteUtf8)
3467 // so it's still a good idea.
3468 TryFlattenIfNotFlat();
3469 Access<StringInputBuffer> buffer(&string_input_buffer);
3470 buffer->Reset(0, this);
3471 int result = 0;
3472 while (buffer->has_more())
3473 result += unibrow::Utf8::Length(buffer->GetNext());
3474 return result;
3475 }
3476
3477
ToAsciiVector()3478 Vector<const char> String::ToAsciiVector() {
3479 ASSERT(IsAsciiRepresentation());
3480 ASSERT(IsFlat());
3481
3482 int offset = 0;
3483 int length = this->length();
3484 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3485 String* string = this;
3486 if (string_tag == kSlicedStringTag) {
3487 SlicedString* sliced = SlicedString::cast(string);
3488 offset += sliced->start();
3489 string = sliced->buffer();
3490 string_tag = StringShape(string).representation_tag();
3491 } else if (string_tag == kConsStringTag) {
3492 ConsString* cons = ConsString::cast(string);
3493 ASSERT(cons->second()->length() == 0);
3494 string = cons->first();
3495 string_tag = StringShape(string).representation_tag();
3496 }
3497 if (string_tag == kSeqStringTag) {
3498 SeqAsciiString* seq = SeqAsciiString::cast(string);
3499 char* start = seq->GetChars();
3500 return Vector<const char>(start + offset, length);
3501 }
3502 ASSERT(string_tag == kExternalStringTag);
3503 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
3504 const char* start = ext->resource()->data();
3505 return Vector<const char>(start + offset, length);
3506 }
3507
3508
ToUC16Vector()3509 Vector<const uc16> String::ToUC16Vector() {
3510 ASSERT(IsTwoByteRepresentation());
3511 ASSERT(IsFlat());
3512
3513 int offset = 0;
3514 int length = this->length();
3515 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3516 String* string = this;
3517 if (string_tag == kSlicedStringTag) {
3518 SlicedString* sliced = SlicedString::cast(string);
3519 offset += sliced->start();
3520 string = String::cast(sliced->buffer());
3521 string_tag = StringShape(string).representation_tag();
3522 } else if (string_tag == kConsStringTag) {
3523 ConsString* cons = ConsString::cast(string);
3524 ASSERT(cons->second()->length() == 0);
3525 string = cons->first();
3526 string_tag = StringShape(string).representation_tag();
3527 }
3528 if (string_tag == kSeqStringTag) {
3529 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
3530 return Vector<const uc16>(seq->GetChars() + offset, length);
3531 }
3532 ASSERT(string_tag == kExternalStringTag);
3533 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
3534 const uc16* start =
3535 reinterpret_cast<const uc16*>(ext->resource()->data());
3536 return Vector<const uc16>(start + offset, length);
3537 }
3538
3539
ToCString(AllowNullsFlag allow_nulls,RobustnessFlag robust_flag,int offset,int length,int * length_return)3540 SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3541 RobustnessFlag robust_flag,
3542 int offset,
3543 int length,
3544 int* length_return) {
3545 ASSERT(NativeAllocationChecker::allocation_allowed());
3546 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3547 return SmartPointer<char>(NULL);
3548 }
3549
3550 // Negative length means the to the end of the string.
3551 if (length < 0) length = kMaxInt - offset;
3552
3553 // Compute the size of the UTF-8 string. Start at the specified offset.
3554 Access<StringInputBuffer> buffer(&string_input_buffer);
3555 buffer->Reset(offset, this);
3556 int character_position = offset;
3557 int utf8_bytes = 0;
3558 while (buffer->has_more()) {
3559 uint16_t character = buffer->GetNext();
3560 if (character_position < offset + length) {
3561 utf8_bytes += unibrow::Utf8::Length(character);
3562 }
3563 character_position++;
3564 }
3565
3566 if (length_return) {
3567 *length_return = utf8_bytes;
3568 }
3569
3570 char* result = NewArray<char>(utf8_bytes + 1);
3571
3572 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
3573 buffer->Rewind();
3574 buffer->Seek(offset);
3575 character_position = offset;
3576 int utf8_byte_position = 0;
3577 while (buffer->has_more()) {
3578 uint16_t character = buffer->GetNext();
3579 if (character_position < offset + length) {
3580 if (allow_nulls == DISALLOW_NULLS && character == 0) {
3581 character = ' ';
3582 }
3583 utf8_byte_position +=
3584 unibrow::Utf8::Encode(result + utf8_byte_position, character);
3585 }
3586 character_position++;
3587 }
3588 result[utf8_byte_position] = 0;
3589 return SmartPointer<char>(result);
3590 }
3591
3592
ToCString(AllowNullsFlag allow_nulls,RobustnessFlag robust_flag,int * length_return)3593 SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3594 RobustnessFlag robust_flag,
3595 int* length_return) {
3596 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
3597 }
3598
3599
GetTwoByteData()3600 const uc16* String::GetTwoByteData() {
3601 return GetTwoByteData(0);
3602 }
3603
3604
GetTwoByteData(unsigned start)3605 const uc16* String::GetTwoByteData(unsigned start) {
3606 ASSERT(!IsAsciiRepresentation());
3607 switch (StringShape(this).representation_tag()) {
3608 case kSeqStringTag:
3609 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
3610 case kExternalStringTag:
3611 return ExternalTwoByteString::cast(this)->
3612 ExternalTwoByteStringGetData(start);
3613 case kSlicedStringTag: {
3614 SlicedString* sliced_string = SlicedString::cast(this);
3615 String* buffer = sliced_string->buffer();
3616 if (StringShape(buffer).IsCons()) {
3617 ConsString* cs = ConsString::cast(buffer);
3618 // Flattened string.
3619 ASSERT(cs->second()->length() == 0);
3620 buffer = cs->first();
3621 }
3622 return buffer->GetTwoByteData(start + sliced_string->start());
3623 }
3624 case kConsStringTag:
3625 UNREACHABLE();
3626 return NULL;
3627 }
3628 UNREACHABLE();
3629 return NULL;
3630 }
3631
3632
ToWideCString(RobustnessFlag robust_flag)3633 SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
3634 ASSERT(NativeAllocationChecker::allocation_allowed());
3635
3636 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3637 return SmartPointer<uc16>();
3638 }
3639
3640 Access<StringInputBuffer> buffer(&string_input_buffer);
3641 buffer->Reset(this);
3642
3643 uc16* result = NewArray<uc16>(length() + 1);
3644
3645 int i = 0;
3646 while (buffer->has_more()) {
3647 uint16_t character = buffer->GetNext();
3648 result[i++] = character;
3649 }
3650 result[i] = 0;
3651 return SmartPointer<uc16>(result);
3652 }
3653
3654
SeqTwoByteStringGetData(unsigned start)3655 const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
3656 return reinterpret_cast<uc16*>(
3657 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
3658 }
3659
3660
SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)3661 void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
3662 unsigned* offset_ptr,
3663 unsigned max_chars) {
3664 unsigned chars_read = 0;
3665 unsigned offset = *offset_ptr;
3666 while (chars_read < max_chars) {
3667 uint16_t c = *reinterpret_cast<uint16_t*>(
3668 reinterpret_cast<char*>(this) -
3669 kHeapObjectTag + kHeaderSize + offset * kShortSize);
3670 if (c <= kMaxAsciiCharCode) {
3671 // Fast case for ASCII characters. Cursor is an input output argument.
3672 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
3673 rbb->util_buffer,
3674 rbb->capacity,
3675 rbb->cursor)) {
3676 break;
3677 }
3678 } else {
3679 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
3680 rbb->util_buffer,
3681 rbb->capacity,
3682 rbb->cursor)) {
3683 break;
3684 }
3685 }
3686 offset++;
3687 chars_read++;
3688 }
3689 *offset_ptr = offset;
3690 rbb->remaining += chars_read;
3691 }
3692
3693
SeqAsciiStringReadBlock(unsigned * remaining,unsigned * offset_ptr,unsigned max_chars)3694 const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
3695 unsigned* remaining,
3696 unsigned* offset_ptr,
3697 unsigned max_chars) {
3698 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
3699 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
3700 *remaining = max_chars;
3701 *offset_ptr += max_chars;
3702 return b;
3703 }
3704
3705
3706 // This will iterate unless the block of string data spans two 'halves' of
3707 // a ConsString, in which case it will recurse. Since the block of string
3708 // data to be read has a maximum size this limits the maximum recursion
3709 // depth to something sane. Since C++ does not have tail call recursion
3710 // elimination, the iteration must be explicit. Since this is not an
3711 // -IntoBuffer method it can delegate to one of the efficient
3712 // *AsciiStringReadBlock routines.
ConsStringReadBlock(ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)3713 const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
3714 unsigned* offset_ptr,
3715 unsigned max_chars) {
3716 ConsString* current = this;
3717 unsigned offset = *offset_ptr;
3718 int offset_correction = 0;
3719
3720 while (true) {
3721 String* left = current->first();
3722 unsigned left_length = (unsigned)left->length();
3723 if (left_length > offset &&
3724 (max_chars <= left_length - offset ||
3725 (rbb->capacity <= left_length - offset &&
3726 (max_chars = left_length - offset, true)))) { // comma operator!
3727 // Left hand side only - iterate unless we have reached the bottom of
3728 // the cons tree. The assignment on the left of the comma operator is
3729 // in order to make use of the fact that the -IntoBuffer routines can
3730 // produce at most 'capacity' characters. This enables us to postpone
3731 // the point where we switch to the -IntoBuffer routines (below) in order
3732 // to maximize the chances of delegating a big chunk of work to the
3733 // efficient *AsciiStringReadBlock routines.
3734 if (StringShape(left).IsCons()) {
3735 current = ConsString::cast(left);
3736 continue;
3737 } else {
3738 const unibrow::byte* answer =
3739 String::ReadBlock(left, rbb, &offset, max_chars);
3740 *offset_ptr = offset + offset_correction;
3741 return answer;
3742 }
3743 } else if (left_length <= offset) {
3744 // Right hand side only - iterate unless we have reached the bottom of
3745 // the cons tree.
3746 String* right = current->second();
3747 offset -= left_length;
3748 offset_correction += left_length;
3749 if (StringShape(right).IsCons()) {
3750 current = ConsString::cast(right);
3751 continue;
3752 } else {
3753 const unibrow::byte* answer =
3754 String::ReadBlock(right, rbb, &offset, max_chars);
3755 *offset_ptr = offset + offset_correction;
3756 return answer;
3757 }
3758 } else {
3759 // The block to be read spans two sides of the ConsString, so we call the
3760 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
3761 // are able to assemble data from several part strings because they use
3762 // the util_buffer to store their data and never return direct pointers
3763 // to their storage. We don't try to read more than the buffer capacity
3764 // here or we can get too much recursion.
3765 ASSERT(rbb->remaining == 0);
3766 ASSERT(rbb->cursor == 0);
3767 current->ConsStringReadBlockIntoBuffer(
3768 rbb,
3769 &offset,
3770 max_chars > rbb->capacity ? rbb->capacity : max_chars);
3771 *offset_ptr = offset + offset_correction;
3772 return rbb->util_buffer;
3773 }
3774 }
3775 }
3776
3777
SlicedStringReadBlock(ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)3778 const unibrow::byte* SlicedString::SlicedStringReadBlock(ReadBlockBuffer* rbb,
3779 unsigned* offset_ptr,
3780 unsigned max_chars) {
3781 String* backing = buffer();
3782 unsigned offset = start() + *offset_ptr;
3783 unsigned length = backing->length();
3784 if (max_chars > length - offset) {
3785 max_chars = length - offset;
3786 }
3787 const unibrow::byte* answer =
3788 String::ReadBlock(backing, rbb, &offset, max_chars);
3789 *offset_ptr = offset - start();
3790 return answer;
3791 }
3792
3793
ExternalAsciiStringGet(int index)3794 uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
3795 ASSERT(index >= 0 && index < length());
3796 return resource()->data()[index];
3797 }
3798
3799
ExternalAsciiStringReadBlock(unsigned * remaining,unsigned * offset_ptr,unsigned max_chars)3800 const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
3801 unsigned* remaining,
3802 unsigned* offset_ptr,
3803 unsigned max_chars) {
3804 // Cast const char* to unibrow::byte* (signedness difference).
3805 const unibrow::byte* b =
3806 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
3807 *remaining = max_chars;
3808 *offset_ptr += max_chars;
3809 return b;
3810 }
3811
3812
ExternalTwoByteStringGetData(unsigned start)3813 const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
3814 unsigned start) {
3815 return resource()->data() + start;
3816 }
3817
3818
ExternalTwoByteStringGet(int index)3819 uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
3820 ASSERT(index >= 0 && index < length());
3821 return resource()->data()[index];
3822 }
3823
3824
ExternalTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)3825 void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
3826 ReadBlockBuffer* rbb,
3827 unsigned* offset_ptr,
3828 unsigned max_chars) {
3829 unsigned chars_read = 0;
3830 unsigned offset = *offset_ptr;
3831 const uint16_t* data = resource()->data();
3832 while (chars_read < max_chars) {
3833 uint16_t c = data[offset];
3834 if (c <= kMaxAsciiCharCode) {
3835 // Fast case for ASCII characters. Cursor is an input output argument.
3836 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
3837 rbb->util_buffer,
3838 rbb->capacity,
3839 rbb->cursor))
3840 break;
3841 } else {
3842 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
3843 rbb->util_buffer,
3844 rbb->capacity,
3845 rbb->cursor))
3846 break;
3847 }
3848 offset++;
3849 chars_read++;
3850 }
3851 *offset_ptr = offset;
3852 rbb->remaining += chars_read;
3853 }
3854
3855
SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)3856 void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
3857 unsigned* offset_ptr,
3858 unsigned max_chars) {
3859 unsigned capacity = rbb->capacity - rbb->cursor;
3860 if (max_chars > capacity) max_chars = capacity;
3861 memcpy(rbb->util_buffer + rbb->cursor,
3862 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
3863 *offset_ptr * kCharSize,
3864 max_chars);
3865 rbb->remaining += max_chars;
3866 *offset_ptr += max_chars;
3867 rbb->cursor += max_chars;
3868 }
3869
3870
ExternalAsciiStringReadBlockIntoBuffer(ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)3871 void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
3872 ReadBlockBuffer* rbb,
3873 unsigned* offset_ptr,
3874 unsigned max_chars) {
3875 unsigned capacity = rbb->capacity - rbb->cursor;
3876 if (max_chars > capacity) max_chars = capacity;
3877 memcpy(rbb->util_buffer + rbb->cursor,
3878 resource()->data() + *offset_ptr,
3879 max_chars);
3880 rbb->remaining += max_chars;
3881 *offset_ptr += max_chars;
3882 rbb->cursor += max_chars;
3883 }
3884
3885
3886 // This method determines the type of string involved and then copies
3887 // a whole chunk of characters into a buffer, or returns a pointer to a buffer
3888 // where they can be found. The pointer is not necessarily valid across a GC
3889 // (see AsciiStringReadBlock).
ReadBlock(String * input,ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)3890 const unibrow::byte* String::ReadBlock(String* input,
3891 ReadBlockBuffer* rbb,
3892 unsigned* offset_ptr,
3893 unsigned max_chars) {
3894 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
3895 if (max_chars == 0) {
3896 rbb->remaining = 0;
3897 return NULL;
3898 }
3899 switch (StringShape(input).representation_tag()) {
3900 case kSeqStringTag:
3901 if (input->IsAsciiRepresentation()) {
3902 SeqAsciiString* str = SeqAsciiString::cast(input);
3903 return str->SeqAsciiStringReadBlock(&rbb->remaining,
3904 offset_ptr,
3905 max_chars);
3906 } else {
3907 SeqTwoByteString* str = SeqTwoByteString::cast(input);
3908 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
3909 offset_ptr,
3910 max_chars);
3911 return rbb->util_buffer;
3912 }
3913 case kConsStringTag:
3914 return ConsString::cast(input)->ConsStringReadBlock(rbb,
3915 offset_ptr,
3916 max_chars);
3917 case kSlicedStringTag:
3918 return SlicedString::cast(input)->SlicedStringReadBlock(rbb,
3919 offset_ptr,
3920 max_chars);
3921 case kExternalStringTag:
3922 if (input->IsAsciiRepresentation()) {
3923 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
3924 &rbb->remaining,
3925 offset_ptr,
3926 max_chars);
3927 } else {
3928 ExternalTwoByteString::cast(input)->
3929 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
3930 offset_ptr,
3931 max_chars);
3932 return rbb->util_buffer;
3933 }
3934 default:
3935 break;
3936 }
3937
3938 UNREACHABLE();
3939 return 0;
3940 }
3941
3942
3943 FlatStringReader* FlatStringReader::top_ = NULL;
3944
3945
FlatStringReader(Handle<String> str)3946 FlatStringReader::FlatStringReader(Handle<String> str)
3947 : str_(str.location()),
3948 length_(str->length()),
3949 prev_(top_) {
3950 top_ = this;
3951 RefreshState();
3952 }
3953
3954
FlatStringReader(Vector<const char> input)3955 FlatStringReader::FlatStringReader(Vector<const char> input)
3956 : str_(NULL),
3957 is_ascii_(true),
3958 length_(input.length()),
3959 start_(input.start()),
3960 prev_(top_) {
3961 top_ = this;
3962 }
3963
3964
~FlatStringReader()3965 FlatStringReader::~FlatStringReader() {
3966 ASSERT_EQ(top_, this);
3967 top_ = prev_;
3968 }
3969
3970
RefreshState()3971 void FlatStringReader::RefreshState() {
3972 if (str_ == NULL) return;
3973 Handle<String> str(str_);
3974 ASSERT(str->IsFlat());
3975 is_ascii_ = str->IsAsciiRepresentation();
3976 if (is_ascii_) {
3977 start_ = str->ToAsciiVector().start();
3978 } else {
3979 start_ = str->ToUC16Vector().start();
3980 }
3981 }
3982
3983
PostGarbageCollectionProcessing()3984 void FlatStringReader::PostGarbageCollectionProcessing() {
3985 FlatStringReader* current = top_;
3986 while (current != NULL) {
3987 current->RefreshState();
3988 current = current->prev_;
3989 }
3990 }
3991
3992
Seek(unsigned pos)3993 void StringInputBuffer::Seek(unsigned pos) {
3994 Reset(pos, input_);
3995 }
3996
3997
Seek(unsigned pos)3998 void SafeStringInputBuffer::Seek(unsigned pos) {
3999 Reset(pos, input_);
4000 }
4001
4002
4003 // This method determines the type of string involved and then copies
4004 // a whole chunk of characters into a buffer. It can be used with strings
4005 // that have been glued together to form a ConsString and which must cooperate
4006 // to fill up a buffer.
ReadBlockIntoBuffer(String * input,ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)4007 void String::ReadBlockIntoBuffer(String* input,
4008 ReadBlockBuffer* rbb,
4009 unsigned* offset_ptr,
4010 unsigned max_chars) {
4011 ASSERT(*offset_ptr <= (unsigned)input->length());
4012 if (max_chars == 0) return;
4013
4014 switch (StringShape(input).representation_tag()) {
4015 case kSeqStringTag:
4016 if (input->IsAsciiRepresentation()) {
4017 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4018 offset_ptr,
4019 max_chars);
4020 return;
4021 } else {
4022 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4023 offset_ptr,
4024 max_chars);
4025 return;
4026 }
4027 case kConsStringTag:
4028 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4029 offset_ptr,
4030 max_chars);
4031 return;
4032 case kSlicedStringTag:
4033 SlicedString::cast(input)->SlicedStringReadBlockIntoBuffer(rbb,
4034 offset_ptr,
4035 max_chars);
4036 return;
4037 case kExternalStringTag:
4038 if (input->IsAsciiRepresentation()) {
4039 ExternalAsciiString::cast(input)->
4040 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4041 } else {
4042 ExternalTwoByteString::cast(input)->
4043 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4044 offset_ptr,
4045 max_chars);
4046 }
4047 return;
4048 default:
4049 break;
4050 }
4051
4052 UNREACHABLE();
4053 return;
4054 }
4055
4056
ReadBlock(String * input,unibrow::byte * util_buffer,unsigned capacity,unsigned * remaining,unsigned * offset_ptr)4057 const unibrow::byte* String::ReadBlock(String* input,
4058 unibrow::byte* util_buffer,
4059 unsigned capacity,
4060 unsigned* remaining,
4061 unsigned* offset_ptr) {
4062 ASSERT(*offset_ptr <= (unsigned)input->length());
4063 unsigned chars = input->length() - *offset_ptr;
4064 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4065 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4066 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4067 *remaining = rbb.remaining;
4068 return answer;
4069 }
4070
4071
ReadBlock(String ** raw_input,unibrow::byte * util_buffer,unsigned capacity,unsigned * remaining,unsigned * offset_ptr)4072 const unibrow::byte* String::ReadBlock(String** raw_input,
4073 unibrow::byte* util_buffer,
4074 unsigned capacity,
4075 unsigned* remaining,
4076 unsigned* offset_ptr) {
4077 Handle<String> input(raw_input);
4078 ASSERT(*offset_ptr <= (unsigned)input->length());
4079 unsigned chars = input->length() - *offset_ptr;
4080 if (chars > capacity) chars = capacity;
4081 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4082 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4083 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4084 *remaining = rbb.remaining;
4085 return rbb.util_buffer;
4086 }
4087
4088
4089 // This will iterate unless the block of string data spans two 'halves' of
4090 // a ConsString, in which case it will recurse. Since the block of string
4091 // data to be read has a maximum size this limits the maximum recursion
4092 // depth to something sane. Since C++ does not have tail call recursion
4093 // elimination, the iteration must be explicit.
ConsStringReadBlockIntoBuffer(ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)4094 void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4095 unsigned* offset_ptr,
4096 unsigned max_chars) {
4097 ConsString* current = this;
4098 unsigned offset = *offset_ptr;
4099 int offset_correction = 0;
4100
4101 while (true) {
4102 String* left = current->first();
4103 unsigned left_length = (unsigned)left->length();
4104 if (left_length > offset &&
4105 max_chars <= left_length - offset) {
4106 // Left hand side only - iterate unless we have reached the bottom of
4107 // the cons tree.
4108 if (StringShape(left).IsCons()) {
4109 current = ConsString::cast(left);
4110 continue;
4111 } else {
4112 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4113 *offset_ptr = offset + offset_correction;
4114 return;
4115 }
4116 } else if (left_length <= offset) {
4117 // Right hand side only - iterate unless we have reached the bottom of
4118 // the cons tree.
4119 offset -= left_length;
4120 offset_correction += left_length;
4121 String* right = current->second();
4122 if (StringShape(right).IsCons()) {
4123 current = ConsString::cast(right);
4124 continue;
4125 } else {
4126 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4127 *offset_ptr = offset + offset_correction;
4128 return;
4129 }
4130 } else {
4131 // The block to be read spans two sides of the ConsString, so we recurse.
4132 // First recurse on the left.
4133 max_chars -= left_length - offset;
4134 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4135 // We may have reached the max or there may not have been enough space
4136 // in the buffer for the characters in the left hand side.
4137 if (offset == left_length) {
4138 // Recurse on the right.
4139 String* right = String::cast(current->second());
4140 offset -= left_length;
4141 offset_correction += left_length;
4142 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4143 }
4144 *offset_ptr = offset + offset_correction;
4145 return;
4146 }
4147 }
4148 }
4149
4150
SlicedStringReadBlockIntoBuffer(ReadBlockBuffer * rbb,unsigned * offset_ptr,unsigned max_chars)4151 void SlicedString::SlicedStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4152 unsigned* offset_ptr,
4153 unsigned max_chars) {
4154 String* backing = buffer();
4155 unsigned offset = start() + *offset_ptr;
4156 unsigned length = backing->length();
4157 if (max_chars > length - offset) {
4158 max_chars = length - offset;
4159 }
4160 String::ReadBlockIntoBuffer(backing, rbb, &offset, max_chars);
4161 *offset_ptr = offset - start();
4162 }
4163
4164
ConsStringIterateBody(ObjectVisitor * v)4165 void ConsString::ConsStringIterateBody(ObjectVisitor* v) {
4166 IteratePointers(v, kFirstOffset, kSecondOffset + kPointerSize);
4167 }
4168
4169
JSGlobalPropertyCellIterateBody(ObjectVisitor * v)4170 void JSGlobalPropertyCell::JSGlobalPropertyCellIterateBody(ObjectVisitor* v) {
4171 IteratePointers(v, kValueOffset, kValueOffset + kPointerSize);
4172 }
4173
4174
ConsStringGet(int index)4175 uint16_t ConsString::ConsStringGet(int index) {
4176 ASSERT(index >= 0 && index < this->length());
4177
4178 // Check for a flattened cons string
4179 if (second()->length() == 0) {
4180 String* left = first();
4181 return left->Get(index);
4182 }
4183
4184 String* string = String::cast(this);
4185
4186 while (true) {
4187 if (StringShape(string).IsCons()) {
4188 ConsString* cons_string = ConsString::cast(string);
4189 String* left = cons_string->first();
4190 if (left->length() > index) {
4191 string = left;
4192 } else {
4193 index -= left->length();
4194 string = cons_string->second();
4195 }
4196 } else {
4197 return string->Get(index);
4198 }
4199 }
4200
4201 UNREACHABLE();
4202 return 0;
4203 }
4204
4205
4206 template <typename sinkchar>
WriteToFlat(String * src,sinkchar * sink,int f,int t)4207 void String::WriteToFlat(String* src,
4208 sinkchar* sink,
4209 int f,
4210 int t) {
4211 String* source = src;
4212 int from = f;
4213 int to = t;
4214 while (true) {
4215 ASSERT(0 <= from && from <= to && to <= source->length());
4216 switch (StringShape(source).full_representation_tag()) {
4217 case kAsciiStringTag | kExternalStringTag: {
4218 CopyChars(sink,
4219 ExternalAsciiString::cast(source)->resource()->data() + from,
4220 to - from);
4221 return;
4222 }
4223 case kTwoByteStringTag | kExternalStringTag: {
4224 const uc16* data =
4225 ExternalTwoByteString::cast(source)->resource()->data();
4226 CopyChars(sink,
4227 data + from,
4228 to - from);
4229 return;
4230 }
4231 case kAsciiStringTag | kSeqStringTag: {
4232 CopyChars(sink,
4233 SeqAsciiString::cast(source)->GetChars() + from,
4234 to - from);
4235 return;
4236 }
4237 case kTwoByteStringTag | kSeqStringTag: {
4238 CopyChars(sink,
4239 SeqTwoByteString::cast(source)->GetChars() + from,
4240 to - from);
4241 return;
4242 }
4243 case kAsciiStringTag | kSlicedStringTag:
4244 case kTwoByteStringTag | kSlicedStringTag: {
4245 SlicedString* sliced_string = SlicedString::cast(source);
4246 int start = sliced_string->start();
4247 from += start;
4248 to += start;
4249 source = String::cast(sliced_string->buffer());
4250 break;
4251 }
4252 case kAsciiStringTag | kConsStringTag:
4253 case kTwoByteStringTag | kConsStringTag: {
4254 ConsString* cons_string = ConsString::cast(source);
4255 String* first = cons_string->first();
4256 int boundary = first->length();
4257 if (to - boundary >= boundary - from) {
4258 // Right hand side is longer. Recurse over left.
4259 if (from < boundary) {
4260 WriteToFlat(first, sink, from, boundary);
4261 sink += boundary - from;
4262 from = 0;
4263 } else {
4264 from -= boundary;
4265 }
4266 to -= boundary;
4267 source = cons_string->second();
4268 } else {
4269 // Left hand side is longer. Recurse over right.
4270 if (to > boundary) {
4271 String* second = cons_string->second();
4272 WriteToFlat(second,
4273 sink + boundary - from,
4274 0,
4275 to - boundary);
4276 to = boundary;
4277 }
4278 source = first;
4279 }
4280 break;
4281 }
4282 }
4283 }
4284 }
4285
4286
SlicedStringIterateBody(ObjectVisitor * v)4287 void SlicedString::SlicedStringIterateBody(ObjectVisitor* v) {
4288 IteratePointer(v, kBufferOffset);
4289 }
4290
4291
SlicedStringGet(int index)4292 uint16_t SlicedString::SlicedStringGet(int index) {
4293 ASSERT(index >= 0 && index < this->length());
4294 // Delegate to the buffer string.
4295 String* underlying = buffer();
4296 return underlying->Get(start() + index);
4297 }
4298
4299
4300 template <typename IteratorA, typename IteratorB>
CompareStringContents(IteratorA * ia,IteratorB * ib)4301 static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4302 // General slow case check. We know that the ia and ib iterators
4303 // have the same length.
4304 while (ia->has_more()) {
4305 uc32 ca = ia->GetNext();
4306 uc32 cb = ib->GetNext();
4307 if (ca != cb)
4308 return false;
4309 }
4310 return true;
4311 }
4312
4313
4314 // Compares the contents of two strings by reading and comparing
4315 // int-sized blocks of characters.
4316 template <typename Char>
CompareRawStringContents(Vector<Char> a,Vector<Char> b)4317 static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4318 int length = a.length();
4319 ASSERT_EQ(length, b.length());
4320 const Char* pa = a.start();
4321 const Char* pb = b.start();
4322 int i = 0;
4323 #ifndef V8_HOST_CAN_READ_UNALIGNED
4324 // If this architecture isn't comfortable reading unaligned ints
4325 // then we have to check that the strings are aligned before
4326 // comparing them blockwise.
4327 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4328 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4329 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
4330 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
4331 #endif
4332 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
4333 int endpoint = length - kStepSize;
4334 // Compare blocks until we reach near the end of the string.
4335 for (; i <= endpoint; i += kStepSize) {
4336 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
4337 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
4338 if (wa != wb) {
4339 return false;
4340 }
4341 }
4342 #ifndef V8_HOST_CAN_READ_UNALIGNED
4343 }
4344 #endif
4345 // Compare the remaining characters that didn't fit into a block.
4346 for (; i < length; i++) {
4347 if (a[i] != b[i]) {
4348 return false;
4349 }
4350 }
4351 return true;
4352 }
4353
4354
4355 static StringInputBuffer string_compare_buffer_b;
4356
4357
4358 template <typename IteratorA>
CompareStringContentsPartial(IteratorA * ia,String * b)4359 static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
4360 if (b->IsFlat()) {
4361 if (b->IsAsciiRepresentation()) {
4362 VectorIterator<char> ib(b->ToAsciiVector());
4363 return CompareStringContents(ia, &ib);
4364 } else {
4365 VectorIterator<uc16> ib(b->ToUC16Vector());
4366 return CompareStringContents(ia, &ib);
4367 }
4368 } else {
4369 string_compare_buffer_b.Reset(0, b);
4370 return CompareStringContents(ia, &string_compare_buffer_b);
4371 }
4372 }
4373
4374
4375 static StringInputBuffer string_compare_buffer_a;
4376
4377
SlowEquals(String * other)4378 bool String::SlowEquals(String* other) {
4379 // Fast check: negative check with lengths.
4380 int len = length();
4381 if (len != other->length()) return false;
4382 if (len == 0) return true;
4383
4384 // Fast check: if hash code is computed for both strings
4385 // a fast negative check can be performed.
4386 if (HasHashCode() && other->HasHashCode()) {
4387 if (Hash() != other->Hash()) return false;
4388 }
4389
4390 if (StringShape(this).IsSequentialAscii() &&
4391 StringShape(other).IsSequentialAscii()) {
4392 const char* str1 = SeqAsciiString::cast(this)->GetChars();
4393 const char* str2 = SeqAsciiString::cast(other)->GetChars();
4394 return CompareRawStringContents(Vector<const char>(str1, len),
4395 Vector<const char>(str2, len));
4396 }
4397
4398 if (this->IsFlat()) {
4399 if (IsAsciiRepresentation()) {
4400 Vector<const char> vec1 = this->ToAsciiVector();
4401 if (other->IsFlat()) {
4402 if (other->IsAsciiRepresentation()) {
4403 Vector<const char> vec2 = other->ToAsciiVector();
4404 return CompareRawStringContents(vec1, vec2);
4405 } else {
4406 VectorIterator<char> buf1(vec1);
4407 VectorIterator<uc16> ib(other->ToUC16Vector());
4408 return CompareStringContents(&buf1, &ib);
4409 }
4410 } else {
4411 VectorIterator<char> buf1(vec1);
4412 string_compare_buffer_b.Reset(0, other);
4413 return CompareStringContents(&buf1, &string_compare_buffer_b);
4414 }
4415 } else {
4416 Vector<const uc16> vec1 = this->ToUC16Vector();
4417 if (other->IsFlat()) {
4418 if (other->IsAsciiRepresentation()) {
4419 VectorIterator<uc16> buf1(vec1);
4420 VectorIterator<char> ib(other->ToAsciiVector());
4421 return CompareStringContents(&buf1, &ib);
4422 } else {
4423 Vector<const uc16> vec2(other->ToUC16Vector());
4424 return CompareRawStringContents(vec1, vec2);
4425 }
4426 } else {
4427 VectorIterator<uc16> buf1(vec1);
4428 string_compare_buffer_b.Reset(0, other);
4429 return CompareStringContents(&buf1, &string_compare_buffer_b);
4430 }
4431 }
4432 } else {
4433 string_compare_buffer_a.Reset(0, this);
4434 return CompareStringContentsPartial(&string_compare_buffer_a, other);
4435 }
4436 }
4437
4438
MarkAsUndetectable()4439 bool String::MarkAsUndetectable() {
4440 if (StringShape(this).IsSymbol()) return false;
4441
4442 Map* map = this->map();
4443 if (map == Heap::short_string_map()) {
4444 this->set_map(Heap::undetectable_short_string_map());
4445 return true;
4446 } else if (map == Heap::medium_string_map()) {
4447 this->set_map(Heap::undetectable_medium_string_map());
4448 return true;
4449 } else if (map == Heap::long_string_map()) {
4450 this->set_map(Heap::undetectable_long_string_map());
4451 return true;
4452 } else if (map == Heap::short_ascii_string_map()) {
4453 this->set_map(Heap::undetectable_short_ascii_string_map());
4454 return true;
4455 } else if (map == Heap::medium_ascii_string_map()) {
4456 this->set_map(Heap::undetectable_medium_ascii_string_map());
4457 return true;
4458 } else if (map == Heap::long_ascii_string_map()) {
4459 this->set_map(Heap::undetectable_long_ascii_string_map());
4460 return true;
4461 }
4462 // Rest cannot be marked as undetectable
4463 return false;
4464 }
4465
4466
IsEqualTo(Vector<const char> str)4467 bool String::IsEqualTo(Vector<const char> str) {
4468 int slen = length();
4469 Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
4470 decoder->Reset(str.start(), str.length());
4471 int i;
4472 for (i = 0; i < slen && decoder->has_more(); i++) {
4473 uc32 r = decoder->GetNext();
4474 if (Get(i) != r) return false;
4475 }
4476 return i == slen && !decoder->has_more();
4477 }
4478
4479
ComputeAndSetHash()4480 uint32_t String::ComputeAndSetHash() {
4481 // Should only be called if hash code has not yet been computed.
4482 ASSERT(!(length_field() & kHashComputedMask));
4483
4484 // Compute the hash code.
4485 StringInputBuffer buffer(this);
4486 uint32_t field = ComputeLengthAndHashField(&buffer, length());
4487
4488 // Store the hash code in the object.
4489 set_length_field(field);
4490
4491 // Check the hash code is there.
4492 ASSERT(length_field() & kHashComputedMask);
4493 uint32_t result = field >> kHashShift;
4494 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
4495 return result;
4496 }
4497
4498
ComputeArrayIndex(unibrow::CharacterStream * buffer,uint32_t * index,int length)4499 bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
4500 uint32_t* index,
4501 int length) {
4502 if (length == 0 || length > kMaxArrayIndexSize) return false;
4503 uc32 ch = buffer->GetNext();
4504
4505 // If the string begins with a '0' character, it must only consist
4506 // of it to be a legal array index.
4507 if (ch == '0') {
4508 *index = 0;
4509 return length == 1;
4510 }
4511
4512 // Convert string to uint32 array index; character by character.
4513 int d = ch - '0';
4514 if (d < 0 || d > 9) return false;
4515 uint32_t result = d;
4516 while (buffer->has_more()) {
4517 d = buffer->GetNext() - '0';
4518 if (d < 0 || d > 9) return false;
4519 // Check that the new result is below the 32 bit limit.
4520 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
4521 result = (result * 10) + d;
4522 }
4523
4524 *index = result;
4525 return true;
4526 }
4527
4528
SlowAsArrayIndex(uint32_t * index)4529 bool String::SlowAsArrayIndex(uint32_t* index) {
4530 if (length() <= kMaxCachedArrayIndexLength) {
4531 Hash(); // force computation of hash code
4532 uint32_t field = length_field();
4533 if ((field & kIsArrayIndexMask) == 0) return false;
4534 *index = (field & ((1 << kShortLengthShift) - 1)) >> kLongLengthShift;
4535 return true;
4536 } else {
4537 StringInputBuffer buffer(this);
4538 return ComputeArrayIndex(&buffer, index, length());
4539 }
4540 }
4541
4542
HashField(uint32_t hash,bool is_array_index)4543 static inline uint32_t HashField(uint32_t hash, bool is_array_index) {
4544 uint32_t result =
4545 (hash << String::kLongLengthShift) | String::kHashComputedMask;
4546 if (is_array_index) result |= String::kIsArrayIndexMask;
4547 return result;
4548 }
4549
4550
GetHashField()4551 uint32_t StringHasher::GetHashField() {
4552 ASSERT(is_valid());
4553 if (length_ <= String::kMaxShortStringSize) {
4554 uint32_t payload;
4555 if (is_array_index()) {
4556 payload = v8::internal::HashField(array_index(), true);
4557 } else {
4558 payload = v8::internal::HashField(GetHash(), false);
4559 }
4560 return (payload & ((1 << String::kShortLengthShift) - 1)) |
4561 (length_ << String::kShortLengthShift);
4562 } else if (length_ <= String::kMaxMediumStringSize) {
4563 uint32_t payload = v8::internal::HashField(GetHash(), false);
4564 return (payload & ((1 << String::kMediumLengthShift) - 1)) |
4565 (length_ << String::kMediumLengthShift);
4566 } else {
4567 return v8::internal::HashField(length_, false);
4568 }
4569 }
4570
4571
ComputeLengthAndHashField(unibrow::CharacterStream * buffer,int length)4572 uint32_t String::ComputeLengthAndHashField(unibrow::CharacterStream* buffer,
4573 int length) {
4574 StringHasher hasher(length);
4575
4576 // Very long strings have a trivial hash that doesn't inspect the
4577 // string contents.
4578 if (hasher.has_trivial_hash()) {
4579 return hasher.GetHashField();
4580 }
4581
4582 // Do the iterative array index computation as long as there is a
4583 // chance this is an array index.
4584 while (buffer->has_more() && hasher.is_array_index()) {
4585 hasher.AddCharacter(buffer->GetNext());
4586 }
4587
4588 // Process the remaining characters without updating the array
4589 // index.
4590 while (buffer->has_more()) {
4591 hasher.AddCharacterNoIndex(buffer->GetNext());
4592 }
4593
4594 return hasher.GetHashField();
4595 }
4596
4597
Slice(int start,int end)4598 Object* String::Slice(int start, int end) {
4599 if (start == 0 && end == length()) return this;
4600 if (StringShape(this).representation_tag() == kSlicedStringTag) {
4601 // Translate slices of a SlicedString into slices of the
4602 // underlying string buffer.
4603 SlicedString* str = SlicedString::cast(this);
4604 String* buf = str->buffer();
4605 return Heap::AllocateSlicedString(buf,
4606 str->start() + start,
4607 str->start() + end);
4608 }
4609 Object* result = Heap::AllocateSlicedString(this, start, end);
4610 if (result->IsFailure()) {
4611 return result;
4612 }
4613 // Due to the way we retry after GC on allocation failure we are not allowed
4614 // to fail on allocation after this point. This is the one-allocation rule.
4615
4616 // Try to flatten a cons string that is under the sliced string.
4617 // This is to avoid memory leaks and possible stack overflows caused by
4618 // building 'towers' of sliced strings on cons strings.
4619 // This may fail due to an allocation failure (when a GC is needed), but it
4620 // will succeed often enough to avoid the problem. We only have to do this
4621 // if Heap::AllocateSlicedString actually returned a SlicedString. It will
4622 // return flat strings for small slices for efficiency reasons.
4623 String* answer = String::cast(result);
4624 if (StringShape(answer).IsSliced() &&
4625 StringShape(this).representation_tag() == kConsStringTag) {
4626 TryFlatten();
4627 // If the flatten succeeded we might as well make the sliced string point
4628 // to the flat string rather than the cons string.
4629 String* second = ConsString::cast(this)->second();
4630 if (second->length() == 0) {
4631 SlicedString::cast(answer)->set_buffer(ConsString::cast(this)->first());
4632 }
4633 }
4634 return answer;
4635 }
4636
4637
PrintOn(FILE * file)4638 void String::PrintOn(FILE* file) {
4639 int length = this->length();
4640 for (int i = 0; i < length; i++) {
4641 fprintf(file, "%c", Get(i));
4642 }
4643 }
4644
4645
CreateBackPointers()4646 void Map::CreateBackPointers() {
4647 DescriptorArray* descriptors = instance_descriptors();
4648 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
4649 if (descriptors->GetType(i) == MAP_TRANSITION) {
4650 // Get target.
4651 Map* target = Map::cast(descriptors->GetValue(i));
4652 #ifdef DEBUG
4653 // Verify target.
4654 Object* source_prototype = prototype();
4655 Object* target_prototype = target->prototype();
4656 ASSERT(source_prototype->IsJSObject() ||
4657 source_prototype->IsMap() ||
4658 source_prototype->IsNull());
4659 ASSERT(target_prototype->IsJSObject() ||
4660 target_prototype->IsNull());
4661 ASSERT(source_prototype->IsMap() ||
4662 source_prototype == target_prototype);
4663 #endif
4664 // Point target back to source. set_prototype() will not let us set
4665 // the prototype to a map, as we do here.
4666 *RawField(target, kPrototypeOffset) = this;
4667 }
4668 }
4669 }
4670
4671
ClearNonLiveTransitions(Object * real_prototype)4672 void Map::ClearNonLiveTransitions(Object* real_prototype) {
4673 // Live DescriptorArray objects will be marked, so we must use
4674 // low-level accessors to get and modify their data.
4675 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
4676 *RawField(this, Map::kInstanceDescriptorsOffset));
4677 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
4678 Smi* NullDescriptorDetails =
4679 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
4680 FixedArray* contents = reinterpret_cast<FixedArray*>(
4681 d->get(DescriptorArray::kContentArrayIndex));
4682 ASSERT(contents->length() >= 2);
4683 for (int i = 0; i < contents->length(); i += 2) {
4684 // If the pair (value, details) is a map transition,
4685 // check if the target is live. If not, null the descriptor.
4686 // Also drop the back pointer for that map transition, so that this
4687 // map is not reached again by following a back pointer from a
4688 // non-live object.
4689 PropertyDetails details(Smi::cast(contents->get(i + 1)));
4690 if (details.type() == MAP_TRANSITION) {
4691 Map* target = reinterpret_cast<Map*>(contents->get(i));
4692 ASSERT(target->IsHeapObject());
4693 if (!target->IsMarked()) {
4694 ASSERT(target->IsMap());
4695 contents->set(i + 1, NullDescriptorDetails, SKIP_WRITE_BARRIER);
4696 contents->set(i, Heap::null_value(), SKIP_WRITE_BARRIER);
4697 ASSERT(target->prototype() == this ||
4698 target->prototype() == real_prototype);
4699 // Getter prototype() is read-only, set_prototype() has side effects.
4700 *RawField(target, Map::kPrototypeOffset) = real_prototype;
4701 }
4702 }
4703 }
4704 }
4705
4706
MapIterateBody(ObjectVisitor * v)4707 void Map::MapIterateBody(ObjectVisitor* v) {
4708 // Assumes all Object* members are contiguously allocated!
4709 IteratePointers(v, kPrototypeOffset, kCodeCacheOffset + kPointerSize);
4710 }
4711
4712
SetInstancePrototype(Object * value)4713 Object* JSFunction::SetInstancePrototype(Object* value) {
4714 ASSERT(value->IsJSObject());
4715
4716 if (has_initial_map()) {
4717 initial_map()->set_prototype(value);
4718 } else {
4719 // Put the value in the initial map field until an initial map is
4720 // needed. At that point, a new initial map is created and the
4721 // prototype is put into the initial map where it belongs.
4722 set_prototype_or_initial_map(value);
4723 }
4724 return value;
4725 }
4726
4727
4728
SetPrototype(Object * value)4729 Object* JSFunction::SetPrototype(Object* value) {
4730 Object* construct_prototype = value;
4731
4732 // If the value is not a JSObject, store the value in the map's
4733 // constructor field so it can be accessed. Also, set the prototype
4734 // used for constructing objects to the original object prototype.
4735 // See ECMA-262 13.2.2.
4736 if (!value->IsJSObject()) {
4737 // Copy the map so this does not affect unrelated functions.
4738 // Remove map transitions because they point to maps with a
4739 // different prototype.
4740 Object* new_map = map()->CopyDropTransitions();
4741 if (new_map->IsFailure()) return new_map;
4742 set_map(Map::cast(new_map));
4743 map()->set_constructor(value);
4744 map()->set_non_instance_prototype(true);
4745 construct_prototype =
4746 Top::context()->global_context()->initial_object_prototype();
4747 } else {
4748 map()->set_non_instance_prototype(false);
4749 }
4750
4751 return SetInstancePrototype(construct_prototype);
4752 }
4753
4754
SetInstanceClassName(String * name)4755 Object* JSFunction::SetInstanceClassName(String* name) {
4756 shared()->set_instance_class_name(name);
4757 return this;
4758 }
4759
4760
GlobalContextFromLiterals(FixedArray * literals)4761 Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
4762 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
4763 }
4764
4765
OddballIterateBody(ObjectVisitor * v)4766 void Oddball::OddballIterateBody(ObjectVisitor* v) {
4767 // Assumes all Object* members are contiguously allocated!
4768 IteratePointers(v, kToStringOffset, kToNumberOffset + kPointerSize);
4769 }
4770
4771
Initialize(const char * to_string,Object * to_number)4772 Object* Oddball::Initialize(const char* to_string, Object* to_number) {
4773 Object* symbol = Heap::LookupAsciiSymbol(to_string);
4774 if (symbol->IsFailure()) return symbol;
4775 set_to_string(String::cast(symbol));
4776 set_to_number(to_number);
4777 return this;
4778 }
4779
4780
HasSourceCode()4781 bool SharedFunctionInfo::HasSourceCode() {
4782 return !script()->IsUndefined() &&
4783 !Script::cast(script())->source()->IsUndefined();
4784 }
4785
4786
GetSourceCode()4787 Object* SharedFunctionInfo::GetSourceCode() {
4788 HandleScope scope;
4789 if (script()->IsUndefined()) return Heap::undefined_value();
4790 Object* source = Script::cast(script())->source();
4791 if (source->IsUndefined()) return Heap::undefined_value();
4792 return *SubString(Handle<String>(String::cast(source)),
4793 start_position(), end_position());
4794 }
4795
4796
CalculateInstanceSize()4797 int SharedFunctionInfo::CalculateInstanceSize() {
4798 int instance_size =
4799 JSObject::kHeaderSize +
4800 expected_nof_properties() * kPointerSize;
4801 if (instance_size > JSObject::kMaxInstanceSize) {
4802 instance_size = JSObject::kMaxInstanceSize;
4803 }
4804 return instance_size;
4805 }
4806
4807
CalculateInObjectProperties()4808 int SharedFunctionInfo::CalculateInObjectProperties() {
4809 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
4810 }
4811
4812
SetThisPropertyAssignmentsInfo(bool only_this_property_assignments,bool only_simple_this_property_assignments,FixedArray * assignments)4813 void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
4814 bool only_this_property_assignments,
4815 bool only_simple_this_property_assignments,
4816 FixedArray* assignments) {
4817 set_compiler_hints(BooleanBit::set(compiler_hints(),
4818 kHasOnlyThisPropertyAssignments,
4819 only_this_property_assignments));
4820 set_compiler_hints(BooleanBit::set(compiler_hints(),
4821 kHasOnlySimpleThisPropertyAssignments,
4822 only_simple_this_property_assignments));
4823 set_this_property_assignments(assignments);
4824 set_this_property_assignments_count(assignments->length() / 3);
4825 }
4826
4827
ClearThisPropertyAssignmentsInfo()4828 void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
4829 set_compiler_hints(BooleanBit::set(compiler_hints(),
4830 kHasOnlyThisPropertyAssignments,
4831 false));
4832 set_compiler_hints(BooleanBit::set(compiler_hints(),
4833 kHasOnlySimpleThisPropertyAssignments,
4834 false));
4835 set_this_property_assignments(Heap::undefined_value());
4836 set_this_property_assignments_count(0);
4837 }
4838
4839
GetThisPropertyAssignmentName(int index)4840 String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
4841 Object* obj = this_property_assignments();
4842 ASSERT(obj->IsFixedArray());
4843 ASSERT(index < this_property_assignments_count());
4844 obj = FixedArray::cast(obj)->get(index * 3);
4845 ASSERT(obj->IsString());
4846 return String::cast(obj);
4847 }
4848
4849
IsThisPropertyAssignmentArgument(int index)4850 bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
4851 Object* obj = this_property_assignments();
4852 ASSERT(obj->IsFixedArray());
4853 ASSERT(index < this_property_assignments_count());
4854 obj = FixedArray::cast(obj)->get(index * 3 + 1);
4855 return Smi::cast(obj)->value() != -1;
4856 }
4857
4858
GetThisPropertyAssignmentArgument(int index)4859 int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
4860 ASSERT(IsThisPropertyAssignmentArgument(index));
4861 Object* obj =
4862 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
4863 return Smi::cast(obj)->value();
4864 }
4865
4866
GetThisPropertyAssignmentConstant(int index)4867 Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
4868 ASSERT(!IsThisPropertyAssignmentArgument(index));
4869 Object* obj =
4870 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
4871 return obj;
4872 }
4873
4874
4875
4876 // Support function for printing the source code to a StringStream
4877 // without any allocation in the heap.
SourceCodePrint(StringStream * accumulator,int max_length)4878 void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
4879 int max_length) {
4880 // For some native functions there is no source.
4881 if (script()->IsUndefined() ||
4882 Script::cast(script())->source()->IsUndefined()) {
4883 accumulator->Add("<No Source>");
4884 return;
4885 }
4886
4887 // Get the slice of the source for this function.
4888 // Don't use String::cast because we don't want more assertion errors while
4889 // we are already creating a stack dump.
4890 String* script_source =
4891 reinterpret_cast<String*>(Script::cast(script())->source());
4892
4893 if (!script_source->LooksValid()) {
4894 accumulator->Add("<Invalid Source>");
4895 return;
4896 }
4897
4898 if (!is_toplevel()) {
4899 accumulator->Add("function ");
4900 Object* name = this->name();
4901 if (name->IsString() && String::cast(name)->length() > 0) {
4902 accumulator->PrintName(name);
4903 }
4904 }
4905
4906 int len = end_position() - start_position();
4907 if (len > max_length) {
4908 accumulator->Put(script_source,
4909 start_position(),
4910 start_position() + max_length);
4911 accumulator->Add("...\n");
4912 } else {
4913 accumulator->Put(script_source, start_position(), end_position());
4914 }
4915 }
4916
4917
SharedFunctionInfoIterateBody(ObjectVisitor * v)4918 void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
4919 IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize);
4920 IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
4921 IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
4922 IteratePointers(v, kThisPropertyAssignmentsOffset,
4923 kThisPropertyAssignmentsOffset + kPointerSize);
4924 }
4925
4926
BeginCodeIteration(Code * code)4927 void ObjectVisitor::BeginCodeIteration(Code* code) {
4928 ASSERT(code->ic_flag() == Code::IC_TARGET_IS_OBJECT);
4929 }
4930
4931
VisitCodeTarget(RelocInfo * rinfo)4932 void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
4933 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
4934 VisitPointer(rinfo->target_object_address());
4935 }
4936
4937
VisitDebugTarget(RelocInfo * rinfo)4938 void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
4939 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) && rinfo->IsCallInstruction());
4940 VisitPointer(rinfo->call_object_address());
4941 }
4942
4943
4944 // Convert relocatable targets from address to code object address. This is
4945 // mainly IC call targets but for debugging straight-line code can be replaced
4946 // with a call instruction which also has to be relocated.
ConvertICTargetsFromAddressToObject()4947 void Code::ConvertICTargetsFromAddressToObject() {
4948 ASSERT(ic_flag() == IC_TARGET_IS_ADDRESS);
4949
4950 for (RelocIterator it(this, RelocInfo::kCodeTargetMask);
4951 !it.done(); it.next()) {
4952 Address ic_addr = it.rinfo()->target_address();
4953 ASSERT(ic_addr != NULL);
4954 HeapObject* code = HeapObject::FromAddress(ic_addr - Code::kHeaderSize);
4955 ASSERT(code->IsHeapObject());
4956 it.rinfo()->set_target_object(code);
4957 }
4958
4959 #ifdef ENABLE_DEBUGGER_SUPPORT
4960 if (Debug::has_break_points()) {
4961 for (RelocIterator it(this, RelocInfo::ModeMask(RelocInfo::JS_RETURN));
4962 !it.done();
4963 it.next()) {
4964 if (it.rinfo()->IsCallInstruction()) {
4965 Address addr = it.rinfo()->call_address();
4966 ASSERT(addr != NULL);
4967 HeapObject* code = HeapObject::FromAddress(addr - Code::kHeaderSize);
4968 ASSERT(code->IsHeapObject());
4969 it.rinfo()->set_call_object(code);
4970 }
4971 }
4972 }
4973 #endif
4974 set_ic_flag(IC_TARGET_IS_OBJECT);
4975 }
4976
4977
CodeIterateBody(ObjectVisitor * v)4978 void Code::CodeIterateBody(ObjectVisitor* v) {
4979 v->BeginCodeIteration(this);
4980
4981 int mode_mask = RelocInfo::kCodeTargetMask |
4982 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
4983 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
4984 RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
4985 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
4986
4987 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
4988 RelocInfo::Mode rmode = it.rinfo()->rmode();
4989 if (rmode == RelocInfo::EMBEDDED_OBJECT) {
4990 v->VisitPointer(it.rinfo()->target_object_address());
4991 } else if (RelocInfo::IsCodeTarget(rmode)) {
4992 v->VisitCodeTarget(it.rinfo());
4993 } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
4994 v->VisitExternalReference(it.rinfo()->target_reference_address());
4995 #ifdef ENABLE_DEBUGGER_SUPPORT
4996 } else if (Debug::has_break_points() &&
4997 RelocInfo::IsJSReturn(rmode) &&
4998 it.rinfo()->IsCallInstruction()) {
4999 v->VisitDebugTarget(it.rinfo());
5000 #endif
5001 } else if (rmode == RelocInfo::RUNTIME_ENTRY) {
5002 v->VisitRuntimeEntry(it.rinfo());
5003 }
5004 }
5005
5006 ScopeInfo<>::IterateScopeInfo(this, v);
5007
5008 v->EndCodeIteration(this);
5009 }
5010
5011
ConvertICTargetsFromObjectToAddress()5012 void Code::ConvertICTargetsFromObjectToAddress() {
5013 ASSERT(ic_flag() == IC_TARGET_IS_OBJECT);
5014
5015 for (RelocIterator it(this, RelocInfo::kCodeTargetMask);
5016 !it.done(); it.next()) {
5017 // We cannot use the safe cast (Code::cast) here, because we may be in
5018 // the middle of relocating old objects during GC and the map pointer in
5019 // the code object may be mangled
5020 Code* code = reinterpret_cast<Code*>(it.rinfo()->target_object());
5021 ASSERT((code != NULL) && code->IsHeapObject());
5022 it.rinfo()->set_target_address(code->instruction_start());
5023 }
5024
5025 #ifdef ENABLE_DEBUGGER_SUPPORT
5026 if (Debug::has_break_points()) {
5027 for (RelocIterator it(this, RelocInfo::ModeMask(RelocInfo::JS_RETURN));
5028 !it.done();
5029 it.next()) {
5030 if (it.rinfo()->IsCallInstruction()) {
5031 Code* code = reinterpret_cast<Code*>(it.rinfo()->call_object());
5032 ASSERT((code != NULL) && code->IsHeapObject());
5033 it.rinfo()->set_call_address(code->instruction_start());
5034 }
5035 }
5036 }
5037 #endif
5038 set_ic_flag(IC_TARGET_IS_ADDRESS);
5039 }
5040
5041
Relocate(int delta)5042 void Code::Relocate(int delta) {
5043 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
5044 it.rinfo()->apply(delta);
5045 }
5046 CPU::FlushICache(instruction_start(), instruction_size());
5047 }
5048
5049
CopyFrom(const CodeDesc & desc)5050 void Code::CopyFrom(const CodeDesc& desc) {
5051 // copy code
5052 memmove(instruction_start(), desc.buffer, desc.instr_size);
5053
5054 // fill gap with zero bytes
5055 { byte* p = instruction_start() + desc.instr_size;
5056 byte* q = relocation_start();
5057 while (p < q) {
5058 *p++ = 0;
5059 }
5060 }
5061
5062 // copy reloc info
5063 memmove(relocation_start(),
5064 desc.buffer + desc.buffer_size - desc.reloc_size,
5065 desc.reloc_size);
5066
5067 // unbox handles and relocate
5068 int delta = instruction_start() - desc.buffer;
5069 int mode_mask = RelocInfo::kCodeTargetMask |
5070 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
5071 RelocInfo::kApplyMask;
5072 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
5073 RelocInfo::Mode mode = it.rinfo()->rmode();
5074 if (mode == RelocInfo::EMBEDDED_OBJECT) {
5075 Object** p = reinterpret_cast<Object**>(it.rinfo()->target_object());
5076 it.rinfo()->set_target_object(*p);
5077 } else if (RelocInfo::IsCodeTarget(mode)) {
5078 // rewrite code handles in inline cache targets to direct
5079 // pointers to the first instruction in the code object
5080 Object** p = reinterpret_cast<Object**>(it.rinfo()->target_object());
5081 Code* code = Code::cast(*p);
5082 it.rinfo()->set_target_address(code->instruction_start());
5083 } else {
5084 it.rinfo()->apply(delta);
5085 }
5086 }
5087 CPU::FlushICache(instruction_start(), instruction_size());
5088 }
5089
5090
5091 // Locate the source position which is closest to the address in the code. This
5092 // is using the source position information embedded in the relocation info.
5093 // The position returned is relative to the beginning of the script where the
5094 // source for this function is found.
SourcePosition(Address pc)5095 int Code::SourcePosition(Address pc) {
5096 int distance = kMaxInt;
5097 int position = RelocInfo::kNoPosition; // Initially no position found.
5098 // Run through all the relocation info to find the best matching source
5099 // position. All the code needs to be considered as the sequence of the
5100 // instructions in the code does not necessarily follow the same order as the
5101 // source.
5102 RelocIterator it(this, RelocInfo::kPositionMask);
5103 while (!it.done()) {
5104 // Only look at positions after the current pc.
5105 if (it.rinfo()->pc() < pc) {
5106 // Get position and distance.
5107 int dist = pc - it.rinfo()->pc();
5108 int pos = it.rinfo()->data();
5109 // If this position is closer than the current candidate or if it has the
5110 // same distance as the current candidate and the position is higher then
5111 // this position is the new candidate.
5112 if ((dist < distance) ||
5113 (dist == distance && pos > position)) {
5114 position = pos;
5115 distance = dist;
5116 }
5117 }
5118 it.next();
5119 }
5120 return position;
5121 }
5122
5123
5124 // Same as Code::SourcePosition above except it only looks for statement
5125 // positions.
SourceStatementPosition(Address pc)5126 int Code::SourceStatementPosition(Address pc) {
5127 // First find the position as close as possible using all position
5128 // information.
5129 int position = SourcePosition(pc);
5130 // Now find the closest statement position before the position.
5131 int statement_position = 0;
5132 RelocIterator it(this, RelocInfo::kPositionMask);
5133 while (!it.done()) {
5134 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
5135 int p = it.rinfo()->data();
5136 if (statement_position < p && p <= position) {
5137 statement_position = p;
5138 }
5139 }
5140 it.next();
5141 }
5142 return statement_position;
5143 }
5144
5145
5146 #ifdef ENABLE_DISASSEMBLER
5147 // Identify kind of code.
Kind2String(Kind kind)5148 const char* Code::Kind2String(Kind kind) {
5149 switch (kind) {
5150 case FUNCTION: return "FUNCTION";
5151 case STUB: return "STUB";
5152 case BUILTIN: return "BUILTIN";
5153 case LOAD_IC: return "LOAD_IC";
5154 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
5155 case STORE_IC: return "STORE_IC";
5156 case KEYED_STORE_IC: return "KEYED_STORE_IC";
5157 case CALL_IC: return "CALL_IC";
5158 }
5159 UNREACHABLE();
5160 return NULL;
5161 }
5162
5163
ICState2String(InlineCacheState state)5164 const char* Code::ICState2String(InlineCacheState state) {
5165 switch (state) {
5166 case UNINITIALIZED: return "UNINITIALIZED";
5167 case PREMONOMORPHIC: return "PREMONOMORPHIC";
5168 case MONOMORPHIC: return "MONOMORPHIC";
5169 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
5170 case MEGAMORPHIC: return "MEGAMORPHIC";
5171 case DEBUG_BREAK: return "DEBUG_BREAK";
5172 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
5173 }
5174 UNREACHABLE();
5175 return NULL;
5176 }
5177
5178
PropertyType2String(PropertyType type)5179 const char* Code::PropertyType2String(PropertyType type) {
5180 switch (type) {
5181 case NORMAL: return "NORMAL";
5182 case FIELD: return "FIELD";
5183 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
5184 case CALLBACKS: return "CALLBACKS";
5185 case INTERCEPTOR: return "INTERCEPTOR";
5186 case MAP_TRANSITION: return "MAP_TRANSITION";
5187 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
5188 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
5189 }
5190 UNREACHABLE();
5191 return NULL;
5192 }
5193
Disassemble(const char * name)5194 void Code::Disassemble(const char* name) {
5195 PrintF("kind = %s\n", Kind2String(kind()));
5196 if (is_inline_cache_stub()) {
5197 PrintF("ic_state = %s\n", ICState2String(ic_state()));
5198 PrintF("ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
5199 if (ic_state() == MONOMORPHIC) {
5200 PrintF("type = %s\n", PropertyType2String(type()));
5201 }
5202 }
5203 if ((name != NULL) && (name[0] != '\0')) {
5204 PrintF("name = %s\n", name);
5205 }
5206
5207 PrintF("Instructions (size = %d)\n", instruction_size());
5208 Disassembler::Decode(NULL, this);
5209 PrintF("\n");
5210
5211 PrintF("RelocInfo (size = %d)\n", relocation_size());
5212 for (RelocIterator it(this); !it.done(); it.next())
5213 it.rinfo()->Print();
5214 PrintF("\n");
5215 }
5216 #endif // ENABLE_DISASSEMBLER
5217
5218
SetFastElements(FixedArray * elems)5219 void JSObject::SetFastElements(FixedArray* elems) {
5220 // We should never end in here with a pixel array.
5221 ASSERT(!HasPixelElements());
5222 #ifdef DEBUG
5223 // Check the provided array is filled with the_hole.
5224 uint32_t len = static_cast<uint32_t>(elems->length());
5225 for (uint32_t i = 0; i < len; i++) ASSERT(elems->get(i)->IsTheHole());
5226 #endif
5227 WriteBarrierMode mode = elems->GetWriteBarrierMode();
5228 switch (GetElementsKind()) {
5229 case FAST_ELEMENTS: {
5230 FixedArray* old_elements = FixedArray::cast(elements());
5231 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
5232 // Fill out the new array with this content and array holes.
5233 for (uint32_t i = 0; i < old_length; i++) {
5234 elems->set(i, old_elements->get(i), mode);
5235 }
5236 break;
5237 }
5238 case DICTIONARY_ELEMENTS: {
5239 NumberDictionary* dictionary = NumberDictionary::cast(elements());
5240 for (int i = 0; i < dictionary->Capacity(); i++) {
5241 Object* key = dictionary->KeyAt(i);
5242 if (key->IsNumber()) {
5243 uint32_t entry = static_cast<uint32_t>(key->Number());
5244 elems->set(entry, dictionary->ValueAt(i), mode);
5245 }
5246 }
5247 break;
5248 }
5249 default:
5250 UNREACHABLE();
5251 break;
5252 }
5253 set_elements(elems);
5254 }
5255
5256
SetSlowElements(Object * len)5257 Object* JSObject::SetSlowElements(Object* len) {
5258 // We should never end in here with a pixel array.
5259 ASSERT(!HasPixelElements());
5260
5261 uint32_t new_length = static_cast<uint32_t>(len->Number());
5262
5263 switch (GetElementsKind()) {
5264 case FAST_ELEMENTS: {
5265 // Make sure we never try to shrink dense arrays into sparse arrays.
5266 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
5267 new_length);
5268 Object* obj = NormalizeElements();
5269 if (obj->IsFailure()) return obj;
5270
5271 // Update length for JSArrays.
5272 if (IsJSArray()) JSArray::cast(this)->set_length(len);
5273 break;
5274 }
5275 case DICTIONARY_ELEMENTS: {
5276 if (IsJSArray()) {
5277 uint32_t old_length =
5278 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
5279 element_dictionary()->RemoveNumberEntries(new_length, old_length),
5280 JSArray::cast(this)->set_length(len);
5281 }
5282 break;
5283 }
5284 default:
5285 UNREACHABLE();
5286 break;
5287 }
5288 return this;
5289 }
5290
5291
Initialize(int capacity)5292 Object* JSArray::Initialize(int capacity) {
5293 ASSERT(capacity >= 0);
5294 set_length(Smi::FromInt(0), SKIP_WRITE_BARRIER);
5295 FixedArray* new_elements;
5296 if (capacity == 0) {
5297 new_elements = Heap::empty_fixed_array();
5298 } else {
5299 Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
5300 if (obj->IsFailure()) return obj;
5301 new_elements = FixedArray::cast(obj);
5302 }
5303 set_elements(new_elements);
5304 return this;
5305 }
5306
5307
Expand(int required_size)5308 void JSArray::Expand(int required_size) {
5309 Handle<JSArray> self(this);
5310 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
5311 int old_size = old_backing->length();
5312 // Doubling in size would be overkill, but leave some slack to avoid
5313 // constantly growing.
5314 int new_size = required_size + (required_size >> 3);
5315 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
5316 // Can't use this any more now because we may have had a GC!
5317 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
5318 self->SetContent(*new_backing);
5319 }
5320
5321
5322 // Computes the new capacity when expanding the elements of a JSObject.
NewElementsCapacity(int old_capacity)5323 static int NewElementsCapacity(int old_capacity) {
5324 // (old_capacity + 50%) + 16
5325 return old_capacity + (old_capacity >> 1) + 16;
5326 }
5327
5328
ArrayLengthRangeError()5329 static Object* ArrayLengthRangeError() {
5330 HandleScope scope;
5331 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
5332 HandleVector<Object>(NULL, 0)));
5333 }
5334
5335
SetElementsLength(Object * len)5336 Object* JSObject::SetElementsLength(Object* len) {
5337 // We should never end in here with a pixel array.
5338 ASSERT(!HasPixelElements());
5339
5340 Object* smi_length = len->ToSmi();
5341 if (smi_length->IsSmi()) {
5342 int value = Smi::cast(smi_length)->value();
5343 if (value < 0) return ArrayLengthRangeError();
5344 switch (GetElementsKind()) {
5345 case FAST_ELEMENTS: {
5346 int old_capacity = FixedArray::cast(elements())->length();
5347 if (value <= old_capacity) {
5348 if (IsJSArray()) {
5349 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
5350 // NOTE: We may be able to optimize this by removing the
5351 // last part of the elements backing storage array and
5352 // setting the capacity to the new size.
5353 for (int i = value; i < old_length; i++) {
5354 FixedArray::cast(elements())->set_the_hole(i);
5355 }
5356 JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
5357 }
5358 return this;
5359 }
5360 int min = NewElementsCapacity(old_capacity);
5361 int new_capacity = value > min ? value : min;
5362 if (new_capacity <= kMaxFastElementsLength ||
5363 !ShouldConvertToSlowElements(new_capacity)) {
5364 Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
5365 if (obj->IsFailure()) return obj;
5366 if (IsJSArray()) JSArray::cast(this)->set_length(smi_length,
5367 SKIP_WRITE_BARRIER);
5368 SetFastElements(FixedArray::cast(obj));
5369 return this;
5370 }
5371 break;
5372 }
5373 case DICTIONARY_ELEMENTS: {
5374 if (IsJSArray()) {
5375 if (value == 0) {
5376 // If the length of a slow array is reset to zero, we clear
5377 // the array and flush backing storage. This has the added
5378 // benefit that the array returns to fast mode.
5379 initialize_elements();
5380 } else {
5381 // Remove deleted elements.
5382 uint32_t old_length =
5383 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
5384 element_dictionary()->RemoveNumberEntries(value, old_length);
5385 }
5386 JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
5387 }
5388 return this;
5389 }
5390 default:
5391 UNREACHABLE();
5392 break;
5393 }
5394 }
5395
5396 // General slow case.
5397 if (len->IsNumber()) {
5398 uint32_t length;
5399 if (Array::IndexFromObject(len, &length)) {
5400 return SetSlowElements(len);
5401 } else {
5402 return ArrayLengthRangeError();
5403 }
5404 }
5405
5406 // len is not a number so make the array size one and
5407 // set only element to len.
5408 Object* obj = Heap::AllocateFixedArray(1);
5409 if (obj->IsFailure()) return obj;
5410 FixedArray::cast(obj)->set(0, len);
5411 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1),
5412 SKIP_WRITE_BARRIER);
5413 set_elements(FixedArray::cast(obj));
5414 return this;
5415 }
5416
5417
HasElementPostInterceptor(JSObject * receiver,uint32_t index)5418 bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
5419 switch (GetElementsKind()) {
5420 case FAST_ELEMENTS: {
5421 uint32_t length = IsJSArray() ?
5422 static_cast<uint32_t>
5423 (Smi::cast(JSArray::cast(this)->length())->value()) :
5424 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5425 if ((index < length) &&
5426 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
5427 return true;
5428 }
5429 break;
5430 }
5431 case PIXEL_ELEMENTS: {
5432 // TODO(iposva): Add testcase.
5433 PixelArray* pixels = PixelArray::cast(elements());
5434 if (index < static_cast<uint32_t>(pixels->length())) {
5435 return true;
5436 }
5437 break;
5438 }
5439 case DICTIONARY_ELEMENTS: {
5440 if (element_dictionary()->FindEntry(index)
5441 != NumberDictionary::kNotFound) {
5442 return true;
5443 }
5444 break;
5445 }
5446 default:
5447 UNREACHABLE();
5448 break;
5449 }
5450
5451 // Handle [] on String objects.
5452 if (this->IsStringObjectWithCharacterAt(index)) return true;
5453
5454 Object* pt = GetPrototype();
5455 if (pt == Heap::null_value()) return false;
5456 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5457 }
5458
5459
HasElementWithInterceptor(JSObject * receiver,uint32_t index)5460 bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
5461 // Make sure that the top context does not change when doing
5462 // callbacks or interceptor calls.
5463 AssertNoContextChange ncc;
5464 HandleScope scope;
5465 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5466 Handle<JSObject> receiver_handle(receiver);
5467 Handle<JSObject> holder_handle(this);
5468 Handle<Object> data_handle(interceptor->data());
5469 v8::AccessorInfo info(v8::Utils::ToLocal(receiver_handle),
5470 v8::Utils::ToLocal(data_handle),
5471 v8::Utils::ToLocal(holder_handle));
5472 if (!interceptor->query()->IsUndefined()) {
5473 v8::IndexedPropertyQuery query =
5474 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
5475 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
5476 v8::Handle<v8::Boolean> result;
5477 {
5478 // Leaving JavaScript.
5479 VMState state(EXTERNAL);
5480 result = query(index, info);
5481 }
5482 if (!result.IsEmpty()) return result->IsTrue();
5483 } else if (!interceptor->getter()->IsUndefined()) {
5484 v8::IndexedPropertyGetter getter =
5485 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
5486 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
5487 v8::Handle<v8::Value> result;
5488 {
5489 // Leaving JavaScript.
5490 VMState state(EXTERNAL);
5491 result = getter(index, info);
5492 }
5493 if (!result.IsEmpty()) return true;
5494 }
5495 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
5496 }
5497
5498
HasLocalElement(uint32_t index)5499 bool JSObject::HasLocalElement(uint32_t index) {
5500 // Check access rights if needed.
5501 if (IsAccessCheckNeeded() &&
5502 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5503 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5504 return false;
5505 }
5506
5507 // Check for lookup interceptor
5508 if (HasIndexedInterceptor()) {
5509 return HasElementWithInterceptor(this, index);
5510 }
5511
5512 // Handle [] on String objects.
5513 if (this->IsStringObjectWithCharacterAt(index)) return true;
5514
5515 switch (GetElementsKind()) {
5516 case FAST_ELEMENTS: {
5517 uint32_t length = IsJSArray() ?
5518 static_cast<uint32_t>
5519 (Smi::cast(JSArray::cast(this)->length())->value()) :
5520 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5521 return (index < length) &&
5522 !FixedArray::cast(elements())->get(index)->IsTheHole();
5523 }
5524 case PIXEL_ELEMENTS: {
5525 PixelArray* pixels = PixelArray::cast(elements());
5526 return (index < static_cast<uint32_t>(pixels->length()));
5527 }
5528 case DICTIONARY_ELEMENTS: {
5529 return element_dictionary()->FindEntry(index)
5530 != NumberDictionary::kNotFound;
5531 }
5532 default:
5533 UNREACHABLE();
5534 break;
5535 }
5536 UNREACHABLE();
5537 return Heap::null_value();
5538 }
5539
5540
HasElementWithReceiver(JSObject * receiver,uint32_t index)5541 bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
5542 // Check access rights if needed.
5543 if (IsAccessCheckNeeded() &&
5544 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5545 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5546 return false;
5547 }
5548
5549 // Check for lookup interceptor
5550 if (HasIndexedInterceptor()) {
5551 return HasElementWithInterceptor(receiver, index);
5552 }
5553
5554 switch (GetElementsKind()) {
5555 case FAST_ELEMENTS: {
5556 uint32_t length = IsJSArray() ?
5557 static_cast<uint32_t>
5558 (Smi::cast(JSArray::cast(this)->length())->value()) :
5559 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5560 if ((index < length) &&
5561 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
5562 break;
5563 }
5564 case PIXEL_ELEMENTS: {
5565 PixelArray* pixels = PixelArray::cast(elements());
5566 if (index < static_cast<uint32_t>(pixels->length())) {
5567 return true;
5568 }
5569 break;
5570 }
5571 case DICTIONARY_ELEMENTS: {
5572 if (element_dictionary()->FindEntry(index)
5573 != NumberDictionary::kNotFound) {
5574 return true;
5575 }
5576 break;
5577 }
5578 default:
5579 UNREACHABLE();
5580 break;
5581 }
5582
5583 // Handle [] on String objects.
5584 if (this->IsStringObjectWithCharacterAt(index)) return true;
5585
5586 Object* pt = GetPrototype();
5587 if (pt == Heap::null_value()) return false;
5588 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5589 }
5590
5591
SetElementWithInterceptor(uint32_t index,Object * value)5592 Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) {
5593 // Make sure that the top context does not change when doing
5594 // callbacks or interceptor calls.
5595 AssertNoContextChange ncc;
5596 HandleScope scope;
5597 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5598 Handle<JSObject> this_handle(this);
5599 Handle<Object> value_handle(value);
5600 if (!interceptor->setter()->IsUndefined()) {
5601 v8::IndexedPropertySetter setter =
5602 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
5603 Handle<Object> data_handle(interceptor->data());
5604 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
5605 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
5606 v8::Utils::ToLocal(data_handle),
5607 v8::Utils::ToLocal(this_handle));
5608 v8::Handle<v8::Value> result;
5609 {
5610 // Leaving JavaScript.
5611 VMState state(EXTERNAL);
5612 result = setter(index, v8::Utils::ToLocal(value_handle), info);
5613 }
5614 RETURN_IF_SCHEDULED_EXCEPTION();
5615 if (!result.IsEmpty()) return *value_handle;
5616 }
5617 Object* raw_result =
5618 this_handle->SetElementWithoutInterceptor(index, *value_handle);
5619 RETURN_IF_SCHEDULED_EXCEPTION();
5620 return raw_result;
5621 }
5622
5623
5624 // Adding n elements in fast case is O(n*n).
5625 // Note: revisit design to have dual undefined values to capture absent
5626 // elements.
SetFastElement(uint32_t index,Object * value)5627 Object* JSObject::SetFastElement(uint32_t index, Object* value) {
5628 ASSERT(HasFastElements());
5629
5630 FixedArray* elms = FixedArray::cast(elements());
5631 uint32_t elms_length = static_cast<uint32_t>(elms->length());
5632
5633 if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
5634 Object* setter = LookupCallbackSetterInPrototypes(index);
5635 if (setter->IsJSFunction()) {
5636 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
5637 }
5638 }
5639
5640 // Check whether there is extra space in fixed array..
5641 if (index < elms_length) {
5642 elms->set(index, value);
5643 if (IsJSArray()) {
5644 // Update the length of the array if needed.
5645 uint32_t array_length = 0;
5646 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(),
5647 &array_length));
5648 if (index >= array_length) {
5649 JSArray::cast(this)->set_length(Smi::FromInt(index + 1),
5650 SKIP_WRITE_BARRIER);
5651 }
5652 }
5653 return value;
5654 }
5655
5656 // Allow gap in fast case.
5657 if ((index - elms_length) < kMaxGap) {
5658 // Try allocating extra space.
5659 int new_capacity = NewElementsCapacity(index+1);
5660 if (new_capacity <= kMaxFastElementsLength ||
5661 !ShouldConvertToSlowElements(new_capacity)) {
5662 ASSERT(static_cast<uint32_t>(new_capacity) > index);
5663 Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
5664 if (obj->IsFailure()) return obj;
5665 SetFastElements(FixedArray::cast(obj));
5666 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(index + 1),
5667 SKIP_WRITE_BARRIER);
5668 FixedArray::cast(elements())->set(index, value);
5669 return value;
5670 }
5671 }
5672
5673 // Otherwise default to slow case.
5674 Object* obj = NormalizeElements();
5675 if (obj->IsFailure()) return obj;
5676 ASSERT(HasDictionaryElements());
5677 return SetElement(index, value);
5678 }
5679
SetElement(uint32_t index,Object * value)5680 Object* JSObject::SetElement(uint32_t index, Object* value) {
5681 // Check access rights if needed.
5682 if (IsAccessCheckNeeded() &&
5683 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
5684 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
5685 return value;
5686 }
5687
5688 if (IsJSGlobalProxy()) {
5689 Object* proto = GetPrototype();
5690 if (proto->IsNull()) return value;
5691 ASSERT(proto->IsJSGlobalObject());
5692 return JSObject::cast(proto)->SetElement(index, value);
5693 }
5694
5695 // Check for lookup interceptor
5696 if (HasIndexedInterceptor()) {
5697 return SetElementWithInterceptor(index, value);
5698 }
5699
5700 return SetElementWithoutInterceptor(index, value);
5701 }
5702
5703
SetElementWithoutInterceptor(uint32_t index,Object * value)5704 Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
5705 switch (GetElementsKind()) {
5706 case FAST_ELEMENTS:
5707 // Fast case.
5708 return SetFastElement(index, value);
5709 case PIXEL_ELEMENTS: {
5710 PixelArray* pixels = PixelArray::cast(elements());
5711 return pixels->SetValue(index, value);
5712 }
5713 case DICTIONARY_ELEMENTS: {
5714 // Insert element in the dictionary.
5715 FixedArray* elms = FixedArray::cast(elements());
5716 NumberDictionary* dictionary = NumberDictionary::cast(elms);
5717
5718 int entry = dictionary->FindEntry(index);
5719 if (entry != NumberDictionary::kNotFound) {
5720 Object* element = dictionary->ValueAt(entry);
5721 PropertyDetails details = dictionary->DetailsAt(entry);
5722 if (details.type() == CALLBACKS) {
5723 // Only accessors allowed as elements.
5724 FixedArray* structure = FixedArray::cast(element);
5725 if (structure->get(kSetterIndex)->IsJSFunction()) {
5726 JSFunction* setter = JSFunction::cast(structure->get(kSetterIndex));
5727 return SetPropertyWithDefinedSetter(setter, value);
5728 } else {
5729 Handle<Object> self(this);
5730 Handle<Object> key(Factory::NewNumberFromUint(index));
5731 Handle<Object> args[2] = { key, self };
5732 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
5733 HandleVector(args, 2)));
5734 }
5735 } else {
5736 dictionary->UpdateMaxNumberKey(index);
5737 dictionary->ValueAtPut(entry, value);
5738 }
5739 } else {
5740 // Index not already used. Look for an accessor in the prototype chain.
5741 if (!IsJSArray()) {
5742 Object* setter = LookupCallbackSetterInPrototypes(index);
5743 if (setter->IsJSFunction()) {
5744 return SetPropertyWithDefinedSetter(JSFunction::cast(setter),
5745 value);
5746 }
5747 }
5748 Object* result = dictionary->AtNumberPut(index, value);
5749 if (result->IsFailure()) return result;
5750 if (elms != FixedArray::cast(result)) {
5751 set_elements(FixedArray::cast(result));
5752 }
5753 }
5754
5755 // Update the array length if this JSObject is an array.
5756 if (IsJSArray()) {
5757 JSArray* array = JSArray::cast(this);
5758 Object* return_value = array->JSArrayUpdateLengthFromIndex(index,
5759 value);
5760 if (return_value->IsFailure()) return return_value;
5761 }
5762
5763 // Attempt to put this object back in fast case.
5764 if (ShouldConvertToFastElements()) {
5765 uint32_t new_length = 0;
5766 if (IsJSArray()) {
5767 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(),
5768 &new_length));
5769 JSArray::cast(this)->set_length(Smi::FromInt(new_length));
5770 } else {
5771 new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
5772 }
5773 Object* obj = Heap::AllocateFixedArrayWithHoles(new_length);
5774 if (obj->IsFailure()) return obj;
5775 SetFastElements(FixedArray::cast(obj));
5776 #ifdef DEBUG
5777 if (FLAG_trace_normalization) {
5778 PrintF("Object elements are fast case again:\n");
5779 Print();
5780 }
5781 #endif
5782 }
5783
5784 return value;
5785 }
5786 default:
5787 UNREACHABLE();
5788 break;
5789 }
5790 // All possible cases have been handled above. Add a return to avoid the
5791 // complaints from the compiler.
5792 UNREACHABLE();
5793 return Heap::null_value();
5794 }
5795
5796
JSArrayUpdateLengthFromIndex(uint32_t index,Object * value)5797 Object* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index, Object* value) {
5798 uint32_t old_len = 0;
5799 CHECK(Array::IndexFromObject(length(), &old_len));
5800 // Check to see if we need to update the length. For now, we make
5801 // sure that the length stays within 32-bits (unsigned).
5802 if (index >= old_len && index != 0xffffffff) {
5803 Object* len =
5804 Heap::NumberFromDouble(static_cast<double>(index) + 1);
5805 if (len->IsFailure()) return len;
5806 set_length(len);
5807 }
5808 return value;
5809 }
5810
5811
GetElementPostInterceptor(JSObject * receiver,uint32_t index)5812 Object* JSObject::GetElementPostInterceptor(JSObject* receiver,
5813 uint32_t index) {
5814 // Get element works for both JSObject and JSArray since
5815 // JSArray::length cannot change.
5816 switch (GetElementsKind()) {
5817 case FAST_ELEMENTS: {
5818 FixedArray* elms = FixedArray::cast(elements());
5819 if (index < static_cast<uint32_t>(elms->length())) {
5820 Object* value = elms->get(index);
5821 if (!value->IsTheHole()) return value;
5822 }
5823 break;
5824 }
5825 case PIXEL_ELEMENTS: {
5826 // TODO(iposva): Add testcase and implement.
5827 UNIMPLEMENTED();
5828 break;
5829 }
5830 case DICTIONARY_ELEMENTS: {
5831 NumberDictionary* dictionary = element_dictionary();
5832 int entry = dictionary->FindEntry(index);
5833 if (entry != NumberDictionary::kNotFound) {
5834 Object* element = dictionary->ValueAt(entry);
5835 PropertyDetails details = dictionary->DetailsAt(entry);
5836 if (details.type() == CALLBACKS) {
5837 // Only accessors allowed as elements.
5838 FixedArray* structure = FixedArray::cast(element);
5839 Object* getter = structure->get(kGetterIndex);
5840 if (getter->IsJSFunction()) {
5841 return GetPropertyWithDefinedGetter(receiver,
5842 JSFunction::cast(getter));
5843 } else {
5844 // Getter is not a function.
5845 return Heap::undefined_value();
5846 }
5847 }
5848 return element;
5849 }
5850 break;
5851 }
5852 default:
5853 UNREACHABLE();
5854 break;
5855 }
5856
5857 // Continue searching via the prototype chain.
5858 Object* pt = GetPrototype();
5859 if (pt == Heap::null_value()) return Heap::undefined_value();
5860 return pt->GetElementWithReceiver(receiver, index);
5861 }
5862
5863
GetElementWithInterceptor(JSObject * receiver,uint32_t index)5864 Object* JSObject::GetElementWithInterceptor(JSObject* receiver,
5865 uint32_t index) {
5866 // Make sure that the top context does not change when doing
5867 // callbacks or interceptor calls.
5868 AssertNoContextChange ncc;
5869 HandleScope scope;
5870 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5871 Handle<JSObject> this_handle(receiver);
5872 Handle<JSObject> holder_handle(this);
5873
5874 if (!interceptor->getter()->IsUndefined()) {
5875 Handle<Object> data_handle(interceptor->data());
5876 v8::IndexedPropertyGetter getter =
5877 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
5878 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
5879 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
5880 v8::Utils::ToLocal(data_handle),
5881 v8::Utils::ToLocal(holder_handle));
5882 v8::Handle<v8::Value> result;
5883 {
5884 // Leaving JavaScript.
5885 VMState state(EXTERNAL);
5886 result = getter(index, info);
5887 }
5888 RETURN_IF_SCHEDULED_EXCEPTION();
5889 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5890 }
5891
5892 Object* raw_result =
5893 holder_handle->GetElementPostInterceptor(*this_handle, index);
5894 RETURN_IF_SCHEDULED_EXCEPTION();
5895 return raw_result;
5896 }
5897
5898
GetElementWithReceiver(JSObject * receiver,uint32_t index)5899 Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) {
5900 // Check access rights if needed.
5901 if (IsAccessCheckNeeded() &&
5902 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
5903 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
5904 return Heap::undefined_value();
5905 }
5906
5907 if (HasIndexedInterceptor()) {
5908 return GetElementWithInterceptor(receiver, index);
5909 }
5910
5911 // Get element works for both JSObject and JSArray since
5912 // JSArray::length cannot change.
5913 switch (GetElementsKind()) {
5914 case FAST_ELEMENTS: {
5915 FixedArray* elms = FixedArray::cast(elements());
5916 if (index < static_cast<uint32_t>(elms->length())) {
5917 Object* value = elms->get(index);
5918 if (!value->IsTheHole()) return value;
5919 }
5920 break;
5921 }
5922 case PIXEL_ELEMENTS: {
5923 PixelArray* pixels = PixelArray::cast(elements());
5924 if (index < static_cast<uint32_t>(pixels->length())) {
5925 uint8_t value = pixels->get(index);
5926 return Smi::FromInt(value);
5927 }
5928 break;
5929 }
5930 case DICTIONARY_ELEMENTS: {
5931 NumberDictionary* dictionary = element_dictionary();
5932 int entry = dictionary->FindEntry(index);
5933 if (entry != NumberDictionary::kNotFound) {
5934 Object* element = dictionary->ValueAt(entry);
5935 PropertyDetails details = dictionary->DetailsAt(entry);
5936 if (details.type() == CALLBACKS) {
5937 // Only accessors allowed as elements.
5938 FixedArray* structure = FixedArray::cast(element);
5939 Object* getter = structure->get(kGetterIndex);
5940 if (getter->IsJSFunction()) {
5941 return GetPropertyWithDefinedGetter(receiver,
5942 JSFunction::cast(getter));
5943 } else {
5944 // Getter is not a function.
5945 return Heap::undefined_value();
5946 }
5947 }
5948 return element;
5949 }
5950 break;
5951 }
5952 }
5953
5954 Object* pt = GetPrototype();
5955 if (pt == Heap::null_value()) return Heap::undefined_value();
5956 return pt->GetElementWithReceiver(receiver, index);
5957 }
5958
5959
HasDenseElements()5960 bool JSObject::HasDenseElements() {
5961 int capacity = 0;
5962 int number_of_elements = 0;
5963
5964 switch (GetElementsKind()) {
5965 case FAST_ELEMENTS: {
5966 FixedArray* elms = FixedArray::cast(elements());
5967 capacity = elms->length();
5968 for (int i = 0; i < capacity; i++) {
5969 if (!elms->get(i)->IsTheHole()) number_of_elements++;
5970 }
5971 break;
5972 }
5973 case PIXEL_ELEMENTS: {
5974 return true;
5975 }
5976 case DICTIONARY_ELEMENTS: {
5977 NumberDictionary* dictionary = NumberDictionary::cast(elements());
5978 capacity = dictionary->Capacity();
5979 number_of_elements = dictionary->NumberOfElements();
5980 break;
5981 }
5982 default:
5983 UNREACHABLE();
5984 break;
5985 }
5986
5987 if (capacity == 0) return true;
5988 return (number_of_elements > (capacity / 2));
5989 }
5990
5991
ShouldConvertToSlowElements(int new_capacity)5992 bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
5993 ASSERT(HasFastElements());
5994 // Keep the array in fast case if the current backing storage is
5995 // almost filled and if the new capacity is no more than twice the
5996 // old capacity.
5997 int elements_length = FixedArray::cast(elements())->length();
5998 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
5999 }
6000
6001
ShouldConvertToFastElements()6002 bool JSObject::ShouldConvertToFastElements() {
6003 ASSERT(HasDictionaryElements());
6004 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6005 // If the elements are sparse, we should not go back to fast case.
6006 if (!HasDenseElements()) return false;
6007 // If an element has been added at a very high index in the elements
6008 // dictionary, we cannot go back to fast case.
6009 if (dictionary->requires_slow_elements()) return false;
6010 // An object requiring access checks is never allowed to have fast
6011 // elements. If it had fast elements we would skip security checks.
6012 if (IsAccessCheckNeeded()) return false;
6013 // If the dictionary backing storage takes up roughly half as much
6014 // space as a fast-case backing storage would the array should have
6015 // fast elements.
6016 uint32_t length = 0;
6017 if (IsJSArray()) {
6018 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), &length));
6019 } else {
6020 length = dictionary->max_number_key();
6021 }
6022 return static_cast<uint32_t>(dictionary->Capacity()) >=
6023 (length / (2 * NumberDictionary::kEntrySize));
6024 }
6025
6026
6027 // Certain compilers request function template instantiation when they
6028 // see the definition of the other template functions in the
6029 // class. This requires us to have the template functions put
6030 // together, so even though this function belongs in objects-debug.cc,
6031 // we keep it here instead to satisfy certain compilers.
6032 #ifdef DEBUG
6033 template<typename Shape, typename Key>
Print()6034 void Dictionary<Shape, Key>::Print() {
6035 int capacity = HashTable<Shape, Key>::Capacity();
6036 for (int i = 0; i < capacity; i++) {
6037 Object* k = HashTable<Shape, Key>::KeyAt(i);
6038 if (HashTable<Shape, Key>::IsKey(k)) {
6039 PrintF(" ");
6040 if (k->IsString()) {
6041 String::cast(k)->StringPrint();
6042 } else {
6043 k->ShortPrint();
6044 }
6045 PrintF(": ");
6046 ValueAt(i)->ShortPrint();
6047 PrintF("\n");
6048 }
6049 }
6050 }
6051 #endif
6052
6053
6054 template<typename Shape, typename Key>
CopyValuesTo(FixedArray * elements)6055 void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
6056 int pos = 0;
6057 int capacity = HashTable<Shape, Key>::Capacity();
6058 WriteBarrierMode mode = elements->GetWriteBarrierMode();
6059 for (int i = 0; i < capacity; i++) {
6060 Object* k = Dictionary<Shape, Key>::KeyAt(i);
6061 if (Dictionary<Shape, Key>::IsKey(k)) {
6062 elements->set(pos++, ValueAt(i), mode);
6063 }
6064 }
6065 ASSERT(pos == elements->length());
6066 }
6067
6068
GetNamedInterceptor()6069 InterceptorInfo* JSObject::GetNamedInterceptor() {
6070 ASSERT(map()->has_named_interceptor());
6071 JSFunction* constructor = JSFunction::cast(map()->constructor());
6072 Object* template_info = constructor->shared()->function_data();
6073 Object* result =
6074 FunctionTemplateInfo::cast(template_info)->named_property_handler();
6075 return InterceptorInfo::cast(result);
6076 }
6077
6078
GetIndexedInterceptor()6079 InterceptorInfo* JSObject::GetIndexedInterceptor() {
6080 ASSERT(map()->has_indexed_interceptor());
6081 JSFunction* constructor = JSFunction::cast(map()->constructor());
6082 Object* template_info = constructor->shared()->function_data();
6083 Object* result =
6084 FunctionTemplateInfo::cast(template_info)->indexed_property_handler();
6085 return InterceptorInfo::cast(result);
6086 }
6087
6088
GetPropertyPostInterceptor(JSObject * receiver,String * name,PropertyAttributes * attributes)6089 Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver,
6090 String* name,
6091 PropertyAttributes* attributes) {
6092 // Check local property in holder, ignore interceptor.
6093 LookupResult result;
6094 LocalLookupRealNamedProperty(name, &result);
6095 if (result.IsValid()) return GetProperty(receiver, &result, name, attributes);
6096 // Continue searching via the prototype chain.
6097 Object* pt = GetPrototype();
6098 *attributes = ABSENT;
6099 if (pt == Heap::null_value()) return Heap::undefined_value();
6100 return pt->GetPropertyWithReceiver(receiver, name, attributes);
6101 }
6102
6103
GetPropertyWithInterceptor(JSObject * receiver,String * name,PropertyAttributes * attributes)6104 Object* JSObject::GetPropertyWithInterceptor(
6105 JSObject* receiver,
6106 String* name,
6107 PropertyAttributes* attributes) {
6108 InterceptorInfo* interceptor = GetNamedInterceptor();
6109 HandleScope scope;
6110 Handle<JSObject> receiver_handle(receiver);
6111 Handle<JSObject> holder_handle(this);
6112 Handle<String> name_handle(name);
6113 Handle<Object> data_handle(interceptor->data());
6114
6115 if (!interceptor->getter()->IsUndefined()) {
6116 v8::NamedPropertyGetter getter =
6117 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
6118 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
6119 v8::AccessorInfo info(v8::Utils::ToLocal(receiver_handle),
6120 v8::Utils::ToLocal(data_handle),
6121 v8::Utils::ToLocal(holder_handle));
6122 v8::Handle<v8::Value> result;
6123 {
6124 // Leaving JavaScript.
6125 VMState state(EXTERNAL);
6126 result = getter(v8::Utils::ToLocal(name_handle), info);
6127 }
6128 RETURN_IF_SCHEDULED_EXCEPTION();
6129 if (!result.IsEmpty()) {
6130 *attributes = NONE;
6131 return *v8::Utils::OpenHandle(*result);
6132 }
6133 }
6134
6135 Object* result = holder_handle->GetPropertyPostInterceptor(
6136 *receiver_handle,
6137 *name_handle,
6138 attributes);
6139 RETURN_IF_SCHEDULED_EXCEPTION();
6140 return result;
6141 }
6142
6143
HasRealNamedProperty(String * key)6144 bool JSObject::HasRealNamedProperty(String* key) {
6145 // Check access rights if needed.
6146 if (IsAccessCheckNeeded() &&
6147 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
6148 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6149 return false;
6150 }
6151
6152 LookupResult result;
6153 LocalLookupRealNamedProperty(key, &result);
6154 if (result.IsValid()) {
6155 switch (result.type()) {
6156 case NORMAL: // fall through.
6157 case FIELD: // fall through.
6158 case CALLBACKS: // fall through.
6159 case CONSTANT_FUNCTION:
6160 return true;
6161 case INTERCEPTOR:
6162 case MAP_TRANSITION:
6163 case CONSTANT_TRANSITION:
6164 case NULL_DESCRIPTOR:
6165 return false;
6166 default:
6167 UNREACHABLE();
6168 }
6169 }
6170
6171 return false;
6172 }
6173
6174
HasRealElementProperty(uint32_t index)6175 bool JSObject::HasRealElementProperty(uint32_t index) {
6176 // Check access rights if needed.
6177 if (IsAccessCheckNeeded() &&
6178 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6179 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6180 return false;
6181 }
6182
6183 // Handle [] on String objects.
6184 if (this->IsStringObjectWithCharacterAt(index)) return true;
6185
6186 switch (GetElementsKind()) {
6187 case FAST_ELEMENTS: {
6188 uint32_t length = IsJSArray() ?
6189 static_cast<uint32_t>(
6190 Smi::cast(JSArray::cast(this)->length())->value()) :
6191 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6192 return (index < length) &&
6193 !FixedArray::cast(elements())->get(index)->IsTheHole();
6194 }
6195 case PIXEL_ELEMENTS: {
6196 PixelArray* pixels = PixelArray::cast(elements());
6197 return index < static_cast<uint32_t>(pixels->length());
6198 }
6199 case DICTIONARY_ELEMENTS: {
6200 return element_dictionary()->FindEntry(index)
6201 != NumberDictionary::kNotFound;
6202 }
6203 default:
6204 UNREACHABLE();
6205 break;
6206 }
6207 // All possibilities have been handled above already.
6208 UNREACHABLE();
6209 return Heap::null_value();
6210 }
6211
6212
HasRealNamedCallbackProperty(String * key)6213 bool JSObject::HasRealNamedCallbackProperty(String* key) {
6214 // Check access rights if needed.
6215 if (IsAccessCheckNeeded() &&
6216 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
6217 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6218 return false;
6219 }
6220
6221 LookupResult result;
6222 LocalLookupRealNamedProperty(key, &result);
6223 return result.IsValid() && (result.type() == CALLBACKS);
6224 }
6225
6226
NumberOfLocalProperties(PropertyAttributes filter)6227 int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
6228 if (HasFastProperties()) {
6229 DescriptorArray* descs = map()->instance_descriptors();
6230 int result = 0;
6231 for (int i = 0; i < descs->number_of_descriptors(); i++) {
6232 PropertyDetails details = descs->GetDetails(i);
6233 if (details.IsProperty() && (details.attributes() & filter) == 0) {
6234 result++;
6235 }
6236 }
6237 return result;
6238 } else {
6239 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
6240 }
6241 }
6242
6243
NumberOfEnumProperties()6244 int JSObject::NumberOfEnumProperties() {
6245 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
6246 }
6247
6248
SwapPairs(FixedArray * numbers,int i,int j)6249 void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
6250 Object* temp = get(i);
6251 set(i, get(j));
6252 set(j, temp);
6253 if (this != numbers) {
6254 temp = numbers->get(i);
6255 numbers->set(i, numbers->get(j));
6256 numbers->set(j, temp);
6257 }
6258 }
6259
6260
InsertionSortPairs(FixedArray * content,FixedArray * numbers,int len)6261 static void InsertionSortPairs(FixedArray* content,
6262 FixedArray* numbers,
6263 int len) {
6264 for (int i = 1; i < len; i++) {
6265 int j = i;
6266 while (j > 0 &&
6267 (NumberToUint32(numbers->get(j - 1)) >
6268 NumberToUint32(numbers->get(j)))) {
6269 content->SwapPairs(numbers, j - 1, j);
6270 j--;
6271 }
6272 }
6273 }
6274
6275
HeapSortPairs(FixedArray * content,FixedArray * numbers,int len)6276 void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
6277 // In-place heap sort.
6278 ASSERT(content->length() == numbers->length());
6279
6280 // Bottom-up max-heap construction.
6281 for (int i = 1; i < len; ++i) {
6282 int child_index = i;
6283 while (child_index > 0) {
6284 int parent_index = ((child_index + 1) >> 1) - 1;
6285 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
6286 uint32_t child_value = NumberToUint32(numbers->get(child_index));
6287 if (parent_value < child_value) {
6288 content->SwapPairs(numbers, parent_index, child_index);
6289 } else {
6290 break;
6291 }
6292 child_index = parent_index;
6293 }
6294 }
6295
6296 // Extract elements and create sorted array.
6297 for (int i = len - 1; i > 0; --i) {
6298 // Put max element at the back of the array.
6299 content->SwapPairs(numbers, 0, i);
6300 // Sift down the new top element.
6301 int parent_index = 0;
6302 while (true) {
6303 int child_index = ((parent_index + 1) << 1) - 1;
6304 if (child_index >= i) break;
6305 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
6306 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
6307 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
6308 if (child_index + 1 >= i || child1_value > child2_value) {
6309 if (parent_value > child1_value) break;
6310 content->SwapPairs(numbers, parent_index, child_index);
6311 parent_index = child_index;
6312 } else {
6313 if (parent_value > child2_value) break;
6314 content->SwapPairs(numbers, parent_index, child_index + 1);
6315 parent_index = child_index + 1;
6316 }
6317 }
6318 }
6319 }
6320
6321
6322 // Sort this array and the numbers as pairs wrt. the (distinct) numbers.
SortPairs(FixedArray * numbers,uint32_t len)6323 void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
6324 ASSERT(this->length() == numbers->length());
6325 // For small arrays, simply use insertion sort.
6326 if (len <= 10) {
6327 InsertionSortPairs(this, numbers, len);
6328 return;
6329 }
6330 // Check the range of indices.
6331 uint32_t min_index = NumberToUint32(numbers->get(0));
6332 uint32_t max_index = min_index;
6333 uint32_t i;
6334 for (i = 1; i < len; i++) {
6335 if (NumberToUint32(numbers->get(i)) < min_index) {
6336 min_index = NumberToUint32(numbers->get(i));
6337 } else if (NumberToUint32(numbers->get(i)) > max_index) {
6338 max_index = NumberToUint32(numbers->get(i));
6339 }
6340 }
6341 if (max_index - min_index + 1 == len) {
6342 // Indices form a contiguous range, unless there are duplicates.
6343 // Do an in-place linear time sort assuming distinct numbers, but
6344 // avoid hanging in case they are not.
6345 for (i = 0; i < len; i++) {
6346 uint32_t p;
6347 uint32_t j = 0;
6348 // While the current element at i is not at its correct position p,
6349 // swap the elements at these two positions.
6350 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
6351 j++ < len) {
6352 SwapPairs(numbers, i, p);
6353 }
6354 }
6355 } else {
6356 HeapSortPairs(this, numbers, len);
6357 return;
6358 }
6359 }
6360
6361
6362 // Fill in the names of local properties into the supplied storage. The main
6363 // purpose of this function is to provide reflection information for the object
6364 // mirrors.
GetLocalPropertyNames(FixedArray * storage,int index)6365 void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
6366 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
6367 if (HasFastProperties()) {
6368 DescriptorArray* descs = map()->instance_descriptors();
6369 for (int i = 0; i < descs->number_of_descriptors(); i++) {
6370 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
6371 }
6372 ASSERT(storage->length() >= index);
6373 } else {
6374 property_dictionary()->CopyKeysTo(storage);
6375 }
6376 }
6377
6378
NumberOfLocalElements(PropertyAttributes filter)6379 int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
6380 return GetLocalElementKeys(NULL, filter);
6381 }
6382
6383
NumberOfEnumElements()6384 int JSObject::NumberOfEnumElements() {
6385 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
6386 }
6387
6388
GetLocalElementKeys(FixedArray * storage,PropertyAttributes filter)6389 int JSObject::GetLocalElementKeys(FixedArray* storage,
6390 PropertyAttributes filter) {
6391 int counter = 0;
6392 switch (GetElementsKind()) {
6393 case FAST_ELEMENTS: {
6394 int length = IsJSArray() ?
6395 Smi::cast(JSArray::cast(this)->length())->value() :
6396 FixedArray::cast(elements())->length();
6397 for (int i = 0; i < length; i++) {
6398 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
6399 if (storage != NULL) {
6400 storage->set(counter, Smi::FromInt(i), SKIP_WRITE_BARRIER);
6401 }
6402 counter++;
6403 }
6404 }
6405 ASSERT(!storage || storage->length() >= counter);
6406 break;
6407 }
6408 case PIXEL_ELEMENTS: {
6409 int length = PixelArray::cast(elements())->length();
6410 while (counter < length) {
6411 if (storage != NULL) {
6412 storage->set(counter, Smi::FromInt(counter), SKIP_WRITE_BARRIER);
6413 }
6414 counter++;
6415 }
6416 ASSERT(!storage || storage->length() >= counter);
6417 break;
6418 }
6419 case DICTIONARY_ELEMENTS: {
6420 if (storage != NULL) {
6421 element_dictionary()->CopyKeysTo(storage, filter);
6422 }
6423 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
6424 break;
6425 }
6426 default:
6427 UNREACHABLE();
6428 break;
6429 }
6430
6431 if (this->IsJSValue()) {
6432 Object* val = JSValue::cast(this)->value();
6433 if (val->IsString()) {
6434 String* str = String::cast(val);
6435 if (storage) {
6436 for (int i = 0; i < str->length(); i++) {
6437 storage->set(counter + i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
6438 }
6439 }
6440 counter += str->length();
6441 }
6442 }
6443 ASSERT(!storage || storage->length() == counter);
6444 return counter;
6445 }
6446
6447
GetEnumElementKeys(FixedArray * storage)6448 int JSObject::GetEnumElementKeys(FixedArray* storage) {
6449 return GetLocalElementKeys(storage,
6450 static_cast<PropertyAttributes>(DONT_ENUM));
6451 }
6452
6453
IsMatch(uint32_t key,Object * other)6454 bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
6455 ASSERT(other->IsNumber());
6456 return key == static_cast<uint32_t>(other->Number());
6457 }
6458
6459
Hash(uint32_t key)6460 uint32_t NumberDictionaryShape::Hash(uint32_t key) {
6461 return ComputeIntegerHash(key);
6462 }
6463
6464
HashForObject(uint32_t key,Object * other)6465 uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) {
6466 ASSERT(other->IsNumber());
6467 return ComputeIntegerHash(static_cast<uint32_t>(other->Number()));
6468 }
6469
6470
AsObject(uint32_t key)6471 Object* NumberDictionaryShape::AsObject(uint32_t key) {
6472 return Heap::NumberFromUint32(key);
6473 }
6474
6475
IsMatch(String * key,Object * other)6476 bool StringDictionaryShape::IsMatch(String* key, Object* other) {
6477 // We know that all entries in a hash table had their hash keys created.
6478 // Use that knowledge to have fast failure.
6479 if (key->Hash() != String::cast(other)->Hash()) return false;
6480 return key->Equals(String::cast(other));
6481 }
6482
6483
Hash(String * key)6484 uint32_t StringDictionaryShape::Hash(String* key) {
6485 return key->Hash();
6486 }
6487
6488
HashForObject(String * key,Object * other)6489 uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) {
6490 return String::cast(other)->Hash();
6491 }
6492
6493
AsObject(String * key)6494 Object* StringDictionaryShape::AsObject(String* key) {
6495 return key;
6496 }
6497
6498
6499 // StringKey simply carries a string object as key.
6500 class StringKey : public HashTableKey {
6501 public:
StringKey(String * string)6502 explicit StringKey(String* string) :
6503 string_(string),
6504 hash_(HashForObject(string)) { }
6505
IsMatch(Object * string)6506 bool IsMatch(Object* string) {
6507 // We know that all entries in a hash table had their hash keys created.
6508 // Use that knowledge to have fast failure.
6509 if (hash_ != HashForObject(string)) {
6510 return false;
6511 }
6512 return string_->Equals(String::cast(string));
6513 }
6514
Hash()6515 uint32_t Hash() { return hash_; }
6516
HashForObject(Object * other)6517 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
6518
AsObject()6519 Object* AsObject() { return string_; }
6520
6521 String* string_;
6522 uint32_t hash_;
6523 };
6524
6525
6526 // StringSharedKeys are used as keys in the eval cache.
6527 class StringSharedKey : public HashTableKey {
6528 public:
StringSharedKey(String * source,SharedFunctionInfo * shared)6529 StringSharedKey(String* source, SharedFunctionInfo* shared)
6530 : source_(source), shared_(shared) { }
6531
IsMatch(Object * other)6532 bool IsMatch(Object* other) {
6533 if (!other->IsFixedArray()) return false;
6534 FixedArray* pair = FixedArray::cast(other);
6535 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
6536 if (shared != shared_) return false;
6537 String* source = String::cast(pair->get(1));
6538 return source->Equals(source_);
6539 }
6540
StringSharedHashHelper(String * source,SharedFunctionInfo * shared)6541 static uint32_t StringSharedHashHelper(String* source,
6542 SharedFunctionInfo* shared) {
6543 uint32_t hash = source->Hash();
6544 if (shared->HasSourceCode()) {
6545 // Instead of using the SharedFunctionInfo pointer in the hash
6546 // code computation, we use a combination of the hash of the
6547 // script source code and the start and end positions. We do
6548 // this to ensure that the cache entries can survive garbage
6549 // collection.
6550 Script* script = Script::cast(shared->script());
6551 hash ^= String::cast(script->source())->Hash();
6552 hash += shared->start_position();
6553 }
6554 return hash;
6555 }
6556
Hash()6557 uint32_t Hash() {
6558 return StringSharedHashHelper(source_, shared_);
6559 }
6560
HashForObject(Object * obj)6561 uint32_t HashForObject(Object* obj) {
6562 FixedArray* pair = FixedArray::cast(obj);
6563 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
6564 String* source = String::cast(pair->get(1));
6565 return StringSharedHashHelper(source, shared);
6566 }
6567
AsObject()6568 Object* AsObject() {
6569 Object* obj = Heap::AllocateFixedArray(2);
6570 if (obj->IsFailure()) return obj;
6571 FixedArray* pair = FixedArray::cast(obj);
6572 pair->set(0, shared_);
6573 pair->set(1, source_);
6574 return pair;
6575 }
6576
6577 private:
6578 String* source_;
6579 SharedFunctionInfo* shared_;
6580 };
6581
6582
6583 // RegExpKey carries the source and flags of a regular expression as key.
6584 class RegExpKey : public HashTableKey {
6585 public:
RegExpKey(String * string,JSRegExp::Flags flags)6586 RegExpKey(String* string, JSRegExp::Flags flags)
6587 : string_(string),
6588 flags_(Smi::FromInt(flags.value())) { }
6589
IsMatch(Object * obj)6590 bool IsMatch(Object* obj) {
6591 FixedArray* val = FixedArray::cast(obj);
6592 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
6593 && (flags_ == val->get(JSRegExp::kFlagsIndex));
6594 }
6595
Hash()6596 uint32_t Hash() { return RegExpHash(string_, flags_); }
6597
AsObject()6598 Object* AsObject() {
6599 // Plain hash maps, which is where regexp keys are used, don't
6600 // use this function.
6601 UNREACHABLE();
6602 return NULL;
6603 }
6604
HashForObject(Object * obj)6605 uint32_t HashForObject(Object* obj) {
6606 FixedArray* val = FixedArray::cast(obj);
6607 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
6608 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
6609 }
6610
RegExpHash(String * string,Smi * flags)6611 static uint32_t RegExpHash(String* string, Smi* flags) {
6612 return string->Hash() + flags->value();
6613 }
6614
6615 String* string_;
6616 Smi* flags_;
6617 };
6618
6619 // Utf8SymbolKey carries a vector of chars as key.
6620 class Utf8SymbolKey : public HashTableKey {
6621 public:
Utf8SymbolKey(Vector<const char> string)6622 explicit Utf8SymbolKey(Vector<const char> string)
6623 : string_(string), length_field_(0) { }
6624
IsMatch(Object * string)6625 bool IsMatch(Object* string) {
6626 return String::cast(string)->IsEqualTo(string_);
6627 }
6628
Hash()6629 uint32_t Hash() {
6630 if (length_field_ != 0) return length_field_ >> String::kHashShift;
6631 unibrow::Utf8InputBuffer<> buffer(string_.start(),
6632 static_cast<unsigned>(string_.length()));
6633 chars_ = buffer.Length();
6634 length_field_ = String::ComputeLengthAndHashField(&buffer, chars_);
6635 uint32_t result = length_field_ >> String::kHashShift;
6636 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
6637 return result;
6638 }
6639
HashForObject(Object * other)6640 uint32_t HashForObject(Object* other) {
6641 return String::cast(other)->Hash();
6642 }
6643
AsObject()6644 Object* AsObject() {
6645 if (length_field_ == 0) Hash();
6646 return Heap::AllocateSymbol(string_, chars_, length_field_);
6647 }
6648
6649 Vector<const char> string_;
6650 uint32_t length_field_;
6651 int chars_; // Caches the number of characters when computing the hash code.
6652 };
6653
6654
6655 // SymbolKey carries a string/symbol object as key.
6656 class SymbolKey : public HashTableKey {
6657 public:
SymbolKey(String * string)6658 explicit SymbolKey(String* string) : string_(string) { }
6659
IsMatch(Object * string)6660 bool IsMatch(Object* string) {
6661 return String::cast(string)->Equals(string_);
6662 }
6663
Hash()6664 uint32_t Hash() { return string_->Hash(); }
6665
HashForObject(Object * other)6666 uint32_t HashForObject(Object* other) {
6667 return String::cast(other)->Hash();
6668 }
6669
AsObject()6670 Object* AsObject() {
6671 // If the string is a cons string, attempt to flatten it so that
6672 // symbols will most often be flat strings.
6673 if (StringShape(string_).IsCons()) {
6674 ConsString* cons_string = ConsString::cast(string_);
6675 cons_string->TryFlatten();
6676 if (cons_string->second()->length() == 0) {
6677 string_ = cons_string->first();
6678 }
6679 }
6680 // Transform string to symbol if possible.
6681 Map* map = Heap::SymbolMapForString(string_);
6682 if (map != NULL) {
6683 string_->set_map(map);
6684 ASSERT(string_->IsSymbol());
6685 return string_;
6686 }
6687 // Otherwise allocate a new symbol.
6688 StringInputBuffer buffer(string_);
6689 return Heap::AllocateInternalSymbol(&buffer,
6690 string_->length(),
6691 string_->length_field());
6692 }
6693
StringHash(Object * obj)6694 static uint32_t StringHash(Object* obj) {
6695 return String::cast(obj)->Hash();
6696 }
6697
6698 String* string_;
6699 };
6700
6701
6702 template<typename Shape, typename Key>
IteratePrefix(ObjectVisitor * v)6703 void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
6704 IteratePointers(v, 0, kElementsStartOffset);
6705 }
6706
6707
6708 template<typename Shape, typename Key>
IterateElements(ObjectVisitor * v)6709 void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
6710 IteratePointers(v,
6711 kElementsStartOffset,
6712 kHeaderSize + length() * kPointerSize);
6713 }
6714
6715
6716 template<typename Shape, typename Key>
Allocate(int at_least_space_for)6717 Object* HashTable<Shape, Key>::Allocate(
6718 int at_least_space_for) {
6719 int capacity = RoundUpToPowerOf2(at_least_space_for);
6720 if (capacity < 4) capacity = 4; // Guarantee min capacity.
6721 Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity));
6722 if (!obj->IsFailure()) {
6723 HashTable::cast(obj)->SetNumberOfElements(0);
6724 HashTable::cast(obj)->SetCapacity(capacity);
6725 }
6726 return obj;
6727 }
6728
6729
6730
6731 // Find entry for key otherwise return -1.
6732 template<typename Shape, typename Key>
FindEntry(Key key)6733 int HashTable<Shape, Key>::FindEntry(Key key) {
6734 uint32_t nof = NumberOfElements();
6735 if (nof == 0) return kNotFound; // Bail out if empty.
6736
6737 uint32_t capacity = Capacity();
6738 uint32_t hash = Shape::Hash(key);
6739 uint32_t entry = GetProbe(hash, 0, capacity);
6740
6741 Object* element = KeyAt(entry);
6742 uint32_t passed_elements = 0;
6743 if (!element->IsNull()) {
6744 if (!element->IsUndefined() && Shape::IsMatch(key, element)) return entry;
6745 if (++passed_elements == nof) return kNotFound;
6746 }
6747 for (uint32_t i = 1; !element->IsUndefined(); i++) {
6748 entry = GetProbe(hash, i, capacity);
6749 element = KeyAt(entry);
6750 if (!element->IsNull()) {
6751 if (!element->IsUndefined() && Shape::IsMatch(key, element)) return entry;
6752 if (++passed_elements == nof) return kNotFound;
6753 }
6754 }
6755 return kNotFound;
6756 }
6757
6758
6759 template<typename Shape, typename Key>
EnsureCapacity(int n,Key key)6760 Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
6761 int capacity = Capacity();
6762 int nof = NumberOfElements() + n;
6763 // Make sure 50% is free
6764 if (nof + (nof >> 1) <= capacity) return this;
6765
6766 Object* obj = Allocate(nof * 2);
6767 if (obj->IsFailure()) return obj;
6768 HashTable* table = HashTable::cast(obj);
6769 WriteBarrierMode mode = table->GetWriteBarrierMode();
6770
6771 // Copy prefix to new array.
6772 for (int i = kPrefixStartIndex;
6773 i < kPrefixStartIndex + Shape::kPrefixSize;
6774 i++) {
6775 table->set(i, get(i), mode);
6776 }
6777 // Rehash the elements.
6778 for (int i = 0; i < capacity; i++) {
6779 uint32_t from_index = EntryToIndex(i);
6780 Object* k = get(from_index);
6781 if (IsKey(k)) {
6782 uint32_t hash = Shape::HashForObject(key, k);
6783 uint32_t insertion_index =
6784 EntryToIndex(table->FindInsertionEntry(hash));
6785 for (int j = 0; j < Shape::kEntrySize; j++) {
6786 table->set(insertion_index + j, get(from_index + j), mode);
6787 }
6788 }
6789 }
6790 table->SetNumberOfElements(NumberOfElements());
6791 return table;
6792 }
6793
6794
6795 template<typename Shape, typename Key>
FindInsertionEntry(uint32_t hash)6796 uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
6797 uint32_t capacity = Capacity();
6798 uint32_t entry = GetProbe(hash, 0, capacity);
6799 Object* element = KeyAt(entry);
6800
6801 for (uint32_t i = 1; !(element->IsUndefined() || element->IsNull()); i++) {
6802 entry = GetProbe(hash, i, capacity);
6803 element = KeyAt(entry);
6804 }
6805
6806 return entry;
6807 }
6808
6809 // Force instantiation of template instances class.
6810 // Please note this list is compiler dependent.
6811
6812 template class HashTable<SymbolTableShape, HashTableKey*>;
6813
6814 template class HashTable<CompilationCacheShape, HashTableKey*>;
6815
6816 template class HashTable<MapCacheShape, HashTableKey*>;
6817
6818 template class Dictionary<StringDictionaryShape, String*>;
6819
6820 template class Dictionary<NumberDictionaryShape, uint32_t>;
6821
6822 template Object* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
6823 int);
6824
6825 template Object* Dictionary<StringDictionaryShape, String*>::Allocate(
6826 int);
6827
6828 template Object* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
6829 uint32_t, Object*);
6830
6831 template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
6832 Object*);
6833
6834 template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
6835 Object*);
6836
6837 template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
6838 FixedArray*, PropertyAttributes);
6839
6840 template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
6841 int, JSObject::DeleteMode);
6842
6843 template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
6844 int, JSObject::DeleteMode);
6845
6846 template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
6847 FixedArray*);
6848
6849 template int
6850 Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
6851 PropertyAttributes);
6852
6853 template Object* Dictionary<StringDictionaryShape, String*>::Add(
6854 String*, Object*, PropertyDetails);
6855
6856 template Object*
6857 Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
6858
6859 template int
6860 Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
6861 PropertyAttributes);
6862
6863 template Object* Dictionary<NumberDictionaryShape, uint32_t>::Add(
6864 uint32_t, Object*, PropertyDetails);
6865
6866 template Object* Dictionary<NumberDictionaryShape, uint32_t>::EnsureCapacity(
6867 int, uint32_t);
6868
6869 template Object* Dictionary<StringDictionaryShape, String*>::EnsureCapacity(
6870 int, String*);
6871
6872 template Object* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
6873 uint32_t, Object*, PropertyDetails, uint32_t);
6874
6875 template Object* Dictionary<StringDictionaryShape, String*>::AddEntry(
6876 String*, Object*, PropertyDetails, uint32_t);
6877
6878 template
6879 int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
6880
6881 template
6882 int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
6883
6884 // Collates undefined and unexisting elements below limit from position
6885 // zero of the elements. The object stays in Dictionary mode.
PrepareSlowElementsForSort(uint32_t limit)6886 Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
6887 ASSERT(HasDictionaryElements());
6888 // Must stay in dictionary mode, either because of requires_slow_elements,
6889 // or because we are not going to sort (and therefore compact) all of the
6890 // elements.
6891 NumberDictionary* dict = element_dictionary();
6892 HeapNumber* result_double = NULL;
6893 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
6894 // Allocate space for result before we start mutating the object.
6895 Object* new_double = Heap::AllocateHeapNumber(0.0);
6896 if (new_double->IsFailure()) return new_double;
6897 result_double = HeapNumber::cast(new_double);
6898 }
6899
6900 int capacity = dict->Capacity();
6901 Object* obj = NumberDictionary::Allocate(dict->Capacity());
6902 if (obj->IsFailure()) return obj;
6903 NumberDictionary* new_dict = NumberDictionary::cast(obj);
6904
6905 AssertNoAllocation no_alloc;
6906
6907 uint32_t pos = 0;
6908 uint32_t undefs = 0;
6909 for (int i = 0; i < capacity; i++) {
6910 Object* k = dict->KeyAt(i);
6911 if (dict->IsKey(k)) {
6912 ASSERT(k->IsNumber());
6913 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
6914 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
6915 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
6916 Object* value = dict->ValueAt(i);
6917 PropertyDetails details = dict->DetailsAt(i);
6918 if (details.type() == CALLBACKS) {
6919 // Bail out and do the sorting of undefineds and array holes in JS.
6920 return Smi::FromInt(-1);
6921 }
6922 uint32_t key = NumberToUint32(k);
6923 if (key < limit) {
6924 if (value->IsUndefined()) {
6925 undefs++;
6926 } else {
6927 new_dict->AddNumberEntry(pos, value, details);
6928 pos++;
6929 }
6930 } else {
6931 new_dict->AddNumberEntry(key, value, details);
6932 }
6933 }
6934 }
6935
6936 uint32_t result = pos;
6937 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
6938 while (undefs > 0) {
6939 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details);
6940 pos++;
6941 undefs--;
6942 }
6943
6944 set_elements(new_dict);
6945
6946 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
6947 return Smi::FromInt(static_cast<int>(result));
6948 }
6949
6950 ASSERT_NE(NULL, result_double);
6951 result_double->set_value(static_cast<double>(result));
6952 return result_double;
6953 }
6954
6955
6956 // Collects all defined (non-hole) and non-undefined (array) elements at
6957 // the start of the elements array.
6958 // If the object is in dictionary mode, it is converted to fast elements
6959 // mode.
PrepareElementsForSort(uint32_t limit)6960 Object* JSObject::PrepareElementsForSort(uint32_t limit) {
6961 ASSERT(!HasPixelElements());
6962
6963 if (HasDictionaryElements()) {
6964 // Convert to fast elements containing only the existing properties.
6965 // Ordering is irrelevant, since we are going to sort anyway.
6966 NumberDictionary* dict = element_dictionary();
6967 if (IsJSArray() || dict->requires_slow_elements() ||
6968 dict->max_number_key() >= limit) {
6969 return PrepareSlowElementsForSort(limit);
6970 }
6971 // Convert to fast elements.
6972
6973 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
6974 Object* new_array =
6975 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
6976 if (new_array->IsFailure()) {
6977 return new_array;
6978 }
6979 FixedArray* fast_elements = FixedArray::cast(new_array);
6980 dict->CopyValuesTo(fast_elements);
6981 set_elements(fast_elements);
6982 }
6983 ASSERT(HasFastElements());
6984
6985 // Collect holes at the end, undefined before that and the rest at the
6986 // start, and return the number of non-hole, non-undefined values.
6987
6988 FixedArray* elements = FixedArray::cast(this->elements());
6989 uint32_t elements_length = static_cast<uint32_t>(elements->length());
6990 if (limit > elements_length) {
6991 limit = elements_length ;
6992 }
6993 if (limit == 0) {
6994 return Smi::FromInt(0);
6995 }
6996
6997 HeapNumber* result_double = NULL;
6998 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
6999 // Pessimistically allocate space for return value before
7000 // we start mutating the array.
7001 Object* new_double = Heap::AllocateHeapNumber(0.0);
7002 if (new_double->IsFailure()) return new_double;
7003 result_double = HeapNumber::cast(new_double);
7004 }
7005
7006 AssertNoAllocation no_alloc;
7007
7008 // Split elements into defined, undefined and the_hole, in that order.
7009 // Only count locations for undefined and the hole, and fill them afterwards.
7010 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode();
7011 unsigned int undefs = limit;
7012 unsigned int holes = limit;
7013 // Assume most arrays contain no holes and undefined values, so minimize the
7014 // number of stores of non-undefined, non-the-hole values.
7015 for (unsigned int i = 0; i < undefs; i++) {
7016 Object* current = elements->get(i);
7017 if (current->IsTheHole()) {
7018 holes--;
7019 undefs--;
7020 } else if (current->IsUndefined()) {
7021 undefs--;
7022 } else {
7023 continue;
7024 }
7025 // Position i needs to be filled.
7026 while (undefs > i) {
7027 current = elements->get(undefs);
7028 if (current->IsTheHole()) {
7029 holes--;
7030 undefs--;
7031 } else if (current->IsUndefined()) {
7032 undefs--;
7033 } else {
7034 elements->set(i, current, write_barrier);
7035 break;
7036 }
7037 }
7038 }
7039 uint32_t result = undefs;
7040 while (undefs < holes) {
7041 elements->set_undefined(undefs);
7042 undefs++;
7043 }
7044 while (holes < limit) {
7045 elements->set_the_hole(holes);
7046 holes++;
7047 }
7048
7049 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7050 return Smi::FromInt(static_cast<int>(result));
7051 }
7052 ASSERT_NE(NULL, result_double);
7053 result_double->set_value(static_cast<double>(result));
7054 return result_double;
7055 }
7056
7057
SetValue(uint32_t index,Object * value)7058 Object* PixelArray::SetValue(uint32_t index, Object* value) {
7059 uint8_t clamped_value = 0;
7060 if (index < static_cast<uint32_t>(length())) {
7061 if (value->IsSmi()) {
7062 int int_value = Smi::cast(value)->value();
7063 if (int_value < 0) {
7064 clamped_value = 0;
7065 } else if (int_value > 255) {
7066 clamped_value = 255;
7067 } else {
7068 clamped_value = static_cast<uint8_t>(int_value);
7069 }
7070 } else if (value->IsHeapNumber()) {
7071 double double_value = HeapNumber::cast(value)->value();
7072 if (!(double_value > 0)) {
7073 // NaN and less than zero clamp to zero.
7074 clamped_value = 0;
7075 } else if (double_value > 255) {
7076 // Greater than 255 clamp to 255.
7077 clamped_value = 255;
7078 } else {
7079 // Other doubles are rounded to the nearest integer.
7080 clamped_value = static_cast<uint8_t>(double_value + 0.5);
7081 }
7082 } else {
7083 // Clamp undefined to zero (default). All other types have been
7084 // converted to a number type further up in the call chain.
7085 ASSERT(value->IsUndefined());
7086 }
7087 set(index, clamped_value);
7088 }
7089 return Smi::FromInt(clamped_value);
7090 }
7091
7092
GetPropertyCell(LookupResult * result)7093 Object* GlobalObject::GetPropertyCell(LookupResult* result) {
7094 ASSERT(!HasFastProperties());
7095 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
7096 ASSERT(value->IsJSGlobalPropertyCell());
7097 return value;
7098 }
7099
7100
EnsurePropertyCell(String * name)7101 Object* GlobalObject::EnsurePropertyCell(String* name) {
7102 ASSERT(!HasFastProperties());
7103 int entry = property_dictionary()->FindEntry(name);
7104 if (entry == StringDictionary::kNotFound) {
7105 Object* cell = Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
7106 if (cell->IsFailure()) return cell;
7107 PropertyDetails details(NONE, NORMAL);
7108 details = details.AsDeleted();
7109 Object* dictionary = property_dictionary()->Add(name, cell, details);
7110 if (dictionary->IsFailure()) return dictionary;
7111 set_properties(StringDictionary::cast(dictionary));
7112 return cell;
7113 } else {
7114 Object* value = property_dictionary()->ValueAt(entry);
7115 ASSERT(value->IsJSGlobalPropertyCell());
7116 return value;
7117 }
7118 }
7119
7120
LookupString(String * string,Object ** s)7121 Object* SymbolTable::LookupString(String* string, Object** s) {
7122 SymbolKey key(string);
7123 return LookupKey(&key, s);
7124 }
7125
7126
LookupSymbolIfExists(String * string,String ** symbol)7127 bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
7128 SymbolKey key(string);
7129 int entry = FindEntry(&key);
7130 if (entry == kNotFound) {
7131 return false;
7132 } else {
7133 String* result = String::cast(KeyAt(entry));
7134 ASSERT(StringShape(result).IsSymbol());
7135 *symbol = result;
7136 return true;
7137 }
7138 }
7139
7140
LookupSymbol(Vector<const char> str,Object ** s)7141 Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
7142 Utf8SymbolKey key(str);
7143 return LookupKey(&key, s);
7144 }
7145
7146
LookupKey(HashTableKey * key,Object ** s)7147 Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
7148 int entry = FindEntry(key);
7149
7150 // Symbol already in table.
7151 if (entry != kNotFound) {
7152 *s = KeyAt(entry);
7153 return this;
7154 }
7155
7156 // Adding new symbol. Grow table if needed.
7157 Object* obj = EnsureCapacity(1, key);
7158 if (obj->IsFailure()) return obj;
7159
7160 // Create symbol object.
7161 Object* symbol = key->AsObject();
7162 if (symbol->IsFailure()) return symbol;
7163
7164 // If the symbol table grew as part of EnsureCapacity, obj is not
7165 // the current symbol table and therefore we cannot use
7166 // SymbolTable::cast here.
7167 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
7168
7169 // Add the new symbol and return it along with the symbol table.
7170 entry = table->FindInsertionEntry(key->Hash());
7171 table->set(EntryToIndex(entry), symbol);
7172 table->ElementAdded();
7173 *s = symbol;
7174 return table;
7175 }
7176
7177
Lookup(String * src)7178 Object* CompilationCacheTable::Lookup(String* src) {
7179 StringKey key(src);
7180 int entry = FindEntry(&key);
7181 if (entry == kNotFound) return Heap::undefined_value();
7182 return get(EntryToIndex(entry) + 1);
7183 }
7184
7185
LookupEval(String * src,Context * context)7186 Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
7187 StringSharedKey key(src, context->closure()->shared());
7188 int entry = FindEntry(&key);
7189 if (entry == kNotFound) return Heap::undefined_value();
7190 return get(EntryToIndex(entry) + 1);
7191 }
7192
7193
LookupRegExp(String * src,JSRegExp::Flags flags)7194 Object* CompilationCacheTable::LookupRegExp(String* src,
7195 JSRegExp::Flags flags) {
7196 RegExpKey key(src, flags);
7197 int entry = FindEntry(&key);
7198 if (entry == kNotFound) return Heap::undefined_value();
7199 return get(EntryToIndex(entry) + 1);
7200 }
7201
7202
Put(String * src,Object * value)7203 Object* CompilationCacheTable::Put(String* src, Object* value) {
7204 StringKey key(src);
7205 Object* obj = EnsureCapacity(1, &key);
7206 if (obj->IsFailure()) return obj;
7207
7208 CompilationCacheTable* cache =
7209 reinterpret_cast<CompilationCacheTable*>(obj);
7210 int entry = cache->FindInsertionEntry(key.Hash());
7211 cache->set(EntryToIndex(entry), src);
7212 cache->set(EntryToIndex(entry) + 1, value);
7213 cache->ElementAdded();
7214 return cache;
7215 }
7216
7217
PutEval(String * src,Context * context,Object * value)7218 Object* CompilationCacheTable::PutEval(String* src,
7219 Context* context,
7220 Object* value) {
7221 StringSharedKey key(src, context->closure()->shared());
7222 Object* obj = EnsureCapacity(1, &key);
7223 if (obj->IsFailure()) return obj;
7224
7225 CompilationCacheTable* cache =
7226 reinterpret_cast<CompilationCacheTable*>(obj);
7227 int entry = cache->FindInsertionEntry(key.Hash());
7228
7229 Object* k = key.AsObject();
7230 if (k->IsFailure()) return k;
7231
7232 cache->set(EntryToIndex(entry), k);
7233 cache->set(EntryToIndex(entry) + 1, value);
7234 cache->ElementAdded();
7235 return cache;
7236 }
7237
7238
PutRegExp(String * src,JSRegExp::Flags flags,FixedArray * value)7239 Object* CompilationCacheTable::PutRegExp(String* src,
7240 JSRegExp::Flags flags,
7241 FixedArray* value) {
7242 RegExpKey key(src, flags);
7243 Object* obj = EnsureCapacity(1, &key);
7244 if (obj->IsFailure()) return obj;
7245
7246 CompilationCacheTable* cache =
7247 reinterpret_cast<CompilationCacheTable*>(obj);
7248 int entry = cache->FindInsertionEntry(key.Hash());
7249 cache->set(EntryToIndex(entry), value);
7250 cache->set(EntryToIndex(entry) + 1, value);
7251 cache->ElementAdded();
7252 return cache;
7253 }
7254
7255
7256 // SymbolsKey used for HashTable where key is array of symbols.
7257 class SymbolsKey : public HashTableKey {
7258 public:
SymbolsKey(FixedArray * symbols)7259 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
7260
IsMatch(Object * symbols)7261 bool IsMatch(Object* symbols) {
7262 FixedArray* o = FixedArray::cast(symbols);
7263 int len = symbols_->length();
7264 if (o->length() != len) return false;
7265 for (int i = 0; i < len; i++) {
7266 if (o->get(i) != symbols_->get(i)) return false;
7267 }
7268 return true;
7269 }
7270
Hash()7271 uint32_t Hash() { return HashForObject(symbols_); }
7272
HashForObject(Object * obj)7273 uint32_t HashForObject(Object* obj) {
7274 FixedArray* symbols = FixedArray::cast(obj);
7275 int len = symbols->length();
7276 uint32_t hash = 0;
7277 for (int i = 0; i < len; i++) {
7278 hash ^= String::cast(symbols->get(i))->Hash();
7279 }
7280 return hash;
7281 }
7282
AsObject()7283 Object* AsObject() { return symbols_; }
7284
7285 private:
7286 FixedArray* symbols_;
7287 };
7288
7289
Lookup(FixedArray * array)7290 Object* MapCache::Lookup(FixedArray* array) {
7291 SymbolsKey key(array);
7292 int entry = FindEntry(&key);
7293 if (entry == kNotFound) return Heap::undefined_value();
7294 return get(EntryToIndex(entry) + 1);
7295 }
7296
7297
Put(FixedArray * array,Map * value)7298 Object* MapCache::Put(FixedArray* array, Map* value) {
7299 SymbolsKey key(array);
7300 Object* obj = EnsureCapacity(1, &key);
7301 if (obj->IsFailure()) return obj;
7302
7303 MapCache* cache = reinterpret_cast<MapCache*>(obj);
7304 int entry = cache->FindInsertionEntry(key.Hash());
7305 cache->set(EntryToIndex(entry), array);
7306 cache->set(EntryToIndex(entry) + 1, value);
7307 cache->ElementAdded();
7308 return cache;
7309 }
7310
7311
7312 template<typename Shape, typename Key>
Allocate(int at_least_space_for)7313 Object* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
7314 Object* obj = HashTable<Shape, Key>::Allocate(at_least_space_for);
7315 // Initialize the next enumeration index.
7316 if (!obj->IsFailure()) {
7317 Dictionary<Shape, Key>::cast(obj)->
7318 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
7319 }
7320 return obj;
7321 }
7322
7323
7324 template<typename Shape, typename Key>
GenerateNewEnumerationIndices()7325 Object* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
7326 int length = HashTable<Shape, Key>::NumberOfElements();
7327
7328 // Allocate and initialize iteration order array.
7329 Object* obj = Heap::AllocateFixedArray(length);
7330 if (obj->IsFailure()) return obj;
7331 FixedArray* iteration_order = FixedArray::cast(obj);
7332 for (int i = 0; i < length; i++) {
7333 iteration_order->set(i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
7334 }
7335
7336 // Allocate array with enumeration order.
7337 obj = Heap::AllocateFixedArray(length);
7338 if (obj->IsFailure()) return obj;
7339 FixedArray* enumeration_order = FixedArray::cast(obj);
7340
7341 // Fill the enumeration order array with property details.
7342 int capacity = HashTable<Shape, Key>::Capacity();
7343 int pos = 0;
7344 for (int i = 0; i < capacity; i++) {
7345 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
7346 enumeration_order->set(pos++,
7347 Smi::FromInt(DetailsAt(i).index()),
7348 SKIP_WRITE_BARRIER);
7349 }
7350 }
7351
7352 // Sort the arrays wrt. enumeration order.
7353 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
7354
7355 // Overwrite the enumeration_order with the enumeration indices.
7356 for (int i = 0; i < length; i++) {
7357 int index = Smi::cast(iteration_order->get(i))->value();
7358 int enum_index = PropertyDetails::kInitialIndex + i;
7359 enumeration_order->set(index,
7360 Smi::FromInt(enum_index),
7361 SKIP_WRITE_BARRIER);
7362 }
7363
7364 // Update the dictionary with new indices.
7365 capacity = HashTable<Shape, Key>::Capacity();
7366 pos = 0;
7367 for (int i = 0; i < capacity; i++) {
7368 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
7369 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
7370 PropertyDetails details = DetailsAt(i);
7371 PropertyDetails new_details =
7372 PropertyDetails(details.attributes(), details.type(), enum_index);
7373 DetailsAtPut(i, new_details);
7374 }
7375 }
7376
7377 // Set the next enumeration index.
7378 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
7379 return this;
7380 }
7381
7382 template<typename Shape, typename Key>
EnsureCapacity(int n,Key key)7383 Object* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
7384 // Check whether there are enough enumeration indices to add n elements.
7385 if (Shape::kIsEnumerable &&
7386 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
7387 // If not, we generate new indices for the properties.
7388 Object* result = GenerateNewEnumerationIndices();
7389 if (result->IsFailure()) return result;
7390 }
7391 return HashTable<Shape, Key>::EnsureCapacity(n, key);
7392 }
7393
7394
RemoveNumberEntries(uint32_t from,uint32_t to)7395 void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
7396 // Do nothing if the interval [from, to) is empty.
7397 if (from >= to) return;
7398
7399 int removed_entries = 0;
7400 Object* sentinel = Heap::null_value();
7401 int capacity = Capacity();
7402 for (int i = 0; i < capacity; i++) {
7403 Object* key = KeyAt(i);
7404 if (key->IsNumber()) {
7405 uint32_t number = static_cast<uint32_t>(key->Number());
7406 if (from <= number && number < to) {
7407 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
7408 removed_entries++;
7409 }
7410 }
7411 }
7412
7413 // Update the number of elements.
7414 SetNumberOfElements(NumberOfElements() - removed_entries);
7415 }
7416
7417
7418 template<typename Shape, typename Key>
DeleteProperty(int entry,JSObject::DeleteMode mode)7419 Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
7420 JSObject::DeleteMode mode) {
7421 PropertyDetails details = DetailsAt(entry);
7422 // Ignore attributes if forcing a deletion.
7423 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
7424 return Heap::false_value();
7425 }
7426 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
7427 HashTable<Shape, Key>::ElementRemoved();
7428 return Heap::true_value();
7429 }
7430
7431
7432 template<typename Shape, typename Key>
AtPut(Key key,Object * value)7433 Object* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
7434 int entry = FindEntry(key);
7435
7436 // If the entry is present set the value;
7437 if (entry != Dictionary<Shape, Key>::kNotFound) {
7438 ValueAtPut(entry, value);
7439 return this;
7440 }
7441
7442 // Check whether the dictionary should be extended.
7443 Object* obj = EnsureCapacity(1, key);
7444 if (obj->IsFailure()) return obj;
7445
7446 Object* k = Shape::AsObject(key);
7447 if (k->IsFailure()) return k;
7448 PropertyDetails details = PropertyDetails(NONE, NORMAL);
7449 return Dictionary<Shape, Key>::cast(obj)->
7450 AddEntry(key, value, details, Shape::Hash(key));
7451 }
7452
7453
7454 template<typename Shape, typename Key>
Add(Key key,Object * value,PropertyDetails details)7455 Object* Dictionary<Shape, Key>::Add(Key key,
7456 Object* value,
7457 PropertyDetails details) {
7458 // Valdate key is absent.
7459 SLOW_ASSERT((FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
7460 // Check whether the dictionary should be extended.
7461 Object* obj = EnsureCapacity(1, key);
7462 if (obj->IsFailure()) return obj;
7463 return Dictionary<Shape, Key>::cast(obj)->
7464 AddEntry(key, value, details, Shape::Hash(key));
7465 }
7466
7467
7468 // Add a key, value pair to the dictionary.
7469 template<typename Shape, typename Key>
AddEntry(Key key,Object * value,PropertyDetails details,uint32_t hash)7470 Object* Dictionary<Shape, Key>::AddEntry(Key key,
7471 Object* value,
7472 PropertyDetails details,
7473 uint32_t hash) {
7474 // Compute the key object.
7475 Object* k = Shape::AsObject(key);
7476 if (k->IsFailure()) return k;
7477
7478 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
7479 // Insert element at empty or deleted entry
7480 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
7481 // Assign an enumeration index to the property and update
7482 // SetNextEnumerationIndex.
7483 int index = NextEnumerationIndex();
7484 details = PropertyDetails(details.attributes(), details.type(), index);
7485 SetNextEnumerationIndex(index + 1);
7486 }
7487 SetEntry(entry, k, value, details);
7488 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
7489 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
7490 HashTable<Shape, Key>::ElementAdded();
7491 return this;
7492 }
7493
7494
UpdateMaxNumberKey(uint32_t key)7495 void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
7496 // If the dictionary requires slow elements an element has already
7497 // been added at a high index.
7498 if (requires_slow_elements()) return;
7499 // Check if this index is high enough that we should require slow
7500 // elements.
7501 if (key > kRequiresSlowElementsLimit) {
7502 set_requires_slow_elements();
7503 return;
7504 }
7505 // Update max key value.
7506 Object* max_index_object = get(kMaxNumberKeyIndex);
7507 if (!max_index_object->IsSmi() || max_number_key() < key) {
7508 FixedArray::set(kMaxNumberKeyIndex,
7509 Smi::FromInt(key << kRequiresSlowElementsTagSize),
7510 SKIP_WRITE_BARRIER);
7511 }
7512 }
7513
7514
AddNumberEntry(uint32_t key,Object * value,PropertyDetails details)7515 Object* NumberDictionary::AddNumberEntry(uint32_t key,
7516 Object* value,
7517 PropertyDetails details) {
7518 UpdateMaxNumberKey(key);
7519 SLOW_ASSERT(FindEntry(key) == kNotFound);
7520 return Add(key, value, details);
7521 }
7522
7523
AtNumberPut(uint32_t key,Object * value)7524 Object* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
7525 UpdateMaxNumberKey(key);
7526 return AtPut(key, value);
7527 }
7528
7529
Set(uint32_t key,Object * value,PropertyDetails details)7530 Object* NumberDictionary::Set(uint32_t key,
7531 Object* value,
7532 PropertyDetails details) {
7533 int entry = FindEntry(key);
7534 if (entry == kNotFound) return AddNumberEntry(key, value, details);
7535 // Preserve enumeration index.
7536 details = PropertyDetails(details.attributes(),
7537 details.type(),
7538 DetailsAt(entry).index());
7539 SetEntry(entry, NumberDictionaryShape::AsObject(key), value, details);
7540 return this;
7541 }
7542
7543
7544
7545 template<typename Shape, typename Key>
NumberOfElementsFilterAttributes(PropertyAttributes filter)7546 int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
7547 PropertyAttributes filter) {
7548 int capacity = HashTable<Shape, Key>::Capacity();
7549 int result = 0;
7550 for (int i = 0; i < capacity; i++) {
7551 Object* k = HashTable<Shape, Key>::KeyAt(i);
7552 if (HashTable<Shape, Key>::IsKey(k)) {
7553 PropertyDetails details = DetailsAt(i);
7554 if (details.IsDeleted()) continue;
7555 PropertyAttributes attr = details.attributes();
7556 if ((attr & filter) == 0) result++;
7557 }
7558 }
7559 return result;
7560 }
7561
7562
7563 template<typename Shape, typename Key>
NumberOfEnumElements()7564 int Dictionary<Shape, Key>::NumberOfEnumElements() {
7565 return NumberOfElementsFilterAttributes(
7566 static_cast<PropertyAttributes>(DONT_ENUM));
7567 }
7568
7569
7570 template<typename Shape, typename Key>
CopyKeysTo(FixedArray * storage,PropertyAttributes filter)7571 void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
7572 PropertyAttributes filter) {
7573 ASSERT(storage->length() >= NumberOfEnumElements());
7574 int capacity = HashTable<Shape, Key>::Capacity();
7575 int index = 0;
7576 for (int i = 0; i < capacity; i++) {
7577 Object* k = HashTable<Shape, Key>::KeyAt(i);
7578 if (HashTable<Shape, Key>::IsKey(k)) {
7579 PropertyDetails details = DetailsAt(i);
7580 if (details.IsDeleted()) continue;
7581 PropertyAttributes attr = details.attributes();
7582 if ((attr & filter) == 0) storage->set(index++, k);
7583 }
7584 }
7585 storage->SortPairs(storage, index);
7586 ASSERT(storage->length() >= index);
7587 }
7588
7589
CopyEnumKeysTo(FixedArray * storage,FixedArray * sort_array)7590 void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
7591 FixedArray* sort_array) {
7592 ASSERT(storage->length() >= NumberOfEnumElements());
7593 int capacity = Capacity();
7594 int index = 0;
7595 for (int i = 0; i < capacity; i++) {
7596 Object* k = KeyAt(i);
7597 if (IsKey(k)) {
7598 PropertyDetails details = DetailsAt(i);
7599 if (details.IsDeleted() || details.IsDontEnum()) continue;
7600 storage->set(index, k);
7601 sort_array->set(index,
7602 Smi::FromInt(details.index()),
7603 SKIP_WRITE_BARRIER);
7604 index++;
7605 }
7606 }
7607 storage->SortPairs(sort_array, sort_array->length());
7608 ASSERT(storage->length() >= index);
7609 }
7610
7611
7612 template<typename Shape, typename Key>
CopyKeysTo(FixedArray * storage)7613 void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
7614 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
7615 static_cast<PropertyAttributes>(NONE)));
7616 int capacity = HashTable<Shape, Key>::Capacity();
7617 int index = 0;
7618 for (int i = 0; i < capacity; i++) {
7619 Object* k = HashTable<Shape, Key>::KeyAt(i);
7620 if (HashTable<Shape, Key>::IsKey(k)) {
7621 PropertyDetails details = DetailsAt(i);
7622 if (details.IsDeleted()) continue;
7623 storage->set(index++, k);
7624 }
7625 }
7626 ASSERT(storage->length() >= index);
7627 }
7628
7629
7630 // Backwards lookup (slow).
7631 template<typename Shape, typename Key>
SlowReverseLookup(Object * value)7632 Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
7633 int capacity = HashTable<Shape, Key>::Capacity();
7634 for (int i = 0; i < capacity; i++) {
7635 Object* k = HashTable<Shape, Key>::KeyAt(i);
7636 if (Dictionary<Shape, Key>::IsKey(k)) {
7637 Object* e = ValueAt(i);
7638 if (e->IsJSGlobalPropertyCell()) {
7639 e = JSGlobalPropertyCell::cast(e)->value();
7640 }
7641 if (e == value) return k;
7642 }
7643 }
7644 return Heap::undefined_value();
7645 }
7646
7647
TransformPropertiesToFastFor(JSObject * obj,int unused_property_fields)7648 Object* StringDictionary::TransformPropertiesToFastFor(
7649 JSObject* obj, int unused_property_fields) {
7650 // Make sure we preserve dictionary representation if there are too many
7651 // descriptors.
7652 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
7653
7654 // Figure out if it is necessary to generate new enumeration indices.
7655 int max_enumeration_index =
7656 NextEnumerationIndex() +
7657 (DescriptorArray::kMaxNumberOfDescriptors -
7658 NumberOfElements());
7659 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
7660 Object* result = GenerateNewEnumerationIndices();
7661 if (result->IsFailure()) return result;
7662 }
7663
7664 int instance_descriptor_length = 0;
7665 int number_of_fields = 0;
7666
7667 // Compute the length of the instance descriptor.
7668 int capacity = Capacity();
7669 for (int i = 0; i < capacity; i++) {
7670 Object* k = KeyAt(i);
7671 if (IsKey(k)) {
7672 Object* value = ValueAt(i);
7673 PropertyType type = DetailsAt(i).type();
7674 ASSERT(type != FIELD);
7675 instance_descriptor_length++;
7676 if (type == NORMAL && !value->IsJSFunction()) number_of_fields += 1;
7677 }
7678 }
7679
7680 // Allocate the instance descriptor.
7681 Object* descriptors_unchecked =
7682 DescriptorArray::Allocate(instance_descriptor_length);
7683 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
7684 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
7685
7686 int inobject_props = obj->map()->inobject_properties();
7687 int number_of_allocated_fields =
7688 number_of_fields + unused_property_fields - inobject_props;
7689
7690 // Allocate the fixed array for the fields.
7691 Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields);
7692 if (fields->IsFailure()) return fields;
7693
7694 // Fill in the instance descriptor and the fields.
7695 int next_descriptor = 0;
7696 int current_offset = 0;
7697 for (int i = 0; i < capacity; i++) {
7698 Object* k = KeyAt(i);
7699 if (IsKey(k)) {
7700 Object* value = ValueAt(i);
7701 // Ensure the key is a symbol before writing into the instance descriptor.
7702 Object* key = Heap::LookupSymbol(String::cast(k));
7703 if (key->IsFailure()) return key;
7704 PropertyDetails details = DetailsAt(i);
7705 PropertyType type = details.type();
7706
7707 if (value->IsJSFunction()) {
7708 ConstantFunctionDescriptor d(String::cast(key),
7709 JSFunction::cast(value),
7710 details.attributes(),
7711 details.index());
7712 descriptors->Set(next_descriptor++, &d);
7713 } else if (type == NORMAL) {
7714 if (current_offset < inobject_props) {
7715 obj->InObjectPropertyAtPut(current_offset,
7716 value,
7717 UPDATE_WRITE_BARRIER);
7718 } else {
7719 int offset = current_offset - inobject_props;
7720 FixedArray::cast(fields)->set(offset, value);
7721 }
7722 FieldDescriptor d(String::cast(key),
7723 current_offset++,
7724 details.attributes(),
7725 details.index());
7726 descriptors->Set(next_descriptor++, &d);
7727 } else if (type == CALLBACKS) {
7728 CallbacksDescriptor d(String::cast(key),
7729 value,
7730 details.attributes(),
7731 details.index());
7732 descriptors->Set(next_descriptor++, &d);
7733 } else {
7734 UNREACHABLE();
7735 }
7736 }
7737 }
7738 ASSERT(current_offset == number_of_fields);
7739
7740 descriptors->Sort();
7741 // Allocate new map.
7742 Object* new_map = obj->map()->CopyDropDescriptors();
7743 if (new_map->IsFailure()) return new_map;
7744
7745 // Transform the object.
7746 obj->set_map(Map::cast(new_map));
7747 obj->map()->set_instance_descriptors(descriptors);
7748 obj->map()->set_unused_property_fields(unused_property_fields);
7749
7750 obj->set_properties(FixedArray::cast(fields));
7751 ASSERT(obj->IsJSObject());
7752
7753 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
7754 // Check that it really works.
7755 ASSERT(obj->HasFastProperties());
7756
7757 return obj;
7758 }
7759
7760
7761 #ifdef ENABLE_DEBUGGER_SUPPORT
7762 // Check if there is a break point at this code position.
HasBreakPoint(int code_position)7763 bool DebugInfo::HasBreakPoint(int code_position) {
7764 // Get the break point info object for this code position.
7765 Object* break_point_info = GetBreakPointInfo(code_position);
7766
7767 // If there is no break point info object or no break points in the break
7768 // point info object there is no break point at this code position.
7769 if (break_point_info->IsUndefined()) return false;
7770 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
7771 }
7772
7773
7774 // Get the break point info object for this code position.
GetBreakPointInfo(int code_position)7775 Object* DebugInfo::GetBreakPointInfo(int code_position) {
7776 // Find the index of the break point info object for this code position.
7777 int index = GetBreakPointInfoIndex(code_position);
7778
7779 // Return the break point info object if any.
7780 if (index == kNoBreakPointInfo) return Heap::undefined_value();
7781 return BreakPointInfo::cast(break_points()->get(index));
7782 }
7783
7784
7785 // Clear a break point at the specified code position.
ClearBreakPoint(Handle<DebugInfo> debug_info,int code_position,Handle<Object> break_point_object)7786 void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
7787 int code_position,
7788 Handle<Object> break_point_object) {
7789 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
7790 if (break_point_info->IsUndefined()) return;
7791 BreakPointInfo::ClearBreakPoint(
7792 Handle<BreakPointInfo>::cast(break_point_info),
7793 break_point_object);
7794 }
7795
7796
SetBreakPoint(Handle<DebugInfo> debug_info,int code_position,int source_position,int statement_position,Handle<Object> break_point_object)7797 void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
7798 int code_position,
7799 int source_position,
7800 int statement_position,
7801 Handle<Object> break_point_object) {
7802 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
7803 if (!break_point_info->IsUndefined()) {
7804 BreakPointInfo::SetBreakPoint(
7805 Handle<BreakPointInfo>::cast(break_point_info),
7806 break_point_object);
7807 return;
7808 }
7809
7810 // Adding a new break point for a code position which did not have any
7811 // break points before. Try to find a free slot.
7812 int index = kNoBreakPointInfo;
7813 for (int i = 0; i < debug_info->break_points()->length(); i++) {
7814 if (debug_info->break_points()->get(i)->IsUndefined()) {
7815 index = i;
7816 break;
7817 }
7818 }
7819 if (index == kNoBreakPointInfo) {
7820 // No free slot - extend break point info array.
7821 Handle<FixedArray> old_break_points =
7822 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
7823 debug_info->set_break_points(*Factory::NewFixedArray(
7824 old_break_points->length() +
7825 Debug::kEstimatedNofBreakPointsInFunction));
7826 Handle<FixedArray> new_break_points =
7827 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
7828 for (int i = 0; i < old_break_points->length(); i++) {
7829 new_break_points->set(i, old_break_points->get(i));
7830 }
7831 index = old_break_points->length();
7832 }
7833 ASSERT(index != kNoBreakPointInfo);
7834
7835 // Allocate new BreakPointInfo object and set the break point.
7836 Handle<BreakPointInfo> new_break_point_info =
7837 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
7838 new_break_point_info->set_code_position(Smi::FromInt(code_position));
7839 new_break_point_info->set_source_position(Smi::FromInt(source_position));
7840 new_break_point_info->
7841 set_statement_position(Smi::FromInt(statement_position));
7842 new_break_point_info->set_break_point_objects(Heap::undefined_value());
7843 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
7844 debug_info->break_points()->set(index, *new_break_point_info);
7845 }
7846
7847
7848 // Get the break point objects for a code position.
GetBreakPointObjects(int code_position)7849 Object* DebugInfo::GetBreakPointObjects(int code_position) {
7850 Object* break_point_info = GetBreakPointInfo(code_position);
7851 if (break_point_info->IsUndefined()) {
7852 return Heap::undefined_value();
7853 }
7854 return BreakPointInfo::cast(break_point_info)->break_point_objects();
7855 }
7856
7857
7858 // Get the total number of break points.
GetBreakPointCount()7859 int DebugInfo::GetBreakPointCount() {
7860 if (break_points()->IsUndefined()) return 0;
7861 int count = 0;
7862 for (int i = 0; i < break_points()->length(); i++) {
7863 if (!break_points()->get(i)->IsUndefined()) {
7864 BreakPointInfo* break_point_info =
7865 BreakPointInfo::cast(break_points()->get(i));
7866 count += break_point_info->GetBreakPointCount();
7867 }
7868 }
7869 return count;
7870 }
7871
7872
FindBreakPointInfo(Handle<DebugInfo> debug_info,Handle<Object> break_point_object)7873 Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
7874 Handle<Object> break_point_object) {
7875 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
7876 for (int i = 0; i < debug_info->break_points()->length(); i++) {
7877 if (!debug_info->break_points()->get(i)->IsUndefined()) {
7878 Handle<BreakPointInfo> break_point_info =
7879 Handle<BreakPointInfo>(BreakPointInfo::cast(
7880 debug_info->break_points()->get(i)));
7881 if (BreakPointInfo::HasBreakPointObject(break_point_info,
7882 break_point_object)) {
7883 return *break_point_info;
7884 }
7885 }
7886 }
7887 return Heap::undefined_value();
7888 }
7889
7890
7891 // Find the index of the break point info object for the specified code
7892 // position.
GetBreakPointInfoIndex(int code_position)7893 int DebugInfo::GetBreakPointInfoIndex(int code_position) {
7894 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
7895 for (int i = 0; i < break_points()->length(); i++) {
7896 if (!break_points()->get(i)->IsUndefined()) {
7897 BreakPointInfo* break_point_info =
7898 BreakPointInfo::cast(break_points()->get(i));
7899 if (break_point_info->code_position()->value() == code_position) {
7900 return i;
7901 }
7902 }
7903 }
7904 return kNoBreakPointInfo;
7905 }
7906
7907
7908 // Remove the specified break point object.
ClearBreakPoint(Handle<BreakPointInfo> break_point_info,Handle<Object> break_point_object)7909 void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
7910 Handle<Object> break_point_object) {
7911 // If there are no break points just ignore.
7912 if (break_point_info->break_point_objects()->IsUndefined()) return;
7913 // If there is a single break point clear it if it is the same.
7914 if (!break_point_info->break_point_objects()->IsFixedArray()) {
7915 if (break_point_info->break_point_objects() == *break_point_object) {
7916 break_point_info->set_break_point_objects(Heap::undefined_value());
7917 }
7918 return;
7919 }
7920 // If there are multiple break points shrink the array
7921 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
7922 Handle<FixedArray> old_array =
7923 Handle<FixedArray>(
7924 FixedArray::cast(break_point_info->break_point_objects()));
7925 Handle<FixedArray> new_array =
7926 Factory::NewFixedArray(old_array->length() - 1);
7927 int found_count = 0;
7928 for (int i = 0; i < old_array->length(); i++) {
7929 if (old_array->get(i) == *break_point_object) {
7930 ASSERT(found_count == 0);
7931 found_count++;
7932 } else {
7933 new_array->set(i - found_count, old_array->get(i));
7934 }
7935 }
7936 // If the break point was found in the list change it.
7937 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
7938 }
7939
7940
7941 // Add the specified break point object.
SetBreakPoint(Handle<BreakPointInfo> break_point_info,Handle<Object> break_point_object)7942 void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
7943 Handle<Object> break_point_object) {
7944 // If there was no break point objects before just set it.
7945 if (break_point_info->break_point_objects()->IsUndefined()) {
7946 break_point_info->set_break_point_objects(*break_point_object);
7947 return;
7948 }
7949 // If the break point object is the same as before just ignore.
7950 if (break_point_info->break_point_objects() == *break_point_object) return;
7951 // If there was one break point object before replace with array.
7952 if (!break_point_info->break_point_objects()->IsFixedArray()) {
7953 Handle<FixedArray> array = Factory::NewFixedArray(2);
7954 array->set(0, break_point_info->break_point_objects());
7955 array->set(1, *break_point_object);
7956 break_point_info->set_break_point_objects(*array);
7957 return;
7958 }
7959 // If there was more than one break point before extend array.
7960 Handle<FixedArray> old_array =
7961 Handle<FixedArray>(
7962 FixedArray::cast(break_point_info->break_point_objects()));
7963 Handle<FixedArray> new_array =
7964 Factory::NewFixedArray(old_array->length() + 1);
7965 for (int i = 0; i < old_array->length(); i++) {
7966 // If the break point was there before just ignore.
7967 if (old_array->get(i) == *break_point_object) return;
7968 new_array->set(i, old_array->get(i));
7969 }
7970 // Add the new break point.
7971 new_array->set(old_array->length(), *break_point_object);
7972 break_point_info->set_break_point_objects(*new_array);
7973 }
7974
7975
HasBreakPointObject(Handle<BreakPointInfo> break_point_info,Handle<Object> break_point_object)7976 bool BreakPointInfo::HasBreakPointObject(
7977 Handle<BreakPointInfo> break_point_info,
7978 Handle<Object> break_point_object) {
7979 // No break point.
7980 if (break_point_info->break_point_objects()->IsUndefined()) return false;
7981 // Single beak point.
7982 if (!break_point_info->break_point_objects()->IsFixedArray()) {
7983 return break_point_info->break_point_objects() == *break_point_object;
7984 }
7985 // Multiple break points.
7986 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
7987 for (int i = 0; i < array->length(); i++) {
7988 if (array->get(i) == *break_point_object) {
7989 return true;
7990 }
7991 }
7992 return false;
7993 }
7994
7995
7996 // Get the number of break points.
GetBreakPointCount()7997 int BreakPointInfo::GetBreakPointCount() {
7998 // No break point.
7999 if (break_point_objects()->IsUndefined()) return 0;
8000 // Single beak point.
8001 if (!break_point_objects()->IsFixedArray()) return 1;
8002 // Multiple break points.
8003 return FixedArray::cast(break_point_objects())->length();
8004 }
8005 #endif
8006
8007
8008 } } // namespace v8::internal
8009