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