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