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