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