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