blob: 118c4891d3555f68b372805da86ce547cef654f0 [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();
2842 if (obj->IsFixedArray()) return obj;
2843 }
2844 }
2845 }
2846
2847 // Allocate the fixed array to hold getter and setter.
2848 Object* structure = Heap::AllocateFixedArray(2, TENURED);
2849 if (structure->IsFailure()) return structure;
2850 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
2851
2852 if (is_element) {
2853 // Normalize object to make this operation simple.
2854 Object* ok = NormalizeElements();
2855 if (ok->IsFailure()) return ok;
2856
2857 // Update the dictionary with the new CALLBACKS property.
2858 Object* dict =
2859 element_dictionary()->Set(index, structure, details);
2860 if (dict->IsFailure()) return dict;
2861
2862 // If name is an index we need to stay in slow case.
2863 NumberDictionary* elements = NumberDictionary::cast(dict);
2864 elements->set_requires_slow_elements();
2865 // Set the potential new dictionary on the object.
2866 set_elements(NumberDictionary::cast(dict));
2867 } else {
2868 // Normalize object to make this operation simple.
2869 Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2870 if (ok->IsFailure()) return ok;
2871
2872 // For the global object allocate a new map to invalidate the global inline
2873 // caches which have a global property cell reference directly in the code.
2874 if (IsGlobalObject()) {
2875 Object* new_map = map()->CopyDropDescriptors();
2876 if (new_map->IsFailure()) return new_map;
2877 set_map(Map::cast(new_map));
2878 }
2879
2880 // Update the dictionary with the new CALLBACKS property.
2881 return SetNormalizedProperty(name, structure, details);
2882 }
2883
2884 return structure;
2885}
2886
2887
2888Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
2889 PropertyAttributes attributes) {
2890 // Check access rights if needed.
2891 if (IsAccessCheckNeeded() &&
2892 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2893 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2894 return Heap::undefined_value();
2895 }
2896
2897 if (IsJSGlobalProxy()) {
2898 Object* proto = GetPrototype();
2899 if (proto->IsNull()) return this;
2900 ASSERT(proto->IsJSGlobalObject());
2901 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
2902 fun, attributes);
2903 }
2904
2905 Object* array = DefineGetterSetter(name, attributes);
2906 if (array->IsFailure() || array->IsUndefined()) return array;
2907 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
2908 return this;
2909}
2910
2911
2912Object* JSObject::LookupAccessor(String* name, bool is_getter) {
2913 // Make sure that the top context does not change when doing callbacks or
2914 // interceptor calls.
2915 AssertNoContextChange ncc;
2916
2917 // Check access rights if needed.
2918 if (IsAccessCheckNeeded() &&
2919 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2920 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2921 return Heap::undefined_value();
2922 }
2923
2924 // Make the lookup and include prototypes.
2925 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
2926 uint32_t index;
2927 if (name->AsArrayIndex(&index)) {
2928 for (Object* obj = this;
2929 obj != Heap::null_value();
2930 obj = JSObject::cast(obj)->GetPrototype()) {
2931 JSObject* js_object = JSObject::cast(obj);
2932 if (js_object->HasDictionaryElements()) {
2933 NumberDictionary* dictionary = js_object->element_dictionary();
2934 int entry = dictionary->FindEntry(index);
2935 if (entry != NumberDictionary::kNotFound) {
2936 Object* element = dictionary->ValueAt(entry);
2937 PropertyDetails details = dictionary->DetailsAt(entry);
2938 if (details.type() == CALLBACKS) {
2939 // Only accessors allowed as elements.
2940 return FixedArray::cast(element)->get(accessor_index);
2941 }
2942 }
2943 }
2944 }
2945 } else {
2946 for (Object* obj = this;
2947 obj != Heap::null_value();
2948 obj = JSObject::cast(obj)->GetPrototype()) {
2949 LookupResult result;
2950 JSObject::cast(obj)->LocalLookup(name, &result);
2951 if (result.IsValid()) {
2952 if (result.IsReadOnly()) return Heap::undefined_value();
2953 if (result.type() == CALLBACKS) {
2954 Object* obj = result.GetCallbackObject();
2955 if (obj->IsFixedArray()) {
2956 return FixedArray::cast(obj)->get(accessor_index);
2957 }
2958 }
2959 }
2960 }
2961 }
2962 return Heap::undefined_value();
2963}
2964
2965
2966Object* JSObject::SlowReverseLookup(Object* value) {
2967 if (HasFastProperties()) {
2968 DescriptorArray* descs = map()->instance_descriptors();
2969 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2970 if (descs->GetType(i) == FIELD) {
2971 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
2972 return descs->GetKey(i);
2973 }
2974 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
2975 if (descs->GetConstantFunction(i) == value) {
2976 return descs->GetKey(i);
2977 }
2978 }
2979 }
2980 return Heap::undefined_value();
2981 } else {
2982 return property_dictionary()->SlowReverseLookup(value);
2983 }
2984}
2985
2986
2987Object* Map::CopyDropDescriptors() {
2988 Object* result = Heap::AllocateMap(instance_type(), instance_size());
2989 if (result->IsFailure()) return result;
2990 Map::cast(result)->set_prototype(prototype());
2991 Map::cast(result)->set_constructor(constructor());
2992 // Don't copy descriptors, so map transitions always remain a forest.
2993 // If we retained the same descriptors we would have two maps
2994 // pointing to the same transition which is bad because the garbage
2995 // collector relies on being able to reverse pointers from transitions
2996 // to maps. If properties need to be retained use CopyDropTransitions.
2997 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
2998 // Please note instance_type and instance_size are set when allocated.
2999 Map::cast(result)->set_inobject_properties(inobject_properties());
3000 Map::cast(result)->set_unused_property_fields(unused_property_fields());
3001
3002 // If the map has pre-allocated properties always start out with a descriptor
3003 // array describing these properties.
3004 if (pre_allocated_property_fields() > 0) {
3005 ASSERT(constructor()->IsJSFunction());
3006 JSFunction* ctor = JSFunction::cast(constructor());
3007 Object* descriptors =
3008 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
3009 if (descriptors->IsFailure()) return descriptors;
3010 Map::cast(result)->set_instance_descriptors(
3011 DescriptorArray::cast(descriptors));
3012 Map::cast(result)->set_pre_allocated_property_fields(
3013 pre_allocated_property_fields());
3014 }
3015 Map::cast(result)->set_bit_field(bit_field());
3016 Map::cast(result)->set_bit_field2(bit_field2());
3017 Map::cast(result)->ClearCodeCache();
3018 return result;
3019}
3020
3021
3022Object* Map::CopyDropTransitions() {
3023 Object* new_map = CopyDropDescriptors();
3024 if (new_map->IsFailure()) return new_map;
3025 Object* descriptors = instance_descriptors()->RemoveTransitions();
3026 if (descriptors->IsFailure()) return descriptors;
3027 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
3028 return cast(new_map);
3029}
3030
3031
3032Object* Map::UpdateCodeCache(String* name, Code* code) {
3033 ASSERT(code->ic_state() == MONOMORPHIC);
3034 FixedArray* cache = code_cache();
3035
3036 // When updating the code cache we disregard the type encoded in the
3037 // flags. This allows call constant stubs to overwrite call field
3038 // stubs, etc.
3039 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
3040
3041 // First check whether we can update existing code cache without
3042 // extending it.
3043 int length = cache->length();
3044 int deleted_index = -1;
3045 for (int i = 0; i < length; i += 2) {
3046 Object* key = cache->get(i);
3047 if (key->IsNull()) {
3048 if (deleted_index < 0) deleted_index = i;
3049 continue;
3050 }
3051 if (key->IsUndefined()) {
3052 if (deleted_index >= 0) i = deleted_index;
3053 cache->set(i + 0, name);
3054 cache->set(i + 1, code);
3055 return this;
3056 }
3057 if (name->Equals(String::cast(key))) {
3058 Code::Flags found = Code::cast(cache->get(i + 1))->flags();
3059 if (Code::RemoveTypeFromFlags(found) == flags) {
3060 cache->set(i + 1, code);
3061 return this;
3062 }
3063 }
3064 }
3065
3066 // Reached the end of the code cache. If there were deleted
3067 // elements, reuse the space for the first of them.
3068 if (deleted_index >= 0) {
3069 cache->set(deleted_index + 0, name);
3070 cache->set(deleted_index + 1, code);
3071 return this;
3072 }
3073
3074 // Extend the code cache with some new entries (at least one).
3075 int new_length = length + ((length >> 1) & ~1) + 2;
3076 ASSERT((new_length & 1) == 0); // must be a multiple of two
3077 Object* result = cache->CopySize(new_length);
3078 if (result->IsFailure()) return result;
3079
3080 // Add the (name, code) pair to the new cache.
3081 cache = FixedArray::cast(result);
3082 cache->set(length + 0, name);
3083 cache->set(length + 1, code);
3084 set_code_cache(cache);
3085 return this;
3086}
3087
3088
3089Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3090 FixedArray* cache = code_cache();
3091 int length = cache->length();
3092 for (int i = 0; i < length; i += 2) {
3093 Object* key = cache->get(i);
3094 // Skip deleted elements.
3095 if (key->IsNull()) continue;
3096 if (key->IsUndefined()) return key;
3097 if (name->Equals(String::cast(key))) {
3098 Code* code = Code::cast(cache->get(i + 1));
3099 if (code->flags() == flags) return code;
3100 }
3101 }
3102 return Heap::undefined_value();
3103}
3104
3105
3106int Map::IndexInCodeCache(Code* code) {
3107 FixedArray* array = code_cache();
3108 int len = array->length();
3109 for (int i = 0; i < len; i += 2) {
3110 if (array->get(i + 1) == code) return i + 1;
3111 }
3112 return -1;
3113}
3114
3115
3116void Map::RemoveFromCodeCache(int index) {
3117 FixedArray* array = code_cache();
3118 ASSERT(array->length() >= index && array->get(index)->IsCode());
3119 // Use null instead of undefined for deleted elements to distinguish
3120 // deleted elements from unused elements. This distinction is used
3121 // when looking up in the cache and when updating the cache.
3122 array->set_null(index - 1); // key
3123 array->set_null(index); // code
3124}
3125
3126
3127void FixedArray::FixedArrayIterateBody(ObjectVisitor* v) {
3128 IteratePointers(v, kHeaderSize, kHeaderSize + length() * kPointerSize);
3129}
3130
3131
3132static bool HasKey(FixedArray* array, Object* key) {
3133 int len0 = array->length();
3134 for (int i = 0; i < len0; i++) {
3135 Object* element = array->get(i);
3136 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3137 if (element->IsString() &&
3138 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3139 return true;
3140 }
3141 }
3142 return false;
3143}
3144
3145
3146Object* FixedArray::AddKeysFromJSArray(JSArray* array) {
Steve Block3ce2e202009-11-05 08:53:23 +00003147 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00003148 switch (array->GetElementsKind()) {
3149 case JSObject::FAST_ELEMENTS:
3150 return UnionOfKeys(FixedArray::cast(array->elements()));
3151 case JSObject::DICTIONARY_ELEMENTS: {
3152 NumberDictionary* dict = array->element_dictionary();
3153 int size = dict->NumberOfElements();
3154
3155 // Allocate a temporary fixed array.
3156 Object* object = Heap::AllocateFixedArray(size);
3157 if (object->IsFailure()) return object;
3158 FixedArray* key_array = FixedArray::cast(object);
3159
3160 int capacity = dict->Capacity();
3161 int pos = 0;
3162 // Copy the elements from the JSArray to the temporary fixed array.
3163 for (int i = 0; i < capacity; i++) {
3164 if (dict->IsKey(dict->KeyAt(i))) {
3165 key_array->set(pos++, dict->ValueAt(i));
3166 }
3167 }
3168 // Compute the union of this and the temporary fixed array.
3169 return UnionOfKeys(key_array);
3170 }
3171 default:
3172 UNREACHABLE();
3173 }
3174 UNREACHABLE();
3175 return Heap::null_value(); // Failure case needs to "return" a value.
3176}
3177
3178
3179Object* FixedArray::UnionOfKeys(FixedArray* other) {
3180 int len0 = length();
3181 int len1 = other->length();
3182 // Optimize if either is empty.
3183 if (len0 == 0) return other;
3184 if (len1 == 0) return this;
3185
3186 // Compute how many elements are not in this.
3187 int extra = 0;
3188 for (int y = 0; y < len1; y++) {
3189 Object* value = other->get(y);
3190 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3191 }
3192
3193 if (extra == 0) return this;
3194
3195 // Allocate the result
3196 Object* obj = Heap::AllocateFixedArray(len0 + extra);
3197 if (obj->IsFailure()) return obj;
3198 // Fill in the content
3199 FixedArray* result = FixedArray::cast(obj);
3200 WriteBarrierMode mode = result->GetWriteBarrierMode();
3201 for (int i = 0; i < len0; i++) {
3202 result->set(i, get(i), mode);
3203 }
3204 // Fill in the extra keys.
3205 int index = 0;
3206 for (int y = 0; y < len1; y++) {
3207 Object* value = other->get(y);
3208 if (!value->IsTheHole() && !HasKey(this, value)) {
3209 result->set(len0 + index, other->get(y), mode);
3210 index++;
3211 }
3212 }
3213 ASSERT(extra == index);
3214 return result;
3215}
3216
3217
3218Object* FixedArray::CopySize(int new_length) {
3219 if (new_length == 0) return Heap::empty_fixed_array();
3220 Object* obj = Heap::AllocateFixedArray(new_length);
3221 if (obj->IsFailure()) return obj;
3222 FixedArray* result = FixedArray::cast(obj);
3223 // Copy the content
3224 int len = length();
3225 if (new_length < len) len = new_length;
3226 result->set_map(map());
3227 WriteBarrierMode mode = result->GetWriteBarrierMode();
3228 for (int i = 0; i < len; i++) {
3229 result->set(i, get(i), mode);
3230 }
3231 return result;
3232}
3233
3234
3235void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
3236 WriteBarrierMode mode = dest->GetWriteBarrierMode();
3237 for (int index = 0; index < len; index++) {
3238 dest->set(dest_pos+index, get(pos+index), mode);
3239 }
3240}
3241
3242
3243#ifdef DEBUG
3244bool FixedArray::IsEqualTo(FixedArray* other) {
3245 if (length() != other->length()) return false;
3246 for (int i = 0 ; i < length(); ++i) {
3247 if (get(i) != other->get(i)) return false;
3248 }
3249 return true;
3250}
3251#endif
3252
3253
3254Object* DescriptorArray::Allocate(int number_of_descriptors) {
3255 if (number_of_descriptors == 0) {
3256 return Heap::empty_descriptor_array();
3257 }
3258 // Allocate the array of keys.
Leon Clarkee46be812010-01-19 14:06:41 +00003259 Object* array =
3260 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
Steve Blocka7e24c12009-10-30 11:49:00 +00003261 if (array->IsFailure()) return array;
3262 // Do not use DescriptorArray::cast on incomplete object.
3263 FixedArray* result = FixedArray::cast(array);
3264
3265 // Allocate the content array and set it in the descriptor array.
3266 array = Heap::AllocateFixedArray(number_of_descriptors << 1);
3267 if (array->IsFailure()) return array;
3268 result->set(kContentArrayIndex, array);
3269 result->set(kEnumerationIndexIndex,
3270 Smi::FromInt(PropertyDetails::kInitialIndex),
3271 SKIP_WRITE_BARRIER);
3272 return result;
3273}
3274
3275
3276void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3277 FixedArray* new_cache) {
3278 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3279 if (HasEnumCache()) {
3280 FixedArray::cast(get(kEnumerationIndexIndex))->
3281 set(kEnumCacheBridgeCacheIndex, new_cache);
3282 } else {
3283 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3284 FixedArray::cast(bridge_storage)->
3285 set(kEnumCacheBridgeCacheIndex, new_cache);
3286 fast_set(FixedArray::cast(bridge_storage),
3287 kEnumCacheBridgeEnumIndex,
3288 get(kEnumerationIndexIndex));
3289 set(kEnumerationIndexIndex, bridge_storage);
3290 }
3291}
3292
3293
3294Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
3295 TransitionFlag transition_flag) {
3296 // Transitions are only kept when inserting another transition.
3297 // This precondition is not required by this function's implementation, but
3298 // is currently required by the semantics of maps, so we check it.
3299 // Conversely, we filter after replacing, so replacing a transition and
3300 // removing all other transitions is not supported.
3301 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3302 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
3303 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
3304
3305 // Ensure the key is a symbol.
3306 Object* result = descriptor->KeyToSymbol();
3307 if (result->IsFailure()) return result;
3308
3309 int transitions = 0;
3310 int null_descriptors = 0;
3311 if (remove_transitions) {
3312 for (int i = 0; i < number_of_descriptors(); i++) {
3313 if (IsTransition(i)) transitions++;
3314 if (IsNullDescriptor(i)) null_descriptors++;
3315 }
3316 } else {
3317 for (int i = 0; i < number_of_descriptors(); i++) {
3318 if (IsNullDescriptor(i)) null_descriptors++;
3319 }
3320 }
3321 int new_size = number_of_descriptors() - transitions - null_descriptors;
3322
3323 // If key is in descriptor, we replace it in-place when filtering.
3324 // Count a null descriptor for key as inserted, not replaced.
3325 int index = Search(descriptor->GetKey());
3326 const bool inserting = (index == kNotFound);
3327 const bool replacing = !inserting;
3328 bool keep_enumeration_index = false;
3329 if (inserting) {
3330 ++new_size;
3331 }
3332 if (replacing) {
3333 // We are replacing an existing descriptor. We keep the enumeration
3334 // index of a visible property.
3335 PropertyType t = PropertyDetails(GetDetails(index)).type();
3336 if (t == CONSTANT_FUNCTION ||
3337 t == FIELD ||
3338 t == CALLBACKS ||
3339 t == INTERCEPTOR) {
3340 keep_enumeration_index = true;
3341 } else if (remove_transitions) {
3342 // Replaced descriptor has been counted as removed if it is
3343 // a transition that will be replaced. Adjust count in this case.
3344 ++new_size;
3345 }
3346 }
3347 result = Allocate(new_size);
3348 if (result->IsFailure()) return result;
3349 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3350 // Set the enumeration index in the descriptors and set the enumeration index
3351 // in the result.
3352 int enumeration_index = NextEnumerationIndex();
3353 if (!descriptor->GetDetails().IsTransition()) {
3354 if (keep_enumeration_index) {
3355 descriptor->SetEnumerationIndex(
3356 PropertyDetails(GetDetails(index)).index());
3357 } else {
3358 descriptor->SetEnumerationIndex(enumeration_index);
3359 ++enumeration_index;
3360 }
3361 }
3362 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3363
3364 // Copy the descriptors, filtering out transitions and null descriptors,
3365 // and inserting or replacing a descriptor.
3366 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
3367 int from_index = 0;
3368 int to_index = 0;
3369
3370 for (; from_index < number_of_descriptors(); from_index++) {
3371 String* key = GetKey(from_index);
3372 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
3373 break;
3374 }
3375 if (IsNullDescriptor(from_index)) continue;
3376 if (remove_transitions && IsTransition(from_index)) continue;
3377 new_descriptors->CopyFrom(to_index++, this, from_index);
3378 }
3379
3380 new_descriptors->Set(to_index++, descriptor);
3381 if (replacing) from_index++;
3382
3383 for (; from_index < number_of_descriptors(); from_index++) {
3384 if (IsNullDescriptor(from_index)) continue;
3385 if (remove_transitions && IsTransition(from_index)) continue;
3386 new_descriptors->CopyFrom(to_index++, this, from_index);
3387 }
3388
3389 ASSERT(to_index == new_descriptors->number_of_descriptors());
3390 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
3391
3392 return new_descriptors;
3393}
3394
3395
3396Object* DescriptorArray::RemoveTransitions() {
3397 // Remove all transitions and null descriptors. Return a copy of the array
3398 // with all transitions removed, or a Failure object if the new array could
3399 // not be allocated.
3400
3401 // Compute the size of the map transition entries to be removed.
3402 int num_removed = 0;
3403 for (int i = 0; i < number_of_descriptors(); i++) {
3404 if (!IsProperty(i)) num_removed++;
3405 }
3406
3407 // Allocate the new descriptor array.
3408 Object* result = Allocate(number_of_descriptors() - num_removed);
3409 if (result->IsFailure()) return result;
3410 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3411
3412 // Copy the content.
3413 int next_descriptor = 0;
3414 for (int i = 0; i < number_of_descriptors(); i++) {
3415 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
3416 }
3417 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
3418
3419 return new_descriptors;
3420}
3421
3422
3423void DescriptorArray::Sort() {
3424 // In-place heap sort.
3425 int len = number_of_descriptors();
3426
3427 // Bottom-up max-heap construction.
3428 for (int i = 1; i < len; ++i) {
3429 int child_index = i;
3430 while (child_index > 0) {
3431 int parent_index = ((child_index + 1) >> 1) - 1;
3432 uint32_t parent_hash = GetKey(parent_index)->Hash();
3433 uint32_t child_hash = GetKey(child_index)->Hash();
3434 if (parent_hash < child_hash) {
3435 Swap(parent_index, child_index);
3436 } else {
3437 break;
3438 }
3439 child_index = parent_index;
3440 }
3441 }
3442
3443 // Extract elements and create sorted array.
3444 for (int i = len - 1; i > 0; --i) {
3445 // Put max element at the back of the array.
3446 Swap(0, i);
3447 // Sift down the new top element.
3448 int parent_index = 0;
3449 while (true) {
3450 int child_index = ((parent_index + 1) << 1) - 1;
3451 if (child_index >= i) break;
3452 uint32_t child1_hash = GetKey(child_index)->Hash();
3453 uint32_t child2_hash = GetKey(child_index + 1)->Hash();
3454 uint32_t parent_hash = GetKey(parent_index)->Hash();
3455 if (child_index + 1 >= i || child1_hash > child2_hash) {
3456 if (parent_hash > child1_hash) break;
3457 Swap(parent_index, child_index);
3458 parent_index = child_index;
3459 } else {
3460 if (parent_hash > child2_hash) break;
3461 Swap(parent_index, child_index + 1);
3462 parent_index = child_index + 1;
3463 }
3464 }
3465 }
3466
3467 SLOW_ASSERT(IsSortedNoDuplicates());
3468}
3469
3470
3471int DescriptorArray::BinarySearch(String* name, int low, int high) {
3472 uint32_t hash = name->Hash();
3473
3474 while (low <= high) {
3475 int mid = (low + high) / 2;
3476 String* mid_name = GetKey(mid);
3477 uint32_t mid_hash = mid_name->Hash();
3478
3479 if (mid_hash > hash) {
3480 high = mid - 1;
3481 continue;
3482 }
3483 if (mid_hash < hash) {
3484 low = mid + 1;
3485 continue;
3486 }
3487 // Found an element with the same hash-code.
3488 ASSERT(hash == mid_hash);
3489 // There might be more, so we find the first one and
3490 // check them all to see if we have a match.
3491 if (name == mid_name && !is_null_descriptor(mid)) return mid;
3492 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
3493 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
3494 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
3495 }
3496 break;
3497 }
3498 return kNotFound;
3499}
3500
3501
3502int DescriptorArray::LinearSearch(String* name, int len) {
3503 uint32_t hash = name->Hash();
3504 for (int number = 0; number < len; number++) {
3505 String* entry = GetKey(number);
3506 if ((entry->Hash() == hash) &&
3507 name->Equals(entry) &&
3508 !is_null_descriptor(number)) {
3509 return number;
3510 }
3511 }
3512 return kNotFound;
3513}
3514
3515
3516#ifdef DEBUG
3517bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
3518 if (IsEmpty()) return other->IsEmpty();
3519 if (other->IsEmpty()) return false;
3520 if (length() != other->length()) return false;
3521 for (int i = 0; i < length(); ++i) {
3522 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
3523 }
3524 return GetContentArray()->IsEqualTo(other->GetContentArray());
3525}
3526#endif
3527
3528
3529static StaticResource<StringInputBuffer> string_input_buffer;
3530
3531
3532bool String::LooksValid() {
3533 if (!Heap::Contains(this)) return false;
3534 return true;
3535}
3536
3537
3538int String::Utf8Length() {
3539 if (IsAsciiRepresentation()) return length();
3540 // Attempt to flatten before accessing the string. It probably
3541 // doesn't make Utf8Length faster, but it is very likely that
3542 // the string will be accessed later (for example by WriteUtf8)
3543 // so it's still a good idea.
3544 TryFlattenIfNotFlat();
3545 Access<StringInputBuffer> buffer(&string_input_buffer);
3546 buffer->Reset(0, this);
3547 int result = 0;
3548 while (buffer->has_more())
3549 result += unibrow::Utf8::Length(buffer->GetNext());
3550 return result;
3551}
3552
3553
3554Vector<const char> String::ToAsciiVector() {
3555 ASSERT(IsAsciiRepresentation());
3556 ASSERT(IsFlat());
3557
3558 int offset = 0;
3559 int length = this->length();
3560 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3561 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00003562 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003563 ConsString* cons = ConsString::cast(string);
3564 ASSERT(cons->second()->length() == 0);
3565 string = cons->first();
3566 string_tag = StringShape(string).representation_tag();
3567 }
3568 if (string_tag == kSeqStringTag) {
3569 SeqAsciiString* seq = SeqAsciiString::cast(string);
3570 char* start = seq->GetChars();
3571 return Vector<const char>(start + offset, length);
3572 }
3573 ASSERT(string_tag == kExternalStringTag);
3574 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
3575 const char* start = ext->resource()->data();
3576 return Vector<const char>(start + offset, length);
3577}
3578
3579
3580Vector<const uc16> String::ToUC16Vector() {
3581 ASSERT(IsTwoByteRepresentation());
3582 ASSERT(IsFlat());
3583
3584 int offset = 0;
3585 int length = this->length();
3586 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3587 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00003588 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003589 ConsString* cons = ConsString::cast(string);
3590 ASSERT(cons->second()->length() == 0);
3591 string = cons->first();
3592 string_tag = StringShape(string).representation_tag();
3593 }
3594 if (string_tag == kSeqStringTag) {
3595 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
3596 return Vector<const uc16>(seq->GetChars() + offset, length);
3597 }
3598 ASSERT(string_tag == kExternalStringTag);
3599 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
3600 const uc16* start =
3601 reinterpret_cast<const uc16*>(ext->resource()->data());
3602 return Vector<const uc16>(start + offset, length);
3603}
3604
3605
3606SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3607 RobustnessFlag robust_flag,
3608 int offset,
3609 int length,
3610 int* length_return) {
3611 ASSERT(NativeAllocationChecker::allocation_allowed());
3612 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3613 return SmartPointer<char>(NULL);
3614 }
3615
3616 // Negative length means the to the end of the string.
3617 if (length < 0) length = kMaxInt - offset;
3618
3619 // Compute the size of the UTF-8 string. Start at the specified offset.
3620 Access<StringInputBuffer> buffer(&string_input_buffer);
3621 buffer->Reset(offset, this);
3622 int character_position = offset;
3623 int utf8_bytes = 0;
3624 while (buffer->has_more()) {
3625 uint16_t character = buffer->GetNext();
3626 if (character_position < offset + length) {
3627 utf8_bytes += unibrow::Utf8::Length(character);
3628 }
3629 character_position++;
3630 }
3631
3632 if (length_return) {
3633 *length_return = utf8_bytes;
3634 }
3635
3636 char* result = NewArray<char>(utf8_bytes + 1);
3637
3638 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
3639 buffer->Rewind();
3640 buffer->Seek(offset);
3641 character_position = offset;
3642 int utf8_byte_position = 0;
3643 while (buffer->has_more()) {
3644 uint16_t character = buffer->GetNext();
3645 if (character_position < offset + length) {
3646 if (allow_nulls == DISALLOW_NULLS && character == 0) {
3647 character = ' ';
3648 }
3649 utf8_byte_position +=
3650 unibrow::Utf8::Encode(result + utf8_byte_position, character);
3651 }
3652 character_position++;
3653 }
3654 result[utf8_byte_position] = 0;
3655 return SmartPointer<char>(result);
3656}
3657
3658
3659SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3660 RobustnessFlag robust_flag,
3661 int* length_return) {
3662 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
3663}
3664
3665
3666const uc16* String::GetTwoByteData() {
3667 return GetTwoByteData(0);
3668}
3669
3670
3671const uc16* String::GetTwoByteData(unsigned start) {
3672 ASSERT(!IsAsciiRepresentation());
3673 switch (StringShape(this).representation_tag()) {
3674 case kSeqStringTag:
3675 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
3676 case kExternalStringTag:
3677 return ExternalTwoByteString::cast(this)->
3678 ExternalTwoByteStringGetData(start);
Steve Blocka7e24c12009-10-30 11:49:00 +00003679 case kConsStringTag:
3680 UNREACHABLE();
3681 return NULL;
3682 }
3683 UNREACHABLE();
3684 return NULL;
3685}
3686
3687
3688SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
3689 ASSERT(NativeAllocationChecker::allocation_allowed());
3690
3691 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3692 return SmartPointer<uc16>();
3693 }
3694
3695 Access<StringInputBuffer> buffer(&string_input_buffer);
3696 buffer->Reset(this);
3697
3698 uc16* result = NewArray<uc16>(length() + 1);
3699
3700 int i = 0;
3701 while (buffer->has_more()) {
3702 uint16_t character = buffer->GetNext();
3703 result[i++] = character;
3704 }
3705 result[i] = 0;
3706 return SmartPointer<uc16>(result);
3707}
3708
3709
3710const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
3711 return reinterpret_cast<uc16*>(
3712 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
3713}
3714
3715
3716void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
3717 unsigned* offset_ptr,
3718 unsigned max_chars) {
3719 unsigned chars_read = 0;
3720 unsigned offset = *offset_ptr;
3721 while (chars_read < max_chars) {
3722 uint16_t c = *reinterpret_cast<uint16_t*>(
3723 reinterpret_cast<char*>(this) -
3724 kHeapObjectTag + kHeaderSize + offset * kShortSize);
3725 if (c <= kMaxAsciiCharCode) {
3726 // Fast case for ASCII characters. Cursor is an input output argument.
3727 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
3728 rbb->util_buffer,
3729 rbb->capacity,
3730 rbb->cursor)) {
3731 break;
3732 }
3733 } else {
3734 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
3735 rbb->util_buffer,
3736 rbb->capacity,
3737 rbb->cursor)) {
3738 break;
3739 }
3740 }
3741 offset++;
3742 chars_read++;
3743 }
3744 *offset_ptr = offset;
3745 rbb->remaining += chars_read;
3746}
3747
3748
3749const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
3750 unsigned* remaining,
3751 unsigned* offset_ptr,
3752 unsigned max_chars) {
3753 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
3754 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
3755 *remaining = max_chars;
3756 *offset_ptr += max_chars;
3757 return b;
3758}
3759
3760
3761// This will iterate unless the block of string data spans two 'halves' of
3762// a ConsString, in which case it will recurse. Since the block of string
3763// data to be read has a maximum size this limits the maximum recursion
3764// depth to something sane. Since C++ does not have tail call recursion
3765// elimination, the iteration must be explicit. Since this is not an
3766// -IntoBuffer method it can delegate to one of the efficient
3767// *AsciiStringReadBlock routines.
3768const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
3769 unsigned* offset_ptr,
3770 unsigned max_chars) {
3771 ConsString* current = this;
3772 unsigned offset = *offset_ptr;
3773 int offset_correction = 0;
3774
3775 while (true) {
3776 String* left = current->first();
3777 unsigned left_length = (unsigned)left->length();
3778 if (left_length > offset &&
3779 (max_chars <= left_length - offset ||
3780 (rbb->capacity <= left_length - offset &&
3781 (max_chars = left_length - offset, true)))) { // comma operator!
3782 // Left hand side only - iterate unless we have reached the bottom of
3783 // the cons tree. The assignment on the left of the comma operator is
3784 // in order to make use of the fact that the -IntoBuffer routines can
3785 // produce at most 'capacity' characters. This enables us to postpone
3786 // the point where we switch to the -IntoBuffer routines (below) in order
3787 // to maximize the chances of delegating a big chunk of work to the
3788 // efficient *AsciiStringReadBlock routines.
3789 if (StringShape(left).IsCons()) {
3790 current = ConsString::cast(left);
3791 continue;
3792 } else {
3793 const unibrow::byte* answer =
3794 String::ReadBlock(left, rbb, &offset, max_chars);
3795 *offset_ptr = offset + offset_correction;
3796 return answer;
3797 }
3798 } else if (left_length <= offset) {
3799 // Right hand side only - iterate unless we have reached the bottom of
3800 // the cons tree.
3801 String* right = current->second();
3802 offset -= left_length;
3803 offset_correction += left_length;
3804 if (StringShape(right).IsCons()) {
3805 current = ConsString::cast(right);
3806 continue;
3807 } else {
3808 const unibrow::byte* answer =
3809 String::ReadBlock(right, rbb, &offset, max_chars);
3810 *offset_ptr = offset + offset_correction;
3811 return answer;
3812 }
3813 } else {
3814 // The block to be read spans two sides of the ConsString, so we call the
3815 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
3816 // are able to assemble data from several part strings because they use
3817 // the util_buffer to store their data and never return direct pointers
3818 // to their storage. We don't try to read more than the buffer capacity
3819 // here or we can get too much recursion.
3820 ASSERT(rbb->remaining == 0);
3821 ASSERT(rbb->cursor == 0);
3822 current->ConsStringReadBlockIntoBuffer(
3823 rbb,
3824 &offset,
3825 max_chars > rbb->capacity ? rbb->capacity : max_chars);
3826 *offset_ptr = offset + offset_correction;
3827 return rbb->util_buffer;
3828 }
3829 }
3830}
3831
3832
Steve Blocka7e24c12009-10-30 11:49:00 +00003833uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
3834 ASSERT(index >= 0 && index < length());
3835 return resource()->data()[index];
3836}
3837
3838
3839const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
3840 unsigned* remaining,
3841 unsigned* offset_ptr,
3842 unsigned max_chars) {
3843 // Cast const char* to unibrow::byte* (signedness difference).
3844 const unibrow::byte* b =
3845 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
3846 *remaining = max_chars;
3847 *offset_ptr += max_chars;
3848 return b;
3849}
3850
3851
3852const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
3853 unsigned start) {
3854 return resource()->data() + start;
3855}
3856
3857
3858uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
3859 ASSERT(index >= 0 && index < length());
3860 return resource()->data()[index];
3861}
3862
3863
3864void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
3865 ReadBlockBuffer* rbb,
3866 unsigned* offset_ptr,
3867 unsigned max_chars) {
3868 unsigned chars_read = 0;
3869 unsigned offset = *offset_ptr;
3870 const uint16_t* data = resource()->data();
3871 while (chars_read < max_chars) {
3872 uint16_t c = data[offset];
3873 if (c <= kMaxAsciiCharCode) {
3874 // Fast case for ASCII characters. Cursor is an input output argument.
3875 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
3876 rbb->util_buffer,
3877 rbb->capacity,
3878 rbb->cursor))
3879 break;
3880 } else {
3881 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
3882 rbb->util_buffer,
3883 rbb->capacity,
3884 rbb->cursor))
3885 break;
3886 }
3887 offset++;
3888 chars_read++;
3889 }
3890 *offset_ptr = offset;
3891 rbb->remaining += chars_read;
3892}
3893
3894
3895void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
3896 unsigned* offset_ptr,
3897 unsigned max_chars) {
3898 unsigned capacity = rbb->capacity - rbb->cursor;
3899 if (max_chars > capacity) max_chars = capacity;
3900 memcpy(rbb->util_buffer + rbb->cursor,
3901 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
3902 *offset_ptr * kCharSize,
3903 max_chars);
3904 rbb->remaining += max_chars;
3905 *offset_ptr += max_chars;
3906 rbb->cursor += max_chars;
3907}
3908
3909
3910void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
3911 ReadBlockBuffer* rbb,
3912 unsigned* offset_ptr,
3913 unsigned max_chars) {
3914 unsigned capacity = rbb->capacity - rbb->cursor;
3915 if (max_chars > capacity) max_chars = capacity;
3916 memcpy(rbb->util_buffer + rbb->cursor,
3917 resource()->data() + *offset_ptr,
3918 max_chars);
3919 rbb->remaining += max_chars;
3920 *offset_ptr += max_chars;
3921 rbb->cursor += max_chars;
3922}
3923
3924
3925// This method determines the type of string involved and then copies
3926// a whole chunk of characters into a buffer, or returns a pointer to a buffer
3927// where they can be found. The pointer is not necessarily valid across a GC
3928// (see AsciiStringReadBlock).
3929const unibrow::byte* String::ReadBlock(String* input,
3930 ReadBlockBuffer* rbb,
3931 unsigned* offset_ptr,
3932 unsigned max_chars) {
3933 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
3934 if (max_chars == 0) {
3935 rbb->remaining = 0;
3936 return NULL;
3937 }
3938 switch (StringShape(input).representation_tag()) {
3939 case kSeqStringTag:
3940 if (input->IsAsciiRepresentation()) {
3941 SeqAsciiString* str = SeqAsciiString::cast(input);
3942 return str->SeqAsciiStringReadBlock(&rbb->remaining,
3943 offset_ptr,
3944 max_chars);
3945 } else {
3946 SeqTwoByteString* str = SeqTwoByteString::cast(input);
3947 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
3948 offset_ptr,
3949 max_chars);
3950 return rbb->util_buffer;
3951 }
3952 case kConsStringTag:
3953 return ConsString::cast(input)->ConsStringReadBlock(rbb,
3954 offset_ptr,
3955 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00003956 case kExternalStringTag:
3957 if (input->IsAsciiRepresentation()) {
3958 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
3959 &rbb->remaining,
3960 offset_ptr,
3961 max_chars);
3962 } else {
3963 ExternalTwoByteString::cast(input)->
3964 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
3965 offset_ptr,
3966 max_chars);
3967 return rbb->util_buffer;
3968 }
3969 default:
3970 break;
3971 }
3972
3973 UNREACHABLE();
3974 return 0;
3975}
3976
3977
3978Relocatable* Relocatable::top_ = NULL;
3979
3980
3981void Relocatable::PostGarbageCollectionProcessing() {
3982 Relocatable* current = top_;
3983 while (current != NULL) {
3984 current->PostGarbageCollection();
3985 current = current->prev_;
3986 }
3987}
3988
3989
3990// Reserve space for statics needing saving and restoring.
3991int Relocatable::ArchiveSpacePerThread() {
3992 return sizeof(top_);
3993}
3994
3995
3996// Archive statics that are thread local.
3997char* Relocatable::ArchiveState(char* to) {
3998 *reinterpret_cast<Relocatable**>(to) = top_;
3999 top_ = NULL;
4000 return to + ArchiveSpacePerThread();
4001}
4002
4003
4004// Restore statics that are thread local.
4005char* Relocatable::RestoreState(char* from) {
4006 top_ = *reinterpret_cast<Relocatable**>(from);
4007 return from + ArchiveSpacePerThread();
4008}
4009
4010
4011char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
4012 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
4013 Iterate(v, top);
4014 return thread_storage + ArchiveSpacePerThread();
4015}
4016
4017
4018void Relocatable::Iterate(ObjectVisitor* v) {
4019 Iterate(v, top_);
4020}
4021
4022
4023void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
4024 Relocatable* current = top;
4025 while (current != NULL) {
4026 current->IterateInstance(v);
4027 current = current->prev_;
4028 }
4029}
4030
4031
4032FlatStringReader::FlatStringReader(Handle<String> str)
4033 : str_(str.location()),
4034 length_(str->length()) {
4035 PostGarbageCollection();
4036}
4037
4038
4039FlatStringReader::FlatStringReader(Vector<const char> input)
4040 : str_(0),
4041 is_ascii_(true),
4042 length_(input.length()),
4043 start_(input.start()) { }
4044
4045
4046void FlatStringReader::PostGarbageCollection() {
4047 if (str_ == NULL) return;
4048 Handle<String> str(str_);
4049 ASSERT(str->IsFlat());
4050 is_ascii_ = str->IsAsciiRepresentation();
4051 if (is_ascii_) {
4052 start_ = str->ToAsciiVector().start();
4053 } else {
4054 start_ = str->ToUC16Vector().start();
4055 }
4056}
4057
4058
4059void StringInputBuffer::Seek(unsigned pos) {
4060 Reset(pos, input_);
4061}
4062
4063
4064void SafeStringInputBuffer::Seek(unsigned pos) {
4065 Reset(pos, input_);
4066}
4067
4068
4069// This method determines the type of string involved and then copies
4070// a whole chunk of characters into a buffer. It can be used with strings
4071// that have been glued together to form a ConsString and which must cooperate
4072// to fill up a buffer.
4073void String::ReadBlockIntoBuffer(String* input,
4074 ReadBlockBuffer* rbb,
4075 unsigned* offset_ptr,
4076 unsigned max_chars) {
4077 ASSERT(*offset_ptr <= (unsigned)input->length());
4078 if (max_chars == 0) return;
4079
4080 switch (StringShape(input).representation_tag()) {
4081 case kSeqStringTag:
4082 if (input->IsAsciiRepresentation()) {
4083 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4084 offset_ptr,
4085 max_chars);
4086 return;
4087 } else {
4088 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4089 offset_ptr,
4090 max_chars);
4091 return;
4092 }
4093 case kConsStringTag:
4094 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4095 offset_ptr,
4096 max_chars);
4097 return;
Steve Blocka7e24c12009-10-30 11:49:00 +00004098 case kExternalStringTag:
4099 if (input->IsAsciiRepresentation()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004100 ExternalAsciiString::cast(input)->
4101 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4102 } else {
4103 ExternalTwoByteString::cast(input)->
4104 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4105 offset_ptr,
4106 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004107 }
4108 return;
4109 default:
4110 break;
4111 }
4112
4113 UNREACHABLE();
4114 return;
4115}
4116
4117
4118const unibrow::byte* String::ReadBlock(String* input,
4119 unibrow::byte* util_buffer,
4120 unsigned capacity,
4121 unsigned* remaining,
4122 unsigned* offset_ptr) {
4123 ASSERT(*offset_ptr <= (unsigned)input->length());
4124 unsigned chars = input->length() - *offset_ptr;
4125 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4126 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4127 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4128 *remaining = rbb.remaining;
4129 return answer;
4130}
4131
4132
4133const unibrow::byte* String::ReadBlock(String** raw_input,
4134 unibrow::byte* util_buffer,
4135 unsigned capacity,
4136 unsigned* remaining,
4137 unsigned* offset_ptr) {
4138 Handle<String> input(raw_input);
4139 ASSERT(*offset_ptr <= (unsigned)input->length());
4140 unsigned chars = input->length() - *offset_ptr;
4141 if (chars > capacity) chars = capacity;
4142 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4143 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4144 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4145 *remaining = rbb.remaining;
4146 return rbb.util_buffer;
4147}
4148
4149
4150// This will iterate unless the block of string data spans two 'halves' of
4151// a ConsString, in which case it will recurse. Since the block of string
4152// data to be read has a maximum size this limits the maximum recursion
4153// depth to something sane. Since C++ does not have tail call recursion
4154// elimination, the iteration must be explicit.
4155void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4156 unsigned* offset_ptr,
4157 unsigned max_chars) {
4158 ConsString* current = this;
4159 unsigned offset = *offset_ptr;
4160 int offset_correction = 0;
4161
4162 while (true) {
4163 String* left = current->first();
4164 unsigned left_length = (unsigned)left->length();
4165 if (left_length > offset &&
4166 max_chars <= left_length - offset) {
4167 // Left hand side only - iterate unless we have reached the bottom of
4168 // the cons tree.
4169 if (StringShape(left).IsCons()) {
4170 current = ConsString::cast(left);
4171 continue;
4172 } else {
4173 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4174 *offset_ptr = offset + offset_correction;
4175 return;
4176 }
4177 } else if (left_length <= offset) {
4178 // Right hand side only - iterate unless we have reached the bottom of
4179 // the cons tree.
4180 offset -= left_length;
4181 offset_correction += left_length;
4182 String* right = current->second();
4183 if (StringShape(right).IsCons()) {
4184 current = ConsString::cast(right);
4185 continue;
4186 } else {
4187 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4188 *offset_ptr = offset + offset_correction;
4189 return;
4190 }
4191 } else {
4192 // The block to be read spans two sides of the ConsString, so we recurse.
4193 // First recurse on the left.
4194 max_chars -= left_length - offset;
4195 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4196 // We may have reached the max or there may not have been enough space
4197 // in the buffer for the characters in the left hand side.
4198 if (offset == left_length) {
4199 // Recurse on the right.
4200 String* right = String::cast(current->second());
4201 offset -= left_length;
4202 offset_correction += left_length;
4203 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4204 }
4205 *offset_ptr = offset + offset_correction;
4206 return;
4207 }
4208 }
4209}
4210
4211
Steve Blocka7e24c12009-10-30 11:49:00 +00004212void ConsString::ConsStringIterateBody(ObjectVisitor* v) {
4213 IteratePointers(v, kFirstOffset, kSecondOffset + kPointerSize);
4214}
4215
4216
4217void JSGlobalPropertyCell::JSGlobalPropertyCellIterateBody(ObjectVisitor* v) {
4218 IteratePointers(v, kValueOffset, kValueOffset + kPointerSize);
4219}
4220
4221
4222uint16_t ConsString::ConsStringGet(int index) {
4223 ASSERT(index >= 0 && index < this->length());
4224
4225 // Check for a flattened cons string
4226 if (second()->length() == 0) {
4227 String* left = first();
4228 return left->Get(index);
4229 }
4230
4231 String* string = String::cast(this);
4232
4233 while (true) {
4234 if (StringShape(string).IsCons()) {
4235 ConsString* cons_string = ConsString::cast(string);
4236 String* left = cons_string->first();
4237 if (left->length() > index) {
4238 string = left;
4239 } else {
4240 index -= left->length();
4241 string = cons_string->second();
4242 }
4243 } else {
4244 return string->Get(index);
4245 }
4246 }
4247
4248 UNREACHABLE();
4249 return 0;
4250}
4251
4252
4253template <typename sinkchar>
4254void String::WriteToFlat(String* src,
4255 sinkchar* sink,
4256 int f,
4257 int t) {
4258 String* source = src;
4259 int from = f;
4260 int to = t;
4261 while (true) {
4262 ASSERT(0 <= from && from <= to && to <= source->length());
4263 switch (StringShape(source).full_representation_tag()) {
4264 case kAsciiStringTag | kExternalStringTag: {
4265 CopyChars(sink,
4266 ExternalAsciiString::cast(source)->resource()->data() + from,
4267 to - from);
4268 return;
4269 }
4270 case kTwoByteStringTag | kExternalStringTag: {
4271 const uc16* data =
4272 ExternalTwoByteString::cast(source)->resource()->data();
4273 CopyChars(sink,
4274 data + from,
4275 to - from);
4276 return;
4277 }
4278 case kAsciiStringTag | kSeqStringTag: {
4279 CopyChars(sink,
4280 SeqAsciiString::cast(source)->GetChars() + from,
4281 to - from);
4282 return;
4283 }
4284 case kTwoByteStringTag | kSeqStringTag: {
4285 CopyChars(sink,
4286 SeqTwoByteString::cast(source)->GetChars() + from,
4287 to - from);
4288 return;
4289 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004290 case kAsciiStringTag | kConsStringTag:
4291 case kTwoByteStringTag | kConsStringTag: {
4292 ConsString* cons_string = ConsString::cast(source);
4293 String* first = cons_string->first();
4294 int boundary = first->length();
4295 if (to - boundary >= boundary - from) {
4296 // Right hand side is longer. Recurse over left.
4297 if (from < boundary) {
4298 WriteToFlat(first, sink, from, boundary);
4299 sink += boundary - from;
4300 from = 0;
4301 } else {
4302 from -= boundary;
4303 }
4304 to -= boundary;
4305 source = cons_string->second();
4306 } else {
4307 // Left hand side is longer. Recurse over right.
4308 if (to > boundary) {
4309 String* second = cons_string->second();
4310 WriteToFlat(second,
4311 sink + boundary - from,
4312 0,
4313 to - boundary);
4314 to = boundary;
4315 }
4316 source = first;
4317 }
4318 break;
4319 }
4320 }
4321 }
4322}
4323
4324
Steve Blockd0582a62009-12-15 09:54:21 +00004325#define FIELD_ADDR(p, offset) \
4326 (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
4327
4328void ExternalAsciiString::ExternalAsciiStringIterateBody(ObjectVisitor* v) {
4329 typedef v8::String::ExternalAsciiStringResource Resource;
4330 v->VisitExternalAsciiString(
4331 reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
Steve Blocka7e24c12009-10-30 11:49:00 +00004332}
4333
4334
Steve Blockd0582a62009-12-15 09:54:21 +00004335void ExternalTwoByteString::ExternalTwoByteStringIterateBody(ObjectVisitor* v) {
4336 typedef v8::String::ExternalStringResource Resource;
4337 v->VisitExternalTwoByteString(
4338 reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
Steve Blocka7e24c12009-10-30 11:49:00 +00004339}
4340
Steve Blockd0582a62009-12-15 09:54:21 +00004341#undef FIELD_ADDR
Steve Blocka7e24c12009-10-30 11:49:00 +00004342
4343template <typename IteratorA, typename IteratorB>
4344static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4345 // General slow case check. We know that the ia and ib iterators
4346 // have the same length.
4347 while (ia->has_more()) {
4348 uc32 ca = ia->GetNext();
4349 uc32 cb = ib->GetNext();
4350 if (ca != cb)
4351 return false;
4352 }
4353 return true;
4354}
4355
4356
4357// Compares the contents of two strings by reading and comparing
4358// int-sized blocks of characters.
4359template <typename Char>
4360static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4361 int length = a.length();
4362 ASSERT_EQ(length, b.length());
4363 const Char* pa = a.start();
4364 const Char* pb = b.start();
4365 int i = 0;
4366#ifndef V8_HOST_CAN_READ_UNALIGNED
4367 // If this architecture isn't comfortable reading unaligned ints
4368 // then we have to check that the strings are aligned before
4369 // comparing them blockwise.
4370 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4371 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4372 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
4373 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
4374#endif
4375 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
4376 int endpoint = length - kStepSize;
4377 // Compare blocks until we reach near the end of the string.
4378 for (; i <= endpoint; i += kStepSize) {
4379 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
4380 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
4381 if (wa != wb) {
4382 return false;
4383 }
4384 }
4385#ifndef V8_HOST_CAN_READ_UNALIGNED
4386 }
4387#endif
4388 // Compare the remaining characters that didn't fit into a block.
4389 for (; i < length; i++) {
4390 if (a[i] != b[i]) {
4391 return false;
4392 }
4393 }
4394 return true;
4395}
4396
4397
4398static StringInputBuffer string_compare_buffer_b;
4399
4400
4401template <typename IteratorA>
4402static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
4403 if (b->IsFlat()) {
4404 if (b->IsAsciiRepresentation()) {
4405 VectorIterator<char> ib(b->ToAsciiVector());
4406 return CompareStringContents(ia, &ib);
4407 } else {
4408 VectorIterator<uc16> ib(b->ToUC16Vector());
4409 return CompareStringContents(ia, &ib);
4410 }
4411 } else {
4412 string_compare_buffer_b.Reset(0, b);
4413 return CompareStringContents(ia, &string_compare_buffer_b);
4414 }
4415}
4416
4417
4418static StringInputBuffer string_compare_buffer_a;
4419
4420
4421bool String::SlowEquals(String* other) {
4422 // Fast check: negative check with lengths.
4423 int len = length();
4424 if (len != other->length()) return false;
4425 if (len == 0) return true;
4426
4427 // Fast check: if hash code is computed for both strings
4428 // a fast negative check can be performed.
4429 if (HasHashCode() && other->HasHashCode()) {
4430 if (Hash() != other->Hash()) return false;
4431 }
4432
4433 if (StringShape(this).IsSequentialAscii() &&
4434 StringShape(other).IsSequentialAscii()) {
4435 const char* str1 = SeqAsciiString::cast(this)->GetChars();
4436 const char* str2 = SeqAsciiString::cast(other)->GetChars();
4437 return CompareRawStringContents(Vector<const char>(str1, len),
4438 Vector<const char>(str2, len));
4439 }
4440
4441 if (this->IsFlat()) {
4442 if (IsAsciiRepresentation()) {
4443 Vector<const char> vec1 = this->ToAsciiVector();
4444 if (other->IsFlat()) {
4445 if (other->IsAsciiRepresentation()) {
4446 Vector<const char> vec2 = other->ToAsciiVector();
4447 return CompareRawStringContents(vec1, vec2);
4448 } else {
4449 VectorIterator<char> buf1(vec1);
4450 VectorIterator<uc16> ib(other->ToUC16Vector());
4451 return CompareStringContents(&buf1, &ib);
4452 }
4453 } else {
4454 VectorIterator<char> buf1(vec1);
4455 string_compare_buffer_b.Reset(0, other);
4456 return CompareStringContents(&buf1, &string_compare_buffer_b);
4457 }
4458 } else {
4459 Vector<const uc16> vec1 = this->ToUC16Vector();
4460 if (other->IsFlat()) {
4461 if (other->IsAsciiRepresentation()) {
4462 VectorIterator<uc16> buf1(vec1);
4463 VectorIterator<char> ib(other->ToAsciiVector());
4464 return CompareStringContents(&buf1, &ib);
4465 } else {
4466 Vector<const uc16> vec2(other->ToUC16Vector());
4467 return CompareRawStringContents(vec1, vec2);
4468 }
4469 } else {
4470 VectorIterator<uc16> buf1(vec1);
4471 string_compare_buffer_b.Reset(0, other);
4472 return CompareStringContents(&buf1, &string_compare_buffer_b);
4473 }
4474 }
4475 } else {
4476 string_compare_buffer_a.Reset(0, this);
4477 return CompareStringContentsPartial(&string_compare_buffer_a, other);
4478 }
4479}
4480
4481
4482bool String::MarkAsUndetectable() {
4483 if (StringShape(this).IsSymbol()) return false;
4484
4485 Map* map = this->map();
Steve Blockd0582a62009-12-15 09:54:21 +00004486 if (map == Heap::string_map()) {
4487 this->set_map(Heap::undetectable_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00004488 return true;
Steve Blockd0582a62009-12-15 09:54:21 +00004489 } else if (map == Heap::ascii_string_map()) {
4490 this->set_map(Heap::undetectable_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00004491 return true;
4492 }
4493 // Rest cannot be marked as undetectable
4494 return false;
4495}
4496
4497
4498bool String::IsEqualTo(Vector<const char> str) {
4499 int slen = length();
4500 Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
4501 decoder->Reset(str.start(), str.length());
4502 int i;
4503 for (i = 0; i < slen && decoder->has_more(); i++) {
4504 uc32 r = decoder->GetNext();
4505 if (Get(i) != r) return false;
4506 }
4507 return i == slen && !decoder->has_more();
4508}
4509
4510
4511uint32_t String::ComputeAndSetHash() {
4512 // Should only be called if hash code has not yet been computed.
Steve Blockd0582a62009-12-15 09:54:21 +00004513 ASSERT(!(hash_field() & kHashComputedMask));
Steve Blocka7e24c12009-10-30 11:49:00 +00004514
4515 // Compute the hash code.
4516 StringInputBuffer buffer(this);
Steve Blockd0582a62009-12-15 09:54:21 +00004517 uint32_t field = ComputeHashField(&buffer, length());
Steve Blocka7e24c12009-10-30 11:49:00 +00004518
4519 // Store the hash code in the object.
Steve Blockd0582a62009-12-15 09:54:21 +00004520 set_hash_field(field);
Steve Blocka7e24c12009-10-30 11:49:00 +00004521
4522 // Check the hash code is there.
Steve Blockd0582a62009-12-15 09:54:21 +00004523 ASSERT(hash_field() & kHashComputedMask);
Steve Blocka7e24c12009-10-30 11:49:00 +00004524 uint32_t result = field >> kHashShift;
4525 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
4526 return result;
4527}
4528
4529
4530bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
4531 uint32_t* index,
4532 int length) {
4533 if (length == 0 || length > kMaxArrayIndexSize) return false;
4534 uc32 ch = buffer->GetNext();
4535
4536 // If the string begins with a '0' character, it must only consist
4537 // of it to be a legal array index.
4538 if (ch == '0') {
4539 *index = 0;
4540 return length == 1;
4541 }
4542
4543 // Convert string to uint32 array index; character by character.
4544 int d = ch - '0';
4545 if (d < 0 || d > 9) return false;
4546 uint32_t result = d;
4547 while (buffer->has_more()) {
4548 d = buffer->GetNext() - '0';
4549 if (d < 0 || d > 9) return false;
4550 // Check that the new result is below the 32 bit limit.
4551 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
4552 result = (result * 10) + d;
4553 }
4554
4555 *index = result;
4556 return true;
4557}
4558
4559
4560bool String::SlowAsArrayIndex(uint32_t* index) {
4561 if (length() <= kMaxCachedArrayIndexLength) {
4562 Hash(); // force computation of hash code
Steve Blockd0582a62009-12-15 09:54:21 +00004563 uint32_t field = hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +00004564 if ((field & kIsArrayIndexMask) == 0) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00004565 // Isolate the array index form the full hash field.
4566 *index = (kArrayIndexHashMask & field) >> kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00004567 return true;
4568 } else {
4569 StringInputBuffer buffer(this);
4570 return ComputeArrayIndex(&buffer, index, length());
4571 }
4572}
4573
4574
Steve Blockd0582a62009-12-15 09:54:21 +00004575static inline uint32_t HashField(uint32_t hash,
4576 bool is_array_index,
4577 int length = -1) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004578 uint32_t result =
Steve Blockd0582a62009-12-15 09:54:21 +00004579 (hash << String::kHashShift) | String::kHashComputedMask;
4580 if (is_array_index) {
4581 // For array indexes mix the length into the hash as an array index could
4582 // be zero.
4583 ASSERT(length > 0);
4584 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
4585 (1 << String::kArrayIndexValueBits));
4586 result |= String::kIsArrayIndexMask;
4587 result |= length << String::kArrayIndexHashLengthShift;
4588 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004589 return result;
4590}
4591
4592
4593uint32_t StringHasher::GetHashField() {
4594 ASSERT(is_valid());
Steve Blockd0582a62009-12-15 09:54:21 +00004595 if (length_ <= String::kMaxHashCalcLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004596 if (is_array_index()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004597 return v8::internal::HashField(array_index(), true, length_);
Steve Blocka7e24c12009-10-30 11:49:00 +00004598 } else {
Steve Blockd0582a62009-12-15 09:54:21 +00004599 return v8::internal::HashField(GetHash(), false);
Steve Blocka7e24c12009-10-30 11:49:00 +00004600 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004601 uint32_t payload = v8::internal::HashField(GetHash(), false);
Steve Blockd0582a62009-12-15 09:54:21 +00004602 return payload;
Steve Blocka7e24c12009-10-30 11:49:00 +00004603 } else {
4604 return v8::internal::HashField(length_, false);
4605 }
4606}
4607
4608
Steve Blockd0582a62009-12-15 09:54:21 +00004609uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
4610 int length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004611 StringHasher hasher(length);
4612
4613 // Very long strings have a trivial hash that doesn't inspect the
4614 // string contents.
4615 if (hasher.has_trivial_hash()) {
4616 return hasher.GetHashField();
4617 }
4618
4619 // Do the iterative array index computation as long as there is a
4620 // chance this is an array index.
4621 while (buffer->has_more() && hasher.is_array_index()) {
4622 hasher.AddCharacter(buffer->GetNext());
4623 }
4624
4625 // Process the remaining characters without updating the array
4626 // index.
4627 while (buffer->has_more()) {
4628 hasher.AddCharacterNoIndex(buffer->GetNext());
4629 }
4630
4631 return hasher.GetHashField();
4632}
4633
4634
Steve Blockd0582a62009-12-15 09:54:21 +00004635Object* String::SubString(int start, int end) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004636 if (start == 0 && end == length()) return this;
Steve Blockd0582a62009-12-15 09:54:21 +00004637 Object* result = Heap::AllocateSubString(this, start, end);
4638 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00004639}
4640
4641
4642void String::PrintOn(FILE* file) {
4643 int length = this->length();
4644 for (int i = 0; i < length; i++) {
4645 fprintf(file, "%c", Get(i));
4646 }
4647}
4648
4649
4650void Map::CreateBackPointers() {
4651 DescriptorArray* descriptors = instance_descriptors();
4652 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
4653 if (descriptors->GetType(i) == MAP_TRANSITION) {
4654 // Get target.
4655 Map* target = Map::cast(descriptors->GetValue(i));
4656#ifdef DEBUG
4657 // Verify target.
4658 Object* source_prototype = prototype();
4659 Object* target_prototype = target->prototype();
4660 ASSERT(source_prototype->IsJSObject() ||
4661 source_prototype->IsMap() ||
4662 source_prototype->IsNull());
4663 ASSERT(target_prototype->IsJSObject() ||
4664 target_prototype->IsNull());
4665 ASSERT(source_prototype->IsMap() ||
4666 source_prototype == target_prototype);
4667#endif
4668 // Point target back to source. set_prototype() will not let us set
4669 // the prototype to a map, as we do here.
4670 *RawField(target, kPrototypeOffset) = this;
4671 }
4672 }
4673}
4674
4675
4676void Map::ClearNonLiveTransitions(Object* real_prototype) {
4677 // Live DescriptorArray objects will be marked, so we must use
4678 // low-level accessors to get and modify their data.
4679 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
4680 *RawField(this, Map::kInstanceDescriptorsOffset));
4681 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
4682 Smi* NullDescriptorDetails =
4683 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
4684 FixedArray* contents = reinterpret_cast<FixedArray*>(
4685 d->get(DescriptorArray::kContentArrayIndex));
4686 ASSERT(contents->length() >= 2);
4687 for (int i = 0; i < contents->length(); i += 2) {
4688 // If the pair (value, details) is a map transition,
4689 // check if the target is live. If not, null the descriptor.
4690 // Also drop the back pointer for that map transition, so that this
4691 // map is not reached again by following a back pointer from a
4692 // non-live object.
4693 PropertyDetails details(Smi::cast(contents->get(i + 1)));
4694 if (details.type() == MAP_TRANSITION) {
4695 Map* target = reinterpret_cast<Map*>(contents->get(i));
4696 ASSERT(target->IsHeapObject());
4697 if (!target->IsMarked()) {
4698 ASSERT(target->IsMap());
4699 contents->set(i + 1, NullDescriptorDetails, SKIP_WRITE_BARRIER);
4700 contents->set(i, Heap::null_value(), SKIP_WRITE_BARRIER);
4701 ASSERT(target->prototype() == this ||
4702 target->prototype() == real_prototype);
4703 // Getter prototype() is read-only, set_prototype() has side effects.
4704 *RawField(target, Map::kPrototypeOffset) = real_prototype;
4705 }
4706 }
4707 }
4708}
4709
4710
4711void Map::MapIterateBody(ObjectVisitor* v) {
4712 // Assumes all Object* members are contiguously allocated!
4713 IteratePointers(v, kPrototypeOffset, kCodeCacheOffset + kPointerSize);
4714}
4715
4716
4717Object* JSFunction::SetInstancePrototype(Object* value) {
4718 ASSERT(value->IsJSObject());
4719
4720 if (has_initial_map()) {
4721 initial_map()->set_prototype(value);
4722 } else {
4723 // Put the value in the initial map field until an initial map is
4724 // needed. At that point, a new initial map is created and the
4725 // prototype is put into the initial map where it belongs.
4726 set_prototype_or_initial_map(value);
4727 }
4728 return value;
4729}
4730
4731
4732
4733Object* JSFunction::SetPrototype(Object* value) {
4734 Object* construct_prototype = value;
4735
4736 // If the value is not a JSObject, store the value in the map's
4737 // constructor field so it can be accessed. Also, set the prototype
4738 // used for constructing objects to the original object prototype.
4739 // See ECMA-262 13.2.2.
4740 if (!value->IsJSObject()) {
4741 // Copy the map so this does not affect unrelated functions.
4742 // Remove map transitions because they point to maps with a
4743 // different prototype.
4744 Object* new_map = map()->CopyDropTransitions();
4745 if (new_map->IsFailure()) return new_map;
4746 set_map(Map::cast(new_map));
4747 map()->set_constructor(value);
4748 map()->set_non_instance_prototype(true);
4749 construct_prototype =
4750 Top::context()->global_context()->initial_object_prototype();
4751 } else {
4752 map()->set_non_instance_prototype(false);
4753 }
4754
4755 return SetInstancePrototype(construct_prototype);
4756}
4757
4758
4759Object* JSFunction::SetInstanceClassName(String* name) {
4760 shared()->set_instance_class_name(name);
4761 return this;
4762}
4763
4764
4765Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
4766 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
4767}
4768
4769
4770void Oddball::OddballIterateBody(ObjectVisitor* v) {
4771 // Assumes all Object* members are contiguously allocated!
4772 IteratePointers(v, kToStringOffset, kToNumberOffset + kPointerSize);
4773}
4774
4775
4776Object* Oddball::Initialize(const char* to_string, Object* to_number) {
4777 Object* symbol = Heap::LookupAsciiSymbol(to_string);
4778 if (symbol->IsFailure()) return symbol;
4779 set_to_string(String::cast(symbol));
4780 set_to_number(to_number);
4781 return this;
4782}
4783
4784
4785bool SharedFunctionInfo::HasSourceCode() {
4786 return !script()->IsUndefined() &&
4787 !Script::cast(script())->source()->IsUndefined();
4788}
4789
4790
4791Object* SharedFunctionInfo::GetSourceCode() {
4792 HandleScope scope;
4793 if (script()->IsUndefined()) return Heap::undefined_value();
4794 Object* source = Script::cast(script())->source();
4795 if (source->IsUndefined()) return Heap::undefined_value();
4796 return *SubString(Handle<String>(String::cast(source)),
4797 start_position(), end_position());
4798}
4799
4800
4801int SharedFunctionInfo::CalculateInstanceSize() {
4802 int instance_size =
4803 JSObject::kHeaderSize +
4804 expected_nof_properties() * kPointerSize;
4805 if (instance_size > JSObject::kMaxInstanceSize) {
4806 instance_size = JSObject::kMaxInstanceSize;
4807 }
4808 return instance_size;
4809}
4810
4811
4812int SharedFunctionInfo::CalculateInObjectProperties() {
4813 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
4814}
4815
4816
4817void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +00004818 bool only_simple_this_property_assignments,
4819 FixedArray* assignments) {
4820 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00004821 kHasOnlySimpleThisPropertyAssignments,
4822 only_simple_this_property_assignments));
4823 set_this_property_assignments(assignments);
4824 set_this_property_assignments_count(assignments->length() / 3);
4825}
4826
4827
4828void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
4829 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00004830 kHasOnlySimpleThisPropertyAssignments,
4831 false));
4832 set_this_property_assignments(Heap::undefined_value());
4833 set_this_property_assignments_count(0);
4834}
4835
4836
4837String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
4838 Object* obj = this_property_assignments();
4839 ASSERT(obj->IsFixedArray());
4840 ASSERT(index < this_property_assignments_count());
4841 obj = FixedArray::cast(obj)->get(index * 3);
4842 ASSERT(obj->IsString());
4843 return String::cast(obj);
4844}
4845
4846
4847bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
4848 Object* obj = this_property_assignments();
4849 ASSERT(obj->IsFixedArray());
4850 ASSERT(index < this_property_assignments_count());
4851 obj = FixedArray::cast(obj)->get(index * 3 + 1);
4852 return Smi::cast(obj)->value() != -1;
4853}
4854
4855
4856int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
4857 ASSERT(IsThisPropertyAssignmentArgument(index));
4858 Object* obj =
4859 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
4860 return Smi::cast(obj)->value();
4861}
4862
4863
4864Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
4865 ASSERT(!IsThisPropertyAssignmentArgument(index));
4866 Object* obj =
4867 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
4868 return obj;
4869}
4870
4871
4872
4873// Support function for printing the source code to a StringStream
4874// without any allocation in the heap.
4875void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
4876 int max_length) {
4877 // For some native functions there is no source.
4878 if (script()->IsUndefined() ||
4879 Script::cast(script())->source()->IsUndefined()) {
4880 accumulator->Add("<No Source>");
4881 return;
4882 }
4883
Steve Blockd0582a62009-12-15 09:54:21 +00004884 // Get the source for the script which this function came from.
Steve Blocka7e24c12009-10-30 11:49:00 +00004885 // Don't use String::cast because we don't want more assertion errors while
4886 // we are already creating a stack dump.
4887 String* script_source =
4888 reinterpret_cast<String*>(Script::cast(script())->source());
4889
4890 if (!script_source->LooksValid()) {
4891 accumulator->Add("<Invalid Source>");
4892 return;
4893 }
4894
4895 if (!is_toplevel()) {
4896 accumulator->Add("function ");
4897 Object* name = this->name();
4898 if (name->IsString() && String::cast(name)->length() > 0) {
4899 accumulator->PrintName(name);
4900 }
4901 }
4902
4903 int len = end_position() - start_position();
4904 if (len > max_length) {
4905 accumulator->Put(script_source,
4906 start_position(),
4907 start_position() + max_length);
4908 accumulator->Add("...\n");
4909 } else {
4910 accumulator->Put(script_source, start_position(), end_position());
4911 }
4912}
4913
4914
4915void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
4916 IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize);
4917 IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
4918 IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
4919 IteratePointers(v, kThisPropertyAssignmentsOffset,
4920 kThisPropertyAssignmentsOffset + kPointerSize);
4921}
4922
4923
4924void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
4925 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
4926 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
4927 Object* old_target = target;
4928 VisitPointer(&target);
4929 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
4930}
4931
4932
4933void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
Steve Block3ce2e202009-11-05 08:53:23 +00004934 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
4935 rinfo->IsPatchedReturnSequence());
Steve Blocka7e24c12009-10-30 11:49:00 +00004936 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
4937 Object* old_target = target;
4938 VisitPointer(&target);
4939 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
4940}
4941
4942
4943void Code::CodeIterateBody(ObjectVisitor* v) {
4944 int mode_mask = RelocInfo::kCodeTargetMask |
4945 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
4946 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
4947 RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
4948 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
4949
4950 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
4951 RelocInfo::Mode rmode = it.rinfo()->rmode();
4952 if (rmode == RelocInfo::EMBEDDED_OBJECT) {
4953 v->VisitPointer(it.rinfo()->target_object_address());
4954 } else if (RelocInfo::IsCodeTarget(rmode)) {
4955 v->VisitCodeTarget(it.rinfo());
4956 } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
4957 v->VisitExternalReference(it.rinfo()->target_reference_address());
4958#ifdef ENABLE_DEBUGGER_SUPPORT
4959 } else if (Debug::has_break_points() &&
4960 RelocInfo::IsJSReturn(rmode) &&
Steve Block3ce2e202009-11-05 08:53:23 +00004961 it.rinfo()->IsPatchedReturnSequence()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004962 v->VisitDebugTarget(it.rinfo());
4963#endif
4964 } else if (rmode == RelocInfo::RUNTIME_ENTRY) {
4965 v->VisitRuntimeEntry(it.rinfo());
4966 }
4967 }
4968
4969 ScopeInfo<>::IterateScopeInfo(this, v);
4970}
4971
4972
Steve Blockd0582a62009-12-15 09:54:21 +00004973void Code::Relocate(intptr_t delta) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004974 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
4975 it.rinfo()->apply(delta);
4976 }
4977 CPU::FlushICache(instruction_start(), instruction_size());
4978}
4979
4980
4981void Code::CopyFrom(const CodeDesc& desc) {
4982 // copy code
4983 memmove(instruction_start(), desc.buffer, desc.instr_size);
4984
4985 // fill gap with zero bytes
4986 { byte* p = instruction_start() + desc.instr_size;
4987 byte* q = relocation_start();
4988 while (p < q) {
4989 *p++ = 0;
4990 }
4991 }
4992
4993 // copy reloc info
4994 memmove(relocation_start(),
4995 desc.buffer + desc.buffer_size - desc.reloc_size,
4996 desc.reloc_size);
4997
4998 // unbox handles and relocate
Steve Block3ce2e202009-11-05 08:53:23 +00004999 intptr_t delta = instruction_start() - desc.buffer;
Steve Blocka7e24c12009-10-30 11:49:00 +00005000 int mode_mask = RelocInfo::kCodeTargetMask |
5001 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
5002 RelocInfo::kApplyMask;
Steve Block3ce2e202009-11-05 08:53:23 +00005003 Assembler* origin = desc.origin; // Needed to find target_object on X64.
Steve Blocka7e24c12009-10-30 11:49:00 +00005004 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
5005 RelocInfo::Mode mode = it.rinfo()->rmode();
5006 if (mode == RelocInfo::EMBEDDED_OBJECT) {
Steve Block3ce2e202009-11-05 08:53:23 +00005007 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005008 it.rinfo()->set_target_object(*p);
5009 } else if (RelocInfo::IsCodeTarget(mode)) {
5010 // rewrite code handles in inline cache targets to direct
5011 // pointers to the first instruction in the code object
Steve Block3ce2e202009-11-05 08:53:23 +00005012 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005013 Code* code = Code::cast(*p);
5014 it.rinfo()->set_target_address(code->instruction_start());
5015 } else {
5016 it.rinfo()->apply(delta);
5017 }
5018 }
5019 CPU::FlushICache(instruction_start(), instruction_size());
5020}
5021
5022
5023// Locate the source position which is closest to the address in the code. This
5024// is using the source position information embedded in the relocation info.
5025// The position returned is relative to the beginning of the script where the
5026// source for this function is found.
5027int Code::SourcePosition(Address pc) {
5028 int distance = kMaxInt;
5029 int position = RelocInfo::kNoPosition; // Initially no position found.
5030 // Run through all the relocation info to find the best matching source
5031 // position. All the code needs to be considered as the sequence of the
5032 // instructions in the code does not necessarily follow the same order as the
5033 // source.
5034 RelocIterator it(this, RelocInfo::kPositionMask);
5035 while (!it.done()) {
5036 // Only look at positions after the current pc.
5037 if (it.rinfo()->pc() < pc) {
5038 // Get position and distance.
Steve Blockd0582a62009-12-15 09:54:21 +00005039
5040 int dist = static_cast<int>(pc - it.rinfo()->pc());
5041 int pos = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005042 // If this position is closer than the current candidate or if it has the
5043 // same distance as the current candidate and the position is higher then
5044 // this position is the new candidate.
5045 if ((dist < distance) ||
5046 (dist == distance && pos > position)) {
5047 position = pos;
5048 distance = dist;
5049 }
5050 }
5051 it.next();
5052 }
5053 return position;
5054}
5055
5056
5057// Same as Code::SourcePosition above except it only looks for statement
5058// positions.
5059int Code::SourceStatementPosition(Address pc) {
5060 // First find the position as close as possible using all position
5061 // information.
5062 int position = SourcePosition(pc);
5063 // Now find the closest statement position before the position.
5064 int statement_position = 0;
5065 RelocIterator it(this, RelocInfo::kPositionMask);
5066 while (!it.done()) {
5067 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
Steve Blockd0582a62009-12-15 09:54:21 +00005068 int p = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005069 if (statement_position < p && p <= position) {
5070 statement_position = p;
5071 }
5072 }
5073 it.next();
5074 }
5075 return statement_position;
5076}
5077
5078
5079#ifdef ENABLE_DISASSEMBLER
5080// Identify kind of code.
5081const char* Code::Kind2String(Kind kind) {
5082 switch (kind) {
5083 case FUNCTION: return "FUNCTION";
5084 case STUB: return "STUB";
5085 case BUILTIN: return "BUILTIN";
5086 case LOAD_IC: return "LOAD_IC";
5087 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
5088 case STORE_IC: return "STORE_IC";
5089 case KEYED_STORE_IC: return "KEYED_STORE_IC";
5090 case CALL_IC: return "CALL_IC";
5091 }
5092 UNREACHABLE();
5093 return NULL;
5094}
5095
5096
5097const char* Code::ICState2String(InlineCacheState state) {
5098 switch (state) {
5099 case UNINITIALIZED: return "UNINITIALIZED";
5100 case PREMONOMORPHIC: return "PREMONOMORPHIC";
5101 case MONOMORPHIC: return "MONOMORPHIC";
5102 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
5103 case MEGAMORPHIC: return "MEGAMORPHIC";
5104 case DEBUG_BREAK: return "DEBUG_BREAK";
5105 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
5106 }
5107 UNREACHABLE();
5108 return NULL;
5109}
5110
5111
5112const char* Code::PropertyType2String(PropertyType type) {
5113 switch (type) {
5114 case NORMAL: return "NORMAL";
5115 case FIELD: return "FIELD";
5116 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
5117 case CALLBACKS: return "CALLBACKS";
5118 case INTERCEPTOR: return "INTERCEPTOR";
5119 case MAP_TRANSITION: return "MAP_TRANSITION";
5120 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
5121 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
5122 }
5123 UNREACHABLE();
5124 return NULL;
5125}
5126
5127void Code::Disassemble(const char* name) {
5128 PrintF("kind = %s\n", Kind2String(kind()));
5129 if (is_inline_cache_stub()) {
5130 PrintF("ic_state = %s\n", ICState2String(ic_state()));
5131 PrintF("ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
5132 if (ic_state() == MONOMORPHIC) {
5133 PrintF("type = %s\n", PropertyType2String(type()));
5134 }
5135 }
5136 if ((name != NULL) && (name[0] != '\0')) {
5137 PrintF("name = %s\n", name);
5138 }
5139
5140 PrintF("Instructions (size = %d)\n", instruction_size());
5141 Disassembler::Decode(NULL, this);
5142 PrintF("\n");
5143
5144 PrintF("RelocInfo (size = %d)\n", relocation_size());
5145 for (RelocIterator it(this); !it.done(); it.next())
5146 it.rinfo()->Print();
5147 PrintF("\n");
5148}
5149#endif // ENABLE_DISASSEMBLER
5150
5151
5152void JSObject::SetFastElements(FixedArray* elems) {
Steve Block3ce2e202009-11-05 08:53:23 +00005153 // We should never end in here with a pixel or external array.
5154 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00005155#ifdef DEBUG
5156 // Check the provided array is filled with the_hole.
5157 uint32_t len = static_cast<uint32_t>(elems->length());
5158 for (uint32_t i = 0; i < len; i++) ASSERT(elems->get(i)->IsTheHole());
5159#endif
5160 WriteBarrierMode mode = elems->GetWriteBarrierMode();
5161 switch (GetElementsKind()) {
5162 case FAST_ELEMENTS: {
5163 FixedArray* old_elements = FixedArray::cast(elements());
5164 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
5165 // Fill out the new array with this content and array holes.
5166 for (uint32_t i = 0; i < old_length; i++) {
5167 elems->set(i, old_elements->get(i), mode);
5168 }
5169 break;
5170 }
5171 case DICTIONARY_ELEMENTS: {
5172 NumberDictionary* dictionary = NumberDictionary::cast(elements());
5173 for (int i = 0; i < dictionary->Capacity(); i++) {
5174 Object* key = dictionary->KeyAt(i);
5175 if (key->IsNumber()) {
5176 uint32_t entry = static_cast<uint32_t>(key->Number());
5177 elems->set(entry, dictionary->ValueAt(i), mode);
5178 }
5179 }
5180 break;
5181 }
5182 default:
5183 UNREACHABLE();
5184 break;
5185 }
5186 set_elements(elems);
5187}
5188
5189
5190Object* JSObject::SetSlowElements(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00005191 // We should never end in here with a pixel or external array.
5192 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00005193
5194 uint32_t new_length = static_cast<uint32_t>(len->Number());
5195
5196 switch (GetElementsKind()) {
5197 case FAST_ELEMENTS: {
5198 // Make sure we never try to shrink dense arrays into sparse arrays.
5199 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
5200 new_length);
5201 Object* obj = NormalizeElements();
5202 if (obj->IsFailure()) return obj;
5203
5204 // Update length for JSArrays.
5205 if (IsJSArray()) JSArray::cast(this)->set_length(len);
5206 break;
5207 }
5208 case DICTIONARY_ELEMENTS: {
5209 if (IsJSArray()) {
5210 uint32_t old_length =
5211 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
5212 element_dictionary()->RemoveNumberEntries(new_length, old_length),
5213 JSArray::cast(this)->set_length(len);
5214 }
5215 break;
5216 }
5217 default:
5218 UNREACHABLE();
5219 break;
5220 }
5221 return this;
5222}
5223
5224
5225Object* JSArray::Initialize(int capacity) {
5226 ASSERT(capacity >= 0);
5227 set_length(Smi::FromInt(0), SKIP_WRITE_BARRIER);
5228 FixedArray* new_elements;
5229 if (capacity == 0) {
5230 new_elements = Heap::empty_fixed_array();
5231 } else {
5232 Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
5233 if (obj->IsFailure()) return obj;
5234 new_elements = FixedArray::cast(obj);
5235 }
5236 set_elements(new_elements);
5237 return this;
5238}
5239
5240
5241void JSArray::Expand(int required_size) {
5242 Handle<JSArray> self(this);
5243 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
5244 int old_size = old_backing->length();
Steve Blockd0582a62009-12-15 09:54:21 +00005245 int new_size = required_size > old_size ? required_size : old_size;
Steve Blocka7e24c12009-10-30 11:49:00 +00005246 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
5247 // Can't use this any more now because we may have had a GC!
5248 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
5249 self->SetContent(*new_backing);
5250}
5251
5252
5253// Computes the new capacity when expanding the elements of a JSObject.
5254static int NewElementsCapacity(int old_capacity) {
5255 // (old_capacity + 50%) + 16
5256 return old_capacity + (old_capacity >> 1) + 16;
5257}
5258
5259
5260static Object* ArrayLengthRangeError() {
5261 HandleScope scope;
5262 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
5263 HandleVector<Object>(NULL, 0)));
5264}
5265
5266
5267Object* JSObject::SetElementsLength(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00005268 // We should never end in here with a pixel or external array.
5269 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00005270
5271 Object* smi_length = len->ToSmi();
5272 if (smi_length->IsSmi()) {
5273 int value = Smi::cast(smi_length)->value();
5274 if (value < 0) return ArrayLengthRangeError();
5275 switch (GetElementsKind()) {
5276 case FAST_ELEMENTS: {
5277 int old_capacity = FixedArray::cast(elements())->length();
5278 if (value <= old_capacity) {
5279 if (IsJSArray()) {
5280 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
5281 // NOTE: We may be able to optimize this by removing the
5282 // last part of the elements backing storage array and
5283 // setting the capacity to the new size.
5284 for (int i = value; i < old_length; i++) {
5285 FixedArray::cast(elements())->set_the_hole(i);
5286 }
5287 JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
5288 }
5289 return this;
5290 }
5291 int min = NewElementsCapacity(old_capacity);
5292 int new_capacity = value > min ? value : min;
5293 if (new_capacity <= kMaxFastElementsLength ||
5294 !ShouldConvertToSlowElements(new_capacity)) {
5295 Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
5296 if (obj->IsFailure()) return obj;
5297 if (IsJSArray()) JSArray::cast(this)->set_length(smi_length,
5298 SKIP_WRITE_BARRIER);
5299 SetFastElements(FixedArray::cast(obj));
5300 return this;
5301 }
5302 break;
5303 }
5304 case DICTIONARY_ELEMENTS: {
5305 if (IsJSArray()) {
5306 if (value == 0) {
5307 // If the length of a slow array is reset to zero, we clear
5308 // the array and flush backing storage. This has the added
5309 // benefit that the array returns to fast mode.
5310 initialize_elements();
5311 } else {
5312 // Remove deleted elements.
5313 uint32_t old_length =
5314 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
5315 element_dictionary()->RemoveNumberEntries(value, old_length);
5316 }
5317 JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
5318 }
5319 return this;
5320 }
5321 default:
5322 UNREACHABLE();
5323 break;
5324 }
5325 }
5326
5327 // General slow case.
5328 if (len->IsNumber()) {
5329 uint32_t length;
5330 if (Array::IndexFromObject(len, &length)) {
5331 return SetSlowElements(len);
5332 } else {
5333 return ArrayLengthRangeError();
5334 }
5335 }
5336
5337 // len is not a number so make the array size one and
5338 // set only element to len.
5339 Object* obj = Heap::AllocateFixedArray(1);
5340 if (obj->IsFailure()) return obj;
5341 FixedArray::cast(obj)->set(0, len);
5342 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1),
5343 SKIP_WRITE_BARRIER);
5344 set_elements(FixedArray::cast(obj));
5345 return this;
5346}
5347
5348
5349bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
5350 switch (GetElementsKind()) {
5351 case FAST_ELEMENTS: {
5352 uint32_t length = IsJSArray() ?
5353 static_cast<uint32_t>
5354 (Smi::cast(JSArray::cast(this)->length())->value()) :
5355 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5356 if ((index < length) &&
5357 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
5358 return true;
5359 }
5360 break;
5361 }
5362 case PIXEL_ELEMENTS: {
5363 // TODO(iposva): Add testcase.
5364 PixelArray* pixels = PixelArray::cast(elements());
5365 if (index < static_cast<uint32_t>(pixels->length())) {
5366 return true;
5367 }
5368 break;
5369 }
Steve Block3ce2e202009-11-05 08:53:23 +00005370 case EXTERNAL_BYTE_ELEMENTS:
5371 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5372 case EXTERNAL_SHORT_ELEMENTS:
5373 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5374 case EXTERNAL_INT_ELEMENTS:
5375 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5376 case EXTERNAL_FLOAT_ELEMENTS: {
5377 // TODO(kbr): Add testcase.
5378 ExternalArray* array = ExternalArray::cast(elements());
5379 if (index < static_cast<uint32_t>(array->length())) {
5380 return true;
5381 }
5382 break;
5383 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005384 case DICTIONARY_ELEMENTS: {
5385 if (element_dictionary()->FindEntry(index)
5386 != NumberDictionary::kNotFound) {
5387 return true;
5388 }
5389 break;
5390 }
5391 default:
5392 UNREACHABLE();
5393 break;
5394 }
5395
5396 // Handle [] on String objects.
5397 if (this->IsStringObjectWithCharacterAt(index)) return true;
5398
5399 Object* pt = GetPrototype();
5400 if (pt == Heap::null_value()) return false;
5401 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5402}
5403
5404
5405bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
5406 // Make sure that the top context does not change when doing
5407 // callbacks or interceptor calls.
5408 AssertNoContextChange ncc;
5409 HandleScope scope;
5410 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5411 Handle<JSObject> receiver_handle(receiver);
5412 Handle<JSObject> holder_handle(this);
5413 CustomArguments args(interceptor->data(), receiver, this);
5414 v8::AccessorInfo info(args.end());
5415 if (!interceptor->query()->IsUndefined()) {
5416 v8::IndexedPropertyQuery query =
5417 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
5418 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
5419 v8::Handle<v8::Boolean> result;
5420 {
5421 // Leaving JavaScript.
5422 VMState state(EXTERNAL);
5423 result = query(index, info);
5424 }
5425 if (!result.IsEmpty()) return result->IsTrue();
5426 } else if (!interceptor->getter()->IsUndefined()) {
5427 v8::IndexedPropertyGetter getter =
5428 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
5429 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
5430 v8::Handle<v8::Value> result;
5431 {
5432 // Leaving JavaScript.
5433 VMState state(EXTERNAL);
5434 result = getter(index, info);
5435 }
5436 if (!result.IsEmpty()) return true;
5437 }
5438 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
5439}
5440
5441
5442bool JSObject::HasLocalElement(uint32_t index) {
5443 // Check access rights if needed.
5444 if (IsAccessCheckNeeded() &&
5445 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5446 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5447 return false;
5448 }
5449
5450 // Check for lookup interceptor
5451 if (HasIndexedInterceptor()) {
5452 return HasElementWithInterceptor(this, index);
5453 }
5454
5455 // Handle [] on String objects.
5456 if (this->IsStringObjectWithCharacterAt(index)) return true;
5457
5458 switch (GetElementsKind()) {
5459 case FAST_ELEMENTS: {
5460 uint32_t length = IsJSArray() ?
5461 static_cast<uint32_t>
5462 (Smi::cast(JSArray::cast(this)->length())->value()) :
5463 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5464 return (index < length) &&
5465 !FixedArray::cast(elements())->get(index)->IsTheHole();
5466 }
5467 case PIXEL_ELEMENTS: {
5468 PixelArray* pixels = PixelArray::cast(elements());
5469 return (index < static_cast<uint32_t>(pixels->length()));
5470 }
Steve Block3ce2e202009-11-05 08:53:23 +00005471 case EXTERNAL_BYTE_ELEMENTS:
5472 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5473 case EXTERNAL_SHORT_ELEMENTS:
5474 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5475 case EXTERNAL_INT_ELEMENTS:
5476 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5477 case EXTERNAL_FLOAT_ELEMENTS: {
5478 ExternalArray* array = ExternalArray::cast(elements());
5479 return (index < static_cast<uint32_t>(array->length()));
5480 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005481 case DICTIONARY_ELEMENTS: {
5482 return element_dictionary()->FindEntry(index)
5483 != NumberDictionary::kNotFound;
5484 }
5485 default:
5486 UNREACHABLE();
5487 break;
5488 }
5489 UNREACHABLE();
5490 return Heap::null_value();
5491}
5492
5493
5494bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
5495 // Check access rights if needed.
5496 if (IsAccessCheckNeeded() &&
5497 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5498 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5499 return false;
5500 }
5501
5502 // Check for lookup interceptor
5503 if (HasIndexedInterceptor()) {
5504 return HasElementWithInterceptor(receiver, index);
5505 }
5506
5507 switch (GetElementsKind()) {
5508 case FAST_ELEMENTS: {
5509 uint32_t length = IsJSArray() ?
5510 static_cast<uint32_t>
5511 (Smi::cast(JSArray::cast(this)->length())->value()) :
5512 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5513 if ((index < length) &&
5514 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
5515 break;
5516 }
5517 case PIXEL_ELEMENTS: {
5518 PixelArray* pixels = PixelArray::cast(elements());
5519 if (index < static_cast<uint32_t>(pixels->length())) {
5520 return true;
5521 }
5522 break;
5523 }
Steve Block3ce2e202009-11-05 08:53:23 +00005524 case EXTERNAL_BYTE_ELEMENTS:
5525 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5526 case EXTERNAL_SHORT_ELEMENTS:
5527 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5528 case EXTERNAL_INT_ELEMENTS:
5529 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5530 case EXTERNAL_FLOAT_ELEMENTS: {
5531 ExternalArray* array = ExternalArray::cast(elements());
5532 if (index < static_cast<uint32_t>(array->length())) {
5533 return true;
5534 }
5535 break;
5536 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005537 case DICTIONARY_ELEMENTS: {
5538 if (element_dictionary()->FindEntry(index)
5539 != NumberDictionary::kNotFound) {
5540 return true;
5541 }
5542 break;
5543 }
5544 default:
5545 UNREACHABLE();
5546 break;
5547 }
5548
5549 // Handle [] on String objects.
5550 if (this->IsStringObjectWithCharacterAt(index)) return true;
5551
5552 Object* pt = GetPrototype();
5553 if (pt == Heap::null_value()) return false;
5554 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5555}
5556
5557
5558Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) {
5559 // Make sure that the top context does not change when doing
5560 // callbacks or interceptor calls.
5561 AssertNoContextChange ncc;
5562 HandleScope scope;
5563 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5564 Handle<JSObject> this_handle(this);
5565 Handle<Object> value_handle(value);
5566 if (!interceptor->setter()->IsUndefined()) {
5567 v8::IndexedPropertySetter setter =
5568 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
5569 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
5570 CustomArguments args(interceptor->data(), this, this);
5571 v8::AccessorInfo info(args.end());
5572 v8::Handle<v8::Value> result;
5573 {
5574 // Leaving JavaScript.
5575 VMState state(EXTERNAL);
5576 result = setter(index, v8::Utils::ToLocal(value_handle), info);
5577 }
5578 RETURN_IF_SCHEDULED_EXCEPTION();
5579 if (!result.IsEmpty()) return *value_handle;
5580 }
5581 Object* raw_result =
5582 this_handle->SetElementWithoutInterceptor(index, *value_handle);
5583 RETURN_IF_SCHEDULED_EXCEPTION();
5584 return raw_result;
5585}
5586
5587
5588// Adding n elements in fast case is O(n*n).
5589// Note: revisit design to have dual undefined values to capture absent
5590// elements.
5591Object* JSObject::SetFastElement(uint32_t index, Object* value) {
5592 ASSERT(HasFastElements());
5593
5594 FixedArray* elms = FixedArray::cast(elements());
5595 uint32_t elms_length = static_cast<uint32_t>(elms->length());
5596
5597 if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
5598 Object* setter = LookupCallbackSetterInPrototypes(index);
5599 if (setter->IsJSFunction()) {
5600 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
5601 }
5602 }
5603
5604 // Check whether there is extra space in fixed array..
5605 if (index < elms_length) {
5606 elms->set(index, value);
5607 if (IsJSArray()) {
5608 // Update the length of the array if needed.
5609 uint32_t array_length = 0;
5610 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(),
5611 &array_length));
5612 if (index >= array_length) {
5613 JSArray::cast(this)->set_length(Smi::FromInt(index + 1),
5614 SKIP_WRITE_BARRIER);
5615 }
5616 }
5617 return value;
5618 }
5619
5620 // Allow gap in fast case.
5621 if ((index - elms_length) < kMaxGap) {
5622 // Try allocating extra space.
5623 int new_capacity = NewElementsCapacity(index+1);
5624 if (new_capacity <= kMaxFastElementsLength ||
5625 !ShouldConvertToSlowElements(new_capacity)) {
5626 ASSERT(static_cast<uint32_t>(new_capacity) > index);
5627 Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
5628 if (obj->IsFailure()) return obj;
5629 SetFastElements(FixedArray::cast(obj));
5630 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(index + 1),
5631 SKIP_WRITE_BARRIER);
5632 FixedArray::cast(elements())->set(index, value);
5633 return value;
5634 }
5635 }
5636
5637 // Otherwise default to slow case.
5638 Object* obj = NormalizeElements();
5639 if (obj->IsFailure()) return obj;
5640 ASSERT(HasDictionaryElements());
5641 return SetElement(index, value);
5642}
5643
5644Object* JSObject::SetElement(uint32_t index, Object* value) {
5645 // Check access rights if needed.
5646 if (IsAccessCheckNeeded() &&
5647 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
5648 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
5649 return value;
5650 }
5651
5652 if (IsJSGlobalProxy()) {
5653 Object* proto = GetPrototype();
5654 if (proto->IsNull()) return value;
5655 ASSERT(proto->IsJSGlobalObject());
5656 return JSObject::cast(proto)->SetElement(index, value);
5657 }
5658
5659 // Check for lookup interceptor
5660 if (HasIndexedInterceptor()) {
5661 return SetElementWithInterceptor(index, value);
5662 }
5663
5664 return SetElementWithoutInterceptor(index, value);
5665}
5666
5667
5668Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
5669 switch (GetElementsKind()) {
5670 case FAST_ELEMENTS:
5671 // Fast case.
5672 return SetFastElement(index, value);
5673 case PIXEL_ELEMENTS: {
5674 PixelArray* pixels = PixelArray::cast(elements());
5675 return pixels->SetValue(index, value);
5676 }
Steve Block3ce2e202009-11-05 08:53:23 +00005677 case EXTERNAL_BYTE_ELEMENTS: {
5678 ExternalByteArray* array = ExternalByteArray::cast(elements());
5679 return array->SetValue(index, value);
5680 }
5681 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5682 ExternalUnsignedByteArray* array =
5683 ExternalUnsignedByteArray::cast(elements());
5684 return array->SetValue(index, value);
5685 }
5686 case EXTERNAL_SHORT_ELEMENTS: {
5687 ExternalShortArray* array = ExternalShortArray::cast(elements());
5688 return array->SetValue(index, value);
5689 }
5690 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5691 ExternalUnsignedShortArray* array =
5692 ExternalUnsignedShortArray::cast(elements());
5693 return array->SetValue(index, value);
5694 }
5695 case EXTERNAL_INT_ELEMENTS: {
5696 ExternalIntArray* array = ExternalIntArray::cast(elements());
5697 return array->SetValue(index, value);
5698 }
5699 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5700 ExternalUnsignedIntArray* array =
5701 ExternalUnsignedIntArray::cast(elements());
5702 return array->SetValue(index, value);
5703 }
5704 case EXTERNAL_FLOAT_ELEMENTS: {
5705 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
5706 return array->SetValue(index, value);
5707 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005708 case DICTIONARY_ELEMENTS: {
5709 // Insert element in the dictionary.
5710 FixedArray* elms = FixedArray::cast(elements());
5711 NumberDictionary* dictionary = NumberDictionary::cast(elms);
5712
5713 int entry = dictionary->FindEntry(index);
5714 if (entry != NumberDictionary::kNotFound) {
5715 Object* element = dictionary->ValueAt(entry);
5716 PropertyDetails details = dictionary->DetailsAt(entry);
5717 if (details.type() == CALLBACKS) {
5718 // Only accessors allowed as elements.
5719 FixedArray* structure = FixedArray::cast(element);
5720 if (structure->get(kSetterIndex)->IsJSFunction()) {
5721 JSFunction* setter = JSFunction::cast(structure->get(kSetterIndex));
5722 return SetPropertyWithDefinedSetter(setter, value);
5723 } else {
5724 Handle<Object> self(this);
5725 Handle<Object> key(Factory::NewNumberFromUint(index));
5726 Handle<Object> args[2] = { key, self };
5727 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
5728 HandleVector(args, 2)));
5729 }
5730 } else {
5731 dictionary->UpdateMaxNumberKey(index);
5732 dictionary->ValueAtPut(entry, value);
5733 }
5734 } else {
5735 // Index not already used. Look for an accessor in the prototype chain.
5736 if (!IsJSArray()) {
5737 Object* setter = LookupCallbackSetterInPrototypes(index);
5738 if (setter->IsJSFunction()) {
5739 return SetPropertyWithDefinedSetter(JSFunction::cast(setter),
5740 value);
5741 }
5742 }
5743 Object* result = dictionary->AtNumberPut(index, value);
5744 if (result->IsFailure()) return result;
5745 if (elms != FixedArray::cast(result)) {
5746 set_elements(FixedArray::cast(result));
5747 }
5748 }
5749
5750 // Update the array length if this JSObject is an array.
5751 if (IsJSArray()) {
5752 JSArray* array = JSArray::cast(this);
5753 Object* return_value = array->JSArrayUpdateLengthFromIndex(index,
5754 value);
5755 if (return_value->IsFailure()) return return_value;
5756 }
5757
5758 // Attempt to put this object back in fast case.
5759 if (ShouldConvertToFastElements()) {
5760 uint32_t new_length = 0;
5761 if (IsJSArray()) {
5762 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(),
5763 &new_length));
5764 JSArray::cast(this)->set_length(Smi::FromInt(new_length));
5765 } else {
5766 new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
5767 }
5768 Object* obj = Heap::AllocateFixedArrayWithHoles(new_length);
5769 if (obj->IsFailure()) return obj;
5770 SetFastElements(FixedArray::cast(obj));
5771#ifdef DEBUG
5772 if (FLAG_trace_normalization) {
5773 PrintF("Object elements are fast case again:\n");
5774 Print();
5775 }
5776#endif
5777 }
5778
5779 return value;
5780 }
5781 default:
5782 UNREACHABLE();
5783 break;
5784 }
5785 // All possible cases have been handled above. Add a return to avoid the
5786 // complaints from the compiler.
5787 UNREACHABLE();
5788 return Heap::null_value();
5789}
5790
5791
5792Object* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index, Object* value) {
5793 uint32_t old_len = 0;
5794 CHECK(Array::IndexFromObject(length(), &old_len));
5795 // Check to see if we need to update the length. For now, we make
5796 // sure that the length stays within 32-bits (unsigned).
5797 if (index >= old_len && index != 0xffffffff) {
5798 Object* len =
5799 Heap::NumberFromDouble(static_cast<double>(index) + 1);
5800 if (len->IsFailure()) return len;
5801 set_length(len);
5802 }
5803 return value;
5804}
5805
5806
5807Object* JSObject::GetElementPostInterceptor(JSObject* receiver,
5808 uint32_t index) {
5809 // Get element works for both JSObject and JSArray since
5810 // JSArray::length cannot change.
5811 switch (GetElementsKind()) {
5812 case FAST_ELEMENTS: {
5813 FixedArray* elms = FixedArray::cast(elements());
5814 if (index < static_cast<uint32_t>(elms->length())) {
5815 Object* value = elms->get(index);
5816 if (!value->IsTheHole()) return value;
5817 }
5818 break;
5819 }
5820 case PIXEL_ELEMENTS: {
5821 // TODO(iposva): Add testcase and implement.
5822 UNIMPLEMENTED();
5823 break;
5824 }
Steve Block3ce2e202009-11-05 08:53:23 +00005825 case EXTERNAL_BYTE_ELEMENTS:
5826 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5827 case EXTERNAL_SHORT_ELEMENTS:
5828 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5829 case EXTERNAL_INT_ELEMENTS:
5830 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5831 case EXTERNAL_FLOAT_ELEMENTS: {
5832 // TODO(kbr): Add testcase and implement.
5833 UNIMPLEMENTED();
5834 break;
5835 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005836 case DICTIONARY_ELEMENTS: {
5837 NumberDictionary* dictionary = element_dictionary();
5838 int entry = dictionary->FindEntry(index);
5839 if (entry != NumberDictionary::kNotFound) {
5840 Object* element = dictionary->ValueAt(entry);
5841 PropertyDetails details = dictionary->DetailsAt(entry);
5842 if (details.type() == CALLBACKS) {
5843 // Only accessors allowed as elements.
5844 FixedArray* structure = FixedArray::cast(element);
5845 Object* getter = structure->get(kGetterIndex);
5846 if (getter->IsJSFunction()) {
5847 return GetPropertyWithDefinedGetter(receiver,
5848 JSFunction::cast(getter));
5849 } else {
5850 // Getter is not a function.
5851 return Heap::undefined_value();
5852 }
5853 }
5854 return element;
5855 }
5856 break;
5857 }
5858 default:
5859 UNREACHABLE();
5860 break;
5861 }
5862
5863 // Continue searching via the prototype chain.
5864 Object* pt = GetPrototype();
5865 if (pt == Heap::null_value()) return Heap::undefined_value();
5866 return pt->GetElementWithReceiver(receiver, index);
5867}
5868
5869
5870Object* JSObject::GetElementWithInterceptor(JSObject* receiver,
5871 uint32_t index) {
5872 // Make sure that the top context does not change when doing
5873 // callbacks or interceptor calls.
5874 AssertNoContextChange ncc;
5875 HandleScope scope;
5876 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5877 Handle<JSObject> this_handle(receiver);
5878 Handle<JSObject> holder_handle(this);
5879
5880 if (!interceptor->getter()->IsUndefined()) {
5881 v8::IndexedPropertyGetter getter =
5882 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
5883 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
5884 CustomArguments args(interceptor->data(), receiver, this);
5885 v8::AccessorInfo info(args.end());
5886 v8::Handle<v8::Value> result;
5887 {
5888 // Leaving JavaScript.
5889 VMState state(EXTERNAL);
5890 result = getter(index, info);
5891 }
5892 RETURN_IF_SCHEDULED_EXCEPTION();
5893 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5894 }
5895
5896 Object* raw_result =
5897 holder_handle->GetElementPostInterceptor(*this_handle, index);
5898 RETURN_IF_SCHEDULED_EXCEPTION();
5899 return raw_result;
5900}
5901
5902
5903Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) {
5904 // Check access rights if needed.
5905 if (IsAccessCheckNeeded() &&
5906 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
5907 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
5908 return Heap::undefined_value();
5909 }
5910
5911 if (HasIndexedInterceptor()) {
5912 return GetElementWithInterceptor(receiver, index);
5913 }
5914
5915 // Get element works for both JSObject and JSArray since
5916 // JSArray::length cannot change.
5917 switch (GetElementsKind()) {
5918 case FAST_ELEMENTS: {
5919 FixedArray* elms = FixedArray::cast(elements());
5920 if (index < static_cast<uint32_t>(elms->length())) {
5921 Object* value = elms->get(index);
5922 if (!value->IsTheHole()) return value;
5923 }
5924 break;
5925 }
5926 case PIXEL_ELEMENTS: {
5927 PixelArray* pixels = PixelArray::cast(elements());
5928 if (index < static_cast<uint32_t>(pixels->length())) {
5929 uint8_t value = pixels->get(index);
5930 return Smi::FromInt(value);
5931 }
5932 break;
5933 }
Steve Block3ce2e202009-11-05 08:53:23 +00005934 case EXTERNAL_BYTE_ELEMENTS: {
5935 ExternalByteArray* array = ExternalByteArray::cast(elements());
5936 if (index < static_cast<uint32_t>(array->length())) {
5937 int8_t value = array->get(index);
5938 return Smi::FromInt(value);
5939 }
5940 break;
5941 }
5942 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5943 ExternalUnsignedByteArray* array =
5944 ExternalUnsignedByteArray::cast(elements());
5945 if (index < static_cast<uint32_t>(array->length())) {
5946 uint8_t value = array->get(index);
5947 return Smi::FromInt(value);
5948 }
5949 break;
5950 }
5951 case EXTERNAL_SHORT_ELEMENTS: {
5952 ExternalShortArray* array = ExternalShortArray::cast(elements());
5953 if (index < static_cast<uint32_t>(array->length())) {
5954 int16_t value = array->get(index);
5955 return Smi::FromInt(value);
5956 }
5957 break;
5958 }
5959 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5960 ExternalUnsignedShortArray* array =
5961 ExternalUnsignedShortArray::cast(elements());
5962 if (index < static_cast<uint32_t>(array->length())) {
5963 uint16_t value = array->get(index);
5964 return Smi::FromInt(value);
5965 }
5966 break;
5967 }
5968 case EXTERNAL_INT_ELEMENTS: {
5969 ExternalIntArray* array = ExternalIntArray::cast(elements());
5970 if (index < static_cast<uint32_t>(array->length())) {
5971 int32_t value = array->get(index);
5972 return Heap::NumberFromInt32(value);
5973 }
5974 break;
5975 }
5976 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5977 ExternalUnsignedIntArray* array =
5978 ExternalUnsignedIntArray::cast(elements());
5979 if (index < static_cast<uint32_t>(array->length())) {
5980 uint32_t value = array->get(index);
5981 return Heap::NumberFromUint32(value);
5982 }
5983 break;
5984 }
5985 case EXTERNAL_FLOAT_ELEMENTS: {
5986 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
5987 if (index < static_cast<uint32_t>(array->length())) {
5988 float value = array->get(index);
5989 return Heap::AllocateHeapNumber(value);
5990 }
5991 break;
5992 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005993 case DICTIONARY_ELEMENTS: {
5994 NumberDictionary* dictionary = element_dictionary();
5995 int entry = dictionary->FindEntry(index);
5996 if (entry != NumberDictionary::kNotFound) {
5997 Object* element = dictionary->ValueAt(entry);
5998 PropertyDetails details = dictionary->DetailsAt(entry);
5999 if (details.type() == CALLBACKS) {
6000 // Only accessors allowed as elements.
6001 FixedArray* structure = FixedArray::cast(element);
6002 Object* getter = structure->get(kGetterIndex);
6003 if (getter->IsJSFunction()) {
6004 return GetPropertyWithDefinedGetter(receiver,
6005 JSFunction::cast(getter));
6006 } else {
6007 // Getter is not a function.
6008 return Heap::undefined_value();
6009 }
6010 }
6011 return element;
6012 }
6013 break;
6014 }
6015 }
6016
6017 Object* pt = GetPrototype();
6018 if (pt == Heap::null_value()) return Heap::undefined_value();
6019 return pt->GetElementWithReceiver(receiver, index);
6020}
6021
6022
6023bool JSObject::HasDenseElements() {
6024 int capacity = 0;
6025 int number_of_elements = 0;
6026
6027 switch (GetElementsKind()) {
6028 case FAST_ELEMENTS: {
6029 FixedArray* elms = FixedArray::cast(elements());
6030 capacity = elms->length();
6031 for (int i = 0; i < capacity; i++) {
6032 if (!elms->get(i)->IsTheHole()) number_of_elements++;
6033 }
6034 break;
6035 }
Steve Block3ce2e202009-11-05 08:53:23 +00006036 case PIXEL_ELEMENTS:
6037 case EXTERNAL_BYTE_ELEMENTS:
6038 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6039 case EXTERNAL_SHORT_ELEMENTS:
6040 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6041 case EXTERNAL_INT_ELEMENTS:
6042 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6043 case EXTERNAL_FLOAT_ELEMENTS: {
Steve Blocka7e24c12009-10-30 11:49:00 +00006044 return true;
6045 }
6046 case DICTIONARY_ELEMENTS: {
6047 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6048 capacity = dictionary->Capacity();
6049 number_of_elements = dictionary->NumberOfElements();
6050 break;
6051 }
6052 default:
6053 UNREACHABLE();
6054 break;
6055 }
6056
6057 if (capacity == 0) return true;
6058 return (number_of_elements > (capacity / 2));
6059}
6060
6061
6062bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
6063 ASSERT(HasFastElements());
6064 // Keep the array in fast case if the current backing storage is
6065 // almost filled and if the new capacity is no more than twice the
6066 // old capacity.
6067 int elements_length = FixedArray::cast(elements())->length();
6068 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
6069}
6070
6071
6072bool JSObject::ShouldConvertToFastElements() {
6073 ASSERT(HasDictionaryElements());
6074 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6075 // If the elements are sparse, we should not go back to fast case.
6076 if (!HasDenseElements()) return false;
6077 // If an element has been added at a very high index in the elements
6078 // dictionary, we cannot go back to fast case.
6079 if (dictionary->requires_slow_elements()) return false;
6080 // An object requiring access checks is never allowed to have fast
6081 // elements. If it had fast elements we would skip security checks.
6082 if (IsAccessCheckNeeded()) return false;
6083 // If the dictionary backing storage takes up roughly half as much
6084 // space as a fast-case backing storage would the array should have
6085 // fast elements.
6086 uint32_t length = 0;
6087 if (IsJSArray()) {
6088 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), &length));
6089 } else {
6090 length = dictionary->max_number_key();
6091 }
6092 return static_cast<uint32_t>(dictionary->Capacity()) >=
6093 (length / (2 * NumberDictionary::kEntrySize));
6094}
6095
6096
6097// Certain compilers request function template instantiation when they
6098// see the definition of the other template functions in the
6099// class. This requires us to have the template functions put
6100// together, so even though this function belongs in objects-debug.cc,
6101// we keep it here instead to satisfy certain compilers.
6102#ifdef DEBUG
6103template<typename Shape, typename Key>
6104void Dictionary<Shape, Key>::Print() {
6105 int capacity = HashTable<Shape, Key>::Capacity();
6106 for (int i = 0; i < capacity; i++) {
6107 Object* k = HashTable<Shape, Key>::KeyAt(i);
6108 if (HashTable<Shape, Key>::IsKey(k)) {
6109 PrintF(" ");
6110 if (k->IsString()) {
6111 String::cast(k)->StringPrint();
6112 } else {
6113 k->ShortPrint();
6114 }
6115 PrintF(": ");
6116 ValueAt(i)->ShortPrint();
6117 PrintF("\n");
6118 }
6119 }
6120}
6121#endif
6122
6123
6124template<typename Shape, typename Key>
6125void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
6126 int pos = 0;
6127 int capacity = HashTable<Shape, Key>::Capacity();
6128 WriteBarrierMode mode = elements->GetWriteBarrierMode();
6129 for (int i = 0; i < capacity; i++) {
6130 Object* k = Dictionary<Shape, Key>::KeyAt(i);
6131 if (Dictionary<Shape, Key>::IsKey(k)) {
6132 elements->set(pos++, ValueAt(i), mode);
6133 }
6134 }
6135 ASSERT(pos == elements->length());
6136}
6137
6138
6139InterceptorInfo* JSObject::GetNamedInterceptor() {
6140 ASSERT(map()->has_named_interceptor());
6141 JSFunction* constructor = JSFunction::cast(map()->constructor());
6142 Object* template_info = constructor->shared()->function_data();
6143 Object* result =
6144 FunctionTemplateInfo::cast(template_info)->named_property_handler();
6145 return InterceptorInfo::cast(result);
6146}
6147
6148
6149InterceptorInfo* JSObject::GetIndexedInterceptor() {
6150 ASSERT(map()->has_indexed_interceptor());
6151 JSFunction* constructor = JSFunction::cast(map()->constructor());
6152 Object* template_info = constructor->shared()->function_data();
6153 Object* result =
6154 FunctionTemplateInfo::cast(template_info)->indexed_property_handler();
6155 return InterceptorInfo::cast(result);
6156}
6157
6158
6159Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver,
6160 String* name,
6161 PropertyAttributes* attributes) {
6162 // Check local property in holder, ignore interceptor.
6163 LookupResult result;
6164 LocalLookupRealNamedProperty(name, &result);
6165 if (result.IsValid()) return GetProperty(receiver, &result, name, attributes);
6166 // Continue searching via the prototype chain.
6167 Object* pt = GetPrototype();
6168 *attributes = ABSENT;
6169 if (pt == Heap::null_value()) return Heap::undefined_value();
6170 return pt->GetPropertyWithReceiver(receiver, name, attributes);
6171}
6172
6173
Steve Blockd0582a62009-12-15 09:54:21 +00006174Object* JSObject::GetLocalPropertyPostInterceptor(
6175 JSObject* receiver,
6176 String* name,
6177 PropertyAttributes* attributes) {
6178 // Check local property in holder, ignore interceptor.
6179 LookupResult result;
6180 LocalLookupRealNamedProperty(name, &result);
6181 if (!result.IsValid()) return Heap::undefined_value();
6182 return GetProperty(receiver, &result, name, attributes);
6183}
6184
6185
Steve Blocka7e24c12009-10-30 11:49:00 +00006186Object* JSObject::GetPropertyWithInterceptor(
6187 JSObject* receiver,
6188 String* name,
6189 PropertyAttributes* attributes) {
6190 InterceptorInfo* interceptor = GetNamedInterceptor();
6191 HandleScope scope;
6192 Handle<JSObject> receiver_handle(receiver);
6193 Handle<JSObject> holder_handle(this);
6194 Handle<String> name_handle(name);
6195
6196 if (!interceptor->getter()->IsUndefined()) {
6197 v8::NamedPropertyGetter getter =
6198 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
6199 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
6200 CustomArguments args(interceptor->data(), receiver, this);
6201 v8::AccessorInfo info(args.end());
6202 v8::Handle<v8::Value> result;
6203 {
6204 // Leaving JavaScript.
6205 VMState state(EXTERNAL);
6206 result = getter(v8::Utils::ToLocal(name_handle), info);
6207 }
6208 RETURN_IF_SCHEDULED_EXCEPTION();
6209 if (!result.IsEmpty()) {
6210 *attributes = NONE;
6211 return *v8::Utils::OpenHandle(*result);
6212 }
6213 }
6214
6215 Object* result = holder_handle->GetPropertyPostInterceptor(
6216 *receiver_handle,
6217 *name_handle,
6218 attributes);
6219 RETURN_IF_SCHEDULED_EXCEPTION();
6220 return result;
6221}
6222
6223
6224bool JSObject::HasRealNamedProperty(String* key) {
6225 // Check access rights if needed.
6226 if (IsAccessCheckNeeded() &&
6227 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
6228 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6229 return false;
6230 }
6231
6232 LookupResult result;
6233 LocalLookupRealNamedProperty(key, &result);
6234 if (result.IsValid()) {
6235 switch (result.type()) {
6236 case NORMAL: // fall through.
6237 case FIELD: // fall through.
6238 case CALLBACKS: // fall through.
6239 case CONSTANT_FUNCTION:
6240 return true;
6241 case INTERCEPTOR:
6242 case MAP_TRANSITION:
6243 case CONSTANT_TRANSITION:
6244 case NULL_DESCRIPTOR:
6245 return false;
6246 default:
6247 UNREACHABLE();
6248 }
6249 }
6250
6251 return false;
6252}
6253
6254
6255bool JSObject::HasRealElementProperty(uint32_t index) {
6256 // Check access rights if needed.
6257 if (IsAccessCheckNeeded() &&
6258 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6259 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6260 return false;
6261 }
6262
6263 // Handle [] on String objects.
6264 if (this->IsStringObjectWithCharacterAt(index)) return true;
6265
6266 switch (GetElementsKind()) {
6267 case FAST_ELEMENTS: {
6268 uint32_t length = IsJSArray() ?
6269 static_cast<uint32_t>(
6270 Smi::cast(JSArray::cast(this)->length())->value()) :
6271 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6272 return (index < length) &&
6273 !FixedArray::cast(elements())->get(index)->IsTheHole();
6274 }
6275 case PIXEL_ELEMENTS: {
6276 PixelArray* pixels = PixelArray::cast(elements());
6277 return index < static_cast<uint32_t>(pixels->length());
6278 }
Steve Block3ce2e202009-11-05 08:53:23 +00006279 case EXTERNAL_BYTE_ELEMENTS:
6280 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6281 case EXTERNAL_SHORT_ELEMENTS:
6282 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6283 case EXTERNAL_INT_ELEMENTS:
6284 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6285 case EXTERNAL_FLOAT_ELEMENTS: {
6286 ExternalArray* array = ExternalArray::cast(elements());
6287 return index < static_cast<uint32_t>(array->length());
6288 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006289 case DICTIONARY_ELEMENTS: {
6290 return element_dictionary()->FindEntry(index)
6291 != NumberDictionary::kNotFound;
6292 }
6293 default:
6294 UNREACHABLE();
6295 break;
6296 }
6297 // All possibilities have been handled above already.
6298 UNREACHABLE();
6299 return Heap::null_value();
6300}
6301
6302
6303bool JSObject::HasRealNamedCallbackProperty(String* key) {
6304 // Check access rights if needed.
6305 if (IsAccessCheckNeeded() &&
6306 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
6307 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6308 return false;
6309 }
6310
6311 LookupResult result;
6312 LocalLookupRealNamedProperty(key, &result);
6313 return result.IsValid() && (result.type() == CALLBACKS);
6314}
6315
6316
6317int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
6318 if (HasFastProperties()) {
6319 DescriptorArray* descs = map()->instance_descriptors();
6320 int result = 0;
6321 for (int i = 0; i < descs->number_of_descriptors(); i++) {
6322 PropertyDetails details = descs->GetDetails(i);
6323 if (details.IsProperty() && (details.attributes() & filter) == 0) {
6324 result++;
6325 }
6326 }
6327 return result;
6328 } else {
6329 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
6330 }
6331}
6332
6333
6334int JSObject::NumberOfEnumProperties() {
6335 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
6336}
6337
6338
6339void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
6340 Object* temp = get(i);
6341 set(i, get(j));
6342 set(j, temp);
6343 if (this != numbers) {
6344 temp = numbers->get(i);
6345 numbers->set(i, numbers->get(j));
6346 numbers->set(j, temp);
6347 }
6348}
6349
6350
6351static void InsertionSortPairs(FixedArray* content,
6352 FixedArray* numbers,
6353 int len) {
6354 for (int i = 1; i < len; i++) {
6355 int j = i;
6356 while (j > 0 &&
6357 (NumberToUint32(numbers->get(j - 1)) >
6358 NumberToUint32(numbers->get(j)))) {
6359 content->SwapPairs(numbers, j - 1, j);
6360 j--;
6361 }
6362 }
6363}
6364
6365
6366void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
6367 // In-place heap sort.
6368 ASSERT(content->length() == numbers->length());
6369
6370 // Bottom-up max-heap construction.
6371 for (int i = 1; i < len; ++i) {
6372 int child_index = i;
6373 while (child_index > 0) {
6374 int parent_index = ((child_index + 1) >> 1) - 1;
6375 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
6376 uint32_t child_value = NumberToUint32(numbers->get(child_index));
6377 if (parent_value < child_value) {
6378 content->SwapPairs(numbers, parent_index, child_index);
6379 } else {
6380 break;
6381 }
6382 child_index = parent_index;
6383 }
6384 }
6385
6386 // Extract elements and create sorted array.
6387 for (int i = len - 1; i > 0; --i) {
6388 // Put max element at the back of the array.
6389 content->SwapPairs(numbers, 0, i);
6390 // Sift down the new top element.
6391 int parent_index = 0;
6392 while (true) {
6393 int child_index = ((parent_index + 1) << 1) - 1;
6394 if (child_index >= i) break;
6395 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
6396 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
6397 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
6398 if (child_index + 1 >= i || child1_value > child2_value) {
6399 if (parent_value > child1_value) break;
6400 content->SwapPairs(numbers, parent_index, child_index);
6401 parent_index = child_index;
6402 } else {
6403 if (parent_value > child2_value) break;
6404 content->SwapPairs(numbers, parent_index, child_index + 1);
6405 parent_index = child_index + 1;
6406 }
6407 }
6408 }
6409}
6410
6411
6412// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
6413void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
6414 ASSERT(this->length() == numbers->length());
6415 // For small arrays, simply use insertion sort.
6416 if (len <= 10) {
6417 InsertionSortPairs(this, numbers, len);
6418 return;
6419 }
6420 // Check the range of indices.
6421 uint32_t min_index = NumberToUint32(numbers->get(0));
6422 uint32_t max_index = min_index;
6423 uint32_t i;
6424 for (i = 1; i < len; i++) {
6425 if (NumberToUint32(numbers->get(i)) < min_index) {
6426 min_index = NumberToUint32(numbers->get(i));
6427 } else if (NumberToUint32(numbers->get(i)) > max_index) {
6428 max_index = NumberToUint32(numbers->get(i));
6429 }
6430 }
6431 if (max_index - min_index + 1 == len) {
6432 // Indices form a contiguous range, unless there are duplicates.
6433 // Do an in-place linear time sort assuming distinct numbers, but
6434 // avoid hanging in case they are not.
6435 for (i = 0; i < len; i++) {
6436 uint32_t p;
6437 uint32_t j = 0;
6438 // While the current element at i is not at its correct position p,
6439 // swap the elements at these two positions.
6440 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
6441 j++ < len) {
6442 SwapPairs(numbers, i, p);
6443 }
6444 }
6445 } else {
6446 HeapSortPairs(this, numbers, len);
6447 return;
6448 }
6449}
6450
6451
6452// Fill in the names of local properties into the supplied storage. The main
6453// purpose of this function is to provide reflection information for the object
6454// mirrors.
6455void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
6456 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
6457 if (HasFastProperties()) {
6458 DescriptorArray* descs = map()->instance_descriptors();
6459 for (int i = 0; i < descs->number_of_descriptors(); i++) {
6460 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
6461 }
6462 ASSERT(storage->length() >= index);
6463 } else {
6464 property_dictionary()->CopyKeysTo(storage);
6465 }
6466}
6467
6468
6469int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
6470 return GetLocalElementKeys(NULL, filter);
6471}
6472
6473
6474int JSObject::NumberOfEnumElements() {
Steve Blockd0582a62009-12-15 09:54:21 +00006475 // Fast case for objects with no elements.
6476 if (!IsJSValue() && HasFastElements()) {
6477 uint32_t length = IsJSArray() ?
6478 static_cast<uint32_t>(
6479 Smi::cast(JSArray::cast(this)->length())->value()) :
6480 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6481 if (length == 0) return 0;
6482 }
6483 // Compute the number of enumerable elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00006484 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
6485}
6486
6487
6488int JSObject::GetLocalElementKeys(FixedArray* storage,
6489 PropertyAttributes filter) {
6490 int counter = 0;
6491 switch (GetElementsKind()) {
6492 case FAST_ELEMENTS: {
6493 int length = IsJSArray() ?
6494 Smi::cast(JSArray::cast(this)->length())->value() :
6495 FixedArray::cast(elements())->length();
6496 for (int i = 0; i < length; i++) {
6497 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
6498 if (storage != NULL) {
6499 storage->set(counter, Smi::FromInt(i), SKIP_WRITE_BARRIER);
6500 }
6501 counter++;
6502 }
6503 }
6504 ASSERT(!storage || storage->length() >= counter);
6505 break;
6506 }
6507 case PIXEL_ELEMENTS: {
6508 int length = PixelArray::cast(elements())->length();
6509 while (counter < length) {
6510 if (storage != NULL) {
6511 storage->set(counter, Smi::FromInt(counter), SKIP_WRITE_BARRIER);
6512 }
6513 counter++;
6514 }
6515 ASSERT(!storage || storage->length() >= counter);
6516 break;
6517 }
Steve Block3ce2e202009-11-05 08:53:23 +00006518 case EXTERNAL_BYTE_ELEMENTS:
6519 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6520 case EXTERNAL_SHORT_ELEMENTS:
6521 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6522 case EXTERNAL_INT_ELEMENTS:
6523 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6524 case EXTERNAL_FLOAT_ELEMENTS: {
6525 int length = ExternalArray::cast(elements())->length();
6526 while (counter < length) {
6527 if (storage != NULL) {
6528 storage->set(counter, Smi::FromInt(counter), SKIP_WRITE_BARRIER);
6529 }
6530 counter++;
6531 }
6532 ASSERT(!storage || storage->length() >= counter);
6533 break;
6534 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006535 case DICTIONARY_ELEMENTS: {
6536 if (storage != NULL) {
6537 element_dictionary()->CopyKeysTo(storage, filter);
6538 }
6539 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
6540 break;
6541 }
6542 default:
6543 UNREACHABLE();
6544 break;
6545 }
6546
6547 if (this->IsJSValue()) {
6548 Object* val = JSValue::cast(this)->value();
6549 if (val->IsString()) {
6550 String* str = String::cast(val);
6551 if (storage) {
6552 for (int i = 0; i < str->length(); i++) {
6553 storage->set(counter + i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
6554 }
6555 }
6556 counter += str->length();
6557 }
6558 }
6559 ASSERT(!storage || storage->length() == counter);
6560 return counter;
6561}
6562
6563
6564int JSObject::GetEnumElementKeys(FixedArray* storage) {
6565 return GetLocalElementKeys(storage,
6566 static_cast<PropertyAttributes>(DONT_ENUM));
6567}
6568
6569
6570bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
6571 ASSERT(other->IsNumber());
6572 return key == static_cast<uint32_t>(other->Number());
6573}
6574
6575
6576uint32_t NumberDictionaryShape::Hash(uint32_t key) {
6577 return ComputeIntegerHash(key);
6578}
6579
6580
6581uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) {
6582 ASSERT(other->IsNumber());
6583 return ComputeIntegerHash(static_cast<uint32_t>(other->Number()));
6584}
6585
6586
6587Object* NumberDictionaryShape::AsObject(uint32_t key) {
6588 return Heap::NumberFromUint32(key);
6589}
6590
6591
6592bool StringDictionaryShape::IsMatch(String* key, Object* other) {
6593 // We know that all entries in a hash table had their hash keys created.
6594 // Use that knowledge to have fast failure.
6595 if (key->Hash() != String::cast(other)->Hash()) return false;
6596 return key->Equals(String::cast(other));
6597}
6598
6599
6600uint32_t StringDictionaryShape::Hash(String* key) {
6601 return key->Hash();
6602}
6603
6604
6605uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) {
6606 return String::cast(other)->Hash();
6607}
6608
6609
6610Object* StringDictionaryShape::AsObject(String* key) {
6611 return key;
6612}
6613
6614
6615// StringKey simply carries a string object as key.
6616class StringKey : public HashTableKey {
6617 public:
6618 explicit StringKey(String* string) :
6619 string_(string),
6620 hash_(HashForObject(string)) { }
6621
6622 bool IsMatch(Object* string) {
6623 // We know that all entries in a hash table had their hash keys created.
6624 // Use that knowledge to have fast failure.
6625 if (hash_ != HashForObject(string)) {
6626 return false;
6627 }
6628 return string_->Equals(String::cast(string));
6629 }
6630
6631 uint32_t Hash() { return hash_; }
6632
6633 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
6634
6635 Object* AsObject() { return string_; }
6636
6637 String* string_;
6638 uint32_t hash_;
6639};
6640
6641
6642// StringSharedKeys are used as keys in the eval cache.
6643class StringSharedKey : public HashTableKey {
6644 public:
6645 StringSharedKey(String* source, SharedFunctionInfo* shared)
6646 : source_(source), shared_(shared) { }
6647
6648 bool IsMatch(Object* other) {
6649 if (!other->IsFixedArray()) return false;
6650 FixedArray* pair = FixedArray::cast(other);
6651 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
6652 if (shared != shared_) return false;
6653 String* source = String::cast(pair->get(1));
6654 return source->Equals(source_);
6655 }
6656
6657 static uint32_t StringSharedHashHelper(String* source,
6658 SharedFunctionInfo* shared) {
6659 uint32_t hash = source->Hash();
6660 if (shared->HasSourceCode()) {
6661 // Instead of using the SharedFunctionInfo pointer in the hash
6662 // code computation, we use a combination of the hash of the
6663 // script source code and the start and end positions. We do
6664 // this to ensure that the cache entries can survive garbage
6665 // collection.
6666 Script* script = Script::cast(shared->script());
6667 hash ^= String::cast(script->source())->Hash();
6668 hash += shared->start_position();
6669 }
6670 return hash;
6671 }
6672
6673 uint32_t Hash() {
6674 return StringSharedHashHelper(source_, shared_);
6675 }
6676
6677 uint32_t HashForObject(Object* obj) {
6678 FixedArray* pair = FixedArray::cast(obj);
6679 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
6680 String* source = String::cast(pair->get(1));
6681 return StringSharedHashHelper(source, shared);
6682 }
6683
6684 Object* AsObject() {
6685 Object* obj = Heap::AllocateFixedArray(2);
6686 if (obj->IsFailure()) return obj;
6687 FixedArray* pair = FixedArray::cast(obj);
6688 pair->set(0, shared_);
6689 pair->set(1, source_);
6690 return pair;
6691 }
6692
6693 private:
6694 String* source_;
6695 SharedFunctionInfo* shared_;
6696};
6697
6698
6699// RegExpKey carries the source and flags of a regular expression as key.
6700class RegExpKey : public HashTableKey {
6701 public:
6702 RegExpKey(String* string, JSRegExp::Flags flags)
6703 : string_(string),
6704 flags_(Smi::FromInt(flags.value())) { }
6705
Steve Block3ce2e202009-11-05 08:53:23 +00006706 // Rather than storing the key in the hash table, a pointer to the
6707 // stored value is stored where the key should be. IsMatch then
6708 // compares the search key to the found object, rather than comparing
6709 // a key to a key.
Steve Blocka7e24c12009-10-30 11:49:00 +00006710 bool IsMatch(Object* obj) {
6711 FixedArray* val = FixedArray::cast(obj);
6712 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
6713 && (flags_ == val->get(JSRegExp::kFlagsIndex));
6714 }
6715
6716 uint32_t Hash() { return RegExpHash(string_, flags_); }
6717
6718 Object* AsObject() {
6719 // Plain hash maps, which is where regexp keys are used, don't
6720 // use this function.
6721 UNREACHABLE();
6722 return NULL;
6723 }
6724
6725 uint32_t HashForObject(Object* obj) {
6726 FixedArray* val = FixedArray::cast(obj);
6727 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
6728 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
6729 }
6730
6731 static uint32_t RegExpHash(String* string, Smi* flags) {
6732 return string->Hash() + flags->value();
6733 }
6734
6735 String* string_;
6736 Smi* flags_;
6737};
6738
6739// Utf8SymbolKey carries a vector of chars as key.
6740class Utf8SymbolKey : public HashTableKey {
6741 public:
6742 explicit Utf8SymbolKey(Vector<const char> string)
Steve Blockd0582a62009-12-15 09:54:21 +00006743 : string_(string), hash_field_(0) { }
Steve Blocka7e24c12009-10-30 11:49:00 +00006744
6745 bool IsMatch(Object* string) {
6746 return String::cast(string)->IsEqualTo(string_);
6747 }
6748
6749 uint32_t Hash() {
Steve Blockd0582a62009-12-15 09:54:21 +00006750 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00006751 unibrow::Utf8InputBuffer<> buffer(string_.start(),
6752 static_cast<unsigned>(string_.length()));
6753 chars_ = buffer.Length();
Steve Blockd0582a62009-12-15 09:54:21 +00006754 hash_field_ = String::ComputeHashField(&buffer, chars_);
6755 uint32_t result = hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00006756 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
6757 return result;
6758 }
6759
6760 uint32_t HashForObject(Object* other) {
6761 return String::cast(other)->Hash();
6762 }
6763
6764 Object* AsObject() {
Steve Blockd0582a62009-12-15 09:54:21 +00006765 if (hash_field_ == 0) Hash();
6766 return Heap::AllocateSymbol(string_, chars_, hash_field_);
Steve Blocka7e24c12009-10-30 11:49:00 +00006767 }
6768
6769 Vector<const char> string_;
Steve Blockd0582a62009-12-15 09:54:21 +00006770 uint32_t hash_field_;
Steve Blocka7e24c12009-10-30 11:49:00 +00006771 int chars_; // Caches the number of characters when computing the hash code.
6772};
6773
6774
6775// SymbolKey carries a string/symbol object as key.
6776class SymbolKey : public HashTableKey {
6777 public:
6778 explicit SymbolKey(String* string) : string_(string) { }
6779
6780 bool IsMatch(Object* string) {
6781 return String::cast(string)->Equals(string_);
6782 }
6783
6784 uint32_t Hash() { return string_->Hash(); }
6785
6786 uint32_t HashForObject(Object* other) {
6787 return String::cast(other)->Hash();
6788 }
6789
6790 Object* AsObject() {
6791 // If the string is a cons string, attempt to flatten it so that
6792 // symbols will most often be flat strings.
6793 if (StringShape(string_).IsCons()) {
6794 ConsString* cons_string = ConsString::cast(string_);
6795 cons_string->TryFlatten();
6796 if (cons_string->second()->length() == 0) {
6797 string_ = cons_string->first();
6798 }
6799 }
6800 // Transform string to symbol if possible.
6801 Map* map = Heap::SymbolMapForString(string_);
6802 if (map != NULL) {
6803 string_->set_map(map);
6804 ASSERT(string_->IsSymbol());
6805 return string_;
6806 }
6807 // Otherwise allocate a new symbol.
6808 StringInputBuffer buffer(string_);
6809 return Heap::AllocateInternalSymbol(&buffer,
6810 string_->length(),
Steve Blockd0582a62009-12-15 09:54:21 +00006811 string_->hash_field());
Steve Blocka7e24c12009-10-30 11:49:00 +00006812 }
6813
6814 static uint32_t StringHash(Object* obj) {
6815 return String::cast(obj)->Hash();
6816 }
6817
6818 String* string_;
6819};
6820
6821
6822template<typename Shape, typename Key>
6823void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
6824 IteratePointers(v, 0, kElementsStartOffset);
6825}
6826
6827
6828template<typename Shape, typename Key>
6829void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
6830 IteratePointers(v,
6831 kElementsStartOffset,
6832 kHeaderSize + length() * kPointerSize);
6833}
6834
6835
6836template<typename Shape, typename Key>
Leon Clarkee46be812010-01-19 14:06:41 +00006837Object* HashTable<Shape, Key>::Allocate(int at_least_space_for) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006838 int capacity = RoundUpToPowerOf2(at_least_space_for);
Leon Clarkee46be812010-01-19 14:06:41 +00006839 if (capacity < 4) {
6840 capacity = 4; // Guarantee min capacity.
6841 } else if (capacity > HashTable::kMaxCapacity) {
6842 return Failure::OutOfMemoryException();
6843 }
6844
Steve Blocka7e24c12009-10-30 11:49:00 +00006845 Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity));
6846 if (!obj->IsFailure()) {
6847 HashTable::cast(obj)->SetNumberOfElements(0);
Leon Clarkee46be812010-01-19 14:06:41 +00006848 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00006849 HashTable::cast(obj)->SetCapacity(capacity);
6850 }
6851 return obj;
6852}
6853
6854
Leon Clarkee46be812010-01-19 14:06:41 +00006855// Find entry for key otherwise return kNotFound.
Steve Blocka7e24c12009-10-30 11:49:00 +00006856template<typename Shape, typename Key>
6857int HashTable<Shape, Key>::FindEntry(Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006858 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00006859 uint32_t entry = FirstProbe(Shape::Hash(key), capacity);
6860 uint32_t count = 1;
6861 // EnsureCapacity will guarantee the hash table is never full.
6862 while (true) {
6863 Object* element = KeyAt(entry);
6864 if (element->IsUndefined()) break; // Empty entry.
6865 if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
6866 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00006867 }
6868 return kNotFound;
6869}
6870
6871
6872template<typename Shape, typename Key>
6873Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
6874 int capacity = Capacity();
6875 int nof = NumberOfElements() + n;
Leon Clarkee46be812010-01-19 14:06:41 +00006876 int nod = NumberOfDeletedElements();
6877 // Return if:
6878 // 50% is still free after adding n elements and
6879 // at most 50% of the free elements are deleted elements.
6880 if ((nof + (nof >> 1) <= capacity) &&
6881 (nod <= (capacity - nof) >> 1)) return this;
Steve Blocka7e24c12009-10-30 11:49:00 +00006882
6883 Object* obj = Allocate(nof * 2);
6884 if (obj->IsFailure()) return obj;
6885 HashTable* table = HashTable::cast(obj);
6886 WriteBarrierMode mode = table->GetWriteBarrierMode();
6887
6888 // Copy prefix to new array.
6889 for (int i = kPrefixStartIndex;
6890 i < kPrefixStartIndex + Shape::kPrefixSize;
6891 i++) {
6892 table->set(i, get(i), mode);
6893 }
6894 // Rehash the elements.
6895 for (int i = 0; i < capacity; i++) {
6896 uint32_t from_index = EntryToIndex(i);
6897 Object* k = get(from_index);
6898 if (IsKey(k)) {
6899 uint32_t hash = Shape::HashForObject(key, k);
6900 uint32_t insertion_index =
6901 EntryToIndex(table->FindInsertionEntry(hash));
6902 for (int j = 0; j < Shape::kEntrySize; j++) {
6903 table->set(insertion_index + j, get(from_index + j), mode);
6904 }
6905 }
6906 }
6907 table->SetNumberOfElements(NumberOfElements());
Leon Clarkee46be812010-01-19 14:06:41 +00006908 table->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00006909 return table;
6910}
6911
6912
Leon Clarkee46be812010-01-19 14:06:41 +00006913
Steve Blocka7e24c12009-10-30 11:49:00 +00006914template<typename Shape, typename Key>
6915uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
6916 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00006917 uint32_t entry = FirstProbe(hash, capacity);
6918 uint32_t count = 1;
6919 // EnsureCapacity will guarantee the hash table is never full.
6920 while (true) {
6921 Object* element = KeyAt(entry);
6922 if (element->IsUndefined() || element->IsNull()) break;
6923 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00006924 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006925 return entry;
6926}
6927
6928// Force instantiation of template instances class.
6929// Please note this list is compiler dependent.
6930
6931template class HashTable<SymbolTableShape, HashTableKey*>;
6932
6933template class HashTable<CompilationCacheShape, HashTableKey*>;
6934
6935template class HashTable<MapCacheShape, HashTableKey*>;
6936
6937template class Dictionary<StringDictionaryShape, String*>;
6938
6939template class Dictionary<NumberDictionaryShape, uint32_t>;
6940
6941template Object* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
6942 int);
6943
6944template Object* Dictionary<StringDictionaryShape, String*>::Allocate(
6945 int);
6946
6947template Object* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
6948 uint32_t, Object*);
6949
6950template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
6951 Object*);
6952
6953template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
6954 Object*);
6955
6956template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
6957 FixedArray*, PropertyAttributes);
6958
6959template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
6960 int, JSObject::DeleteMode);
6961
6962template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
6963 int, JSObject::DeleteMode);
6964
6965template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
6966 FixedArray*);
6967
6968template int
6969Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
6970 PropertyAttributes);
6971
6972template Object* Dictionary<StringDictionaryShape, String*>::Add(
6973 String*, Object*, PropertyDetails);
6974
6975template Object*
6976Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
6977
6978template int
6979Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
6980 PropertyAttributes);
6981
6982template Object* Dictionary<NumberDictionaryShape, uint32_t>::Add(
6983 uint32_t, Object*, PropertyDetails);
6984
6985template Object* Dictionary<NumberDictionaryShape, uint32_t>::EnsureCapacity(
6986 int, uint32_t);
6987
6988template Object* Dictionary<StringDictionaryShape, String*>::EnsureCapacity(
6989 int, String*);
6990
6991template Object* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
6992 uint32_t, Object*, PropertyDetails, uint32_t);
6993
6994template Object* Dictionary<StringDictionaryShape, String*>::AddEntry(
6995 String*, Object*, PropertyDetails, uint32_t);
6996
6997template
6998int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
6999
7000template
7001int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
7002
Leon Clarkee46be812010-01-19 14:06:41 +00007003template
7004int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
7005
7006
Steve Blocka7e24c12009-10-30 11:49:00 +00007007// Collates undefined and unexisting elements below limit from position
7008// zero of the elements. The object stays in Dictionary mode.
7009Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
7010 ASSERT(HasDictionaryElements());
7011 // Must stay in dictionary mode, either because of requires_slow_elements,
7012 // or because we are not going to sort (and therefore compact) all of the
7013 // elements.
7014 NumberDictionary* dict = element_dictionary();
7015 HeapNumber* result_double = NULL;
7016 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7017 // Allocate space for result before we start mutating the object.
7018 Object* new_double = Heap::AllocateHeapNumber(0.0);
7019 if (new_double->IsFailure()) return new_double;
7020 result_double = HeapNumber::cast(new_double);
7021 }
7022
7023 int capacity = dict->Capacity();
7024 Object* obj = NumberDictionary::Allocate(dict->Capacity());
7025 if (obj->IsFailure()) return obj;
7026 NumberDictionary* new_dict = NumberDictionary::cast(obj);
7027
7028 AssertNoAllocation no_alloc;
7029
7030 uint32_t pos = 0;
7031 uint32_t undefs = 0;
7032 for (int i = 0; i < capacity; i++) {
7033 Object* k = dict->KeyAt(i);
7034 if (dict->IsKey(k)) {
7035 ASSERT(k->IsNumber());
7036 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
7037 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
7038 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
7039 Object* value = dict->ValueAt(i);
7040 PropertyDetails details = dict->DetailsAt(i);
7041 if (details.type() == CALLBACKS) {
7042 // Bail out and do the sorting of undefineds and array holes in JS.
7043 return Smi::FromInt(-1);
7044 }
7045 uint32_t key = NumberToUint32(k);
7046 if (key < limit) {
7047 if (value->IsUndefined()) {
7048 undefs++;
7049 } else {
7050 new_dict->AddNumberEntry(pos, value, details);
7051 pos++;
7052 }
7053 } else {
7054 new_dict->AddNumberEntry(key, value, details);
7055 }
7056 }
7057 }
7058
7059 uint32_t result = pos;
7060 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
7061 while (undefs > 0) {
7062 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details);
7063 pos++;
7064 undefs--;
7065 }
7066
7067 set_elements(new_dict);
7068
7069 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7070 return Smi::FromInt(static_cast<int>(result));
7071 }
7072
7073 ASSERT_NE(NULL, result_double);
7074 result_double->set_value(static_cast<double>(result));
7075 return result_double;
7076}
7077
7078
7079// Collects all defined (non-hole) and non-undefined (array) elements at
7080// the start of the elements array.
7081// If the object is in dictionary mode, it is converted to fast elements
7082// mode.
7083Object* JSObject::PrepareElementsForSort(uint32_t limit) {
Steve Block3ce2e202009-11-05 08:53:23 +00007084 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00007085
7086 if (HasDictionaryElements()) {
7087 // Convert to fast elements containing only the existing properties.
7088 // Ordering is irrelevant, since we are going to sort anyway.
7089 NumberDictionary* dict = element_dictionary();
7090 if (IsJSArray() || dict->requires_slow_elements() ||
7091 dict->max_number_key() >= limit) {
7092 return PrepareSlowElementsForSort(limit);
7093 }
7094 // Convert to fast elements.
7095
7096 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
7097 Object* new_array =
7098 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
7099 if (new_array->IsFailure()) {
7100 return new_array;
7101 }
7102 FixedArray* fast_elements = FixedArray::cast(new_array);
7103 dict->CopyValuesTo(fast_elements);
7104 set_elements(fast_elements);
7105 }
7106 ASSERT(HasFastElements());
7107
7108 // Collect holes at the end, undefined before that and the rest at the
7109 // start, and return the number of non-hole, non-undefined values.
7110
7111 FixedArray* elements = FixedArray::cast(this->elements());
7112 uint32_t elements_length = static_cast<uint32_t>(elements->length());
7113 if (limit > elements_length) {
7114 limit = elements_length ;
7115 }
7116 if (limit == 0) {
7117 return Smi::FromInt(0);
7118 }
7119
7120 HeapNumber* result_double = NULL;
7121 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7122 // Pessimistically allocate space for return value before
7123 // we start mutating the array.
7124 Object* new_double = Heap::AllocateHeapNumber(0.0);
7125 if (new_double->IsFailure()) return new_double;
7126 result_double = HeapNumber::cast(new_double);
7127 }
7128
7129 AssertNoAllocation no_alloc;
7130
7131 // Split elements into defined, undefined and the_hole, in that order.
7132 // Only count locations for undefined and the hole, and fill them afterwards.
7133 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode();
7134 unsigned int undefs = limit;
7135 unsigned int holes = limit;
7136 // Assume most arrays contain no holes and undefined values, so minimize the
7137 // number of stores of non-undefined, non-the-hole values.
7138 for (unsigned int i = 0; i < undefs; i++) {
7139 Object* current = elements->get(i);
7140 if (current->IsTheHole()) {
7141 holes--;
7142 undefs--;
7143 } else if (current->IsUndefined()) {
7144 undefs--;
7145 } else {
7146 continue;
7147 }
7148 // Position i needs to be filled.
7149 while (undefs > i) {
7150 current = elements->get(undefs);
7151 if (current->IsTheHole()) {
7152 holes--;
7153 undefs--;
7154 } else if (current->IsUndefined()) {
7155 undefs--;
7156 } else {
7157 elements->set(i, current, write_barrier);
7158 break;
7159 }
7160 }
7161 }
7162 uint32_t result = undefs;
7163 while (undefs < holes) {
7164 elements->set_undefined(undefs);
7165 undefs++;
7166 }
7167 while (holes < limit) {
7168 elements->set_the_hole(holes);
7169 holes++;
7170 }
7171
7172 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7173 return Smi::FromInt(static_cast<int>(result));
7174 }
7175 ASSERT_NE(NULL, result_double);
7176 result_double->set_value(static_cast<double>(result));
7177 return result_double;
7178}
7179
7180
7181Object* PixelArray::SetValue(uint32_t index, Object* value) {
7182 uint8_t clamped_value = 0;
7183 if (index < static_cast<uint32_t>(length())) {
7184 if (value->IsSmi()) {
7185 int int_value = Smi::cast(value)->value();
7186 if (int_value < 0) {
7187 clamped_value = 0;
7188 } else if (int_value > 255) {
7189 clamped_value = 255;
7190 } else {
7191 clamped_value = static_cast<uint8_t>(int_value);
7192 }
7193 } else if (value->IsHeapNumber()) {
7194 double double_value = HeapNumber::cast(value)->value();
7195 if (!(double_value > 0)) {
7196 // NaN and less than zero clamp to zero.
7197 clamped_value = 0;
7198 } else if (double_value > 255) {
7199 // Greater than 255 clamp to 255.
7200 clamped_value = 255;
7201 } else {
7202 // Other doubles are rounded to the nearest integer.
7203 clamped_value = static_cast<uint8_t>(double_value + 0.5);
7204 }
7205 } else {
7206 // Clamp undefined to zero (default). All other types have been
7207 // converted to a number type further up in the call chain.
7208 ASSERT(value->IsUndefined());
7209 }
7210 set(index, clamped_value);
7211 }
7212 return Smi::FromInt(clamped_value);
7213}
7214
7215
Steve Block3ce2e202009-11-05 08:53:23 +00007216template<typename ExternalArrayClass, typename ValueType>
7217static Object* ExternalArrayIntSetter(ExternalArrayClass* receiver,
7218 uint32_t index,
7219 Object* value) {
7220 ValueType cast_value = 0;
7221 if (index < static_cast<uint32_t>(receiver->length())) {
7222 if (value->IsSmi()) {
7223 int int_value = Smi::cast(value)->value();
7224 cast_value = static_cast<ValueType>(int_value);
7225 } else if (value->IsHeapNumber()) {
7226 double double_value = HeapNumber::cast(value)->value();
7227 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
7228 } else {
7229 // Clamp undefined to zero (default). All other types have been
7230 // converted to a number type further up in the call chain.
7231 ASSERT(value->IsUndefined());
7232 }
7233 receiver->set(index, cast_value);
7234 }
7235 return Heap::NumberFromInt32(cast_value);
7236}
7237
7238
7239Object* ExternalByteArray::SetValue(uint32_t index, Object* value) {
7240 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
7241 (this, index, value);
7242}
7243
7244
7245Object* ExternalUnsignedByteArray::SetValue(uint32_t index, Object* value) {
7246 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
7247 (this, index, value);
7248}
7249
7250
7251Object* ExternalShortArray::SetValue(uint32_t index, Object* value) {
7252 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
7253 (this, index, value);
7254}
7255
7256
7257Object* ExternalUnsignedShortArray::SetValue(uint32_t index, Object* value) {
7258 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
7259 (this, index, value);
7260}
7261
7262
7263Object* ExternalIntArray::SetValue(uint32_t index, Object* value) {
7264 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
7265 (this, index, value);
7266}
7267
7268
7269Object* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
7270 uint32_t cast_value = 0;
7271 if (index < static_cast<uint32_t>(length())) {
7272 if (value->IsSmi()) {
7273 int int_value = Smi::cast(value)->value();
7274 cast_value = static_cast<uint32_t>(int_value);
7275 } else if (value->IsHeapNumber()) {
7276 double double_value = HeapNumber::cast(value)->value();
7277 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
7278 } else {
7279 // Clamp undefined to zero (default). All other types have been
7280 // converted to a number type further up in the call chain.
7281 ASSERT(value->IsUndefined());
7282 }
7283 set(index, cast_value);
7284 }
7285 return Heap::NumberFromUint32(cast_value);
7286}
7287
7288
7289Object* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
7290 float cast_value = 0;
7291 if (index < static_cast<uint32_t>(length())) {
7292 if (value->IsSmi()) {
7293 int int_value = Smi::cast(value)->value();
7294 cast_value = static_cast<float>(int_value);
7295 } else if (value->IsHeapNumber()) {
7296 double double_value = HeapNumber::cast(value)->value();
7297 cast_value = static_cast<float>(double_value);
7298 } else {
7299 // Clamp undefined to zero (default). All other types have been
7300 // converted to a number type further up in the call chain.
7301 ASSERT(value->IsUndefined());
7302 }
7303 set(index, cast_value);
7304 }
7305 return Heap::AllocateHeapNumber(cast_value);
7306}
7307
7308
Steve Blocka7e24c12009-10-30 11:49:00 +00007309Object* GlobalObject::GetPropertyCell(LookupResult* result) {
7310 ASSERT(!HasFastProperties());
7311 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
7312 ASSERT(value->IsJSGlobalPropertyCell());
7313 return value;
7314}
7315
7316
7317Object* GlobalObject::EnsurePropertyCell(String* name) {
7318 ASSERT(!HasFastProperties());
7319 int entry = property_dictionary()->FindEntry(name);
7320 if (entry == StringDictionary::kNotFound) {
7321 Object* cell = Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
7322 if (cell->IsFailure()) return cell;
7323 PropertyDetails details(NONE, NORMAL);
7324 details = details.AsDeleted();
7325 Object* dictionary = property_dictionary()->Add(name, cell, details);
7326 if (dictionary->IsFailure()) return dictionary;
7327 set_properties(StringDictionary::cast(dictionary));
7328 return cell;
7329 } else {
7330 Object* value = property_dictionary()->ValueAt(entry);
7331 ASSERT(value->IsJSGlobalPropertyCell());
7332 return value;
7333 }
7334}
7335
7336
7337Object* SymbolTable::LookupString(String* string, Object** s) {
7338 SymbolKey key(string);
7339 return LookupKey(&key, s);
7340}
7341
7342
Steve Blockd0582a62009-12-15 09:54:21 +00007343// This class is used for looking up two character strings in the symbol table.
7344// If we don't have a hit we don't want to waste much time so we unroll the
7345// string hash calculation loop here for speed. Doesn't work if the two
7346// characters form a decimal integer, since such strings have a different hash
7347// algorithm.
7348class TwoCharHashTableKey : public HashTableKey {
7349 public:
7350 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
7351 : c1_(c1), c2_(c2) {
7352 // Char 1.
7353 uint32_t hash = c1 + (c1 << 10);
7354 hash ^= hash >> 6;
7355 // Char 2.
7356 hash += c2;
7357 hash += hash << 10;
7358 hash ^= hash >> 6;
7359 // GetHash.
7360 hash += hash << 3;
7361 hash ^= hash >> 11;
7362 hash += hash << 15;
7363 if (hash == 0) hash = 27;
7364#ifdef DEBUG
7365 StringHasher hasher(2);
7366 hasher.AddCharacter(c1);
7367 hasher.AddCharacter(c2);
7368 // If this assert fails then we failed to reproduce the two-character
7369 // version of the string hashing algorithm above. One reason could be
7370 // that we were passed two digits as characters, since the hash
7371 // algorithm is different in that case.
7372 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
7373#endif
7374 hash_ = hash;
7375 }
7376
7377 bool IsMatch(Object* o) {
7378 if (!o->IsString()) return false;
7379 String* other = String::cast(o);
7380 if (other->length() != 2) return false;
7381 if (other->Get(0) != c1_) return false;
7382 return other->Get(1) == c2_;
7383 }
7384
7385 uint32_t Hash() { return hash_; }
7386 uint32_t HashForObject(Object* key) {
7387 if (!key->IsString()) return 0;
7388 return String::cast(key)->Hash();
7389 }
7390
7391 Object* AsObject() {
7392 // The TwoCharHashTableKey is only used for looking in the symbol
7393 // table, not for adding to it.
7394 UNREACHABLE();
7395 return NULL;
7396 }
7397 private:
7398 uint32_t c1_;
7399 uint32_t c2_;
7400 uint32_t hash_;
7401};
7402
7403
Steve Blocka7e24c12009-10-30 11:49:00 +00007404bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
7405 SymbolKey key(string);
7406 int entry = FindEntry(&key);
7407 if (entry == kNotFound) {
7408 return false;
7409 } else {
7410 String* result = String::cast(KeyAt(entry));
7411 ASSERT(StringShape(result).IsSymbol());
7412 *symbol = result;
7413 return true;
7414 }
7415}
7416
7417
Steve Blockd0582a62009-12-15 09:54:21 +00007418bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
7419 uint32_t c2,
7420 String** symbol) {
7421 TwoCharHashTableKey key(c1, c2);
7422 int entry = FindEntry(&key);
7423 if (entry == kNotFound) {
7424 return false;
7425 } else {
7426 String* result = String::cast(KeyAt(entry));
7427 ASSERT(StringShape(result).IsSymbol());
7428 *symbol = result;
7429 return true;
7430 }
7431}
7432
7433
Steve Blocka7e24c12009-10-30 11:49:00 +00007434Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
7435 Utf8SymbolKey key(str);
7436 return LookupKey(&key, s);
7437}
7438
7439
7440Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
7441 int entry = FindEntry(key);
7442
7443 // Symbol already in table.
7444 if (entry != kNotFound) {
7445 *s = KeyAt(entry);
7446 return this;
7447 }
7448
7449 // Adding new symbol. Grow table if needed.
7450 Object* obj = EnsureCapacity(1, key);
7451 if (obj->IsFailure()) return obj;
7452
7453 // Create symbol object.
7454 Object* symbol = key->AsObject();
7455 if (symbol->IsFailure()) return symbol;
7456
7457 // If the symbol table grew as part of EnsureCapacity, obj is not
7458 // the current symbol table and therefore we cannot use
7459 // SymbolTable::cast here.
7460 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
7461
7462 // Add the new symbol and return it along with the symbol table.
7463 entry = table->FindInsertionEntry(key->Hash());
7464 table->set(EntryToIndex(entry), symbol);
7465 table->ElementAdded();
7466 *s = symbol;
7467 return table;
7468}
7469
7470
7471Object* CompilationCacheTable::Lookup(String* src) {
7472 StringKey key(src);
7473 int entry = FindEntry(&key);
7474 if (entry == kNotFound) return Heap::undefined_value();
7475 return get(EntryToIndex(entry) + 1);
7476}
7477
7478
7479Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
7480 StringSharedKey key(src, context->closure()->shared());
7481 int entry = FindEntry(&key);
7482 if (entry == kNotFound) return Heap::undefined_value();
7483 return get(EntryToIndex(entry) + 1);
7484}
7485
7486
7487Object* CompilationCacheTable::LookupRegExp(String* src,
7488 JSRegExp::Flags flags) {
7489 RegExpKey key(src, flags);
7490 int entry = FindEntry(&key);
7491 if (entry == kNotFound) return Heap::undefined_value();
7492 return get(EntryToIndex(entry) + 1);
7493}
7494
7495
7496Object* CompilationCacheTable::Put(String* src, Object* value) {
7497 StringKey key(src);
7498 Object* obj = EnsureCapacity(1, &key);
7499 if (obj->IsFailure()) return obj;
7500
7501 CompilationCacheTable* cache =
7502 reinterpret_cast<CompilationCacheTable*>(obj);
7503 int entry = cache->FindInsertionEntry(key.Hash());
7504 cache->set(EntryToIndex(entry), src);
7505 cache->set(EntryToIndex(entry) + 1, value);
7506 cache->ElementAdded();
7507 return cache;
7508}
7509
7510
7511Object* CompilationCacheTable::PutEval(String* src,
7512 Context* context,
7513 Object* value) {
7514 StringSharedKey key(src, context->closure()->shared());
7515 Object* obj = EnsureCapacity(1, &key);
7516 if (obj->IsFailure()) return obj;
7517
7518 CompilationCacheTable* cache =
7519 reinterpret_cast<CompilationCacheTable*>(obj);
7520 int entry = cache->FindInsertionEntry(key.Hash());
7521
7522 Object* k = key.AsObject();
7523 if (k->IsFailure()) return k;
7524
7525 cache->set(EntryToIndex(entry), k);
7526 cache->set(EntryToIndex(entry) + 1, value);
7527 cache->ElementAdded();
7528 return cache;
7529}
7530
7531
7532Object* CompilationCacheTable::PutRegExp(String* src,
7533 JSRegExp::Flags flags,
7534 FixedArray* value) {
7535 RegExpKey key(src, flags);
7536 Object* obj = EnsureCapacity(1, &key);
7537 if (obj->IsFailure()) return obj;
7538
7539 CompilationCacheTable* cache =
7540 reinterpret_cast<CompilationCacheTable*>(obj);
7541 int entry = cache->FindInsertionEntry(key.Hash());
Steve Block3ce2e202009-11-05 08:53:23 +00007542 // We store the value in the key slot, and compare the search key
7543 // to the stored value with a custon IsMatch function during lookups.
Steve Blocka7e24c12009-10-30 11:49:00 +00007544 cache->set(EntryToIndex(entry), value);
7545 cache->set(EntryToIndex(entry) + 1, value);
7546 cache->ElementAdded();
7547 return cache;
7548}
7549
7550
7551// SymbolsKey used for HashTable where key is array of symbols.
7552class SymbolsKey : public HashTableKey {
7553 public:
7554 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
7555
7556 bool IsMatch(Object* symbols) {
7557 FixedArray* o = FixedArray::cast(symbols);
7558 int len = symbols_->length();
7559 if (o->length() != len) return false;
7560 for (int i = 0; i < len; i++) {
7561 if (o->get(i) != symbols_->get(i)) return false;
7562 }
7563 return true;
7564 }
7565
7566 uint32_t Hash() { return HashForObject(symbols_); }
7567
7568 uint32_t HashForObject(Object* obj) {
7569 FixedArray* symbols = FixedArray::cast(obj);
7570 int len = symbols->length();
7571 uint32_t hash = 0;
7572 for (int i = 0; i < len; i++) {
7573 hash ^= String::cast(symbols->get(i))->Hash();
7574 }
7575 return hash;
7576 }
7577
7578 Object* AsObject() { return symbols_; }
7579
7580 private:
7581 FixedArray* symbols_;
7582};
7583
7584
7585Object* MapCache::Lookup(FixedArray* array) {
7586 SymbolsKey key(array);
7587 int entry = FindEntry(&key);
7588 if (entry == kNotFound) return Heap::undefined_value();
7589 return get(EntryToIndex(entry) + 1);
7590}
7591
7592
7593Object* MapCache::Put(FixedArray* array, Map* value) {
7594 SymbolsKey key(array);
7595 Object* obj = EnsureCapacity(1, &key);
7596 if (obj->IsFailure()) return obj;
7597
7598 MapCache* cache = reinterpret_cast<MapCache*>(obj);
7599 int entry = cache->FindInsertionEntry(key.Hash());
7600 cache->set(EntryToIndex(entry), array);
7601 cache->set(EntryToIndex(entry) + 1, value);
7602 cache->ElementAdded();
7603 return cache;
7604}
7605
7606
7607template<typename Shape, typename Key>
7608Object* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
7609 Object* obj = HashTable<Shape, Key>::Allocate(at_least_space_for);
7610 // Initialize the next enumeration index.
7611 if (!obj->IsFailure()) {
7612 Dictionary<Shape, Key>::cast(obj)->
7613 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
7614 }
7615 return obj;
7616}
7617
7618
7619template<typename Shape, typename Key>
7620Object* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
7621 int length = HashTable<Shape, Key>::NumberOfElements();
7622
7623 // Allocate and initialize iteration order array.
7624 Object* obj = Heap::AllocateFixedArray(length);
7625 if (obj->IsFailure()) return obj;
7626 FixedArray* iteration_order = FixedArray::cast(obj);
7627 for (int i = 0; i < length; i++) {
7628 iteration_order->set(i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
7629 }
7630
7631 // Allocate array with enumeration order.
7632 obj = Heap::AllocateFixedArray(length);
7633 if (obj->IsFailure()) return obj;
7634 FixedArray* enumeration_order = FixedArray::cast(obj);
7635
7636 // Fill the enumeration order array with property details.
7637 int capacity = HashTable<Shape, Key>::Capacity();
7638 int pos = 0;
7639 for (int i = 0; i < capacity; i++) {
7640 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
7641 enumeration_order->set(pos++,
7642 Smi::FromInt(DetailsAt(i).index()),
7643 SKIP_WRITE_BARRIER);
7644 }
7645 }
7646
7647 // Sort the arrays wrt. enumeration order.
7648 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
7649
7650 // Overwrite the enumeration_order with the enumeration indices.
7651 for (int i = 0; i < length; i++) {
7652 int index = Smi::cast(iteration_order->get(i))->value();
7653 int enum_index = PropertyDetails::kInitialIndex + i;
7654 enumeration_order->set(index,
7655 Smi::FromInt(enum_index),
7656 SKIP_WRITE_BARRIER);
7657 }
7658
7659 // Update the dictionary with new indices.
7660 capacity = HashTable<Shape, Key>::Capacity();
7661 pos = 0;
7662 for (int i = 0; i < capacity; i++) {
7663 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
7664 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
7665 PropertyDetails details = DetailsAt(i);
7666 PropertyDetails new_details =
7667 PropertyDetails(details.attributes(), details.type(), enum_index);
7668 DetailsAtPut(i, new_details);
7669 }
7670 }
7671
7672 // Set the next enumeration index.
7673 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
7674 return this;
7675}
7676
7677template<typename Shape, typename Key>
7678Object* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
7679 // Check whether there are enough enumeration indices to add n elements.
7680 if (Shape::kIsEnumerable &&
7681 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
7682 // If not, we generate new indices for the properties.
7683 Object* result = GenerateNewEnumerationIndices();
7684 if (result->IsFailure()) return result;
7685 }
7686 return HashTable<Shape, Key>::EnsureCapacity(n, key);
7687}
7688
7689
7690void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
7691 // Do nothing if the interval [from, to) is empty.
7692 if (from >= to) return;
7693
7694 int removed_entries = 0;
7695 Object* sentinel = Heap::null_value();
7696 int capacity = Capacity();
7697 for (int i = 0; i < capacity; i++) {
7698 Object* key = KeyAt(i);
7699 if (key->IsNumber()) {
7700 uint32_t number = static_cast<uint32_t>(key->Number());
7701 if (from <= number && number < to) {
7702 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
7703 removed_entries++;
7704 }
7705 }
7706 }
7707
7708 // Update the number of elements.
Leon Clarkee46be812010-01-19 14:06:41 +00007709 ElementsRemoved(removed_entries);
Steve Blocka7e24c12009-10-30 11:49:00 +00007710}
7711
7712
7713template<typename Shape, typename Key>
7714Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
7715 JSObject::DeleteMode mode) {
7716 PropertyDetails details = DetailsAt(entry);
7717 // Ignore attributes if forcing a deletion.
7718 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
7719 return Heap::false_value();
7720 }
7721 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
7722 HashTable<Shape, Key>::ElementRemoved();
7723 return Heap::true_value();
7724}
7725
7726
7727template<typename Shape, typename Key>
7728Object* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
7729 int entry = FindEntry(key);
7730
7731 // If the entry is present set the value;
7732 if (entry != Dictionary<Shape, Key>::kNotFound) {
7733 ValueAtPut(entry, value);
7734 return this;
7735 }
7736
7737 // Check whether the dictionary should be extended.
7738 Object* obj = EnsureCapacity(1, key);
7739 if (obj->IsFailure()) return obj;
7740
7741 Object* k = Shape::AsObject(key);
7742 if (k->IsFailure()) return k;
7743 PropertyDetails details = PropertyDetails(NONE, NORMAL);
7744 return Dictionary<Shape, Key>::cast(obj)->
7745 AddEntry(key, value, details, Shape::Hash(key));
7746}
7747
7748
7749template<typename Shape, typename Key>
7750Object* Dictionary<Shape, Key>::Add(Key key,
7751 Object* value,
7752 PropertyDetails details) {
7753 // Valdate key is absent.
7754 SLOW_ASSERT((FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
7755 // Check whether the dictionary should be extended.
7756 Object* obj = EnsureCapacity(1, key);
7757 if (obj->IsFailure()) return obj;
7758 return Dictionary<Shape, Key>::cast(obj)->
7759 AddEntry(key, value, details, Shape::Hash(key));
7760}
7761
7762
7763// Add a key, value pair to the dictionary.
7764template<typename Shape, typename Key>
7765Object* Dictionary<Shape, Key>::AddEntry(Key key,
7766 Object* value,
7767 PropertyDetails details,
7768 uint32_t hash) {
7769 // Compute the key object.
7770 Object* k = Shape::AsObject(key);
7771 if (k->IsFailure()) return k;
7772
7773 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
7774 // Insert element at empty or deleted entry
7775 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
7776 // Assign an enumeration index to the property and update
7777 // SetNextEnumerationIndex.
7778 int index = NextEnumerationIndex();
7779 details = PropertyDetails(details.attributes(), details.type(), index);
7780 SetNextEnumerationIndex(index + 1);
7781 }
7782 SetEntry(entry, k, value, details);
7783 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
7784 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
7785 HashTable<Shape, Key>::ElementAdded();
7786 return this;
7787}
7788
7789
7790void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
7791 // If the dictionary requires slow elements an element has already
7792 // been added at a high index.
7793 if (requires_slow_elements()) return;
7794 // Check if this index is high enough that we should require slow
7795 // elements.
7796 if (key > kRequiresSlowElementsLimit) {
7797 set_requires_slow_elements();
7798 return;
7799 }
7800 // Update max key value.
7801 Object* max_index_object = get(kMaxNumberKeyIndex);
7802 if (!max_index_object->IsSmi() || max_number_key() < key) {
7803 FixedArray::set(kMaxNumberKeyIndex,
7804 Smi::FromInt(key << kRequiresSlowElementsTagSize),
7805 SKIP_WRITE_BARRIER);
7806 }
7807}
7808
7809
7810Object* NumberDictionary::AddNumberEntry(uint32_t key,
7811 Object* value,
7812 PropertyDetails details) {
7813 UpdateMaxNumberKey(key);
7814 SLOW_ASSERT(FindEntry(key) == kNotFound);
7815 return Add(key, value, details);
7816}
7817
7818
7819Object* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
7820 UpdateMaxNumberKey(key);
7821 return AtPut(key, value);
7822}
7823
7824
7825Object* NumberDictionary::Set(uint32_t key,
7826 Object* value,
7827 PropertyDetails details) {
7828 int entry = FindEntry(key);
7829 if (entry == kNotFound) return AddNumberEntry(key, value, details);
7830 // Preserve enumeration index.
7831 details = PropertyDetails(details.attributes(),
7832 details.type(),
7833 DetailsAt(entry).index());
7834 SetEntry(entry, NumberDictionaryShape::AsObject(key), value, details);
7835 return this;
7836}
7837
7838
7839
7840template<typename Shape, typename Key>
7841int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
7842 PropertyAttributes filter) {
7843 int capacity = HashTable<Shape, Key>::Capacity();
7844 int result = 0;
7845 for (int i = 0; i < capacity; i++) {
7846 Object* k = HashTable<Shape, Key>::KeyAt(i);
7847 if (HashTable<Shape, Key>::IsKey(k)) {
7848 PropertyDetails details = DetailsAt(i);
7849 if (details.IsDeleted()) continue;
7850 PropertyAttributes attr = details.attributes();
7851 if ((attr & filter) == 0) result++;
7852 }
7853 }
7854 return result;
7855}
7856
7857
7858template<typename Shape, typename Key>
7859int Dictionary<Shape, Key>::NumberOfEnumElements() {
7860 return NumberOfElementsFilterAttributes(
7861 static_cast<PropertyAttributes>(DONT_ENUM));
7862}
7863
7864
7865template<typename Shape, typename Key>
7866void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
7867 PropertyAttributes filter) {
7868 ASSERT(storage->length() >= NumberOfEnumElements());
7869 int capacity = HashTable<Shape, Key>::Capacity();
7870 int index = 0;
7871 for (int i = 0; i < capacity; i++) {
7872 Object* k = HashTable<Shape, Key>::KeyAt(i);
7873 if (HashTable<Shape, Key>::IsKey(k)) {
7874 PropertyDetails details = DetailsAt(i);
7875 if (details.IsDeleted()) continue;
7876 PropertyAttributes attr = details.attributes();
7877 if ((attr & filter) == 0) storage->set(index++, k);
7878 }
7879 }
7880 storage->SortPairs(storage, index);
7881 ASSERT(storage->length() >= index);
7882}
7883
7884
7885void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
7886 FixedArray* sort_array) {
7887 ASSERT(storage->length() >= NumberOfEnumElements());
7888 int capacity = Capacity();
7889 int index = 0;
7890 for (int i = 0; i < capacity; i++) {
7891 Object* k = KeyAt(i);
7892 if (IsKey(k)) {
7893 PropertyDetails details = DetailsAt(i);
7894 if (details.IsDeleted() || details.IsDontEnum()) continue;
7895 storage->set(index, k);
7896 sort_array->set(index,
7897 Smi::FromInt(details.index()),
7898 SKIP_WRITE_BARRIER);
7899 index++;
7900 }
7901 }
7902 storage->SortPairs(sort_array, sort_array->length());
7903 ASSERT(storage->length() >= index);
7904}
7905
7906
7907template<typename Shape, typename Key>
7908void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
7909 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
7910 static_cast<PropertyAttributes>(NONE)));
7911 int capacity = HashTable<Shape, Key>::Capacity();
7912 int index = 0;
7913 for (int i = 0; i < capacity; i++) {
7914 Object* k = HashTable<Shape, Key>::KeyAt(i);
7915 if (HashTable<Shape, Key>::IsKey(k)) {
7916 PropertyDetails details = DetailsAt(i);
7917 if (details.IsDeleted()) continue;
7918 storage->set(index++, k);
7919 }
7920 }
7921 ASSERT(storage->length() >= index);
7922}
7923
7924
7925// Backwards lookup (slow).
7926template<typename Shape, typename Key>
7927Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
7928 int capacity = HashTable<Shape, Key>::Capacity();
7929 for (int i = 0; i < capacity; i++) {
7930 Object* k = HashTable<Shape, Key>::KeyAt(i);
7931 if (Dictionary<Shape, Key>::IsKey(k)) {
7932 Object* e = ValueAt(i);
7933 if (e->IsJSGlobalPropertyCell()) {
7934 e = JSGlobalPropertyCell::cast(e)->value();
7935 }
7936 if (e == value) return k;
7937 }
7938 }
7939 return Heap::undefined_value();
7940}
7941
7942
7943Object* StringDictionary::TransformPropertiesToFastFor(
7944 JSObject* obj, int unused_property_fields) {
7945 // Make sure we preserve dictionary representation if there are too many
7946 // descriptors.
7947 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
7948
7949 // Figure out if it is necessary to generate new enumeration indices.
7950 int max_enumeration_index =
7951 NextEnumerationIndex() +
7952 (DescriptorArray::kMaxNumberOfDescriptors -
7953 NumberOfElements());
7954 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
7955 Object* result = GenerateNewEnumerationIndices();
7956 if (result->IsFailure()) return result;
7957 }
7958
7959 int instance_descriptor_length = 0;
7960 int number_of_fields = 0;
7961
7962 // Compute the length of the instance descriptor.
7963 int capacity = Capacity();
7964 for (int i = 0; i < capacity; i++) {
7965 Object* k = KeyAt(i);
7966 if (IsKey(k)) {
7967 Object* value = ValueAt(i);
7968 PropertyType type = DetailsAt(i).type();
7969 ASSERT(type != FIELD);
7970 instance_descriptor_length++;
Leon Clarkee46be812010-01-19 14:06:41 +00007971 if (type == NORMAL &&
7972 (!value->IsJSFunction() || Heap::InNewSpace(value))) {
7973 number_of_fields += 1;
7974 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007975 }
7976 }
7977
7978 // Allocate the instance descriptor.
7979 Object* descriptors_unchecked =
7980 DescriptorArray::Allocate(instance_descriptor_length);
7981 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
7982 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
7983
7984 int inobject_props = obj->map()->inobject_properties();
7985 int number_of_allocated_fields =
7986 number_of_fields + unused_property_fields - inobject_props;
7987
7988 // Allocate the fixed array for the fields.
7989 Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields);
7990 if (fields->IsFailure()) return fields;
7991
7992 // Fill in the instance descriptor and the fields.
7993 int next_descriptor = 0;
7994 int current_offset = 0;
7995 for (int i = 0; i < capacity; i++) {
7996 Object* k = KeyAt(i);
7997 if (IsKey(k)) {
7998 Object* value = ValueAt(i);
7999 // Ensure the key is a symbol before writing into the instance descriptor.
8000 Object* key = Heap::LookupSymbol(String::cast(k));
8001 if (key->IsFailure()) return key;
8002 PropertyDetails details = DetailsAt(i);
8003 PropertyType type = details.type();
8004
Leon Clarkee46be812010-01-19 14:06:41 +00008005 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008006 ConstantFunctionDescriptor d(String::cast(key),
8007 JSFunction::cast(value),
8008 details.attributes(),
8009 details.index());
8010 descriptors->Set(next_descriptor++, &d);
8011 } else if (type == NORMAL) {
8012 if (current_offset < inobject_props) {
8013 obj->InObjectPropertyAtPut(current_offset,
8014 value,
8015 UPDATE_WRITE_BARRIER);
8016 } else {
8017 int offset = current_offset - inobject_props;
8018 FixedArray::cast(fields)->set(offset, value);
8019 }
8020 FieldDescriptor d(String::cast(key),
8021 current_offset++,
8022 details.attributes(),
8023 details.index());
8024 descriptors->Set(next_descriptor++, &d);
8025 } else if (type == CALLBACKS) {
8026 CallbacksDescriptor d(String::cast(key),
8027 value,
8028 details.attributes(),
8029 details.index());
8030 descriptors->Set(next_descriptor++, &d);
8031 } else {
8032 UNREACHABLE();
8033 }
8034 }
8035 }
8036 ASSERT(current_offset == number_of_fields);
8037
8038 descriptors->Sort();
8039 // Allocate new map.
8040 Object* new_map = obj->map()->CopyDropDescriptors();
8041 if (new_map->IsFailure()) return new_map;
8042
8043 // Transform the object.
8044 obj->set_map(Map::cast(new_map));
8045 obj->map()->set_instance_descriptors(descriptors);
8046 obj->map()->set_unused_property_fields(unused_property_fields);
8047
8048 obj->set_properties(FixedArray::cast(fields));
8049 ASSERT(obj->IsJSObject());
8050
8051 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
8052 // Check that it really works.
8053 ASSERT(obj->HasFastProperties());
8054
8055 return obj;
8056}
8057
8058
8059#ifdef ENABLE_DEBUGGER_SUPPORT
8060// Check if there is a break point at this code position.
8061bool DebugInfo::HasBreakPoint(int code_position) {
8062 // Get the break point info object for this code position.
8063 Object* break_point_info = GetBreakPointInfo(code_position);
8064
8065 // If there is no break point info object or no break points in the break
8066 // point info object there is no break point at this code position.
8067 if (break_point_info->IsUndefined()) return false;
8068 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
8069}
8070
8071
8072// Get the break point info object for this code position.
8073Object* DebugInfo::GetBreakPointInfo(int code_position) {
8074 // Find the index of the break point info object for this code position.
8075 int index = GetBreakPointInfoIndex(code_position);
8076
8077 // Return the break point info object if any.
8078 if (index == kNoBreakPointInfo) return Heap::undefined_value();
8079 return BreakPointInfo::cast(break_points()->get(index));
8080}
8081
8082
8083// Clear a break point at the specified code position.
8084void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
8085 int code_position,
8086 Handle<Object> break_point_object) {
8087 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
8088 if (break_point_info->IsUndefined()) return;
8089 BreakPointInfo::ClearBreakPoint(
8090 Handle<BreakPointInfo>::cast(break_point_info),
8091 break_point_object);
8092}
8093
8094
8095void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
8096 int code_position,
8097 int source_position,
8098 int statement_position,
8099 Handle<Object> break_point_object) {
8100 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
8101 if (!break_point_info->IsUndefined()) {
8102 BreakPointInfo::SetBreakPoint(
8103 Handle<BreakPointInfo>::cast(break_point_info),
8104 break_point_object);
8105 return;
8106 }
8107
8108 // Adding a new break point for a code position which did not have any
8109 // break points before. Try to find a free slot.
8110 int index = kNoBreakPointInfo;
8111 for (int i = 0; i < debug_info->break_points()->length(); i++) {
8112 if (debug_info->break_points()->get(i)->IsUndefined()) {
8113 index = i;
8114 break;
8115 }
8116 }
8117 if (index == kNoBreakPointInfo) {
8118 // No free slot - extend break point info array.
8119 Handle<FixedArray> old_break_points =
8120 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
8121 debug_info->set_break_points(*Factory::NewFixedArray(
8122 old_break_points->length() +
8123 Debug::kEstimatedNofBreakPointsInFunction));
8124 Handle<FixedArray> new_break_points =
8125 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
8126 for (int i = 0; i < old_break_points->length(); i++) {
8127 new_break_points->set(i, old_break_points->get(i));
8128 }
8129 index = old_break_points->length();
8130 }
8131 ASSERT(index != kNoBreakPointInfo);
8132
8133 // Allocate new BreakPointInfo object and set the break point.
8134 Handle<BreakPointInfo> new_break_point_info =
8135 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
8136 new_break_point_info->set_code_position(Smi::FromInt(code_position));
8137 new_break_point_info->set_source_position(Smi::FromInt(source_position));
8138 new_break_point_info->
8139 set_statement_position(Smi::FromInt(statement_position));
8140 new_break_point_info->set_break_point_objects(Heap::undefined_value());
8141 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
8142 debug_info->break_points()->set(index, *new_break_point_info);
8143}
8144
8145
8146// Get the break point objects for a code position.
8147Object* DebugInfo::GetBreakPointObjects(int code_position) {
8148 Object* break_point_info = GetBreakPointInfo(code_position);
8149 if (break_point_info->IsUndefined()) {
8150 return Heap::undefined_value();
8151 }
8152 return BreakPointInfo::cast(break_point_info)->break_point_objects();
8153}
8154
8155
8156// Get the total number of break points.
8157int DebugInfo::GetBreakPointCount() {
8158 if (break_points()->IsUndefined()) return 0;
8159 int count = 0;
8160 for (int i = 0; i < break_points()->length(); i++) {
8161 if (!break_points()->get(i)->IsUndefined()) {
8162 BreakPointInfo* break_point_info =
8163 BreakPointInfo::cast(break_points()->get(i));
8164 count += break_point_info->GetBreakPointCount();
8165 }
8166 }
8167 return count;
8168}
8169
8170
8171Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
8172 Handle<Object> break_point_object) {
8173 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
8174 for (int i = 0; i < debug_info->break_points()->length(); i++) {
8175 if (!debug_info->break_points()->get(i)->IsUndefined()) {
8176 Handle<BreakPointInfo> break_point_info =
8177 Handle<BreakPointInfo>(BreakPointInfo::cast(
8178 debug_info->break_points()->get(i)));
8179 if (BreakPointInfo::HasBreakPointObject(break_point_info,
8180 break_point_object)) {
8181 return *break_point_info;
8182 }
8183 }
8184 }
8185 return Heap::undefined_value();
8186}
8187
8188
8189// Find the index of the break point info object for the specified code
8190// position.
8191int DebugInfo::GetBreakPointInfoIndex(int code_position) {
8192 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
8193 for (int i = 0; i < break_points()->length(); i++) {
8194 if (!break_points()->get(i)->IsUndefined()) {
8195 BreakPointInfo* break_point_info =
8196 BreakPointInfo::cast(break_points()->get(i));
8197 if (break_point_info->code_position()->value() == code_position) {
8198 return i;
8199 }
8200 }
8201 }
8202 return kNoBreakPointInfo;
8203}
8204
8205
8206// Remove the specified break point object.
8207void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
8208 Handle<Object> break_point_object) {
8209 // If there are no break points just ignore.
8210 if (break_point_info->break_point_objects()->IsUndefined()) return;
8211 // If there is a single break point clear it if it is the same.
8212 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8213 if (break_point_info->break_point_objects() == *break_point_object) {
8214 break_point_info->set_break_point_objects(Heap::undefined_value());
8215 }
8216 return;
8217 }
8218 // If there are multiple break points shrink the array
8219 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
8220 Handle<FixedArray> old_array =
8221 Handle<FixedArray>(
8222 FixedArray::cast(break_point_info->break_point_objects()));
8223 Handle<FixedArray> new_array =
8224 Factory::NewFixedArray(old_array->length() - 1);
8225 int found_count = 0;
8226 for (int i = 0; i < old_array->length(); i++) {
8227 if (old_array->get(i) == *break_point_object) {
8228 ASSERT(found_count == 0);
8229 found_count++;
8230 } else {
8231 new_array->set(i - found_count, old_array->get(i));
8232 }
8233 }
8234 // If the break point was found in the list change it.
8235 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
8236}
8237
8238
8239// Add the specified break point object.
8240void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
8241 Handle<Object> break_point_object) {
8242 // If there was no break point objects before just set it.
8243 if (break_point_info->break_point_objects()->IsUndefined()) {
8244 break_point_info->set_break_point_objects(*break_point_object);
8245 return;
8246 }
8247 // If the break point object is the same as before just ignore.
8248 if (break_point_info->break_point_objects() == *break_point_object) return;
8249 // If there was one break point object before replace with array.
8250 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8251 Handle<FixedArray> array = Factory::NewFixedArray(2);
8252 array->set(0, break_point_info->break_point_objects());
8253 array->set(1, *break_point_object);
8254 break_point_info->set_break_point_objects(*array);
8255 return;
8256 }
8257 // If there was more than one break point before extend array.
8258 Handle<FixedArray> old_array =
8259 Handle<FixedArray>(
8260 FixedArray::cast(break_point_info->break_point_objects()));
8261 Handle<FixedArray> new_array =
8262 Factory::NewFixedArray(old_array->length() + 1);
8263 for (int i = 0; i < old_array->length(); i++) {
8264 // If the break point was there before just ignore.
8265 if (old_array->get(i) == *break_point_object) return;
8266 new_array->set(i, old_array->get(i));
8267 }
8268 // Add the new break point.
8269 new_array->set(old_array->length(), *break_point_object);
8270 break_point_info->set_break_point_objects(*new_array);
8271}
8272
8273
8274bool BreakPointInfo::HasBreakPointObject(
8275 Handle<BreakPointInfo> break_point_info,
8276 Handle<Object> break_point_object) {
8277 // No break point.
8278 if (break_point_info->break_point_objects()->IsUndefined()) return false;
8279 // Single beak point.
8280 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8281 return break_point_info->break_point_objects() == *break_point_object;
8282 }
8283 // Multiple break points.
8284 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
8285 for (int i = 0; i < array->length(); i++) {
8286 if (array->get(i) == *break_point_object) {
8287 return true;
8288 }
8289 }
8290 return false;
8291}
8292
8293
8294// Get the number of break points.
8295int BreakPointInfo::GetBreakPointCount() {
8296 // No break point.
8297 if (break_point_objects()->IsUndefined()) return 0;
8298 // Single beak point.
8299 if (!break_point_objects()->IsFixedArray()) return 1;
8300 // Multiple break points.
8301 return FixedArray::cast(break_point_objects())->length();
8302}
8303#endif
8304
8305
8306} } // namespace v8::internal