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