blob: 8288f6367e29662913e1eb0134c7c008f4d9a0cf [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();
Leon Clarkef7060e22010-06-03 12:02:55 +0100192 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000193}
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) {
Leon Clarkef7060e22010-06-03 12:02:55 +0100634 return cs->first();
Steve Blocka7e24c12009-10-30 11:49:00 +0000635 }
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());
Leon Clarkef7060e22010-06-03 12:02:55 +0100672 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +0000673 }
674 default:
675 return this;
676 }
677}
678
679
680bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
Steve Block8defd9f2010-07-08 12:39:36 +0100681 // Externalizing twice leaks the external resource, so it's
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100682 // prohibited by the API.
683 ASSERT(!this->IsExternalString());
Steve Blocka7e24c12009-10-30 11:49:00 +0000684#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000685 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000686 // Assert that the resource and the string are equivalent.
687 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100688 ScopedVector<uc16> smart_chars(this->length());
689 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
690 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000691 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100692 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000693 }
694#endif // DEBUG
695
696 int size = this->Size(); // Byte size of the original string.
697 if (size < ExternalString::kSize) {
698 // The string is too small to fit an external String in its place. This can
699 // only happen for zero length strings.
700 return false;
701 }
702 ASSERT(size >= ExternalString::kSize);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100703 bool is_ascii = this->IsAsciiRepresentation();
Steve Blocka7e24c12009-10-30 11:49:00 +0000704 bool is_symbol = this->IsSymbol();
705 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000706 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000707
708 // Morph the object to an external string by adjusting the map and
709 // reinitializing the fields.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100710 this->set_map(is_ascii ?
711 Heap::external_string_with_ascii_data_map() :
712 Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000713 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
714 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000715 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000716 self->set_resource(resource);
717 // Additionally make the object into an external symbol if the original string
718 // was a symbol to start with.
719 if (is_symbol) {
720 self->Hash(); // Force regeneration of the hash value.
721 // Now morph this external string into a external symbol.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100722 this->set_map(is_ascii ?
723 Heap::external_symbol_with_ascii_data_map() :
724 Heap::external_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000725 }
726
727 // Fill the remainder of the string with dead wood.
728 int new_size = this->Size(); // Byte size of the external String object.
729 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
730 return true;
731}
732
733
734bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
735#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000736 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000737 // Assert that the resource and the string are equivalent.
738 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100739 ScopedVector<char> smart_chars(this->length());
740 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
741 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000742 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100743 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000744 }
745#endif // DEBUG
746
747 int size = this->Size(); // Byte size of the original string.
748 if (size < ExternalString::kSize) {
749 // The string is too small to fit an external String in its place. This can
750 // only happen for zero length strings.
751 return false;
752 }
753 ASSERT(size >= ExternalString::kSize);
754 bool is_symbol = this->IsSymbol();
755 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000756 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000757
758 // Morph the object to an external string by adjusting the map and
759 // reinitializing the fields.
Steve Blockd0582a62009-12-15 09:54:21 +0000760 this->set_map(Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000761 ExternalAsciiString* self = ExternalAsciiString::cast(this);
762 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000763 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000764 self->set_resource(resource);
765 // Additionally make the object into an external symbol if the original string
766 // was a symbol to start with.
767 if (is_symbol) {
768 self->Hash(); // Force regeneration of the hash value.
769 // Now morph this external string into a external symbol.
Steve Blockd0582a62009-12-15 09:54:21 +0000770 this->set_map(Heap::external_ascii_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000771 }
772
773 // Fill the remainder of the string with dead wood.
774 int new_size = this->Size(); // Byte size of the external String object.
775 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
776 return true;
777}
778
779
780void String::StringShortPrint(StringStream* accumulator) {
781 int len = length();
Steve Blockd0582a62009-12-15 09:54:21 +0000782 if (len > kMaxShortPrintLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000783 accumulator->Add("<Very long string[%u]>", len);
784 return;
785 }
786
787 if (!LooksValid()) {
788 accumulator->Add("<Invalid String>");
789 return;
790 }
791
792 StringInputBuffer buf(this);
793
794 bool truncated = false;
795 if (len > kMaxShortPrintLength) {
796 len = kMaxShortPrintLength;
797 truncated = true;
798 }
799 bool ascii = true;
800 for (int i = 0; i < len; i++) {
801 int c = buf.GetNext();
802
803 if (c < 32 || c >= 127) {
804 ascii = false;
805 }
806 }
807 buf.Reset(this);
808 if (ascii) {
809 accumulator->Add("<String[%u]: ", length());
810 for (int i = 0; i < len; i++) {
811 accumulator->Put(buf.GetNext());
812 }
813 accumulator->Put('>');
814 } else {
815 // Backslash indicates that the string contains control
816 // characters and that backslashes are therefore escaped.
817 accumulator->Add("<String[%u]\\: ", length());
818 for (int i = 0; i < len; i++) {
819 int c = buf.GetNext();
820 if (c == '\n') {
821 accumulator->Add("\\n");
822 } else if (c == '\r') {
823 accumulator->Add("\\r");
824 } else if (c == '\\') {
825 accumulator->Add("\\\\");
826 } else if (c < 32 || c > 126) {
827 accumulator->Add("\\x%02x", c);
828 } else {
829 accumulator->Put(c);
830 }
831 }
832 if (truncated) {
833 accumulator->Put('.');
834 accumulator->Put('.');
835 accumulator->Put('.');
836 }
837 accumulator->Put('>');
838 }
839 return;
840}
841
842
843void JSObject::JSObjectShortPrint(StringStream* accumulator) {
844 switch (map()->instance_type()) {
845 case JS_ARRAY_TYPE: {
846 double length = JSArray::cast(this)->length()->Number();
847 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
848 break;
849 }
850 case JS_REGEXP_TYPE: {
851 accumulator->Add("<JS RegExp>");
852 break;
853 }
854 case JS_FUNCTION_TYPE: {
855 Object* fun_name = JSFunction::cast(this)->shared()->name();
856 bool printed = false;
857 if (fun_name->IsString()) {
858 String* str = String::cast(fun_name);
859 if (str->length() > 0) {
860 accumulator->Add("<JS Function ");
861 accumulator->Put(str);
862 accumulator->Put('>');
863 printed = true;
864 }
865 }
866 if (!printed) {
867 accumulator->Add("<JS Function>");
868 }
869 break;
870 }
871 // All other JSObjects are rather similar to each other (JSObject,
872 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
873 default: {
874 Object* constructor = map()->constructor();
875 bool printed = false;
876 if (constructor->IsHeapObject() &&
877 !Heap::Contains(HeapObject::cast(constructor))) {
878 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
879 } else {
880 bool global_object = IsJSGlobalProxy();
881 if (constructor->IsJSFunction()) {
882 if (!Heap::Contains(JSFunction::cast(constructor)->shared())) {
883 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
884 } else {
885 Object* constructor_name =
886 JSFunction::cast(constructor)->shared()->name();
887 if (constructor_name->IsString()) {
888 String* str = String::cast(constructor_name);
889 if (str->length() > 0) {
890 bool vowel = AnWord(str);
891 accumulator->Add("<%sa%s ",
892 global_object ? "Global Object: " : "",
893 vowel ? "n" : "");
894 accumulator->Put(str);
895 accumulator->Put('>');
896 printed = true;
897 }
898 }
899 }
900 }
901 if (!printed) {
902 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
903 }
904 }
905 if (IsJSValue()) {
906 accumulator->Add(" value = ");
907 JSValue::cast(this)->value()->ShortPrint(accumulator);
908 }
909 accumulator->Put('>');
910 break;
911 }
912 }
913}
914
915
916void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
917 // if (!Heap::InNewSpace(this)) PrintF("*", this);
918 if (!Heap::Contains(this)) {
919 accumulator->Add("!!!INVALID POINTER!!!");
920 return;
921 }
922 if (!Heap::Contains(map())) {
923 accumulator->Add("!!!INVALID MAP!!!");
924 return;
925 }
926
927 accumulator->Add("%p ", this);
928
929 if (IsString()) {
930 String::cast(this)->StringShortPrint(accumulator);
931 return;
932 }
933 if (IsJSObject()) {
934 JSObject::cast(this)->JSObjectShortPrint(accumulator);
935 return;
936 }
937 switch (map()->instance_type()) {
938 case MAP_TYPE:
939 accumulator->Add("<Map>");
940 break;
941 case FIXED_ARRAY_TYPE:
942 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
943 break;
944 case BYTE_ARRAY_TYPE:
945 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
946 break;
947 case PIXEL_ARRAY_TYPE:
948 accumulator->Add("<PixelArray[%u]>", PixelArray::cast(this)->length());
949 break;
Steve Block3ce2e202009-11-05 08:53:23 +0000950 case EXTERNAL_BYTE_ARRAY_TYPE:
951 accumulator->Add("<ExternalByteArray[%u]>",
952 ExternalByteArray::cast(this)->length());
953 break;
954 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
955 accumulator->Add("<ExternalUnsignedByteArray[%u]>",
956 ExternalUnsignedByteArray::cast(this)->length());
957 break;
958 case EXTERNAL_SHORT_ARRAY_TYPE:
959 accumulator->Add("<ExternalShortArray[%u]>",
960 ExternalShortArray::cast(this)->length());
961 break;
962 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
963 accumulator->Add("<ExternalUnsignedShortArray[%u]>",
964 ExternalUnsignedShortArray::cast(this)->length());
965 break;
966 case EXTERNAL_INT_ARRAY_TYPE:
967 accumulator->Add("<ExternalIntArray[%u]>",
968 ExternalIntArray::cast(this)->length());
969 break;
970 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
971 accumulator->Add("<ExternalUnsignedIntArray[%u]>",
972 ExternalUnsignedIntArray::cast(this)->length());
973 break;
974 case EXTERNAL_FLOAT_ARRAY_TYPE:
975 accumulator->Add("<ExternalFloatArray[%u]>",
976 ExternalFloatArray::cast(this)->length());
977 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000978 case SHARED_FUNCTION_INFO_TYPE:
979 accumulator->Add("<SharedFunctionInfo>");
980 break;
981#define MAKE_STRUCT_CASE(NAME, Name, name) \
982 case NAME##_TYPE: \
983 accumulator->Put('<'); \
984 accumulator->Add(#Name); \
985 accumulator->Put('>'); \
986 break;
987 STRUCT_LIST(MAKE_STRUCT_CASE)
988#undef MAKE_STRUCT_CASE
989 case CODE_TYPE:
990 accumulator->Add("<Code>");
991 break;
992 case ODDBALL_TYPE: {
993 if (IsUndefined())
994 accumulator->Add("<undefined>");
995 else if (IsTheHole())
996 accumulator->Add("<the hole>");
997 else if (IsNull())
998 accumulator->Add("<null>");
999 else if (IsTrue())
1000 accumulator->Add("<true>");
1001 else if (IsFalse())
1002 accumulator->Add("<false>");
1003 else
1004 accumulator->Add("<Odd Oddball>");
1005 break;
1006 }
1007 case HEAP_NUMBER_TYPE:
1008 accumulator->Add("<Number: ");
1009 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
1010 accumulator->Put('>');
1011 break;
1012 case PROXY_TYPE:
1013 accumulator->Add("<Proxy>");
1014 break;
1015 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1016 accumulator->Add("Cell for ");
1017 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator);
1018 break;
1019 default:
1020 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
1021 break;
1022 }
1023}
1024
1025
1026int HeapObject::SlowSizeFromMap(Map* map) {
1027 // Avoid calling functions such as FixedArray::cast during GC, which
1028 // read map pointer of this object again.
1029 InstanceType instance_type = map->instance_type();
1030 uint32_t type = static_cast<uint32_t>(instance_type);
1031
1032 if (instance_type < FIRST_NONSTRING_TYPE
1033 && (StringShape(instance_type).IsSequential())) {
1034 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1035 SeqAsciiString* seq_ascii_this = reinterpret_cast<SeqAsciiString*>(this);
1036 return seq_ascii_this->SeqAsciiStringSize(instance_type);
1037 } else {
1038 SeqTwoByteString* self = reinterpret_cast<SeqTwoByteString*>(this);
1039 return self->SeqTwoByteStringSize(instance_type);
1040 }
1041 }
1042
1043 switch (instance_type) {
1044 case FIXED_ARRAY_TYPE:
1045 return reinterpret_cast<FixedArray*>(this)->FixedArraySize();
1046 case BYTE_ARRAY_TYPE:
1047 return reinterpret_cast<ByteArray*>(this)->ByteArraySize();
1048 case CODE_TYPE:
1049 return reinterpret_cast<Code*>(this)->CodeSize();
1050 case MAP_TYPE:
1051 return Map::kSize;
1052 default:
1053 return map->instance_size();
1054 }
1055}
1056
1057
1058void HeapObject::Iterate(ObjectVisitor* v) {
1059 // Handle header
1060 IteratePointer(v, kMapOffset);
1061 // Handle object body
1062 Map* m = map();
1063 IterateBody(m->instance_type(), SizeFromMap(m), v);
1064}
1065
1066
1067void HeapObject::IterateBody(InstanceType type, int object_size,
1068 ObjectVisitor* v) {
1069 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
1070 // During GC, the map pointer field is encoded.
1071 if (type < FIRST_NONSTRING_TYPE) {
1072 switch (type & kStringRepresentationMask) {
1073 case kSeqStringTag:
1074 break;
1075 case kConsStringTag:
1076 reinterpret_cast<ConsString*>(this)->ConsStringIterateBody(v);
1077 break;
Steve Blockd0582a62009-12-15 09:54:21 +00001078 case kExternalStringTag:
1079 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1080 reinterpret_cast<ExternalAsciiString*>(this)->
1081 ExternalAsciiStringIterateBody(v);
1082 } else {
1083 reinterpret_cast<ExternalTwoByteString*>(this)->
1084 ExternalTwoByteStringIterateBody(v);
1085 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001086 break;
1087 }
1088 return;
1089 }
1090
1091 switch (type) {
1092 case FIXED_ARRAY_TYPE:
1093 reinterpret_cast<FixedArray*>(this)->FixedArrayIterateBody(v);
1094 break;
1095 case JS_OBJECT_TYPE:
1096 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
1097 case JS_VALUE_TYPE:
1098 case JS_ARRAY_TYPE:
1099 case JS_REGEXP_TYPE:
1100 case JS_FUNCTION_TYPE:
1101 case JS_GLOBAL_PROXY_TYPE:
1102 case JS_GLOBAL_OBJECT_TYPE:
1103 case JS_BUILTINS_OBJECT_TYPE:
1104 reinterpret_cast<JSObject*>(this)->JSObjectIterateBody(object_size, v);
1105 break;
1106 case ODDBALL_TYPE:
1107 reinterpret_cast<Oddball*>(this)->OddballIterateBody(v);
1108 break;
1109 case PROXY_TYPE:
1110 reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
1111 break;
1112 case MAP_TYPE:
1113 reinterpret_cast<Map*>(this)->MapIterateBody(v);
1114 break;
1115 case CODE_TYPE:
1116 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1117 break;
1118 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1119 reinterpret_cast<JSGlobalPropertyCell*>(this)
1120 ->JSGlobalPropertyCellIterateBody(v);
1121 break;
1122 case HEAP_NUMBER_TYPE:
1123 case FILLER_TYPE:
1124 case BYTE_ARRAY_TYPE:
1125 case PIXEL_ARRAY_TYPE:
Steve Block3ce2e202009-11-05 08:53:23 +00001126 case EXTERNAL_BYTE_ARRAY_TYPE:
1127 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1128 case EXTERNAL_SHORT_ARRAY_TYPE:
1129 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1130 case EXTERNAL_INT_ARRAY_TYPE:
1131 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1132 case EXTERNAL_FLOAT_ARRAY_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001133 break;
1134 case SHARED_FUNCTION_INFO_TYPE: {
1135 SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(this);
1136 shared->SharedFunctionInfoIterateBody(v);
1137 break;
1138 }
1139#define MAKE_STRUCT_CASE(NAME, Name, name) \
1140 case NAME##_TYPE:
1141 STRUCT_LIST(MAKE_STRUCT_CASE)
1142#undef MAKE_STRUCT_CASE
1143 IterateStructBody(object_size, v);
1144 break;
1145 default:
1146 PrintF("Unknown type: %d\n", type);
1147 UNREACHABLE();
1148 }
1149}
1150
1151
1152void HeapObject::IterateStructBody(int object_size, ObjectVisitor* v) {
1153 IteratePointers(v, HeapObject::kHeaderSize, object_size);
1154}
1155
1156
1157Object* HeapNumber::HeapNumberToBoolean() {
1158 // NaN, +0, and -0 should return the false object
1159 switch (fpclassify(value())) {
1160 case FP_NAN: // fall through
1161 case FP_ZERO: return Heap::false_value();
1162 default: return Heap::true_value();
1163 }
1164}
1165
1166
1167void HeapNumber::HeapNumberPrint() {
1168 PrintF("%.16g", Number());
1169}
1170
1171
1172void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1173 // The Windows version of vsnprintf can allocate when printing a %g string
1174 // into a buffer that may not be big enough. We don't want random memory
1175 // allocation when producing post-crash stack traces, so we print into a
1176 // buffer that is plenty big enough for any floating point number, then
1177 // print that using vsnprintf (which may truncate but never allocate if
1178 // there is no more space in the buffer).
1179 EmbeddedVector<char, 100> buffer;
1180 OS::SNPrintF(buffer, "%.16g", Number());
1181 accumulator->Add("%s", buffer.start());
1182}
1183
1184
1185String* JSObject::class_name() {
1186 if (IsJSFunction()) {
1187 return Heap::function_class_symbol();
1188 }
1189 if (map()->constructor()->IsJSFunction()) {
1190 JSFunction* constructor = JSFunction::cast(map()->constructor());
1191 return String::cast(constructor->shared()->instance_class_name());
1192 }
1193 // If the constructor is not present, return "Object".
1194 return Heap::Object_symbol();
1195}
1196
1197
1198String* JSObject::constructor_name() {
1199 if (IsJSFunction()) {
Steve Block6ded16b2010-05-10 14:33:55 +01001200 return Heap::closure_symbol();
Steve Blocka7e24c12009-10-30 11:49:00 +00001201 }
1202 if (map()->constructor()->IsJSFunction()) {
1203 JSFunction* constructor = JSFunction::cast(map()->constructor());
1204 String* name = String::cast(constructor->shared()->name());
1205 return name->length() > 0 ? name : constructor->shared()->inferred_name();
1206 }
1207 // If the constructor is not present, return "Object".
1208 return Heap::Object_symbol();
1209}
1210
1211
1212void JSObject::JSObjectIterateBody(int object_size, ObjectVisitor* v) {
1213 // Iterate over all fields in the body. Assumes all are Object*.
1214 IteratePointers(v, kPropertiesOffset, object_size);
1215}
1216
1217
1218Object* JSObject::AddFastPropertyUsingMap(Map* new_map,
1219 String* name,
1220 Object* value) {
1221 int index = new_map->PropertyIndexFor(name);
1222 if (map()->unused_property_fields() == 0) {
1223 ASSERT(map()->unused_property_fields() == 0);
1224 int new_unused = new_map->unused_property_fields();
1225 Object* values =
1226 properties()->CopySize(properties()->length() + new_unused + 1);
1227 if (values->IsFailure()) return values;
1228 set_properties(FixedArray::cast(values));
1229 }
1230 set_map(new_map);
1231 return FastPropertyAtPut(index, value);
1232}
1233
1234
1235Object* JSObject::AddFastProperty(String* name,
1236 Object* value,
1237 PropertyAttributes attributes) {
1238 // Normalize the object if the name is an actual string (not the
1239 // hidden symbols) and is not a real identifier.
1240 StringInputBuffer buffer(name);
1241 if (!Scanner::IsIdentifier(&buffer) && name != Heap::hidden_symbol()) {
1242 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1243 if (obj->IsFailure()) return obj;
1244 return AddSlowProperty(name, value, attributes);
1245 }
1246
1247 DescriptorArray* old_descriptors = map()->instance_descriptors();
1248 // Compute the new index for new field.
1249 int index = map()->NextFreePropertyIndex();
1250
1251 // Allocate new instance descriptors with (name, index) added
1252 FieldDescriptor new_field(name, index, attributes);
1253 Object* new_descriptors =
1254 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1255 if (new_descriptors->IsFailure()) return new_descriptors;
1256
1257 // Only allow map transition if the object's map is NOT equal to the
1258 // global object_function's map and there is not a transition for name.
1259 bool allow_map_transition =
1260 !old_descriptors->Contains(name) &&
1261 (Top::context()->global_context()->object_function()->map() != map());
1262
1263 ASSERT(index < map()->inobject_properties() ||
1264 (index - map()->inobject_properties()) < properties()->length() ||
1265 map()->unused_property_fields() == 0);
1266 // Allocate a new map for the object.
1267 Object* r = map()->CopyDropDescriptors();
1268 if (r->IsFailure()) return r;
1269 Map* new_map = Map::cast(r);
1270 if (allow_map_transition) {
1271 // Allocate new instance descriptors for the old map with map transition.
1272 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
1273 Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1274 if (r->IsFailure()) return r;
1275 old_descriptors = DescriptorArray::cast(r);
1276 }
1277
1278 if (map()->unused_property_fields() == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01001279 if (properties()->length() > MaxFastProperties()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001280 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1281 if (obj->IsFailure()) return obj;
1282 return AddSlowProperty(name, value, attributes);
1283 }
1284 // Make room for the new value
1285 Object* values =
1286 properties()->CopySize(properties()->length() + kFieldsAdded);
1287 if (values->IsFailure()) return values;
1288 set_properties(FixedArray::cast(values));
1289 new_map->set_unused_property_fields(kFieldsAdded - 1);
1290 } else {
1291 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
1292 }
1293 // We have now allocated all the necessary objects.
1294 // All the changes can be applied at once, so they are atomic.
1295 map()->set_instance_descriptors(old_descriptors);
1296 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1297 set_map(new_map);
1298 return FastPropertyAtPut(index, value);
1299}
1300
1301
1302Object* JSObject::AddConstantFunctionProperty(String* name,
1303 JSFunction* function,
1304 PropertyAttributes attributes) {
Leon Clarkee46be812010-01-19 14:06:41 +00001305 ASSERT(!Heap::InNewSpace(function));
1306
Steve Blocka7e24c12009-10-30 11:49:00 +00001307 // Allocate new instance descriptors with (name, function) added
1308 ConstantFunctionDescriptor d(name, function, attributes);
1309 Object* new_descriptors =
1310 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1311 if (new_descriptors->IsFailure()) return new_descriptors;
1312
1313 // Allocate a new map for the object.
1314 Object* new_map = map()->CopyDropDescriptors();
1315 if (new_map->IsFailure()) return new_map;
1316
1317 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1318 Map::cast(new_map)->set_instance_descriptors(descriptors);
1319 Map* old_map = map();
1320 set_map(Map::cast(new_map));
1321
1322 // If the old map is the global object map (from new Object()),
1323 // then transitions are not added to it, so we are done.
1324 if (old_map == Top::context()->global_context()->object_function()->map()) {
1325 return function;
1326 }
1327
1328 // Do not add CONSTANT_TRANSITIONS to global objects
1329 if (IsGlobalObject()) {
1330 return function;
1331 }
1332
1333 // Add a CONSTANT_TRANSITION descriptor to the old map,
1334 // so future assignments to this property on other objects
1335 // of the same type will create a normal field, not a constant function.
1336 // Don't do this for special properties, with non-trival attributes.
1337 if (attributes != NONE) {
1338 return function;
1339 }
1340 ConstTransitionDescriptor mark(name);
1341 new_descriptors =
1342 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1343 if (new_descriptors->IsFailure()) {
1344 return function; // We have accomplished the main goal, so return success.
1345 }
1346 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1347
1348 return function;
1349}
1350
1351
1352// Add property in slow mode
1353Object* JSObject::AddSlowProperty(String* name,
1354 Object* value,
1355 PropertyAttributes attributes) {
1356 ASSERT(!HasFastProperties());
1357 StringDictionary* dict = property_dictionary();
1358 Object* store_value = value;
1359 if (IsGlobalObject()) {
1360 // In case name is an orphaned property reuse the cell.
1361 int entry = dict->FindEntry(name);
1362 if (entry != StringDictionary::kNotFound) {
1363 store_value = dict->ValueAt(entry);
1364 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1365 // Assign an enumeration index to the property and update
1366 // SetNextEnumerationIndex.
1367 int index = dict->NextEnumerationIndex();
1368 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1369 dict->SetNextEnumerationIndex(index + 1);
1370 dict->SetEntry(entry, name, store_value, details);
1371 return value;
1372 }
1373 store_value = Heap::AllocateJSGlobalPropertyCell(value);
1374 if (store_value->IsFailure()) return store_value;
1375 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1376 }
1377 PropertyDetails details = PropertyDetails(attributes, NORMAL);
1378 Object* result = dict->Add(name, store_value, details);
1379 if (result->IsFailure()) return result;
1380 if (dict != result) set_properties(StringDictionary::cast(result));
1381 return value;
1382}
1383
1384
1385Object* JSObject::AddProperty(String* name,
1386 Object* value,
1387 PropertyAttributes attributes) {
1388 ASSERT(!IsJSGlobalProxy());
Steve Block8defd9f2010-07-08 12:39:36 +01001389 if (!map()->is_extensible()) {
1390 Handle<Object> args[1] = {Handle<String>(name)};
1391 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
1392 HandleVector(args, 1)));
1393 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001394 if (HasFastProperties()) {
1395 // Ensure the descriptor array does not get too big.
1396 if (map()->instance_descriptors()->number_of_descriptors() <
1397 DescriptorArray::kMaxNumberOfDescriptors) {
Leon Clarkee46be812010-01-19 14:06:41 +00001398 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001399 return AddConstantFunctionProperty(name,
1400 JSFunction::cast(value),
1401 attributes);
1402 } else {
1403 return AddFastProperty(name, value, attributes);
1404 }
1405 } else {
1406 // Normalize the object to prevent very large instance descriptors.
1407 // This eliminates unwanted N^2 allocation and lookup behavior.
1408 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1409 if (obj->IsFailure()) return obj;
1410 }
1411 }
1412 return AddSlowProperty(name, value, attributes);
1413}
1414
1415
1416Object* JSObject::SetPropertyPostInterceptor(String* name,
1417 Object* value,
1418 PropertyAttributes attributes) {
1419 // Check local property, ignore interceptor.
1420 LookupResult result;
1421 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001422 if (result.IsFound()) {
1423 // An existing property, a map transition or a null descriptor was
1424 // found. Use set property to handle all these cases.
1425 return SetProperty(&result, name, value, attributes);
1426 }
1427 // Add a new real property.
Steve Blocka7e24c12009-10-30 11:49:00 +00001428 return AddProperty(name, value, attributes);
1429}
1430
1431
1432Object* JSObject::ReplaceSlowProperty(String* name,
Steve Blockd0582a62009-12-15 09:54:21 +00001433 Object* value,
1434 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001435 StringDictionary* dictionary = property_dictionary();
1436 int old_index = dictionary->FindEntry(name);
1437 int new_enumeration_index = 0; // 0 means "Use the next available index."
1438 if (old_index != -1) {
1439 // All calls to ReplaceSlowProperty have had all transitions removed.
1440 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1441 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1442 }
1443
1444 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
1445 return SetNormalizedProperty(name, value, new_details);
1446}
1447
Steve Blockd0582a62009-12-15 09:54:21 +00001448
Steve Blocka7e24c12009-10-30 11:49:00 +00001449Object* JSObject::ConvertDescriptorToFieldAndMapTransition(
1450 String* name,
1451 Object* new_value,
1452 PropertyAttributes attributes) {
1453 Map* old_map = map();
1454 Object* result = ConvertDescriptorToField(name, new_value, attributes);
1455 if (result->IsFailure()) return result;
1456 // If we get to this point we have succeeded - do not return failure
1457 // after this point. Later stuff is optional.
1458 if (!HasFastProperties()) {
1459 return result;
1460 }
1461 // Do not add transitions to the map of "new Object()".
1462 if (map() == Top::context()->global_context()->object_function()->map()) {
1463 return result;
1464 }
1465
1466 MapTransitionDescriptor transition(name,
1467 map(),
1468 attributes);
1469 Object* new_descriptors =
1470 old_map->instance_descriptors()->
1471 CopyInsert(&transition, KEEP_TRANSITIONS);
1472 if (new_descriptors->IsFailure()) return result; // Yes, return _result_.
1473 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1474 return result;
1475}
1476
1477
1478Object* JSObject::ConvertDescriptorToField(String* name,
1479 Object* new_value,
1480 PropertyAttributes attributes) {
1481 if (map()->unused_property_fields() == 0 &&
Steve Block8defd9f2010-07-08 12:39:36 +01001482 properties()->length() > MaxFastProperties()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001483 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1484 if (obj->IsFailure()) return obj;
1485 return ReplaceSlowProperty(name, new_value, attributes);
1486 }
1487
1488 int index = map()->NextFreePropertyIndex();
1489 FieldDescriptor new_field(name, index, attributes);
1490 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
1491 Object* descriptors_unchecked = map()->instance_descriptors()->
1492 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1493 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
1494 DescriptorArray* new_descriptors =
1495 DescriptorArray::cast(descriptors_unchecked);
1496
1497 // Make a new map for the object.
1498 Object* new_map_unchecked = map()->CopyDropDescriptors();
1499 if (new_map_unchecked->IsFailure()) return new_map_unchecked;
1500 Map* new_map = Map::cast(new_map_unchecked);
1501 new_map->set_instance_descriptors(new_descriptors);
1502
1503 // Make new properties array if necessary.
1504 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1505 int new_unused_property_fields = map()->unused_property_fields() - 1;
1506 if (map()->unused_property_fields() == 0) {
1507 new_unused_property_fields = kFieldsAdded - 1;
1508 Object* new_properties_unchecked =
1509 properties()->CopySize(properties()->length() + kFieldsAdded);
1510 if (new_properties_unchecked->IsFailure()) return new_properties_unchecked;
1511 new_properties = FixedArray::cast(new_properties_unchecked);
1512 }
1513
1514 // Update pointers to commit changes.
1515 // Object points to the new map.
1516 new_map->set_unused_property_fields(new_unused_property_fields);
1517 set_map(new_map);
1518 if (new_properties) {
1519 set_properties(FixedArray::cast(new_properties));
1520 }
1521 return FastPropertyAtPut(index, new_value);
1522}
1523
1524
1525
1526Object* JSObject::SetPropertyWithInterceptor(String* name,
1527 Object* value,
1528 PropertyAttributes attributes) {
1529 HandleScope scope;
1530 Handle<JSObject> this_handle(this);
1531 Handle<String> name_handle(name);
1532 Handle<Object> value_handle(value);
1533 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1534 if (!interceptor->setter()->IsUndefined()) {
1535 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name));
1536 CustomArguments args(interceptor->data(), this, this);
1537 v8::AccessorInfo info(args.end());
1538 v8::NamedPropertySetter setter =
1539 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1540 v8::Handle<v8::Value> result;
1541 {
1542 // Leaving JavaScript.
1543 VMState state(EXTERNAL);
1544 Handle<Object> value_unhole(value->IsTheHole() ?
1545 Heap::undefined_value() :
1546 value);
1547 result = setter(v8::Utils::ToLocal(name_handle),
1548 v8::Utils::ToLocal(value_unhole),
1549 info);
1550 }
1551 RETURN_IF_SCHEDULED_EXCEPTION();
1552 if (!result.IsEmpty()) return *value_handle;
1553 }
1554 Object* raw_result = this_handle->SetPropertyPostInterceptor(*name_handle,
1555 *value_handle,
1556 attributes);
1557 RETURN_IF_SCHEDULED_EXCEPTION();
1558 return raw_result;
1559}
1560
1561
1562Object* JSObject::SetProperty(String* name,
1563 Object* value,
1564 PropertyAttributes attributes) {
1565 LookupResult result;
1566 LocalLookup(name, &result);
1567 return SetProperty(&result, name, value, attributes);
1568}
1569
1570
1571Object* JSObject::SetPropertyWithCallback(Object* structure,
1572 String* name,
1573 Object* value,
1574 JSObject* holder) {
1575 HandleScope scope;
1576
1577 // We should never get here to initialize a const with the hole
1578 // value since a const declaration would conflict with the setter.
1579 ASSERT(!value->IsTheHole());
1580 Handle<Object> value_handle(value);
1581
1582 // To accommodate both the old and the new api we switch on the
1583 // data structure used to store the callbacks. Eventually proxy
1584 // callbacks should be phased out.
1585 if (structure->IsProxy()) {
1586 AccessorDescriptor* callback =
1587 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
1588 Object* obj = (callback->setter)(this, value, callback->data);
1589 RETURN_IF_SCHEDULED_EXCEPTION();
1590 if (obj->IsFailure()) return obj;
1591 return *value_handle;
1592 }
1593
1594 if (structure->IsAccessorInfo()) {
1595 // api style callbacks
1596 AccessorInfo* data = AccessorInfo::cast(structure);
1597 Object* call_obj = data->setter();
1598 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1599 if (call_fun == NULL) return value;
1600 Handle<String> key(name);
1601 LOG(ApiNamedPropertyAccess("store", this, name));
1602 CustomArguments args(data->data(), this, JSObject::cast(holder));
1603 v8::AccessorInfo info(args.end());
1604 {
1605 // Leaving JavaScript.
1606 VMState state(EXTERNAL);
1607 call_fun(v8::Utils::ToLocal(key),
1608 v8::Utils::ToLocal(value_handle),
1609 info);
1610 }
1611 RETURN_IF_SCHEDULED_EXCEPTION();
1612 return *value_handle;
1613 }
1614
1615 if (structure->IsFixedArray()) {
1616 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1617 if (setter->IsJSFunction()) {
1618 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
1619 } else {
1620 Handle<String> key(name);
1621 Handle<Object> holder_handle(holder);
1622 Handle<Object> args[2] = { key, holder_handle };
1623 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
1624 HandleVector(args, 2)));
1625 }
1626 }
1627
1628 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +01001629 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001630}
1631
1632
1633Object* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1634 Object* value) {
1635 Handle<Object> value_handle(value);
1636 Handle<JSFunction> fun(JSFunction::cast(setter));
1637 Handle<JSObject> self(this);
1638#ifdef ENABLE_DEBUGGER_SUPPORT
1639 // Handle stepping into a setter if step into is active.
1640 if (Debug::StepInActive()) {
1641 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
1642 }
1643#endif
1644 bool has_pending_exception;
1645 Object** argv[] = { value_handle.location() };
1646 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1647 // Check for pending exception and return the result.
1648 if (has_pending_exception) return Failure::Exception();
1649 return *value_handle;
1650}
1651
1652
1653void JSObject::LookupCallbackSetterInPrototypes(String* name,
1654 LookupResult* result) {
1655 for (Object* pt = GetPrototype();
1656 pt != Heap::null_value();
1657 pt = pt->GetPrototype()) {
1658 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001659 if (result->IsProperty()) {
1660 if (result->IsReadOnly()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001661 result->NotFound();
1662 return;
1663 }
1664 if (result->type() == CALLBACKS) {
1665 return;
1666 }
1667 }
1668 }
1669 result->NotFound();
1670}
1671
1672
Leon Clarkef7060e22010-06-03 12:02:55 +01001673bool JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
1674 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001675 for (Object* pt = GetPrototype();
1676 pt != Heap::null_value();
1677 pt = pt->GetPrototype()) {
1678 if (!JSObject::cast(pt)->HasDictionaryElements()) {
1679 continue;
1680 }
1681 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1682 int entry = dictionary->FindEntry(index);
1683 if (entry != NumberDictionary::kNotFound) {
1684 Object* element = dictionary->ValueAt(entry);
1685 PropertyDetails details = dictionary->DetailsAt(entry);
1686 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01001687 SetElementWithCallback(element, index, value, JSObject::cast(pt));
1688 return true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001689 }
1690 }
1691 }
Leon Clarkef7060e22010-06-03 12:02:55 +01001692 return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001693}
1694
1695
1696void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1697 DescriptorArray* descriptors = map()->instance_descriptors();
1698 int number = DescriptorLookupCache::Lookup(descriptors, name);
1699 if (number == DescriptorLookupCache::kAbsent) {
1700 number = descriptors->Search(name);
1701 DescriptorLookupCache::Update(descriptors, name, number);
1702 }
1703 if (number != DescriptorArray::kNotFound) {
1704 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1705 } else {
1706 result->NotFound();
1707 }
1708}
1709
1710
1711void JSObject::LocalLookupRealNamedProperty(String* name,
1712 LookupResult* result) {
1713 if (IsJSGlobalProxy()) {
1714 Object* proto = GetPrototype();
1715 if (proto->IsNull()) return result->NotFound();
1716 ASSERT(proto->IsJSGlobalObject());
1717 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
1718 }
1719
1720 if (HasFastProperties()) {
1721 LookupInDescriptor(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001722 if (result->IsFound()) {
1723 // A property, a map transition or a null descriptor was found.
1724 // We return all of these result types because
1725 // LocalLookupRealNamedProperty is used when setting properties
1726 // where map transitions and null descriptors are handled.
Steve Blocka7e24c12009-10-30 11:49:00 +00001727 ASSERT(result->holder() == this && result->type() != NORMAL);
1728 // Disallow caching for uninitialized constants. These can only
1729 // occur as fields.
1730 if (result->IsReadOnly() && result->type() == FIELD &&
1731 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
1732 result->DisallowCaching();
1733 }
1734 return;
1735 }
1736 } else {
1737 int entry = property_dictionary()->FindEntry(name);
1738 if (entry != StringDictionary::kNotFound) {
1739 Object* value = property_dictionary()->ValueAt(entry);
1740 if (IsGlobalObject()) {
1741 PropertyDetails d = property_dictionary()->DetailsAt(entry);
1742 if (d.IsDeleted()) {
1743 result->NotFound();
1744 return;
1745 }
1746 value = JSGlobalPropertyCell::cast(value)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001747 }
1748 // Make sure to disallow caching for uninitialized constants
1749 // found in the dictionary-mode objects.
1750 if (value->IsTheHole()) result->DisallowCaching();
1751 result->DictionaryResult(this, entry);
1752 return;
1753 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001754 }
1755 result->NotFound();
1756}
1757
1758
1759void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
1760 LocalLookupRealNamedProperty(name, result);
1761 if (result->IsProperty()) return;
1762
1763 LookupRealNamedPropertyInPrototypes(name, result);
1764}
1765
1766
1767void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
1768 LookupResult* result) {
1769 for (Object* pt = GetPrototype();
1770 pt != Heap::null_value();
1771 pt = JSObject::cast(pt)->GetPrototype()) {
1772 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001773 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001774 }
1775 result->NotFound();
1776}
1777
1778
1779// We only need to deal with CALLBACKS and INTERCEPTORS
1780Object* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
1781 String* name,
1782 Object* value) {
1783 if (!result->IsProperty()) {
1784 LookupCallbackSetterInPrototypes(name, result);
1785 }
1786
1787 if (result->IsProperty()) {
1788 if (!result->IsReadOnly()) {
1789 switch (result->type()) {
1790 case CALLBACKS: {
1791 Object* obj = result->GetCallbackObject();
1792 if (obj->IsAccessorInfo()) {
1793 AccessorInfo* info = AccessorInfo::cast(obj);
1794 if (info->all_can_write()) {
1795 return SetPropertyWithCallback(result->GetCallbackObject(),
1796 name,
1797 value,
1798 result->holder());
1799 }
1800 }
1801 break;
1802 }
1803 case INTERCEPTOR: {
1804 // Try lookup real named properties. Note that only property can be
1805 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
1806 LookupResult r;
1807 LookupRealNamedProperty(name, &r);
1808 if (r.IsProperty()) {
1809 return SetPropertyWithFailedAccessCheck(&r, name, value);
1810 }
1811 break;
1812 }
1813 default: {
1814 break;
1815 }
1816 }
1817 }
1818 }
1819
1820 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
1821 return value;
1822}
1823
1824
1825Object* JSObject::SetProperty(LookupResult* result,
1826 String* name,
1827 Object* value,
1828 PropertyAttributes attributes) {
1829 // Make sure that the top context does not change when doing callbacks or
1830 // interceptor calls.
1831 AssertNoContextChange ncc;
1832
Steve Blockd0582a62009-12-15 09:54:21 +00001833 // Optimization for 2-byte strings often used as keys in a decompression
1834 // dictionary. We make these short keys into symbols to avoid constantly
1835 // reallocating them.
1836 if (!name->IsSymbol() && name->length() <= 2) {
1837 Object* symbol_version = Heap::LookupSymbol(name);
1838 if (!symbol_version->IsFailure()) name = String::cast(symbol_version);
1839 }
1840
Steve Blocka7e24c12009-10-30 11:49:00 +00001841 // Check access rights if needed.
1842 if (IsAccessCheckNeeded()
1843 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1844 return SetPropertyWithFailedAccessCheck(result, name, value);
1845 }
1846
1847 if (IsJSGlobalProxy()) {
1848 Object* proto = GetPrototype();
1849 if (proto->IsNull()) return value;
1850 ASSERT(proto->IsJSGlobalObject());
1851 return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
1852 }
1853
1854 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
1855 // We could not find a local property so let's check whether there is an
1856 // accessor that wants to handle the property.
1857 LookupResult accessor_result;
1858 LookupCallbackSetterInPrototypes(name, &accessor_result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001859 if (accessor_result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001860 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
1861 name,
1862 value,
1863 accessor_result.holder());
1864 }
1865 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001866 if (!result->IsFound()) {
1867 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001868 return AddProperty(name, value, attributes);
1869 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001870 if (result->IsReadOnly() && result->IsProperty()) return value;
1871 // This is a real property that is not read-only, or it is a
1872 // transition or null descriptor and there are no setters in the prototypes.
1873 switch (result->type()) {
1874 case NORMAL:
1875 return SetNormalizedProperty(result, value);
1876 case FIELD:
1877 return FastPropertyAtPut(result->GetFieldIndex(), value);
1878 case MAP_TRANSITION:
1879 if (attributes == result->GetAttributes()) {
1880 // Only use map transition if the attributes match.
1881 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1882 name,
1883 value);
1884 }
1885 return ConvertDescriptorToField(name, value, attributes);
1886 case CONSTANT_FUNCTION:
1887 // Only replace the function if necessary.
1888 if (value == result->GetConstantFunction()) return value;
1889 // Preserve the attributes of this existing property.
1890 attributes = result->GetAttributes();
1891 return ConvertDescriptorToField(name, value, attributes);
1892 case CALLBACKS:
1893 return SetPropertyWithCallback(result->GetCallbackObject(),
1894 name,
1895 value,
1896 result->holder());
1897 case INTERCEPTOR:
1898 return SetPropertyWithInterceptor(name, value, attributes);
1899 case CONSTANT_TRANSITION:
1900 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
1901 // if the value is a function.
1902 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1903 case NULL_DESCRIPTOR:
1904 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1905 default:
1906 UNREACHABLE();
1907 }
1908 UNREACHABLE();
1909 return value;
1910}
1911
1912
1913// Set a real local property, even if it is READ_ONLY. If the property is not
1914// present, add it with attributes NONE. This code is an exact clone of
1915// SetProperty, with the check for IsReadOnly and the check for a
1916// callback setter removed. The two lines looking up the LookupResult
1917// result are also added. If one of the functions is changed, the other
1918// should be.
1919Object* JSObject::IgnoreAttributesAndSetLocalProperty(
1920 String* name,
1921 Object* value,
1922 PropertyAttributes attributes) {
1923 // Make sure that the top context does not change when doing callbacks or
1924 // interceptor calls.
1925 AssertNoContextChange ncc;
Andrei Popescu402d9372010-02-26 13:31:12 +00001926 LookupResult result;
1927 LocalLookup(name, &result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001928 // Check access rights if needed.
1929 if (IsAccessCheckNeeded()
Andrei Popescu402d9372010-02-26 13:31:12 +00001930 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1931 return SetPropertyWithFailedAccessCheck(&result, name, value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001932 }
1933
1934 if (IsJSGlobalProxy()) {
1935 Object* proto = GetPrototype();
1936 if (proto->IsNull()) return value;
1937 ASSERT(proto->IsJSGlobalObject());
1938 return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
1939 name,
1940 value,
1941 attributes);
1942 }
1943
1944 // Check for accessor in prototype chain removed here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00001945 if (!result.IsFound()) {
1946 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001947 return AddProperty(name, value, attributes);
1948 }
Steve Block6ded16b2010-05-10 14:33:55 +01001949
Andrei Popescu402d9372010-02-26 13:31:12 +00001950 PropertyDetails details = PropertyDetails(attributes, NORMAL);
1951
Steve Blocka7e24c12009-10-30 11:49:00 +00001952 // Check of IsReadOnly removed from here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00001953 switch (result.type()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001954 case NORMAL:
Andrei Popescu402d9372010-02-26 13:31:12 +00001955 return SetNormalizedProperty(name, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00001956 case FIELD:
Andrei Popescu402d9372010-02-26 13:31:12 +00001957 return FastPropertyAtPut(result.GetFieldIndex(), value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001958 case MAP_TRANSITION:
Andrei Popescu402d9372010-02-26 13:31:12 +00001959 if (attributes == result.GetAttributes()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001960 // Only use map transition if the attributes match.
Andrei Popescu402d9372010-02-26 13:31:12 +00001961 return AddFastPropertyUsingMap(result.GetTransitionMap(),
Steve Blocka7e24c12009-10-30 11:49:00 +00001962 name,
1963 value);
1964 }
1965 return ConvertDescriptorToField(name, value, attributes);
1966 case CONSTANT_FUNCTION:
1967 // Only replace the function if necessary.
Andrei Popescu402d9372010-02-26 13:31:12 +00001968 if (value == result.GetConstantFunction()) return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00001969 // Preserve the attributes of this existing property.
Andrei Popescu402d9372010-02-26 13:31:12 +00001970 attributes = result.GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +00001971 return ConvertDescriptorToField(name, value, attributes);
1972 case CALLBACKS:
1973 case INTERCEPTOR:
1974 // Override callback in clone
1975 return ConvertDescriptorToField(name, value, attributes);
1976 case CONSTANT_TRANSITION:
1977 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
1978 // if the value is a function.
1979 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1980 case NULL_DESCRIPTOR:
1981 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1982 default:
1983 UNREACHABLE();
1984 }
1985 UNREACHABLE();
1986 return value;
1987}
1988
1989
1990PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
1991 JSObject* receiver,
1992 String* name,
1993 bool continue_search) {
1994 // Check local property, ignore interceptor.
1995 LookupResult result;
1996 LocalLookupRealNamedProperty(name, &result);
1997 if (result.IsProperty()) return result.GetAttributes();
1998
1999 if (continue_search) {
2000 // Continue searching via the prototype chain.
2001 Object* pt = GetPrototype();
2002 if (pt != Heap::null_value()) {
2003 return JSObject::cast(pt)->
2004 GetPropertyAttributeWithReceiver(receiver, name);
2005 }
2006 }
2007 return ABSENT;
2008}
2009
2010
2011PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
2012 JSObject* receiver,
2013 String* name,
2014 bool continue_search) {
2015 // Make sure that the top context does not change when doing
2016 // callbacks or interceptor calls.
2017 AssertNoContextChange ncc;
2018
2019 HandleScope scope;
2020 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2021 Handle<JSObject> receiver_handle(receiver);
2022 Handle<JSObject> holder_handle(this);
2023 Handle<String> name_handle(name);
2024 CustomArguments args(interceptor->data(), receiver, this);
2025 v8::AccessorInfo info(args.end());
2026 if (!interceptor->query()->IsUndefined()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002027 v8::NamedPropertyQuery query =
2028 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
Steve Blocka7e24c12009-10-30 11:49:00 +00002029 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002030 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00002031 {
2032 // Leaving JavaScript.
2033 VMState state(EXTERNAL);
2034 result = query(v8::Utils::ToLocal(name_handle), info);
2035 }
2036 if (!result.IsEmpty()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002037 ASSERT(result->IsInt32());
2038 return static_cast<PropertyAttributes>(result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002039 }
2040 } else if (!interceptor->getter()->IsUndefined()) {
2041 v8::NamedPropertyGetter getter =
2042 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
2043 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
2044 v8::Handle<v8::Value> result;
2045 {
2046 // Leaving JavaScript.
2047 VMState state(EXTERNAL);
2048 result = getter(v8::Utils::ToLocal(name_handle), info);
2049 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002050 if (!result.IsEmpty()) return DONT_ENUM;
Steve Blocka7e24c12009-10-30 11:49:00 +00002051 }
2052 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2053 *name_handle,
2054 continue_search);
2055}
2056
2057
2058PropertyAttributes JSObject::GetPropertyAttributeWithReceiver(
2059 JSObject* receiver,
2060 String* key) {
2061 uint32_t index = 0;
2062 if (key->AsArrayIndex(&index)) {
2063 if (HasElementWithReceiver(receiver, index)) return NONE;
2064 return ABSENT;
2065 }
2066 // Named property.
2067 LookupResult result;
2068 Lookup(key, &result);
2069 return GetPropertyAttribute(receiver, &result, key, true);
2070}
2071
2072
2073PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
2074 LookupResult* result,
2075 String* name,
2076 bool continue_search) {
2077 // Check access rights if needed.
2078 if (IsAccessCheckNeeded() &&
2079 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2080 return GetPropertyAttributeWithFailedAccessCheck(receiver,
2081 result,
2082 name,
2083 continue_search);
2084 }
Andrei Popescu402d9372010-02-26 13:31:12 +00002085 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002086 switch (result->type()) {
2087 case NORMAL: // fall through
2088 case FIELD:
2089 case CONSTANT_FUNCTION:
2090 case CALLBACKS:
2091 return result->GetAttributes();
2092 case INTERCEPTOR:
2093 return result->holder()->
2094 GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
Steve Blocka7e24c12009-10-30 11:49:00 +00002095 default:
2096 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +00002097 }
2098 }
2099 return ABSENT;
2100}
2101
2102
2103PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) {
2104 // Check whether the name is an array index.
2105 uint32_t index = 0;
2106 if (name->AsArrayIndex(&index)) {
2107 if (HasLocalElement(index)) return NONE;
2108 return ABSENT;
2109 }
2110 // Named property.
2111 LookupResult result;
2112 LocalLookup(name, &result);
2113 return GetPropertyAttribute(this, &result, name, false);
2114}
2115
2116
2117Object* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2118 int expected_additional_properties) {
2119 if (!HasFastProperties()) return this;
2120
2121 // The global object is always normalized.
2122 ASSERT(!IsGlobalObject());
2123
2124 // Allocate new content.
2125 int property_count = map()->NumberOfDescribedProperties();
2126 if (expected_additional_properties > 0) {
2127 property_count += expected_additional_properties;
2128 } else {
2129 property_count += 2; // Make space for two more properties.
2130 }
2131 Object* obj =
Steve Block6ded16b2010-05-10 14:33:55 +01002132 StringDictionary::Allocate(property_count);
Steve Blocka7e24c12009-10-30 11:49:00 +00002133 if (obj->IsFailure()) return obj;
2134 StringDictionary* dictionary = StringDictionary::cast(obj);
2135
2136 DescriptorArray* descs = map()->instance_descriptors();
2137 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2138 PropertyDetails details = descs->GetDetails(i);
2139 switch (details.type()) {
2140 case CONSTANT_FUNCTION: {
2141 PropertyDetails d =
2142 PropertyDetails(details.attributes(), NORMAL, details.index());
2143 Object* value = descs->GetConstantFunction(i);
2144 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2145 if (result->IsFailure()) return result;
2146 dictionary = StringDictionary::cast(result);
2147 break;
2148 }
2149 case FIELD: {
2150 PropertyDetails d =
2151 PropertyDetails(details.attributes(), NORMAL, details.index());
2152 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
2153 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2154 if (result->IsFailure()) return result;
2155 dictionary = StringDictionary::cast(result);
2156 break;
2157 }
2158 case CALLBACKS: {
2159 PropertyDetails d =
2160 PropertyDetails(details.attributes(), CALLBACKS, details.index());
2161 Object* value = descs->GetCallbacksObject(i);
2162 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2163 if (result->IsFailure()) return result;
2164 dictionary = StringDictionary::cast(result);
2165 break;
2166 }
2167 case MAP_TRANSITION:
2168 case CONSTANT_TRANSITION:
2169 case NULL_DESCRIPTOR:
2170 case INTERCEPTOR:
2171 break;
2172 default:
2173 UNREACHABLE();
2174 }
2175 }
2176
2177 // Copy the next enumeration index from instance descriptor.
2178 int index = map()->instance_descriptors()->NextEnumerationIndex();
2179 dictionary->SetNextEnumerationIndex(index);
2180
2181 // Allocate new map.
2182 obj = map()->CopyDropDescriptors();
2183 if (obj->IsFailure()) return obj;
2184 Map* new_map = Map::cast(obj);
2185
2186 // Clear inobject properties if needed by adjusting the instance size and
2187 // putting in a filler object instead of the inobject properties.
2188 if (mode == CLEAR_INOBJECT_PROPERTIES && map()->inobject_properties() > 0) {
2189 int instance_size_delta = map()->inobject_properties() * kPointerSize;
2190 int new_instance_size = map()->instance_size() - instance_size_delta;
2191 new_map->set_inobject_properties(0);
2192 new_map->set_instance_size(new_instance_size);
2193 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2194 instance_size_delta);
2195 }
2196 new_map->set_unused_property_fields(0);
2197
2198 // We have now successfully allocated all the necessary objects.
2199 // Changes can now be made with the guarantee that all of them take effect.
2200 set_map(new_map);
2201 map()->set_instance_descriptors(Heap::empty_descriptor_array());
2202
2203 set_properties(dictionary);
2204
2205 Counters::props_to_dictionary.Increment();
2206
2207#ifdef DEBUG
2208 if (FLAG_trace_normalization) {
2209 PrintF("Object properties have been normalized:\n");
2210 Print();
2211 }
2212#endif
2213 return this;
2214}
2215
2216
2217Object* JSObject::TransformToFastProperties(int unused_property_fields) {
2218 if (HasFastProperties()) return this;
2219 ASSERT(!IsGlobalObject());
2220 return property_dictionary()->
2221 TransformPropertiesToFastFor(this, unused_property_fields);
2222}
2223
2224
2225Object* JSObject::NormalizeElements() {
Steve Block3ce2e202009-11-05 08:53:23 +00002226 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002227 if (HasDictionaryElements()) return this;
Steve Block8defd9f2010-07-08 12:39:36 +01002228 ASSERT(map()->has_fast_elements());
2229
2230 Object* obj = map()->GetSlowElementsMap();
2231 if (obj->IsFailure()) return obj;
2232 Map* new_map = Map::cast(obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00002233
2234 // Get number of entries.
2235 FixedArray* array = FixedArray::cast(elements());
2236
2237 // Compute the effective length.
2238 int length = IsJSArray() ?
2239 Smi::cast(JSArray::cast(this)->length())->value() :
2240 array->length();
Steve Block8defd9f2010-07-08 12:39:36 +01002241 obj = NumberDictionary::Allocate(length);
Steve Blocka7e24c12009-10-30 11:49:00 +00002242 if (obj->IsFailure()) return obj;
2243 NumberDictionary* dictionary = NumberDictionary::cast(obj);
2244 // Copy entries.
2245 for (int i = 0; i < length; i++) {
2246 Object* value = array->get(i);
2247 if (!value->IsTheHole()) {
2248 PropertyDetails details = PropertyDetails(NONE, NORMAL);
2249 Object* result = dictionary->AddNumberEntry(i, array->get(i), details);
2250 if (result->IsFailure()) return result;
2251 dictionary = NumberDictionary::cast(result);
2252 }
2253 }
Steve Block8defd9f2010-07-08 12:39:36 +01002254 // Switch to using the dictionary as the backing storage for
2255 // elements. Set the new map first to satify the elements type
2256 // assert in set_elements().
2257 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002258 set_elements(dictionary);
2259
2260 Counters::elements_to_dictionary.Increment();
2261
2262#ifdef DEBUG
2263 if (FLAG_trace_normalization) {
2264 PrintF("Object elements have been normalized:\n");
2265 Print();
2266 }
2267#endif
2268
2269 return this;
2270}
2271
2272
2273Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) {
2274 // Check local property, ignore interceptor.
2275 LookupResult result;
2276 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002277 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002278
2279 // Normalize object if needed.
2280 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2281 if (obj->IsFailure()) return obj;
2282
2283 return DeleteNormalizedProperty(name, mode);
2284}
2285
2286
2287Object* JSObject::DeletePropertyWithInterceptor(String* name) {
2288 HandleScope scope;
2289 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2290 Handle<String> name_handle(name);
2291 Handle<JSObject> this_handle(this);
2292 if (!interceptor->deleter()->IsUndefined()) {
2293 v8::NamedPropertyDeleter deleter =
2294 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2295 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2296 CustomArguments args(interceptor->data(), this, this);
2297 v8::AccessorInfo info(args.end());
2298 v8::Handle<v8::Boolean> result;
2299 {
2300 // Leaving JavaScript.
2301 VMState state(EXTERNAL);
2302 result = deleter(v8::Utils::ToLocal(name_handle), info);
2303 }
2304 RETURN_IF_SCHEDULED_EXCEPTION();
2305 if (!result.IsEmpty()) {
2306 ASSERT(result->IsBoolean());
2307 return *v8::Utils::OpenHandle(*result);
2308 }
2309 }
2310 Object* raw_result =
2311 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
2312 RETURN_IF_SCHEDULED_EXCEPTION();
2313 return raw_result;
2314}
2315
2316
2317Object* JSObject::DeleteElementPostInterceptor(uint32_t index,
2318 DeleteMode mode) {
Steve Block3ce2e202009-11-05 08:53:23 +00002319 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002320 switch (GetElementsKind()) {
2321 case FAST_ELEMENTS: {
2322 uint32_t length = IsJSArray() ?
2323 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2324 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2325 if (index < length) {
2326 FixedArray::cast(elements())->set_the_hole(index);
2327 }
2328 break;
2329 }
2330 case DICTIONARY_ELEMENTS: {
2331 NumberDictionary* dictionary = element_dictionary();
2332 int entry = dictionary->FindEntry(index);
2333 if (entry != NumberDictionary::kNotFound) {
2334 return dictionary->DeleteProperty(entry, mode);
2335 }
2336 break;
2337 }
2338 default:
2339 UNREACHABLE();
2340 break;
2341 }
2342 return Heap::true_value();
2343}
2344
2345
2346Object* JSObject::DeleteElementWithInterceptor(uint32_t index) {
2347 // Make sure that the top context does not change when doing
2348 // callbacks or interceptor calls.
2349 AssertNoContextChange ncc;
2350 HandleScope scope;
2351 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2352 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2353 v8::IndexedPropertyDeleter deleter =
2354 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2355 Handle<JSObject> this_handle(this);
2356 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2357 CustomArguments args(interceptor->data(), this, this);
2358 v8::AccessorInfo info(args.end());
2359 v8::Handle<v8::Boolean> result;
2360 {
2361 // Leaving JavaScript.
2362 VMState state(EXTERNAL);
2363 result = deleter(index, info);
2364 }
2365 RETURN_IF_SCHEDULED_EXCEPTION();
2366 if (!result.IsEmpty()) {
2367 ASSERT(result->IsBoolean());
2368 return *v8::Utils::OpenHandle(*result);
2369 }
2370 Object* raw_result =
2371 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
2372 RETURN_IF_SCHEDULED_EXCEPTION();
2373 return raw_result;
2374}
2375
2376
2377Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
2378 // Check access rights if needed.
2379 if (IsAccessCheckNeeded() &&
2380 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
2381 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2382 return Heap::false_value();
2383 }
2384
2385 if (IsJSGlobalProxy()) {
2386 Object* proto = GetPrototype();
2387 if (proto->IsNull()) return Heap::false_value();
2388 ASSERT(proto->IsJSGlobalObject());
2389 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
2390 }
2391
2392 if (HasIndexedInterceptor()) {
2393 // Skip interceptor if forcing deletion.
2394 if (mode == FORCE_DELETION) {
2395 return DeleteElementPostInterceptor(index, mode);
2396 }
2397 return DeleteElementWithInterceptor(index);
2398 }
2399
2400 switch (GetElementsKind()) {
2401 case FAST_ELEMENTS: {
2402 uint32_t length = IsJSArray() ?
2403 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2404 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2405 if (index < length) {
2406 FixedArray::cast(elements())->set_the_hole(index);
2407 }
2408 break;
2409 }
Steve Block3ce2e202009-11-05 08:53:23 +00002410 case PIXEL_ELEMENTS:
2411 case EXTERNAL_BYTE_ELEMENTS:
2412 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2413 case EXTERNAL_SHORT_ELEMENTS:
2414 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2415 case EXTERNAL_INT_ELEMENTS:
2416 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2417 case EXTERNAL_FLOAT_ELEMENTS:
2418 // Pixel and external array elements cannot be deleted. Just
2419 // silently ignore here.
Steve Blocka7e24c12009-10-30 11:49:00 +00002420 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00002421 case DICTIONARY_ELEMENTS: {
2422 NumberDictionary* dictionary = element_dictionary();
2423 int entry = dictionary->FindEntry(index);
2424 if (entry != NumberDictionary::kNotFound) {
2425 return dictionary->DeleteProperty(entry, mode);
2426 }
2427 break;
2428 }
2429 default:
2430 UNREACHABLE();
2431 break;
2432 }
2433 return Heap::true_value();
2434}
2435
2436
2437Object* JSObject::DeleteProperty(String* name, DeleteMode mode) {
2438 // ECMA-262, 3rd, 8.6.2.5
2439 ASSERT(name->IsString());
2440
2441 // Check access rights if needed.
2442 if (IsAccessCheckNeeded() &&
2443 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2444 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2445 return Heap::false_value();
2446 }
2447
2448 if (IsJSGlobalProxy()) {
2449 Object* proto = GetPrototype();
2450 if (proto->IsNull()) return Heap::false_value();
2451 ASSERT(proto->IsJSGlobalObject());
2452 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
2453 }
2454
2455 uint32_t index = 0;
2456 if (name->AsArrayIndex(&index)) {
2457 return DeleteElement(index, mode);
2458 } else {
2459 LookupResult result;
2460 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002461 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002462 // Ignore attributes if forcing a deletion.
2463 if (result.IsDontDelete() && mode != FORCE_DELETION) {
2464 return Heap::false_value();
2465 }
2466 // Check for interceptor.
2467 if (result.type() == INTERCEPTOR) {
2468 // Skip interceptor if forcing a deletion.
2469 if (mode == FORCE_DELETION) {
2470 return DeletePropertyPostInterceptor(name, mode);
2471 }
2472 return DeletePropertyWithInterceptor(name);
2473 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002474 // Normalize object if needed.
2475 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2476 if (obj->IsFailure()) return obj;
2477 // Make sure the properties are normalized before removing the entry.
2478 return DeleteNormalizedProperty(name, mode);
2479 }
2480}
2481
2482
2483// Check whether this object references another object.
2484bool JSObject::ReferencesObject(Object* obj) {
2485 AssertNoAllocation no_alloc;
2486
2487 // Is the object the constructor for this object?
2488 if (map()->constructor() == obj) {
2489 return true;
2490 }
2491
2492 // Is the object the prototype for this object?
2493 if (map()->prototype() == obj) {
2494 return true;
2495 }
2496
2497 // Check if the object is among the named properties.
2498 Object* key = SlowReverseLookup(obj);
2499 if (key != Heap::undefined_value()) {
2500 return true;
2501 }
2502
2503 // Check if the object is among the indexed properties.
2504 switch (GetElementsKind()) {
2505 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002506 case EXTERNAL_BYTE_ELEMENTS:
2507 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2508 case EXTERNAL_SHORT_ELEMENTS:
2509 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2510 case EXTERNAL_INT_ELEMENTS:
2511 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2512 case EXTERNAL_FLOAT_ELEMENTS:
2513 // Raw pixels and external arrays do not reference other
2514 // objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00002515 break;
2516 case FAST_ELEMENTS: {
2517 int length = IsJSArray() ?
2518 Smi::cast(JSArray::cast(this)->length())->value() :
2519 FixedArray::cast(elements())->length();
2520 for (int i = 0; i < length; i++) {
2521 Object* element = FixedArray::cast(elements())->get(i);
2522 if (!element->IsTheHole() && element == obj) {
2523 return true;
2524 }
2525 }
2526 break;
2527 }
2528 case DICTIONARY_ELEMENTS: {
2529 key = element_dictionary()->SlowReverseLookup(obj);
2530 if (key != Heap::undefined_value()) {
2531 return true;
2532 }
2533 break;
2534 }
2535 default:
2536 UNREACHABLE();
2537 break;
2538 }
2539
Steve Block6ded16b2010-05-10 14:33:55 +01002540 // For functions check the context.
2541 if (IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002542 // Get the constructor function for arguments array.
2543 JSObject* arguments_boilerplate =
2544 Top::context()->global_context()->arguments_boilerplate();
2545 JSFunction* arguments_function =
2546 JSFunction::cast(arguments_boilerplate->map()->constructor());
2547
2548 // Get the context and don't check if it is the global context.
2549 JSFunction* f = JSFunction::cast(this);
2550 Context* context = f->context();
2551 if (context->IsGlobalContext()) {
2552 return false;
2553 }
2554
2555 // Check the non-special context slots.
2556 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2557 // Only check JS objects.
2558 if (context->get(i)->IsJSObject()) {
2559 JSObject* ctxobj = JSObject::cast(context->get(i));
2560 // If it is an arguments array check the content.
2561 if (ctxobj->map()->constructor() == arguments_function) {
2562 if (ctxobj->ReferencesObject(obj)) {
2563 return true;
2564 }
2565 } else if (ctxobj == obj) {
2566 return true;
2567 }
2568 }
2569 }
2570
2571 // Check the context extension if any.
2572 if (context->has_extension()) {
2573 return context->extension()->ReferencesObject(obj);
2574 }
2575 }
2576
2577 // No references to object.
2578 return false;
2579}
2580
2581
Steve Block8defd9f2010-07-08 12:39:36 +01002582Object* JSObject::PreventExtensions() {
2583 // If there are fast elements we normalize.
2584 if (HasFastElements()) {
2585 NormalizeElements();
2586 }
2587 // Make sure that we never go back to fast case.
2588 element_dictionary()->set_requires_slow_elements();
2589
2590 // Do a map transition, other objects with this map may still
2591 // be extensible.
2592 Object* new_map = map()->CopyDropTransitions();
2593 if (new_map->IsFailure()) return new_map;
2594 Map::cast(new_map)->set_is_extensible(false);
2595 set_map(Map::cast(new_map));
2596 ASSERT(!map()->is_extensible());
2597 return new_map;
2598}
2599
2600
Steve Blocka7e24c12009-10-30 11:49:00 +00002601// Tests for the fast common case for property enumeration:
Steve Blockd0582a62009-12-15 09:54:21 +00002602// - This object and all prototypes has an enum cache (which means that it has
2603// no interceptors and needs no access checks).
2604// - This object has no elements.
2605// - No prototype has enumerable properties/elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002606bool JSObject::IsSimpleEnum() {
Steve Blocka7e24c12009-10-30 11:49:00 +00002607 for (Object* o = this;
2608 o != Heap::null_value();
2609 o = JSObject::cast(o)->GetPrototype()) {
2610 JSObject* curr = JSObject::cast(o);
Steve Blocka7e24c12009-10-30 11:49:00 +00002611 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00002612 ASSERT(!curr->HasNamedInterceptor());
2613 ASSERT(!curr->HasIndexedInterceptor());
2614 ASSERT(!curr->IsAccessCheckNeeded());
Steve Blocka7e24c12009-10-30 11:49:00 +00002615 if (curr->NumberOfEnumElements() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002616 if (curr != this) {
2617 FixedArray* curr_fixed_array =
2618 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
Steve Blockd0582a62009-12-15 09:54:21 +00002619 if (curr_fixed_array->length() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002620 }
2621 }
2622 return true;
2623}
2624
2625
2626int Map::NumberOfDescribedProperties() {
2627 int result = 0;
2628 DescriptorArray* descs = instance_descriptors();
2629 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2630 if (descs->IsProperty(i)) result++;
2631 }
2632 return result;
2633}
2634
2635
2636int Map::PropertyIndexFor(String* name) {
2637 DescriptorArray* descs = instance_descriptors();
2638 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2639 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
2640 return descs->GetFieldIndex(i);
2641 }
2642 }
2643 return -1;
2644}
2645
2646
2647int Map::NextFreePropertyIndex() {
2648 int max_index = -1;
2649 DescriptorArray* descs = instance_descriptors();
2650 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2651 if (descs->GetType(i) == FIELD) {
2652 int current_index = descs->GetFieldIndex(i);
2653 if (current_index > max_index) max_index = current_index;
2654 }
2655 }
2656 return max_index + 1;
2657}
2658
2659
2660AccessorDescriptor* Map::FindAccessor(String* name) {
2661 DescriptorArray* descs = instance_descriptors();
2662 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2663 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
2664 return descs->GetCallbacks(i);
2665 }
2666 }
2667 return NULL;
2668}
2669
2670
2671void JSObject::LocalLookup(String* name, LookupResult* result) {
2672 ASSERT(name->IsString());
2673
2674 if (IsJSGlobalProxy()) {
2675 Object* proto = GetPrototype();
2676 if (proto->IsNull()) return result->NotFound();
2677 ASSERT(proto->IsJSGlobalObject());
2678 return JSObject::cast(proto)->LocalLookup(name, result);
2679 }
2680
2681 // Do not use inline caching if the object is a non-global object
2682 // that requires access checks.
2683 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
2684 result->DisallowCaching();
2685 }
2686
2687 // Check __proto__ before interceptor.
2688 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
2689 result->ConstantResult(this);
2690 return;
2691 }
2692
2693 // Check for lookup interceptor except when bootstrapping.
2694 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2695 result->InterceptorResult(this);
2696 return;
2697 }
2698
2699 LocalLookupRealNamedProperty(name, result);
2700}
2701
2702
2703void JSObject::Lookup(String* name, LookupResult* result) {
2704 // Ecma-262 3rd 8.6.2.4
2705 for (Object* current = this;
2706 current != Heap::null_value();
2707 current = JSObject::cast(current)->GetPrototype()) {
2708 JSObject::cast(current)->LocalLookup(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002709 if (result->IsProperty()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002710 }
2711 result->NotFound();
2712}
2713
2714
2715// Search object and it's prototype chain for callback properties.
2716void JSObject::LookupCallback(String* name, LookupResult* result) {
2717 for (Object* current = this;
2718 current != Heap::null_value();
2719 current = JSObject::cast(current)->GetPrototype()) {
2720 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002721 if (result->IsProperty() && result->type() == CALLBACKS) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002722 }
2723 result->NotFound();
2724}
2725
2726
2727Object* JSObject::DefineGetterSetter(String* name,
2728 PropertyAttributes attributes) {
2729 // Make sure that the top context does not change when doing callbacks or
2730 // interceptor calls.
2731 AssertNoContextChange ncc;
2732
Steve Blocka7e24c12009-10-30 11:49:00 +00002733 // Try to flatten before operating on the string.
Steve Block6ded16b2010-05-10 14:33:55 +01002734 name->TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00002735
Leon Clarkef7060e22010-06-03 12:02:55 +01002736 if (!CanSetCallback(name)) {
2737 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002738 }
2739
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002740 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00002741 bool is_element = name->AsArrayIndex(&index);
2742 if (is_element && IsJSArray()) return Heap::undefined_value();
2743
2744 if (is_element) {
2745 switch (GetElementsKind()) {
2746 case FAST_ELEMENTS:
2747 break;
2748 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002749 case EXTERNAL_BYTE_ELEMENTS:
2750 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2751 case EXTERNAL_SHORT_ELEMENTS:
2752 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2753 case EXTERNAL_INT_ELEMENTS:
2754 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2755 case EXTERNAL_FLOAT_ELEMENTS:
2756 // Ignore getters and setters on pixel and external array
2757 // elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002758 return Heap::undefined_value();
2759 case DICTIONARY_ELEMENTS: {
2760 // Lookup the index.
2761 NumberDictionary* dictionary = element_dictionary();
2762 int entry = dictionary->FindEntry(index);
2763 if (entry != NumberDictionary::kNotFound) {
2764 Object* result = dictionary->ValueAt(entry);
2765 PropertyDetails details = dictionary->DetailsAt(entry);
2766 if (details.IsReadOnly()) return Heap::undefined_value();
2767 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002768 if (result->IsFixedArray()) {
2769 return result;
2770 }
2771 // Otherwise allow to override it.
Steve Blocka7e24c12009-10-30 11:49:00 +00002772 }
2773 }
2774 break;
2775 }
2776 default:
2777 UNREACHABLE();
2778 break;
2779 }
2780 } else {
2781 // Lookup the name.
2782 LookupResult result;
2783 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002784 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002785 if (result.IsReadOnly()) return Heap::undefined_value();
2786 if (result.type() == CALLBACKS) {
2787 Object* obj = result.GetCallbackObject();
Leon Clarkef7060e22010-06-03 12:02:55 +01002788 // Need to preserve old getters/setters.
Leon Clarked91b9f72010-01-27 17:25:45 +00002789 if (obj->IsFixedArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002790 // Use set to update attributes.
2791 return SetPropertyCallback(name, obj, attributes);
Leon Clarked91b9f72010-01-27 17:25:45 +00002792 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002793 }
2794 }
2795 }
2796
2797 // Allocate the fixed array to hold getter and setter.
2798 Object* structure = Heap::AllocateFixedArray(2, TENURED);
2799 if (structure->IsFailure()) return structure;
Steve Blocka7e24c12009-10-30 11:49:00 +00002800
2801 if (is_element) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002802 return SetElementCallback(index, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002803 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01002804 return SetPropertyCallback(name, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002805 }
Leon Clarkef7060e22010-06-03 12:02:55 +01002806}
2807
2808
2809bool JSObject::CanSetCallback(String* name) {
2810 ASSERT(!IsAccessCheckNeeded()
2811 || Top::MayNamedAccess(this, name, v8::ACCESS_SET));
2812
2813 // Check if there is an API defined callback object which prohibits
2814 // callback overwriting in this object or it's prototype chain.
2815 // This mechanism is needed for instance in a browser setting, where
2816 // certain accessors such as window.location should not be allowed
2817 // to be overwritten because allowing overwriting could potentially
2818 // cause security problems.
2819 LookupResult callback_result;
2820 LookupCallback(name, &callback_result);
2821 if (callback_result.IsProperty()) {
2822 Object* obj = callback_result.GetCallbackObject();
2823 if (obj->IsAccessorInfo() &&
2824 AccessorInfo::cast(obj)->prohibits_overwriting()) {
2825 return false;
2826 }
2827 }
2828
2829 return true;
2830}
2831
2832
2833Object* JSObject::SetElementCallback(uint32_t index,
2834 Object* structure,
2835 PropertyAttributes attributes) {
2836 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
2837
2838 // Normalize elements to make this operation simple.
2839 Object* ok = NormalizeElements();
2840 if (ok->IsFailure()) return ok;
2841
2842 // Update the dictionary with the new CALLBACKS property.
2843 Object* dict =
2844 element_dictionary()->Set(index, structure, details);
2845 if (dict->IsFailure()) return dict;
2846
2847 NumberDictionary* elements = NumberDictionary::cast(dict);
2848 elements->set_requires_slow_elements();
2849 // Set the potential new dictionary on the object.
2850 set_elements(elements);
Steve Blocka7e24c12009-10-30 11:49:00 +00002851
2852 return structure;
2853}
2854
2855
Leon Clarkef7060e22010-06-03 12:02:55 +01002856Object* JSObject::SetPropertyCallback(String* name,
2857 Object* structure,
2858 PropertyAttributes attributes) {
2859 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
2860
2861 bool convert_back_to_fast = HasFastProperties() &&
2862 (map()->instance_descriptors()->number_of_descriptors()
2863 < DescriptorArray::kMaxNumberOfDescriptors);
2864
2865 // Normalize object to make this operation simple.
2866 Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2867 if (ok->IsFailure()) return ok;
2868
2869 // For the global object allocate a new map to invalidate the global inline
2870 // caches which have a global property cell reference directly in the code.
2871 if (IsGlobalObject()) {
2872 Object* new_map = map()->CopyDropDescriptors();
2873 if (new_map->IsFailure()) return new_map;
2874 set_map(Map::cast(new_map));
2875 }
2876
2877 // Update the dictionary with the new CALLBACKS property.
2878 Object* result = SetNormalizedProperty(name, structure, details);
2879 if (result->IsFailure()) return result;
2880
2881 if (convert_back_to_fast) {
2882 ok = TransformToFastProperties(0);
2883 if (ok->IsFailure()) return ok;
2884 }
2885 return result;
2886}
2887
Steve Blocka7e24c12009-10-30 11:49:00 +00002888Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
2889 PropertyAttributes attributes) {
2890 // Check access rights if needed.
2891 if (IsAccessCheckNeeded() &&
Leon Clarkef7060e22010-06-03 12:02:55 +01002892 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
2893 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Steve Blocka7e24c12009-10-30 11:49:00 +00002894 return Heap::undefined_value();
2895 }
2896
2897 if (IsJSGlobalProxy()) {
2898 Object* proto = GetPrototype();
2899 if (proto->IsNull()) return this;
2900 ASSERT(proto->IsJSGlobalObject());
2901 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
2902 fun, attributes);
2903 }
2904
2905 Object* array = DefineGetterSetter(name, attributes);
2906 if (array->IsFailure() || array->IsUndefined()) return array;
2907 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
2908 return this;
2909}
2910
2911
Leon Clarkef7060e22010-06-03 12:02:55 +01002912Object* JSObject::DefineAccessor(AccessorInfo* info) {
2913 String* name = String::cast(info->name());
2914 // Check access rights if needed.
2915 if (IsAccessCheckNeeded() &&
2916 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
2917 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
2918 return Heap::undefined_value();
2919 }
2920
2921 if (IsJSGlobalProxy()) {
2922 Object* proto = GetPrototype();
2923 if (proto->IsNull()) return this;
2924 ASSERT(proto->IsJSGlobalObject());
2925 return JSObject::cast(proto)->DefineAccessor(info);
2926 }
2927
2928 // Make sure that the top context does not change when doing callbacks or
2929 // interceptor calls.
2930 AssertNoContextChange ncc;
2931
2932 // Try to flatten before operating on the string.
2933 name->TryFlatten();
2934
2935 if (!CanSetCallback(name)) {
2936 return Heap::undefined_value();
2937 }
2938
2939 uint32_t index = 0;
2940 bool is_element = name->AsArrayIndex(&index);
2941
2942 if (is_element) {
2943 if (IsJSArray()) return Heap::undefined_value();
2944
2945 // Accessors overwrite previous callbacks (cf. with getters/setters).
2946 switch (GetElementsKind()) {
2947 case FAST_ELEMENTS:
2948 break;
2949 case PIXEL_ELEMENTS:
2950 case EXTERNAL_BYTE_ELEMENTS:
2951 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2952 case EXTERNAL_SHORT_ELEMENTS:
2953 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2954 case EXTERNAL_INT_ELEMENTS:
2955 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2956 case EXTERNAL_FLOAT_ELEMENTS:
2957 // Ignore getters and setters on pixel and external array
2958 // elements.
2959 return Heap::undefined_value();
2960 case DICTIONARY_ELEMENTS:
2961 break;
2962 default:
2963 UNREACHABLE();
2964 break;
2965 }
2966
2967 SetElementCallback(index, info, info->property_attributes());
2968 } else {
2969 // Lookup the name.
2970 LookupResult result;
2971 LocalLookup(name, &result);
2972 // ES5 forbids turning a property into an accessor if it's not
2973 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
2974 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
2975 return Heap::undefined_value();
2976 }
2977 SetPropertyCallback(name, info, info->property_attributes());
2978 }
2979
2980 return this;
2981}
2982
2983
Steve Blocka7e24c12009-10-30 11:49:00 +00002984Object* JSObject::LookupAccessor(String* name, bool is_getter) {
2985 // Make sure that the top context does not change when doing callbacks or
2986 // interceptor calls.
2987 AssertNoContextChange ncc;
2988
2989 // Check access rights if needed.
2990 if (IsAccessCheckNeeded() &&
2991 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2992 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2993 return Heap::undefined_value();
2994 }
2995
2996 // Make the lookup and include prototypes.
2997 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002998 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00002999 if (name->AsArrayIndex(&index)) {
3000 for (Object* obj = this;
3001 obj != Heap::null_value();
3002 obj = JSObject::cast(obj)->GetPrototype()) {
3003 JSObject* js_object = JSObject::cast(obj);
3004 if (js_object->HasDictionaryElements()) {
3005 NumberDictionary* dictionary = js_object->element_dictionary();
3006 int entry = dictionary->FindEntry(index);
3007 if (entry != NumberDictionary::kNotFound) {
3008 Object* element = dictionary->ValueAt(entry);
3009 PropertyDetails details = dictionary->DetailsAt(entry);
3010 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003011 if (element->IsFixedArray()) {
3012 return FixedArray::cast(element)->get(accessor_index);
3013 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003014 }
3015 }
3016 }
3017 }
3018 } else {
3019 for (Object* obj = this;
3020 obj != Heap::null_value();
3021 obj = JSObject::cast(obj)->GetPrototype()) {
3022 LookupResult result;
3023 JSObject::cast(obj)->LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00003024 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003025 if (result.IsReadOnly()) return Heap::undefined_value();
3026 if (result.type() == CALLBACKS) {
3027 Object* obj = result.GetCallbackObject();
3028 if (obj->IsFixedArray()) {
3029 return FixedArray::cast(obj)->get(accessor_index);
3030 }
3031 }
3032 }
3033 }
3034 }
3035 return Heap::undefined_value();
3036}
3037
3038
3039Object* JSObject::SlowReverseLookup(Object* value) {
3040 if (HasFastProperties()) {
3041 DescriptorArray* descs = map()->instance_descriptors();
3042 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3043 if (descs->GetType(i) == FIELD) {
3044 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3045 return descs->GetKey(i);
3046 }
3047 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3048 if (descs->GetConstantFunction(i) == value) {
3049 return descs->GetKey(i);
3050 }
3051 }
3052 }
3053 return Heap::undefined_value();
3054 } else {
3055 return property_dictionary()->SlowReverseLookup(value);
3056 }
3057}
3058
3059
3060Object* Map::CopyDropDescriptors() {
3061 Object* result = Heap::AllocateMap(instance_type(), instance_size());
3062 if (result->IsFailure()) return result;
3063 Map::cast(result)->set_prototype(prototype());
3064 Map::cast(result)->set_constructor(constructor());
3065 // Don't copy descriptors, so map transitions always remain a forest.
3066 // If we retained the same descriptors we would have two maps
3067 // pointing to the same transition which is bad because the garbage
3068 // collector relies on being able to reverse pointers from transitions
3069 // to maps. If properties need to be retained use CopyDropTransitions.
3070 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
3071 // Please note instance_type and instance_size are set when allocated.
3072 Map::cast(result)->set_inobject_properties(inobject_properties());
3073 Map::cast(result)->set_unused_property_fields(unused_property_fields());
3074
3075 // If the map has pre-allocated properties always start out with a descriptor
3076 // array describing these properties.
3077 if (pre_allocated_property_fields() > 0) {
3078 ASSERT(constructor()->IsJSFunction());
3079 JSFunction* ctor = JSFunction::cast(constructor());
3080 Object* descriptors =
3081 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
3082 if (descriptors->IsFailure()) return descriptors;
3083 Map::cast(result)->set_instance_descriptors(
3084 DescriptorArray::cast(descriptors));
3085 Map::cast(result)->set_pre_allocated_property_fields(
3086 pre_allocated_property_fields());
3087 }
3088 Map::cast(result)->set_bit_field(bit_field());
3089 Map::cast(result)->set_bit_field2(bit_field2());
3090 Map::cast(result)->ClearCodeCache();
3091 return result;
3092}
3093
3094
3095Object* Map::CopyDropTransitions() {
3096 Object* new_map = CopyDropDescriptors();
3097 if (new_map->IsFailure()) return new_map;
3098 Object* descriptors = instance_descriptors()->RemoveTransitions();
3099 if (descriptors->IsFailure()) return descriptors;
3100 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
Steve Block8defd9f2010-07-08 12:39:36 +01003101 return new_map;
Steve Blocka7e24c12009-10-30 11:49:00 +00003102}
3103
3104
3105Object* Map::UpdateCodeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003106 // Allocate the code cache if not present.
3107 if (code_cache()->IsFixedArray()) {
3108 Object* result = Heap::AllocateCodeCache();
3109 if (result->IsFailure()) return result;
3110 set_code_cache(result);
3111 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003112
Steve Block6ded16b2010-05-10 14:33:55 +01003113 // Update the code cache.
3114 return CodeCache::cast(code_cache())->Update(name, code);
3115}
3116
3117
3118Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3119 // Do a lookup if a code cache exists.
3120 if (!code_cache()->IsFixedArray()) {
3121 return CodeCache::cast(code_cache())->Lookup(name, flags);
3122 } else {
3123 return Heap::undefined_value();
3124 }
3125}
3126
3127
3128int Map::IndexInCodeCache(Object* name, Code* code) {
3129 // Get the internal index if a code cache exists.
3130 if (!code_cache()->IsFixedArray()) {
3131 return CodeCache::cast(code_cache())->GetIndex(name, code);
3132 }
3133 return -1;
3134}
3135
3136
3137void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
3138 // No GC is supposed to happen between a call to IndexInCodeCache and
3139 // RemoveFromCodeCache so the code cache must be there.
3140 ASSERT(!code_cache()->IsFixedArray());
3141 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
3142}
3143
3144
3145Object* CodeCache::Update(String* name, Code* code) {
3146 ASSERT(code->ic_state() == MONOMORPHIC);
3147
3148 // The number of monomorphic stubs for normal load/store/call IC's can grow to
3149 // a large number and therefore they need to go into a hash table. They are
3150 // used to load global properties from cells.
3151 if (code->type() == NORMAL) {
3152 // Make sure that a hash table is allocated for the normal load code cache.
3153 if (normal_type_cache()->IsUndefined()) {
3154 Object* result =
3155 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
3156 if (result->IsFailure()) return result;
3157 set_normal_type_cache(result);
3158 }
3159 return UpdateNormalTypeCache(name, code);
3160 } else {
3161 ASSERT(default_cache()->IsFixedArray());
3162 return UpdateDefaultCache(name, code);
3163 }
3164}
3165
3166
3167Object* CodeCache::UpdateDefaultCache(String* name, Code* code) {
3168 // When updating the default code cache we disregard the type encoded in the
Steve Blocka7e24c12009-10-30 11:49:00 +00003169 // flags. This allows call constant stubs to overwrite call field
3170 // stubs, etc.
3171 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
3172
3173 // First check whether we can update existing code cache without
3174 // extending it.
Steve Block6ded16b2010-05-10 14:33:55 +01003175 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003176 int length = cache->length();
3177 int deleted_index = -1;
Steve Block6ded16b2010-05-10 14:33:55 +01003178 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003179 Object* key = cache->get(i);
3180 if (key->IsNull()) {
3181 if (deleted_index < 0) deleted_index = i;
3182 continue;
3183 }
3184 if (key->IsUndefined()) {
3185 if (deleted_index >= 0) i = deleted_index;
Steve Block6ded16b2010-05-10 14:33:55 +01003186 cache->set(i + kCodeCacheEntryNameOffset, name);
3187 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003188 return this;
3189 }
3190 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003191 Code::Flags found =
3192 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
Steve Blocka7e24c12009-10-30 11:49:00 +00003193 if (Code::RemoveTypeFromFlags(found) == flags) {
Steve Block6ded16b2010-05-10 14:33:55 +01003194 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003195 return this;
3196 }
3197 }
3198 }
3199
3200 // Reached the end of the code cache. If there were deleted
3201 // elements, reuse the space for the first of them.
3202 if (deleted_index >= 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01003203 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
3204 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003205 return this;
3206 }
3207
Steve Block6ded16b2010-05-10 14:33:55 +01003208 // Extend the code cache with some new entries (at least one). Must be a
3209 // multiple of the entry size.
3210 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
3211 new_length = new_length - new_length % kCodeCacheEntrySize;
3212 ASSERT((new_length % kCodeCacheEntrySize) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00003213 Object* result = cache->CopySize(new_length);
3214 if (result->IsFailure()) return result;
3215
3216 // Add the (name, code) pair to the new cache.
3217 cache = FixedArray::cast(result);
Steve Block6ded16b2010-05-10 14:33:55 +01003218 cache->set(length + kCodeCacheEntryNameOffset, name);
3219 cache->set(length + kCodeCacheEntryCodeOffset, code);
3220 set_default_cache(cache);
Steve Blocka7e24c12009-10-30 11:49:00 +00003221 return this;
3222}
3223
3224
Steve Block6ded16b2010-05-10 14:33:55 +01003225Object* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
3226 // Adding a new entry can cause a new cache to be allocated.
3227 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3228 Object* new_cache = cache->Put(name, code);
3229 if (new_cache->IsFailure()) return new_cache;
3230 set_normal_type_cache(new_cache);
3231 return this;
3232}
3233
3234
3235Object* CodeCache::Lookup(String* name, Code::Flags flags) {
3236 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
3237 return LookupNormalTypeCache(name, flags);
3238 } else {
3239 return LookupDefaultCache(name, flags);
3240 }
3241}
3242
3243
3244Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
3245 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003246 int length = cache->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003247 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
3248 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
Steve Blocka7e24c12009-10-30 11:49:00 +00003249 // Skip deleted elements.
3250 if (key->IsNull()) continue;
3251 if (key->IsUndefined()) return key;
3252 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003253 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
3254 if (code->flags() == flags) {
3255 return code;
3256 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003257 }
3258 }
3259 return Heap::undefined_value();
3260}
3261
3262
Steve Block6ded16b2010-05-10 14:33:55 +01003263Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
3264 if (!normal_type_cache()->IsUndefined()) {
3265 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3266 return cache->Lookup(name, flags);
3267 } else {
3268 return Heap::undefined_value();
3269 }
3270}
3271
3272
3273int CodeCache::GetIndex(Object* name, Code* code) {
3274 if (code->type() == NORMAL) {
3275 if (normal_type_cache()->IsUndefined()) return -1;
3276 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3277 return cache->GetIndex(String::cast(name), code->flags());
3278 }
3279
3280 FixedArray* array = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003281 int len = array->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003282 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
3283 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003284 }
3285 return -1;
3286}
3287
3288
Steve Block6ded16b2010-05-10 14:33:55 +01003289void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
3290 if (code->type() == NORMAL) {
3291 ASSERT(!normal_type_cache()->IsUndefined());
3292 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3293 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
3294 cache->RemoveByIndex(index);
3295 } else {
3296 FixedArray* array = default_cache();
3297 ASSERT(array->length() >= index && array->get(index)->IsCode());
3298 // Use null instead of undefined for deleted elements to distinguish
3299 // deleted elements from unused elements. This distinction is used
3300 // when looking up in the cache and when updating the cache.
3301 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
3302 array->set_null(index - 1); // Name.
3303 array->set_null(index); // Code.
3304 }
3305}
3306
3307
3308// The key in the code cache hash table consists of the property name and the
3309// code object. The actual match is on the name and the code flags. If a key
3310// is created using the flags and not a code object it can only be used for
3311// lookup not to create a new entry.
3312class CodeCacheHashTableKey : public HashTableKey {
3313 public:
3314 CodeCacheHashTableKey(String* name, Code::Flags flags)
3315 : name_(name), flags_(flags), code_(NULL) { }
3316
3317 CodeCacheHashTableKey(String* name, Code* code)
3318 : name_(name),
3319 flags_(code->flags()),
3320 code_(code) { }
3321
3322
3323 bool IsMatch(Object* other) {
3324 if (!other->IsFixedArray()) return false;
3325 FixedArray* pair = FixedArray::cast(other);
3326 String* name = String::cast(pair->get(0));
3327 Code::Flags flags = Code::cast(pair->get(1))->flags();
3328 if (flags != flags_) {
3329 return false;
3330 }
3331 return name_->Equals(name);
3332 }
3333
3334 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
3335 return name->Hash() ^ flags;
3336 }
3337
3338 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
3339
3340 uint32_t HashForObject(Object* obj) {
3341 FixedArray* pair = FixedArray::cast(obj);
3342 String* name = String::cast(pair->get(0));
3343 Code* code = Code::cast(pair->get(1));
3344 return NameFlagsHashHelper(name, code->flags());
3345 }
3346
3347 Object* AsObject() {
3348 ASSERT(code_ != NULL);
3349 Object* obj = Heap::AllocateFixedArray(2);
3350 if (obj->IsFailure()) return obj;
3351 FixedArray* pair = FixedArray::cast(obj);
3352 pair->set(0, name_);
3353 pair->set(1, code_);
3354 return pair;
3355 }
3356
3357 private:
3358 String* name_;
3359 Code::Flags flags_;
3360 Code* code_;
3361};
3362
3363
3364Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
3365 CodeCacheHashTableKey key(name, flags);
3366 int entry = FindEntry(&key);
3367 if (entry == kNotFound) return Heap::undefined_value();
3368 return get(EntryToIndex(entry) + 1);
3369}
3370
3371
3372Object* CodeCacheHashTable::Put(String* name, Code* code) {
3373 CodeCacheHashTableKey key(name, code);
3374 Object* obj = EnsureCapacity(1, &key);
3375 if (obj->IsFailure()) return obj;
3376
3377 // Don't use this, as the table might have grown.
3378 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
3379
3380 int entry = cache->FindInsertionEntry(key.Hash());
3381 Object* k = key.AsObject();
3382 if (k->IsFailure()) return k;
3383
3384 cache->set(EntryToIndex(entry), k);
3385 cache->set(EntryToIndex(entry) + 1, code);
3386 cache->ElementAdded();
3387 return cache;
3388}
3389
3390
3391int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
3392 CodeCacheHashTableKey key(name, flags);
3393 int entry = FindEntry(&key);
3394 return (entry == kNotFound) ? -1 : entry;
3395}
3396
3397
3398void CodeCacheHashTable::RemoveByIndex(int index) {
3399 ASSERT(index >= 0);
3400 set(EntryToIndex(index), Heap::null_value());
3401 set(EntryToIndex(index) + 1, Heap::null_value());
3402 ElementRemoved();
Steve Blocka7e24c12009-10-30 11:49:00 +00003403}
3404
3405
3406void FixedArray::FixedArrayIterateBody(ObjectVisitor* v) {
3407 IteratePointers(v, kHeaderSize, kHeaderSize + length() * kPointerSize);
3408}
3409
3410
3411static bool HasKey(FixedArray* array, Object* key) {
3412 int len0 = array->length();
3413 for (int i = 0; i < len0; i++) {
3414 Object* element = array->get(i);
3415 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3416 if (element->IsString() &&
3417 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3418 return true;
3419 }
3420 }
3421 return false;
3422}
3423
3424
3425Object* FixedArray::AddKeysFromJSArray(JSArray* array) {
Steve Block3ce2e202009-11-05 08:53:23 +00003426 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00003427 switch (array->GetElementsKind()) {
3428 case JSObject::FAST_ELEMENTS:
3429 return UnionOfKeys(FixedArray::cast(array->elements()));
3430 case JSObject::DICTIONARY_ELEMENTS: {
3431 NumberDictionary* dict = array->element_dictionary();
3432 int size = dict->NumberOfElements();
3433
3434 // Allocate a temporary fixed array.
3435 Object* object = Heap::AllocateFixedArray(size);
3436 if (object->IsFailure()) return object;
3437 FixedArray* key_array = FixedArray::cast(object);
3438
3439 int capacity = dict->Capacity();
3440 int pos = 0;
3441 // Copy the elements from the JSArray to the temporary fixed array.
3442 for (int i = 0; i < capacity; i++) {
3443 if (dict->IsKey(dict->KeyAt(i))) {
3444 key_array->set(pos++, dict->ValueAt(i));
3445 }
3446 }
3447 // Compute the union of this and the temporary fixed array.
3448 return UnionOfKeys(key_array);
3449 }
3450 default:
3451 UNREACHABLE();
3452 }
3453 UNREACHABLE();
3454 return Heap::null_value(); // Failure case needs to "return" a value.
3455}
3456
3457
3458Object* FixedArray::UnionOfKeys(FixedArray* other) {
3459 int len0 = length();
3460 int len1 = other->length();
3461 // Optimize if either is empty.
3462 if (len0 == 0) return other;
3463 if (len1 == 0) return this;
3464
3465 // Compute how many elements are not in this.
3466 int extra = 0;
3467 for (int y = 0; y < len1; y++) {
3468 Object* value = other->get(y);
3469 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3470 }
3471
3472 if (extra == 0) return this;
3473
3474 // Allocate the result
3475 Object* obj = Heap::AllocateFixedArray(len0 + extra);
3476 if (obj->IsFailure()) return obj;
3477 // Fill in the content
Leon Clarke4515c472010-02-03 11:58:03 +00003478 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003479 FixedArray* result = FixedArray::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00003480 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003481 for (int i = 0; i < len0; i++) {
3482 result->set(i, get(i), mode);
3483 }
3484 // Fill in the extra keys.
3485 int index = 0;
3486 for (int y = 0; y < len1; y++) {
3487 Object* value = other->get(y);
3488 if (!value->IsTheHole() && !HasKey(this, value)) {
3489 result->set(len0 + index, other->get(y), mode);
3490 index++;
3491 }
3492 }
3493 ASSERT(extra == index);
3494 return result;
3495}
3496
3497
3498Object* FixedArray::CopySize(int new_length) {
3499 if (new_length == 0) return Heap::empty_fixed_array();
3500 Object* obj = Heap::AllocateFixedArray(new_length);
3501 if (obj->IsFailure()) return obj;
3502 FixedArray* result = FixedArray::cast(obj);
3503 // Copy the content
Leon Clarke4515c472010-02-03 11:58:03 +00003504 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003505 int len = length();
3506 if (new_length < len) len = new_length;
3507 result->set_map(map());
Leon Clarke4515c472010-02-03 11:58:03 +00003508 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003509 for (int i = 0; i < len; i++) {
3510 result->set(i, get(i), mode);
3511 }
3512 return result;
3513}
3514
3515
3516void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
Leon Clarke4515c472010-02-03 11:58:03 +00003517 AssertNoAllocation no_gc;
3518 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003519 for (int index = 0; index < len; index++) {
3520 dest->set(dest_pos+index, get(pos+index), mode);
3521 }
3522}
3523
3524
3525#ifdef DEBUG
3526bool FixedArray::IsEqualTo(FixedArray* other) {
3527 if (length() != other->length()) return false;
3528 for (int i = 0 ; i < length(); ++i) {
3529 if (get(i) != other->get(i)) return false;
3530 }
3531 return true;
3532}
3533#endif
3534
3535
3536Object* DescriptorArray::Allocate(int number_of_descriptors) {
3537 if (number_of_descriptors == 0) {
3538 return Heap::empty_descriptor_array();
3539 }
3540 // Allocate the array of keys.
Leon Clarkee46be812010-01-19 14:06:41 +00003541 Object* array =
3542 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
Steve Blocka7e24c12009-10-30 11:49:00 +00003543 if (array->IsFailure()) return array;
3544 // Do not use DescriptorArray::cast on incomplete object.
3545 FixedArray* result = FixedArray::cast(array);
3546
3547 // Allocate the content array and set it in the descriptor array.
3548 array = Heap::AllocateFixedArray(number_of_descriptors << 1);
3549 if (array->IsFailure()) return array;
3550 result->set(kContentArrayIndex, array);
3551 result->set(kEnumerationIndexIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00003552 Smi::FromInt(PropertyDetails::kInitialIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +00003553 return result;
3554}
3555
3556
3557void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3558 FixedArray* new_cache) {
3559 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3560 if (HasEnumCache()) {
3561 FixedArray::cast(get(kEnumerationIndexIndex))->
3562 set(kEnumCacheBridgeCacheIndex, new_cache);
3563 } else {
3564 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3565 FixedArray::cast(bridge_storage)->
3566 set(kEnumCacheBridgeCacheIndex, new_cache);
3567 fast_set(FixedArray::cast(bridge_storage),
3568 kEnumCacheBridgeEnumIndex,
3569 get(kEnumerationIndexIndex));
3570 set(kEnumerationIndexIndex, bridge_storage);
3571 }
3572}
3573
3574
3575Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
3576 TransitionFlag transition_flag) {
3577 // Transitions are only kept when inserting another transition.
3578 // This precondition is not required by this function's implementation, but
3579 // is currently required by the semantics of maps, so we check it.
3580 // Conversely, we filter after replacing, so replacing a transition and
3581 // removing all other transitions is not supported.
3582 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3583 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
3584 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
3585
3586 // Ensure the key is a symbol.
3587 Object* result = descriptor->KeyToSymbol();
3588 if (result->IsFailure()) return result;
3589
3590 int transitions = 0;
3591 int null_descriptors = 0;
3592 if (remove_transitions) {
3593 for (int i = 0; i < number_of_descriptors(); i++) {
3594 if (IsTransition(i)) transitions++;
3595 if (IsNullDescriptor(i)) null_descriptors++;
3596 }
3597 } else {
3598 for (int i = 0; i < number_of_descriptors(); i++) {
3599 if (IsNullDescriptor(i)) null_descriptors++;
3600 }
3601 }
3602 int new_size = number_of_descriptors() - transitions - null_descriptors;
3603
3604 // If key is in descriptor, we replace it in-place when filtering.
3605 // Count a null descriptor for key as inserted, not replaced.
3606 int index = Search(descriptor->GetKey());
3607 const bool inserting = (index == kNotFound);
3608 const bool replacing = !inserting;
3609 bool keep_enumeration_index = false;
3610 if (inserting) {
3611 ++new_size;
3612 }
3613 if (replacing) {
3614 // We are replacing an existing descriptor. We keep the enumeration
3615 // index of a visible property.
3616 PropertyType t = PropertyDetails(GetDetails(index)).type();
3617 if (t == CONSTANT_FUNCTION ||
3618 t == FIELD ||
3619 t == CALLBACKS ||
3620 t == INTERCEPTOR) {
3621 keep_enumeration_index = true;
3622 } else if (remove_transitions) {
3623 // Replaced descriptor has been counted as removed if it is
3624 // a transition that will be replaced. Adjust count in this case.
3625 ++new_size;
3626 }
3627 }
3628 result = Allocate(new_size);
3629 if (result->IsFailure()) return result;
3630 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3631 // Set the enumeration index in the descriptors and set the enumeration index
3632 // in the result.
3633 int enumeration_index = NextEnumerationIndex();
3634 if (!descriptor->GetDetails().IsTransition()) {
3635 if (keep_enumeration_index) {
3636 descriptor->SetEnumerationIndex(
3637 PropertyDetails(GetDetails(index)).index());
3638 } else {
3639 descriptor->SetEnumerationIndex(enumeration_index);
3640 ++enumeration_index;
3641 }
3642 }
3643 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3644
3645 // Copy the descriptors, filtering out transitions and null descriptors,
3646 // and inserting or replacing a descriptor.
3647 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
3648 int from_index = 0;
3649 int to_index = 0;
3650
3651 for (; from_index < number_of_descriptors(); from_index++) {
3652 String* key = GetKey(from_index);
3653 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
3654 break;
3655 }
3656 if (IsNullDescriptor(from_index)) continue;
3657 if (remove_transitions && IsTransition(from_index)) continue;
3658 new_descriptors->CopyFrom(to_index++, this, from_index);
3659 }
3660
3661 new_descriptors->Set(to_index++, descriptor);
3662 if (replacing) from_index++;
3663
3664 for (; from_index < number_of_descriptors(); from_index++) {
3665 if (IsNullDescriptor(from_index)) continue;
3666 if (remove_transitions && IsTransition(from_index)) continue;
3667 new_descriptors->CopyFrom(to_index++, this, from_index);
3668 }
3669
3670 ASSERT(to_index == new_descriptors->number_of_descriptors());
3671 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
3672
3673 return new_descriptors;
3674}
3675
3676
3677Object* DescriptorArray::RemoveTransitions() {
3678 // Remove all transitions and null descriptors. Return a copy of the array
3679 // with all transitions removed, or a Failure object if the new array could
3680 // not be allocated.
3681
3682 // Compute the size of the map transition entries to be removed.
3683 int num_removed = 0;
3684 for (int i = 0; i < number_of_descriptors(); i++) {
3685 if (!IsProperty(i)) num_removed++;
3686 }
3687
3688 // Allocate the new descriptor array.
3689 Object* result = Allocate(number_of_descriptors() - num_removed);
3690 if (result->IsFailure()) return result;
3691 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3692
3693 // Copy the content.
3694 int next_descriptor = 0;
3695 for (int i = 0; i < number_of_descriptors(); i++) {
3696 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
3697 }
3698 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
3699
3700 return new_descriptors;
3701}
3702
3703
3704void DescriptorArray::Sort() {
3705 // In-place heap sort.
3706 int len = number_of_descriptors();
3707
3708 // Bottom-up max-heap construction.
Steve Block6ded16b2010-05-10 14:33:55 +01003709 // Index of the last node with children
3710 const int max_parent_index = (len / 2) - 1;
3711 for (int i = max_parent_index; i >= 0; --i) {
3712 int parent_index = i;
3713 const uint32_t parent_hash = GetKey(i)->Hash();
3714 while (parent_index <= max_parent_index) {
3715 int child_index = 2 * parent_index + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003716 uint32_t child_hash = GetKey(child_index)->Hash();
Steve Block6ded16b2010-05-10 14:33:55 +01003717 if (child_index + 1 < len) {
3718 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
3719 if (right_child_hash > child_hash) {
3720 child_index++;
3721 child_hash = right_child_hash;
3722 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003723 }
Steve Block6ded16b2010-05-10 14:33:55 +01003724 if (child_hash <= parent_hash) break;
3725 Swap(parent_index, child_index);
3726 // Now element at child_index could be < its children.
3727 parent_index = child_index; // parent_hash remains correct.
Steve Blocka7e24c12009-10-30 11:49:00 +00003728 }
3729 }
3730
3731 // Extract elements and create sorted array.
3732 for (int i = len - 1; i > 0; --i) {
3733 // Put max element at the back of the array.
3734 Swap(0, i);
3735 // Sift down the new top element.
3736 int parent_index = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01003737 const uint32_t parent_hash = GetKey(parent_index)->Hash();
3738 const int max_parent_index = (i / 2) - 1;
3739 while (parent_index <= max_parent_index) {
3740 int child_index = parent_index * 2 + 1;
3741 uint32_t child_hash = GetKey(child_index)->Hash();
3742 if (child_index + 1 < i) {
3743 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
3744 if (right_child_hash > child_hash) {
3745 child_index++;
3746 child_hash = right_child_hash;
3747 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003748 }
Steve Block6ded16b2010-05-10 14:33:55 +01003749 if (child_hash <= parent_hash) break;
3750 Swap(parent_index, child_index);
3751 parent_index = child_index;
Steve Blocka7e24c12009-10-30 11:49:00 +00003752 }
3753 }
3754
3755 SLOW_ASSERT(IsSortedNoDuplicates());
3756}
3757
3758
3759int DescriptorArray::BinarySearch(String* name, int low, int high) {
3760 uint32_t hash = name->Hash();
3761
3762 while (low <= high) {
3763 int mid = (low + high) / 2;
3764 String* mid_name = GetKey(mid);
3765 uint32_t mid_hash = mid_name->Hash();
3766
3767 if (mid_hash > hash) {
3768 high = mid - 1;
3769 continue;
3770 }
3771 if (mid_hash < hash) {
3772 low = mid + 1;
3773 continue;
3774 }
3775 // Found an element with the same hash-code.
3776 ASSERT(hash == mid_hash);
3777 // There might be more, so we find the first one and
3778 // check them all to see if we have a match.
3779 if (name == mid_name && !is_null_descriptor(mid)) return mid;
3780 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
3781 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
3782 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
3783 }
3784 break;
3785 }
3786 return kNotFound;
3787}
3788
3789
3790int DescriptorArray::LinearSearch(String* name, int len) {
3791 uint32_t hash = name->Hash();
3792 for (int number = 0; number < len; number++) {
3793 String* entry = GetKey(number);
3794 if ((entry->Hash() == hash) &&
3795 name->Equals(entry) &&
3796 !is_null_descriptor(number)) {
3797 return number;
3798 }
3799 }
3800 return kNotFound;
3801}
3802
3803
3804#ifdef DEBUG
3805bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
3806 if (IsEmpty()) return other->IsEmpty();
3807 if (other->IsEmpty()) return false;
3808 if (length() != other->length()) return false;
3809 for (int i = 0; i < length(); ++i) {
3810 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
3811 }
3812 return GetContentArray()->IsEqualTo(other->GetContentArray());
3813}
3814#endif
3815
3816
3817static StaticResource<StringInputBuffer> string_input_buffer;
3818
3819
3820bool String::LooksValid() {
3821 if (!Heap::Contains(this)) return false;
3822 return true;
3823}
3824
3825
3826int String::Utf8Length() {
3827 if (IsAsciiRepresentation()) return length();
3828 // Attempt to flatten before accessing the string. It probably
3829 // doesn't make Utf8Length faster, but it is very likely that
3830 // the string will be accessed later (for example by WriteUtf8)
3831 // so it's still a good idea.
Steve Block6ded16b2010-05-10 14:33:55 +01003832 TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00003833 Access<StringInputBuffer> buffer(&string_input_buffer);
3834 buffer->Reset(0, this);
3835 int result = 0;
3836 while (buffer->has_more())
3837 result += unibrow::Utf8::Length(buffer->GetNext());
3838 return result;
3839}
3840
3841
3842Vector<const char> String::ToAsciiVector() {
3843 ASSERT(IsAsciiRepresentation());
3844 ASSERT(IsFlat());
3845
3846 int offset = 0;
3847 int length = this->length();
3848 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3849 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00003850 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003851 ConsString* cons = ConsString::cast(string);
3852 ASSERT(cons->second()->length() == 0);
3853 string = cons->first();
3854 string_tag = StringShape(string).representation_tag();
3855 }
3856 if (string_tag == kSeqStringTag) {
3857 SeqAsciiString* seq = SeqAsciiString::cast(string);
3858 char* start = seq->GetChars();
3859 return Vector<const char>(start + offset, length);
3860 }
3861 ASSERT(string_tag == kExternalStringTag);
3862 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
3863 const char* start = ext->resource()->data();
3864 return Vector<const char>(start + offset, length);
3865}
3866
3867
3868Vector<const uc16> String::ToUC16Vector() {
3869 ASSERT(IsTwoByteRepresentation());
3870 ASSERT(IsFlat());
3871
3872 int offset = 0;
3873 int length = this->length();
3874 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3875 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00003876 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003877 ConsString* cons = ConsString::cast(string);
3878 ASSERT(cons->second()->length() == 0);
3879 string = cons->first();
3880 string_tag = StringShape(string).representation_tag();
3881 }
3882 if (string_tag == kSeqStringTag) {
3883 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
3884 return Vector<const uc16>(seq->GetChars() + offset, length);
3885 }
3886 ASSERT(string_tag == kExternalStringTag);
3887 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
3888 const uc16* start =
3889 reinterpret_cast<const uc16*>(ext->resource()->data());
3890 return Vector<const uc16>(start + offset, length);
3891}
3892
3893
3894SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3895 RobustnessFlag robust_flag,
3896 int offset,
3897 int length,
3898 int* length_return) {
3899 ASSERT(NativeAllocationChecker::allocation_allowed());
3900 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3901 return SmartPointer<char>(NULL);
3902 }
3903
3904 // Negative length means the to the end of the string.
3905 if (length < 0) length = kMaxInt - offset;
3906
3907 // Compute the size of the UTF-8 string. Start at the specified offset.
3908 Access<StringInputBuffer> buffer(&string_input_buffer);
3909 buffer->Reset(offset, this);
3910 int character_position = offset;
3911 int utf8_bytes = 0;
3912 while (buffer->has_more()) {
3913 uint16_t character = buffer->GetNext();
3914 if (character_position < offset + length) {
3915 utf8_bytes += unibrow::Utf8::Length(character);
3916 }
3917 character_position++;
3918 }
3919
3920 if (length_return) {
3921 *length_return = utf8_bytes;
3922 }
3923
3924 char* result = NewArray<char>(utf8_bytes + 1);
3925
3926 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
3927 buffer->Rewind();
3928 buffer->Seek(offset);
3929 character_position = offset;
3930 int utf8_byte_position = 0;
3931 while (buffer->has_more()) {
3932 uint16_t character = buffer->GetNext();
3933 if (character_position < offset + length) {
3934 if (allow_nulls == DISALLOW_NULLS && character == 0) {
3935 character = ' ';
3936 }
3937 utf8_byte_position +=
3938 unibrow::Utf8::Encode(result + utf8_byte_position, character);
3939 }
3940 character_position++;
3941 }
3942 result[utf8_byte_position] = 0;
3943 return SmartPointer<char>(result);
3944}
3945
3946
3947SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3948 RobustnessFlag robust_flag,
3949 int* length_return) {
3950 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
3951}
3952
3953
3954const uc16* String::GetTwoByteData() {
3955 return GetTwoByteData(0);
3956}
3957
3958
3959const uc16* String::GetTwoByteData(unsigned start) {
3960 ASSERT(!IsAsciiRepresentation());
3961 switch (StringShape(this).representation_tag()) {
3962 case kSeqStringTag:
3963 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
3964 case kExternalStringTag:
3965 return ExternalTwoByteString::cast(this)->
3966 ExternalTwoByteStringGetData(start);
Steve Blocka7e24c12009-10-30 11:49:00 +00003967 case kConsStringTag:
3968 UNREACHABLE();
3969 return NULL;
3970 }
3971 UNREACHABLE();
3972 return NULL;
3973}
3974
3975
3976SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
3977 ASSERT(NativeAllocationChecker::allocation_allowed());
3978
3979 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3980 return SmartPointer<uc16>();
3981 }
3982
3983 Access<StringInputBuffer> buffer(&string_input_buffer);
3984 buffer->Reset(this);
3985
3986 uc16* result = NewArray<uc16>(length() + 1);
3987
3988 int i = 0;
3989 while (buffer->has_more()) {
3990 uint16_t character = buffer->GetNext();
3991 result[i++] = character;
3992 }
3993 result[i] = 0;
3994 return SmartPointer<uc16>(result);
3995}
3996
3997
3998const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
3999 return reinterpret_cast<uc16*>(
4000 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
4001}
4002
4003
4004void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4005 unsigned* offset_ptr,
4006 unsigned max_chars) {
4007 unsigned chars_read = 0;
4008 unsigned offset = *offset_ptr;
4009 while (chars_read < max_chars) {
4010 uint16_t c = *reinterpret_cast<uint16_t*>(
4011 reinterpret_cast<char*>(this) -
4012 kHeapObjectTag + kHeaderSize + offset * kShortSize);
4013 if (c <= kMaxAsciiCharCode) {
4014 // Fast case for ASCII characters. Cursor is an input output argument.
4015 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4016 rbb->util_buffer,
4017 rbb->capacity,
4018 rbb->cursor)) {
4019 break;
4020 }
4021 } else {
4022 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4023 rbb->util_buffer,
4024 rbb->capacity,
4025 rbb->cursor)) {
4026 break;
4027 }
4028 }
4029 offset++;
4030 chars_read++;
4031 }
4032 *offset_ptr = offset;
4033 rbb->remaining += chars_read;
4034}
4035
4036
4037const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
4038 unsigned* remaining,
4039 unsigned* offset_ptr,
4040 unsigned max_chars) {
4041 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
4042 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
4043 *remaining = max_chars;
4044 *offset_ptr += max_chars;
4045 return b;
4046}
4047
4048
4049// This will iterate unless the block of string data spans two 'halves' of
4050// a ConsString, in which case it will recurse. Since the block of string
4051// data to be read has a maximum size this limits the maximum recursion
4052// depth to something sane. Since C++ does not have tail call recursion
4053// elimination, the iteration must be explicit. Since this is not an
4054// -IntoBuffer method it can delegate to one of the efficient
4055// *AsciiStringReadBlock routines.
4056const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
4057 unsigned* offset_ptr,
4058 unsigned max_chars) {
4059 ConsString* current = this;
4060 unsigned offset = *offset_ptr;
4061 int offset_correction = 0;
4062
4063 while (true) {
4064 String* left = current->first();
4065 unsigned left_length = (unsigned)left->length();
4066 if (left_length > offset &&
4067 (max_chars <= left_length - offset ||
4068 (rbb->capacity <= left_length - offset &&
4069 (max_chars = left_length - offset, true)))) { // comma operator!
4070 // Left hand side only - iterate unless we have reached the bottom of
4071 // the cons tree. The assignment on the left of the comma operator is
4072 // in order to make use of the fact that the -IntoBuffer routines can
4073 // produce at most 'capacity' characters. This enables us to postpone
4074 // the point where we switch to the -IntoBuffer routines (below) in order
4075 // to maximize the chances of delegating a big chunk of work to the
4076 // efficient *AsciiStringReadBlock routines.
4077 if (StringShape(left).IsCons()) {
4078 current = ConsString::cast(left);
4079 continue;
4080 } else {
4081 const unibrow::byte* answer =
4082 String::ReadBlock(left, rbb, &offset, max_chars);
4083 *offset_ptr = offset + offset_correction;
4084 return answer;
4085 }
4086 } else if (left_length <= offset) {
4087 // Right hand side only - iterate unless we have reached the bottom of
4088 // the cons tree.
4089 String* right = current->second();
4090 offset -= left_length;
4091 offset_correction += left_length;
4092 if (StringShape(right).IsCons()) {
4093 current = ConsString::cast(right);
4094 continue;
4095 } else {
4096 const unibrow::byte* answer =
4097 String::ReadBlock(right, rbb, &offset, max_chars);
4098 *offset_ptr = offset + offset_correction;
4099 return answer;
4100 }
4101 } else {
4102 // The block to be read spans two sides of the ConsString, so we call the
4103 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
4104 // are able to assemble data from several part strings because they use
4105 // the util_buffer to store their data and never return direct pointers
4106 // to their storage. We don't try to read more than the buffer capacity
4107 // here or we can get too much recursion.
4108 ASSERT(rbb->remaining == 0);
4109 ASSERT(rbb->cursor == 0);
4110 current->ConsStringReadBlockIntoBuffer(
4111 rbb,
4112 &offset,
4113 max_chars > rbb->capacity ? rbb->capacity : max_chars);
4114 *offset_ptr = offset + offset_correction;
4115 return rbb->util_buffer;
4116 }
4117 }
4118}
4119
4120
Steve Blocka7e24c12009-10-30 11:49:00 +00004121uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
4122 ASSERT(index >= 0 && index < length());
4123 return resource()->data()[index];
4124}
4125
4126
4127const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
4128 unsigned* remaining,
4129 unsigned* offset_ptr,
4130 unsigned max_chars) {
4131 // Cast const char* to unibrow::byte* (signedness difference).
4132 const unibrow::byte* b =
4133 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
4134 *remaining = max_chars;
4135 *offset_ptr += max_chars;
4136 return b;
4137}
4138
4139
4140const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
4141 unsigned start) {
4142 return resource()->data() + start;
4143}
4144
4145
4146uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
4147 ASSERT(index >= 0 && index < length());
4148 return resource()->data()[index];
4149}
4150
4151
4152void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
4153 ReadBlockBuffer* rbb,
4154 unsigned* offset_ptr,
4155 unsigned max_chars) {
4156 unsigned chars_read = 0;
4157 unsigned offset = *offset_ptr;
4158 const uint16_t* data = resource()->data();
4159 while (chars_read < max_chars) {
4160 uint16_t c = data[offset];
4161 if (c <= kMaxAsciiCharCode) {
4162 // Fast case for ASCII characters. Cursor is an input output argument.
4163 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4164 rbb->util_buffer,
4165 rbb->capacity,
4166 rbb->cursor))
4167 break;
4168 } else {
4169 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4170 rbb->util_buffer,
4171 rbb->capacity,
4172 rbb->cursor))
4173 break;
4174 }
4175 offset++;
4176 chars_read++;
4177 }
4178 *offset_ptr = offset;
4179 rbb->remaining += chars_read;
4180}
4181
4182
4183void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4184 unsigned* offset_ptr,
4185 unsigned max_chars) {
4186 unsigned capacity = rbb->capacity - rbb->cursor;
4187 if (max_chars > capacity) max_chars = capacity;
4188 memcpy(rbb->util_buffer + rbb->cursor,
4189 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
4190 *offset_ptr * kCharSize,
4191 max_chars);
4192 rbb->remaining += max_chars;
4193 *offset_ptr += max_chars;
4194 rbb->cursor += max_chars;
4195}
4196
4197
4198void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
4199 ReadBlockBuffer* rbb,
4200 unsigned* offset_ptr,
4201 unsigned max_chars) {
4202 unsigned capacity = rbb->capacity - rbb->cursor;
4203 if (max_chars > capacity) max_chars = capacity;
4204 memcpy(rbb->util_buffer + rbb->cursor,
4205 resource()->data() + *offset_ptr,
4206 max_chars);
4207 rbb->remaining += max_chars;
4208 *offset_ptr += max_chars;
4209 rbb->cursor += max_chars;
4210}
4211
4212
4213// This method determines the type of string involved and then copies
4214// a whole chunk of characters into a buffer, or returns a pointer to a buffer
4215// where they can be found. The pointer is not necessarily valid across a GC
4216// (see AsciiStringReadBlock).
4217const unibrow::byte* String::ReadBlock(String* input,
4218 ReadBlockBuffer* rbb,
4219 unsigned* offset_ptr,
4220 unsigned max_chars) {
4221 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
4222 if (max_chars == 0) {
4223 rbb->remaining = 0;
4224 return NULL;
4225 }
4226 switch (StringShape(input).representation_tag()) {
4227 case kSeqStringTag:
4228 if (input->IsAsciiRepresentation()) {
4229 SeqAsciiString* str = SeqAsciiString::cast(input);
4230 return str->SeqAsciiStringReadBlock(&rbb->remaining,
4231 offset_ptr,
4232 max_chars);
4233 } else {
4234 SeqTwoByteString* str = SeqTwoByteString::cast(input);
4235 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4236 offset_ptr,
4237 max_chars);
4238 return rbb->util_buffer;
4239 }
4240 case kConsStringTag:
4241 return ConsString::cast(input)->ConsStringReadBlock(rbb,
4242 offset_ptr,
4243 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004244 case kExternalStringTag:
4245 if (input->IsAsciiRepresentation()) {
4246 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
4247 &rbb->remaining,
4248 offset_ptr,
4249 max_chars);
4250 } else {
4251 ExternalTwoByteString::cast(input)->
4252 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4253 offset_ptr,
4254 max_chars);
4255 return rbb->util_buffer;
4256 }
4257 default:
4258 break;
4259 }
4260
4261 UNREACHABLE();
4262 return 0;
4263}
4264
4265
4266Relocatable* Relocatable::top_ = NULL;
4267
4268
4269void Relocatable::PostGarbageCollectionProcessing() {
4270 Relocatable* current = top_;
4271 while (current != NULL) {
4272 current->PostGarbageCollection();
4273 current = current->prev_;
4274 }
4275}
4276
4277
4278// Reserve space for statics needing saving and restoring.
4279int Relocatable::ArchiveSpacePerThread() {
4280 return sizeof(top_);
4281}
4282
4283
4284// Archive statics that are thread local.
4285char* Relocatable::ArchiveState(char* to) {
4286 *reinterpret_cast<Relocatable**>(to) = top_;
4287 top_ = NULL;
4288 return to + ArchiveSpacePerThread();
4289}
4290
4291
4292// Restore statics that are thread local.
4293char* Relocatable::RestoreState(char* from) {
4294 top_ = *reinterpret_cast<Relocatable**>(from);
4295 return from + ArchiveSpacePerThread();
4296}
4297
4298
4299char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
4300 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
4301 Iterate(v, top);
4302 return thread_storage + ArchiveSpacePerThread();
4303}
4304
4305
4306void Relocatable::Iterate(ObjectVisitor* v) {
4307 Iterate(v, top_);
4308}
4309
4310
4311void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
4312 Relocatable* current = top;
4313 while (current != NULL) {
4314 current->IterateInstance(v);
4315 current = current->prev_;
4316 }
4317}
4318
4319
4320FlatStringReader::FlatStringReader(Handle<String> str)
4321 : str_(str.location()),
4322 length_(str->length()) {
4323 PostGarbageCollection();
4324}
4325
4326
4327FlatStringReader::FlatStringReader(Vector<const char> input)
4328 : str_(0),
4329 is_ascii_(true),
4330 length_(input.length()),
4331 start_(input.start()) { }
4332
4333
4334void FlatStringReader::PostGarbageCollection() {
4335 if (str_ == NULL) return;
4336 Handle<String> str(str_);
4337 ASSERT(str->IsFlat());
4338 is_ascii_ = str->IsAsciiRepresentation();
4339 if (is_ascii_) {
4340 start_ = str->ToAsciiVector().start();
4341 } else {
4342 start_ = str->ToUC16Vector().start();
4343 }
4344}
4345
4346
4347void StringInputBuffer::Seek(unsigned pos) {
4348 Reset(pos, input_);
4349}
4350
4351
4352void SafeStringInputBuffer::Seek(unsigned pos) {
4353 Reset(pos, input_);
4354}
4355
4356
4357// This method determines the type of string involved and then copies
4358// a whole chunk of characters into a buffer. It can be used with strings
4359// that have been glued together to form a ConsString and which must cooperate
4360// to fill up a buffer.
4361void String::ReadBlockIntoBuffer(String* input,
4362 ReadBlockBuffer* rbb,
4363 unsigned* offset_ptr,
4364 unsigned max_chars) {
4365 ASSERT(*offset_ptr <= (unsigned)input->length());
4366 if (max_chars == 0) return;
4367
4368 switch (StringShape(input).representation_tag()) {
4369 case kSeqStringTag:
4370 if (input->IsAsciiRepresentation()) {
4371 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4372 offset_ptr,
4373 max_chars);
4374 return;
4375 } else {
4376 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4377 offset_ptr,
4378 max_chars);
4379 return;
4380 }
4381 case kConsStringTag:
4382 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4383 offset_ptr,
4384 max_chars);
4385 return;
Steve Blocka7e24c12009-10-30 11:49:00 +00004386 case kExternalStringTag:
4387 if (input->IsAsciiRepresentation()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004388 ExternalAsciiString::cast(input)->
4389 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4390 } else {
4391 ExternalTwoByteString::cast(input)->
4392 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4393 offset_ptr,
4394 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004395 }
4396 return;
4397 default:
4398 break;
4399 }
4400
4401 UNREACHABLE();
4402 return;
4403}
4404
4405
4406const unibrow::byte* String::ReadBlock(String* input,
4407 unibrow::byte* util_buffer,
4408 unsigned capacity,
4409 unsigned* remaining,
4410 unsigned* offset_ptr) {
4411 ASSERT(*offset_ptr <= (unsigned)input->length());
4412 unsigned chars = input->length() - *offset_ptr;
4413 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4414 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4415 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4416 *remaining = rbb.remaining;
4417 return answer;
4418}
4419
4420
4421const unibrow::byte* String::ReadBlock(String** raw_input,
4422 unibrow::byte* util_buffer,
4423 unsigned capacity,
4424 unsigned* remaining,
4425 unsigned* offset_ptr) {
4426 Handle<String> input(raw_input);
4427 ASSERT(*offset_ptr <= (unsigned)input->length());
4428 unsigned chars = input->length() - *offset_ptr;
4429 if (chars > capacity) chars = capacity;
4430 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4431 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4432 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4433 *remaining = rbb.remaining;
4434 return rbb.util_buffer;
4435}
4436
4437
4438// This will iterate unless the block of string data spans two 'halves' of
4439// a ConsString, in which case it will recurse. Since the block of string
4440// data to be read has a maximum size this limits the maximum recursion
4441// depth to something sane. Since C++ does not have tail call recursion
4442// elimination, the iteration must be explicit.
4443void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4444 unsigned* offset_ptr,
4445 unsigned max_chars) {
4446 ConsString* current = this;
4447 unsigned offset = *offset_ptr;
4448 int offset_correction = 0;
4449
4450 while (true) {
4451 String* left = current->first();
4452 unsigned left_length = (unsigned)left->length();
4453 if (left_length > offset &&
4454 max_chars <= left_length - offset) {
4455 // Left hand side only - iterate unless we have reached the bottom of
4456 // the cons tree.
4457 if (StringShape(left).IsCons()) {
4458 current = ConsString::cast(left);
4459 continue;
4460 } else {
4461 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4462 *offset_ptr = offset + offset_correction;
4463 return;
4464 }
4465 } else if (left_length <= offset) {
4466 // Right hand side only - iterate unless we have reached the bottom of
4467 // the cons tree.
4468 offset -= left_length;
4469 offset_correction += left_length;
4470 String* right = current->second();
4471 if (StringShape(right).IsCons()) {
4472 current = ConsString::cast(right);
4473 continue;
4474 } else {
4475 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4476 *offset_ptr = offset + offset_correction;
4477 return;
4478 }
4479 } else {
4480 // The block to be read spans two sides of the ConsString, so we recurse.
4481 // First recurse on the left.
4482 max_chars -= left_length - offset;
4483 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4484 // We may have reached the max or there may not have been enough space
4485 // in the buffer for the characters in the left hand side.
4486 if (offset == left_length) {
4487 // Recurse on the right.
4488 String* right = String::cast(current->second());
4489 offset -= left_length;
4490 offset_correction += left_length;
4491 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4492 }
4493 *offset_ptr = offset + offset_correction;
4494 return;
4495 }
4496 }
4497}
4498
4499
Steve Blocka7e24c12009-10-30 11:49:00 +00004500void ConsString::ConsStringIterateBody(ObjectVisitor* v) {
4501 IteratePointers(v, kFirstOffset, kSecondOffset + kPointerSize);
4502}
4503
4504
4505void JSGlobalPropertyCell::JSGlobalPropertyCellIterateBody(ObjectVisitor* v) {
4506 IteratePointers(v, kValueOffset, kValueOffset + kPointerSize);
4507}
4508
4509
4510uint16_t ConsString::ConsStringGet(int index) {
4511 ASSERT(index >= 0 && index < this->length());
4512
4513 // Check for a flattened cons string
4514 if (second()->length() == 0) {
4515 String* left = first();
4516 return left->Get(index);
4517 }
4518
4519 String* string = String::cast(this);
4520
4521 while (true) {
4522 if (StringShape(string).IsCons()) {
4523 ConsString* cons_string = ConsString::cast(string);
4524 String* left = cons_string->first();
4525 if (left->length() > index) {
4526 string = left;
4527 } else {
4528 index -= left->length();
4529 string = cons_string->second();
4530 }
4531 } else {
4532 return string->Get(index);
4533 }
4534 }
4535
4536 UNREACHABLE();
4537 return 0;
4538}
4539
4540
4541template <typename sinkchar>
4542void String::WriteToFlat(String* src,
4543 sinkchar* sink,
4544 int f,
4545 int t) {
4546 String* source = src;
4547 int from = f;
4548 int to = t;
4549 while (true) {
4550 ASSERT(0 <= from && from <= to && to <= source->length());
4551 switch (StringShape(source).full_representation_tag()) {
4552 case kAsciiStringTag | kExternalStringTag: {
4553 CopyChars(sink,
4554 ExternalAsciiString::cast(source)->resource()->data() + from,
4555 to - from);
4556 return;
4557 }
4558 case kTwoByteStringTag | kExternalStringTag: {
4559 const uc16* data =
4560 ExternalTwoByteString::cast(source)->resource()->data();
4561 CopyChars(sink,
4562 data + from,
4563 to - from);
4564 return;
4565 }
4566 case kAsciiStringTag | kSeqStringTag: {
4567 CopyChars(sink,
4568 SeqAsciiString::cast(source)->GetChars() + from,
4569 to - from);
4570 return;
4571 }
4572 case kTwoByteStringTag | kSeqStringTag: {
4573 CopyChars(sink,
4574 SeqTwoByteString::cast(source)->GetChars() + from,
4575 to - from);
4576 return;
4577 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004578 case kAsciiStringTag | kConsStringTag:
4579 case kTwoByteStringTag | kConsStringTag: {
4580 ConsString* cons_string = ConsString::cast(source);
4581 String* first = cons_string->first();
4582 int boundary = first->length();
4583 if (to - boundary >= boundary - from) {
4584 // Right hand side is longer. Recurse over left.
4585 if (from < boundary) {
4586 WriteToFlat(first, sink, from, boundary);
4587 sink += boundary - from;
4588 from = 0;
4589 } else {
4590 from -= boundary;
4591 }
4592 to -= boundary;
4593 source = cons_string->second();
4594 } else {
4595 // Left hand side is longer. Recurse over right.
4596 if (to > boundary) {
4597 String* second = cons_string->second();
4598 WriteToFlat(second,
4599 sink + boundary - from,
4600 0,
4601 to - boundary);
4602 to = boundary;
4603 }
4604 source = first;
4605 }
4606 break;
4607 }
4608 }
4609 }
4610}
4611
4612
Steve Blockd0582a62009-12-15 09:54:21 +00004613#define FIELD_ADDR(p, offset) \
4614 (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
4615
4616void ExternalAsciiString::ExternalAsciiStringIterateBody(ObjectVisitor* v) {
4617 typedef v8::String::ExternalAsciiStringResource Resource;
4618 v->VisitExternalAsciiString(
4619 reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
Steve Blocka7e24c12009-10-30 11:49:00 +00004620}
4621
4622
Steve Blockd0582a62009-12-15 09:54:21 +00004623void ExternalTwoByteString::ExternalTwoByteStringIterateBody(ObjectVisitor* v) {
4624 typedef v8::String::ExternalStringResource Resource;
4625 v->VisitExternalTwoByteString(
4626 reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
Steve Blocka7e24c12009-10-30 11:49:00 +00004627}
4628
Steve Blockd0582a62009-12-15 09:54:21 +00004629#undef FIELD_ADDR
Steve Blocka7e24c12009-10-30 11:49:00 +00004630
4631template <typename IteratorA, typename IteratorB>
4632static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4633 // General slow case check. We know that the ia and ib iterators
4634 // have the same length.
4635 while (ia->has_more()) {
4636 uc32 ca = ia->GetNext();
4637 uc32 cb = ib->GetNext();
4638 if (ca != cb)
4639 return false;
4640 }
4641 return true;
4642}
4643
4644
4645// Compares the contents of two strings by reading and comparing
4646// int-sized blocks of characters.
4647template <typename Char>
4648static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4649 int length = a.length();
4650 ASSERT_EQ(length, b.length());
4651 const Char* pa = a.start();
4652 const Char* pb = b.start();
4653 int i = 0;
4654#ifndef V8_HOST_CAN_READ_UNALIGNED
4655 // If this architecture isn't comfortable reading unaligned ints
4656 // then we have to check that the strings are aligned before
4657 // comparing them blockwise.
4658 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4659 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4660 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
4661 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
4662#endif
4663 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
4664 int endpoint = length - kStepSize;
4665 // Compare blocks until we reach near the end of the string.
4666 for (; i <= endpoint; i += kStepSize) {
4667 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
4668 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
4669 if (wa != wb) {
4670 return false;
4671 }
4672 }
4673#ifndef V8_HOST_CAN_READ_UNALIGNED
4674 }
4675#endif
4676 // Compare the remaining characters that didn't fit into a block.
4677 for (; i < length; i++) {
4678 if (a[i] != b[i]) {
4679 return false;
4680 }
4681 }
4682 return true;
4683}
4684
4685
4686static StringInputBuffer string_compare_buffer_b;
4687
4688
4689template <typename IteratorA>
4690static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
4691 if (b->IsFlat()) {
4692 if (b->IsAsciiRepresentation()) {
4693 VectorIterator<char> ib(b->ToAsciiVector());
4694 return CompareStringContents(ia, &ib);
4695 } else {
4696 VectorIterator<uc16> ib(b->ToUC16Vector());
4697 return CompareStringContents(ia, &ib);
4698 }
4699 } else {
4700 string_compare_buffer_b.Reset(0, b);
4701 return CompareStringContents(ia, &string_compare_buffer_b);
4702 }
4703}
4704
4705
4706static StringInputBuffer string_compare_buffer_a;
4707
4708
4709bool String::SlowEquals(String* other) {
4710 // Fast check: negative check with lengths.
4711 int len = length();
4712 if (len != other->length()) return false;
4713 if (len == 0) return true;
4714
4715 // Fast check: if hash code is computed for both strings
4716 // a fast negative check can be performed.
4717 if (HasHashCode() && other->HasHashCode()) {
4718 if (Hash() != other->Hash()) return false;
4719 }
4720
Leon Clarkef7060e22010-06-03 12:02:55 +01004721 // We know the strings are both non-empty. Compare the first chars
4722 // before we try to flatten the strings.
4723 if (this->Get(0) != other->Get(0)) return false;
4724
4725 String* lhs = this->TryFlattenGetString();
4726 String* rhs = other->TryFlattenGetString();
4727
4728 if (StringShape(lhs).IsSequentialAscii() &&
4729 StringShape(rhs).IsSequentialAscii()) {
4730 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
4731 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
Steve Blocka7e24c12009-10-30 11:49:00 +00004732 return CompareRawStringContents(Vector<const char>(str1, len),
4733 Vector<const char>(str2, len));
4734 }
4735
Leon Clarkef7060e22010-06-03 12:02:55 +01004736 if (lhs->IsFlat()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004737 if (IsAsciiRepresentation()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01004738 Vector<const char> vec1 = lhs->ToAsciiVector();
4739 if (rhs->IsFlat()) {
4740 if (rhs->IsAsciiRepresentation()) {
4741 Vector<const char> vec2 = rhs->ToAsciiVector();
Steve Blocka7e24c12009-10-30 11:49:00 +00004742 return CompareRawStringContents(vec1, vec2);
4743 } else {
4744 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004745 VectorIterator<uc16> ib(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004746 return CompareStringContents(&buf1, &ib);
4747 }
4748 } else {
4749 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004750 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004751 return CompareStringContents(&buf1, &string_compare_buffer_b);
4752 }
4753 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004754 Vector<const uc16> vec1 = lhs->ToUC16Vector();
4755 if (rhs->IsFlat()) {
4756 if (rhs->IsAsciiRepresentation()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004757 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004758 VectorIterator<char> ib(rhs->ToAsciiVector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004759 return CompareStringContents(&buf1, &ib);
4760 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004761 Vector<const uc16> vec2(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004762 return CompareRawStringContents(vec1, vec2);
4763 }
4764 } else {
4765 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004766 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004767 return CompareStringContents(&buf1, &string_compare_buffer_b);
4768 }
4769 }
4770 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004771 string_compare_buffer_a.Reset(0, lhs);
4772 return CompareStringContentsPartial(&string_compare_buffer_a, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004773 }
4774}
4775
4776
4777bool String::MarkAsUndetectable() {
4778 if (StringShape(this).IsSymbol()) return false;
4779
4780 Map* map = this->map();
Steve Blockd0582a62009-12-15 09:54:21 +00004781 if (map == Heap::string_map()) {
4782 this->set_map(Heap::undetectable_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00004783 return true;
Steve Blockd0582a62009-12-15 09:54:21 +00004784 } else if (map == Heap::ascii_string_map()) {
4785 this->set_map(Heap::undetectable_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00004786 return true;
4787 }
4788 // Rest cannot be marked as undetectable
4789 return false;
4790}
4791
4792
4793bool String::IsEqualTo(Vector<const char> str) {
4794 int slen = length();
4795 Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
4796 decoder->Reset(str.start(), str.length());
4797 int i;
4798 for (i = 0; i < slen && decoder->has_more(); i++) {
4799 uc32 r = decoder->GetNext();
4800 if (Get(i) != r) return false;
4801 }
4802 return i == slen && !decoder->has_more();
4803}
4804
4805
Steve Block6ded16b2010-05-10 14:33:55 +01004806template <typename schar>
4807static inline uint32_t HashSequentialString(const schar* chars, int length) {
4808 StringHasher hasher(length);
4809 if (!hasher.has_trivial_hash()) {
4810 int i;
4811 for (i = 0; hasher.is_array_index() && (i < length); i++) {
4812 hasher.AddCharacter(chars[i]);
4813 }
4814 for (; i < length; i++) {
4815 hasher.AddCharacterNoIndex(chars[i]);
4816 }
4817 }
4818 return hasher.GetHashField();
4819}
4820
4821
Steve Blocka7e24c12009-10-30 11:49:00 +00004822uint32_t String::ComputeAndSetHash() {
4823 // Should only be called if hash code has not yet been computed.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004824 ASSERT(!HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00004825
Steve Block6ded16b2010-05-10 14:33:55 +01004826 const int len = length();
4827
Steve Blocka7e24c12009-10-30 11:49:00 +00004828 // Compute the hash code.
Steve Block6ded16b2010-05-10 14:33:55 +01004829 uint32_t field = 0;
4830 if (StringShape(this).IsSequentialAscii()) {
4831 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
4832 } else if (StringShape(this).IsSequentialTwoByte()) {
4833 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
4834 } else {
4835 StringInputBuffer buffer(this);
4836 field = ComputeHashField(&buffer, len);
4837 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004838
4839 // Store the hash code in the object.
Steve Blockd0582a62009-12-15 09:54:21 +00004840 set_hash_field(field);
Steve Blocka7e24c12009-10-30 11:49:00 +00004841
4842 // Check the hash code is there.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004843 ASSERT(HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00004844 uint32_t result = field >> kHashShift;
4845 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
4846 return result;
4847}
4848
4849
4850bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
4851 uint32_t* index,
4852 int length) {
4853 if (length == 0 || length > kMaxArrayIndexSize) return false;
4854 uc32 ch = buffer->GetNext();
4855
4856 // If the string begins with a '0' character, it must only consist
4857 // of it to be a legal array index.
4858 if (ch == '0') {
4859 *index = 0;
4860 return length == 1;
4861 }
4862
4863 // Convert string to uint32 array index; character by character.
4864 int d = ch - '0';
4865 if (d < 0 || d > 9) return false;
4866 uint32_t result = d;
4867 while (buffer->has_more()) {
4868 d = buffer->GetNext() - '0';
4869 if (d < 0 || d > 9) return false;
4870 // Check that the new result is below the 32 bit limit.
4871 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
4872 result = (result * 10) + d;
4873 }
4874
4875 *index = result;
4876 return true;
4877}
4878
4879
4880bool String::SlowAsArrayIndex(uint32_t* index) {
4881 if (length() <= kMaxCachedArrayIndexLength) {
4882 Hash(); // force computation of hash code
Steve Blockd0582a62009-12-15 09:54:21 +00004883 uint32_t field = hash_field();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004884 if ((field & kIsNotArrayIndexMask) != 0) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00004885 // Isolate the array index form the full hash field.
4886 *index = (kArrayIndexHashMask & field) >> kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00004887 return true;
4888 } else {
4889 StringInputBuffer buffer(this);
4890 return ComputeArrayIndex(&buffer, index, length());
4891 }
4892}
4893
4894
Steve Blockd0582a62009-12-15 09:54:21 +00004895static inline uint32_t HashField(uint32_t hash,
4896 bool is_array_index,
4897 int length = -1) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004898 uint32_t result = (hash << String::kHashShift);
Steve Blockd0582a62009-12-15 09:54:21 +00004899 if (is_array_index) {
4900 // For array indexes mix the length into the hash as an array index could
4901 // be zero.
4902 ASSERT(length > 0);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004903 ASSERT(length <= String::kMaxArrayIndexSize);
Steve Blockd0582a62009-12-15 09:54:21 +00004904 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
4905 (1 << String::kArrayIndexValueBits));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004906 ASSERT(String::kMaxArrayIndexSize < (1 << String::kArrayIndexValueBits));
4907 result &= ~String::kIsNotArrayIndexMask;
Steve Blockd0582a62009-12-15 09:54:21 +00004908 result |= length << String::kArrayIndexHashLengthShift;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004909 } else {
4910 result |= String::kIsNotArrayIndexMask;
Steve Blockd0582a62009-12-15 09:54:21 +00004911 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004912 return result;
4913}
4914
4915
4916uint32_t StringHasher::GetHashField() {
4917 ASSERT(is_valid());
Steve Blockd0582a62009-12-15 09:54:21 +00004918 if (length_ <= String::kMaxHashCalcLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004919 if (is_array_index()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004920 return v8::internal::HashField(array_index(), true, length_);
Steve Blocka7e24c12009-10-30 11:49:00 +00004921 } else {
Steve Blockd0582a62009-12-15 09:54:21 +00004922 return v8::internal::HashField(GetHash(), false);
Steve Blocka7e24c12009-10-30 11:49:00 +00004923 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004924 uint32_t payload = v8::internal::HashField(GetHash(), false);
Steve Blockd0582a62009-12-15 09:54:21 +00004925 return payload;
Steve Blocka7e24c12009-10-30 11:49:00 +00004926 } else {
4927 return v8::internal::HashField(length_, false);
4928 }
4929}
4930
4931
Steve Blockd0582a62009-12-15 09:54:21 +00004932uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
4933 int length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004934 StringHasher hasher(length);
4935
4936 // Very long strings have a trivial hash that doesn't inspect the
4937 // string contents.
4938 if (hasher.has_trivial_hash()) {
4939 return hasher.GetHashField();
4940 }
4941
4942 // Do the iterative array index computation as long as there is a
4943 // chance this is an array index.
4944 while (buffer->has_more() && hasher.is_array_index()) {
4945 hasher.AddCharacter(buffer->GetNext());
4946 }
4947
4948 // Process the remaining characters without updating the array
4949 // index.
4950 while (buffer->has_more()) {
4951 hasher.AddCharacterNoIndex(buffer->GetNext());
4952 }
4953
4954 return hasher.GetHashField();
4955}
4956
4957
Steve Block6ded16b2010-05-10 14:33:55 +01004958Object* String::SubString(int start, int end, PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004959 if (start == 0 && end == length()) return this;
Steve Block6ded16b2010-05-10 14:33:55 +01004960 Object* result = Heap::AllocateSubString(this, start, end, pretenure);
Steve Blockd0582a62009-12-15 09:54:21 +00004961 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00004962}
4963
4964
4965void String::PrintOn(FILE* file) {
4966 int length = this->length();
4967 for (int i = 0; i < length; i++) {
4968 fprintf(file, "%c", Get(i));
4969 }
4970}
4971
4972
4973void Map::CreateBackPointers() {
4974 DescriptorArray* descriptors = instance_descriptors();
4975 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
4976 if (descriptors->GetType(i) == MAP_TRANSITION) {
4977 // Get target.
4978 Map* target = Map::cast(descriptors->GetValue(i));
4979#ifdef DEBUG
4980 // Verify target.
4981 Object* source_prototype = prototype();
4982 Object* target_prototype = target->prototype();
4983 ASSERT(source_prototype->IsJSObject() ||
4984 source_prototype->IsMap() ||
4985 source_prototype->IsNull());
4986 ASSERT(target_prototype->IsJSObject() ||
4987 target_prototype->IsNull());
4988 ASSERT(source_prototype->IsMap() ||
4989 source_prototype == target_prototype);
4990#endif
4991 // Point target back to source. set_prototype() will not let us set
4992 // the prototype to a map, as we do here.
4993 *RawField(target, kPrototypeOffset) = this;
4994 }
4995 }
4996}
4997
4998
4999void Map::ClearNonLiveTransitions(Object* real_prototype) {
5000 // Live DescriptorArray objects will be marked, so we must use
5001 // low-level accessors to get and modify their data.
5002 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
5003 *RawField(this, Map::kInstanceDescriptorsOffset));
5004 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
5005 Smi* NullDescriptorDetails =
5006 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
5007 FixedArray* contents = reinterpret_cast<FixedArray*>(
5008 d->get(DescriptorArray::kContentArrayIndex));
5009 ASSERT(contents->length() >= 2);
5010 for (int i = 0; i < contents->length(); i += 2) {
5011 // If the pair (value, details) is a map transition,
5012 // check if the target is live. If not, null the descriptor.
5013 // Also drop the back pointer for that map transition, so that this
5014 // map is not reached again by following a back pointer from a
5015 // non-live object.
5016 PropertyDetails details(Smi::cast(contents->get(i + 1)));
5017 if (details.type() == MAP_TRANSITION) {
5018 Map* target = reinterpret_cast<Map*>(contents->get(i));
5019 ASSERT(target->IsHeapObject());
5020 if (!target->IsMarked()) {
5021 ASSERT(target->IsMap());
Leon Clarke4515c472010-02-03 11:58:03 +00005022 contents->set(i + 1, NullDescriptorDetails);
5023 contents->set_null(i);
Steve Blocka7e24c12009-10-30 11:49:00 +00005024 ASSERT(target->prototype() == this ||
5025 target->prototype() == real_prototype);
5026 // Getter prototype() is read-only, set_prototype() has side effects.
5027 *RawField(target, Map::kPrototypeOffset) = real_prototype;
5028 }
5029 }
5030 }
5031}
5032
5033
5034void Map::MapIterateBody(ObjectVisitor* v) {
5035 // Assumes all Object* members are contiguously allocated!
5036 IteratePointers(v, kPrototypeOffset, kCodeCacheOffset + kPointerSize);
5037}
5038
5039
5040Object* JSFunction::SetInstancePrototype(Object* value) {
5041 ASSERT(value->IsJSObject());
5042
5043 if (has_initial_map()) {
5044 initial_map()->set_prototype(value);
5045 } else {
5046 // Put the value in the initial map field until an initial map is
5047 // needed. At that point, a new initial map is created and the
5048 // prototype is put into the initial map where it belongs.
5049 set_prototype_or_initial_map(value);
5050 }
Kristian Monsen25f61362010-05-21 11:50:48 +01005051 Heap::ClearInstanceofCache();
Steve Blocka7e24c12009-10-30 11:49:00 +00005052 return value;
5053}
5054
5055
5056
5057Object* JSFunction::SetPrototype(Object* value) {
Steve Block6ded16b2010-05-10 14:33:55 +01005058 ASSERT(should_have_prototype());
Steve Blocka7e24c12009-10-30 11:49:00 +00005059 Object* construct_prototype = value;
5060
5061 // If the value is not a JSObject, store the value in the map's
5062 // constructor field so it can be accessed. Also, set the prototype
5063 // used for constructing objects to the original object prototype.
5064 // See ECMA-262 13.2.2.
5065 if (!value->IsJSObject()) {
5066 // Copy the map so this does not affect unrelated functions.
5067 // Remove map transitions because they point to maps with a
5068 // different prototype.
5069 Object* new_map = map()->CopyDropTransitions();
5070 if (new_map->IsFailure()) return new_map;
5071 set_map(Map::cast(new_map));
5072 map()->set_constructor(value);
5073 map()->set_non_instance_prototype(true);
5074 construct_prototype =
5075 Top::context()->global_context()->initial_object_prototype();
5076 } else {
5077 map()->set_non_instance_prototype(false);
5078 }
5079
5080 return SetInstancePrototype(construct_prototype);
5081}
5082
5083
Steve Block6ded16b2010-05-10 14:33:55 +01005084Object* JSFunction::RemovePrototype() {
5085 ASSERT(map() == context()->global_context()->function_map());
5086 set_map(context()->global_context()->function_without_prototype_map());
5087 set_prototype_or_initial_map(Heap::the_hole_value());
5088 return this;
5089}
5090
5091
Steve Blocka7e24c12009-10-30 11:49:00 +00005092Object* JSFunction::SetInstanceClassName(String* name) {
5093 shared()->set_instance_class_name(name);
5094 return this;
5095}
5096
5097
5098Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
5099 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
5100}
5101
5102
5103void Oddball::OddballIterateBody(ObjectVisitor* v) {
5104 // Assumes all Object* members are contiguously allocated!
5105 IteratePointers(v, kToStringOffset, kToNumberOffset + kPointerSize);
5106}
5107
5108
5109Object* Oddball::Initialize(const char* to_string, Object* to_number) {
5110 Object* symbol = Heap::LookupAsciiSymbol(to_string);
5111 if (symbol->IsFailure()) return symbol;
5112 set_to_string(String::cast(symbol));
5113 set_to_number(to_number);
5114 return this;
5115}
5116
5117
5118bool SharedFunctionInfo::HasSourceCode() {
5119 return !script()->IsUndefined() &&
5120 !Script::cast(script())->source()->IsUndefined();
5121}
5122
5123
5124Object* SharedFunctionInfo::GetSourceCode() {
5125 HandleScope scope;
5126 if (script()->IsUndefined()) return Heap::undefined_value();
5127 Object* source = Script::cast(script())->source();
5128 if (source->IsUndefined()) return Heap::undefined_value();
5129 return *SubString(Handle<String>(String::cast(source)),
5130 start_position(), end_position());
5131}
5132
5133
5134int SharedFunctionInfo::CalculateInstanceSize() {
5135 int instance_size =
5136 JSObject::kHeaderSize +
5137 expected_nof_properties() * kPointerSize;
5138 if (instance_size > JSObject::kMaxInstanceSize) {
5139 instance_size = JSObject::kMaxInstanceSize;
5140 }
5141 return instance_size;
5142}
5143
5144
5145int SharedFunctionInfo::CalculateInObjectProperties() {
5146 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
5147}
5148
5149
Andrei Popescu402d9372010-02-26 13:31:12 +00005150bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
5151 // Check the basic conditions for generating inline constructor code.
5152 if (!FLAG_inline_new
5153 || !has_only_simple_this_property_assignments()
5154 || this_property_assignments_count() == 0) {
5155 return false;
5156 }
5157
5158 // If the prototype is null inline constructors cause no problems.
5159 if (!prototype->IsJSObject()) {
5160 ASSERT(prototype->IsNull());
5161 return true;
5162 }
5163
5164 // Traverse the proposed prototype chain looking for setters for properties of
5165 // the same names as are set by the inline constructor.
5166 for (Object* obj = prototype;
5167 obj != Heap::null_value();
5168 obj = obj->GetPrototype()) {
5169 JSObject* js_object = JSObject::cast(obj);
5170 for (int i = 0; i < this_property_assignments_count(); i++) {
5171 LookupResult result;
5172 String* name = GetThisPropertyAssignmentName(i);
5173 js_object->LocalLookupRealNamedProperty(name, &result);
5174 if (result.IsProperty() && result.type() == CALLBACKS) {
5175 return false;
5176 }
5177 }
5178 }
5179
5180 return true;
5181}
5182
5183
Steve Blocka7e24c12009-10-30 11:49:00 +00005184void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +00005185 bool only_simple_this_property_assignments,
5186 FixedArray* assignments) {
5187 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005188 kHasOnlySimpleThisPropertyAssignments,
5189 only_simple_this_property_assignments));
5190 set_this_property_assignments(assignments);
5191 set_this_property_assignments_count(assignments->length() / 3);
5192}
5193
5194
5195void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
5196 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005197 kHasOnlySimpleThisPropertyAssignments,
5198 false));
5199 set_this_property_assignments(Heap::undefined_value());
5200 set_this_property_assignments_count(0);
5201}
5202
5203
5204String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
5205 Object* obj = this_property_assignments();
5206 ASSERT(obj->IsFixedArray());
5207 ASSERT(index < this_property_assignments_count());
5208 obj = FixedArray::cast(obj)->get(index * 3);
5209 ASSERT(obj->IsString());
5210 return String::cast(obj);
5211}
5212
5213
5214bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
5215 Object* obj = this_property_assignments();
5216 ASSERT(obj->IsFixedArray());
5217 ASSERT(index < this_property_assignments_count());
5218 obj = FixedArray::cast(obj)->get(index * 3 + 1);
5219 return Smi::cast(obj)->value() != -1;
5220}
5221
5222
5223int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
5224 ASSERT(IsThisPropertyAssignmentArgument(index));
5225 Object* obj =
5226 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
5227 return Smi::cast(obj)->value();
5228}
5229
5230
5231Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
5232 ASSERT(!IsThisPropertyAssignmentArgument(index));
5233 Object* obj =
5234 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
5235 return obj;
5236}
5237
5238
Steve Blocka7e24c12009-10-30 11:49:00 +00005239// Support function for printing the source code to a StringStream
5240// without any allocation in the heap.
5241void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
5242 int max_length) {
5243 // For some native functions there is no source.
5244 if (script()->IsUndefined() ||
5245 Script::cast(script())->source()->IsUndefined()) {
5246 accumulator->Add("<No Source>");
5247 return;
5248 }
5249
Steve Blockd0582a62009-12-15 09:54:21 +00005250 // Get the source for the script which this function came from.
Steve Blocka7e24c12009-10-30 11:49:00 +00005251 // Don't use String::cast because we don't want more assertion errors while
5252 // we are already creating a stack dump.
5253 String* script_source =
5254 reinterpret_cast<String*>(Script::cast(script())->source());
5255
5256 if (!script_source->LooksValid()) {
5257 accumulator->Add("<Invalid Source>");
5258 return;
5259 }
5260
5261 if (!is_toplevel()) {
5262 accumulator->Add("function ");
5263 Object* name = this->name();
5264 if (name->IsString() && String::cast(name)->length() > 0) {
5265 accumulator->PrintName(name);
5266 }
5267 }
5268
5269 int len = end_position() - start_position();
5270 if (len > max_length) {
5271 accumulator->Put(script_source,
5272 start_position(),
5273 start_position() + max_length);
5274 accumulator->Add("...\n");
5275 } else {
5276 accumulator->Put(script_source, start_position(), end_position());
5277 }
5278}
5279
5280
5281void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
Steve Block6ded16b2010-05-10 14:33:55 +01005282 IteratePointers(v,
5283 kNameOffset,
5284 kThisPropertyAssignmentsOffset + kPointerSize);
Steve Blocka7e24c12009-10-30 11:49:00 +00005285}
5286
5287
5288void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
5289 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
5290 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
5291 Object* old_target = target;
5292 VisitPointer(&target);
5293 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5294}
5295
5296
5297void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005298 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
5299 rinfo->IsPatchedReturnSequence()) ||
5300 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
5301 rinfo->IsPatchedDebugBreakSlotSequence()));
Steve Blocka7e24c12009-10-30 11:49:00 +00005302 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
5303 Object* old_target = target;
5304 VisitPointer(&target);
5305 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5306}
5307
5308
5309void Code::CodeIterateBody(ObjectVisitor* v) {
5310 int mode_mask = RelocInfo::kCodeTargetMask |
5311 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
5312 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
5313 RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005314 RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) |
Steve Blocka7e24c12009-10-30 11:49:00 +00005315 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
5316
5317 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01005318 it.rinfo()->Visit(v);
Steve Blocka7e24c12009-10-30 11:49:00 +00005319 }
5320
5321 ScopeInfo<>::IterateScopeInfo(this, v);
5322}
5323
5324
Steve Blockd0582a62009-12-15 09:54:21 +00005325void Code::Relocate(intptr_t delta) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005326 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
5327 it.rinfo()->apply(delta);
5328 }
5329 CPU::FlushICache(instruction_start(), instruction_size());
5330}
5331
5332
5333void Code::CopyFrom(const CodeDesc& desc) {
5334 // copy code
5335 memmove(instruction_start(), desc.buffer, desc.instr_size);
5336
5337 // fill gap with zero bytes
5338 { byte* p = instruction_start() + desc.instr_size;
5339 byte* q = relocation_start();
5340 while (p < q) {
5341 *p++ = 0;
5342 }
5343 }
5344
5345 // copy reloc info
5346 memmove(relocation_start(),
5347 desc.buffer + desc.buffer_size - desc.reloc_size,
5348 desc.reloc_size);
5349
5350 // unbox handles and relocate
Steve Block3ce2e202009-11-05 08:53:23 +00005351 intptr_t delta = instruction_start() - desc.buffer;
Steve Blocka7e24c12009-10-30 11:49:00 +00005352 int mode_mask = RelocInfo::kCodeTargetMask |
5353 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
5354 RelocInfo::kApplyMask;
Steve Block3ce2e202009-11-05 08:53:23 +00005355 Assembler* origin = desc.origin; // Needed to find target_object on X64.
Steve Blocka7e24c12009-10-30 11:49:00 +00005356 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
5357 RelocInfo::Mode mode = it.rinfo()->rmode();
5358 if (mode == RelocInfo::EMBEDDED_OBJECT) {
Steve Block3ce2e202009-11-05 08:53:23 +00005359 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005360 it.rinfo()->set_target_object(*p);
5361 } else if (RelocInfo::IsCodeTarget(mode)) {
5362 // rewrite code handles in inline cache targets to direct
5363 // pointers to the first instruction in the code object
Steve Block3ce2e202009-11-05 08:53:23 +00005364 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005365 Code* code = Code::cast(*p);
5366 it.rinfo()->set_target_address(code->instruction_start());
5367 } else {
5368 it.rinfo()->apply(delta);
5369 }
5370 }
5371 CPU::FlushICache(instruction_start(), instruction_size());
5372}
5373
5374
5375// Locate the source position which is closest to the address in the code. This
5376// is using the source position information embedded in the relocation info.
5377// The position returned is relative to the beginning of the script where the
5378// source for this function is found.
5379int Code::SourcePosition(Address pc) {
5380 int distance = kMaxInt;
5381 int position = RelocInfo::kNoPosition; // Initially no position found.
5382 // Run through all the relocation info to find the best matching source
5383 // position. All the code needs to be considered as the sequence of the
5384 // instructions in the code does not necessarily follow the same order as the
5385 // source.
5386 RelocIterator it(this, RelocInfo::kPositionMask);
5387 while (!it.done()) {
5388 // Only look at positions after the current pc.
5389 if (it.rinfo()->pc() < pc) {
5390 // Get position and distance.
Steve Blockd0582a62009-12-15 09:54:21 +00005391
5392 int dist = static_cast<int>(pc - it.rinfo()->pc());
5393 int pos = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005394 // If this position is closer than the current candidate or if it has the
5395 // same distance as the current candidate and the position is higher then
5396 // this position is the new candidate.
5397 if ((dist < distance) ||
5398 (dist == distance && pos > position)) {
5399 position = pos;
5400 distance = dist;
5401 }
5402 }
5403 it.next();
5404 }
5405 return position;
5406}
5407
5408
5409// Same as Code::SourcePosition above except it only looks for statement
5410// positions.
5411int Code::SourceStatementPosition(Address pc) {
5412 // First find the position as close as possible using all position
5413 // information.
5414 int position = SourcePosition(pc);
5415 // Now find the closest statement position before the position.
5416 int statement_position = 0;
5417 RelocIterator it(this, RelocInfo::kPositionMask);
5418 while (!it.done()) {
5419 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
Steve Blockd0582a62009-12-15 09:54:21 +00005420 int p = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005421 if (statement_position < p && p <= position) {
5422 statement_position = p;
5423 }
5424 }
5425 it.next();
5426 }
5427 return statement_position;
5428}
5429
5430
5431#ifdef ENABLE_DISASSEMBLER
5432// Identify kind of code.
5433const char* Code::Kind2String(Kind kind) {
5434 switch (kind) {
5435 case FUNCTION: return "FUNCTION";
5436 case STUB: return "STUB";
5437 case BUILTIN: return "BUILTIN";
5438 case LOAD_IC: return "LOAD_IC";
5439 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
5440 case STORE_IC: return "STORE_IC";
5441 case KEYED_STORE_IC: return "KEYED_STORE_IC";
5442 case CALL_IC: return "CALL_IC";
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005443 case KEYED_CALL_IC: return "KEYED_CALL_IC";
Steve Block6ded16b2010-05-10 14:33:55 +01005444 case BINARY_OP_IC: return "BINARY_OP_IC";
Steve Blocka7e24c12009-10-30 11:49:00 +00005445 }
5446 UNREACHABLE();
5447 return NULL;
5448}
5449
5450
5451const char* Code::ICState2String(InlineCacheState state) {
5452 switch (state) {
5453 case UNINITIALIZED: return "UNINITIALIZED";
5454 case PREMONOMORPHIC: return "PREMONOMORPHIC";
5455 case MONOMORPHIC: return "MONOMORPHIC";
5456 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
5457 case MEGAMORPHIC: return "MEGAMORPHIC";
5458 case DEBUG_BREAK: return "DEBUG_BREAK";
5459 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
5460 }
5461 UNREACHABLE();
5462 return NULL;
5463}
5464
5465
5466const char* Code::PropertyType2String(PropertyType type) {
5467 switch (type) {
5468 case NORMAL: return "NORMAL";
5469 case FIELD: return "FIELD";
5470 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
5471 case CALLBACKS: return "CALLBACKS";
5472 case INTERCEPTOR: return "INTERCEPTOR";
5473 case MAP_TRANSITION: return "MAP_TRANSITION";
5474 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
5475 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
5476 }
5477 UNREACHABLE();
5478 return NULL;
5479}
5480
5481void Code::Disassemble(const char* name) {
5482 PrintF("kind = %s\n", Kind2String(kind()));
5483 if (is_inline_cache_stub()) {
5484 PrintF("ic_state = %s\n", ICState2String(ic_state()));
5485 PrintF("ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
5486 if (ic_state() == MONOMORPHIC) {
5487 PrintF("type = %s\n", PropertyType2String(type()));
5488 }
5489 }
5490 if ((name != NULL) && (name[0] != '\0')) {
5491 PrintF("name = %s\n", name);
5492 }
5493
5494 PrintF("Instructions (size = %d)\n", instruction_size());
5495 Disassembler::Decode(NULL, this);
5496 PrintF("\n");
5497
5498 PrintF("RelocInfo (size = %d)\n", relocation_size());
5499 for (RelocIterator it(this); !it.done(); it.next())
5500 it.rinfo()->Print();
5501 PrintF("\n");
5502}
5503#endif // ENABLE_DISASSEMBLER
5504
5505
Steve Block8defd9f2010-07-08 12:39:36 +01005506Object* JSObject::SetFastElementsCapacityAndLength(int capacity, int length) {
Steve Block3ce2e202009-11-05 08:53:23 +00005507 // We should never end in here with a pixel or external array.
5508 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Block8defd9f2010-07-08 12:39:36 +01005509
5510 Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
5511 if (obj->IsFailure()) return obj;
5512 FixedArray* elems = FixedArray::cast(obj);
5513
5514 obj = map()->GetFastElementsMap();
5515 if (obj->IsFailure()) return obj;
5516 Map* new_map = Map::cast(obj);
5517
Leon Clarke4515c472010-02-03 11:58:03 +00005518 AssertNoAllocation no_gc;
5519 WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00005520 switch (GetElementsKind()) {
5521 case FAST_ELEMENTS: {
5522 FixedArray* old_elements = FixedArray::cast(elements());
5523 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
5524 // Fill out the new array with this content and array holes.
5525 for (uint32_t i = 0; i < old_length; i++) {
5526 elems->set(i, old_elements->get(i), mode);
5527 }
5528 break;
5529 }
5530 case DICTIONARY_ELEMENTS: {
5531 NumberDictionary* dictionary = NumberDictionary::cast(elements());
5532 for (int i = 0; i < dictionary->Capacity(); i++) {
5533 Object* key = dictionary->KeyAt(i);
5534 if (key->IsNumber()) {
5535 uint32_t entry = static_cast<uint32_t>(key->Number());
5536 elems->set(entry, dictionary->ValueAt(i), mode);
5537 }
5538 }
5539 break;
5540 }
5541 default:
5542 UNREACHABLE();
5543 break;
5544 }
Steve Block8defd9f2010-07-08 12:39:36 +01005545
5546 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00005547 set_elements(elems);
Steve Block8defd9f2010-07-08 12:39:36 +01005548
5549 if (IsJSArray()) {
5550 JSArray::cast(this)->set_length(Smi::FromInt(length));
5551 }
5552
5553 return this;
Steve Blocka7e24c12009-10-30 11:49:00 +00005554}
5555
5556
5557Object* JSObject::SetSlowElements(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00005558 // We should never end in here with a pixel or external array.
5559 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00005560
5561 uint32_t new_length = static_cast<uint32_t>(len->Number());
5562
5563 switch (GetElementsKind()) {
5564 case FAST_ELEMENTS: {
5565 // Make sure we never try to shrink dense arrays into sparse arrays.
5566 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
5567 new_length);
5568 Object* obj = NormalizeElements();
5569 if (obj->IsFailure()) return obj;
5570
5571 // Update length for JSArrays.
5572 if (IsJSArray()) JSArray::cast(this)->set_length(len);
5573 break;
5574 }
5575 case DICTIONARY_ELEMENTS: {
5576 if (IsJSArray()) {
5577 uint32_t old_length =
Steve Block6ded16b2010-05-10 14:33:55 +01005578 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
Steve Blocka7e24c12009-10-30 11:49:00 +00005579 element_dictionary()->RemoveNumberEntries(new_length, old_length),
5580 JSArray::cast(this)->set_length(len);
5581 }
5582 break;
5583 }
5584 default:
5585 UNREACHABLE();
5586 break;
5587 }
5588 return this;
5589}
5590
5591
5592Object* JSArray::Initialize(int capacity) {
5593 ASSERT(capacity >= 0);
Leon Clarke4515c472010-02-03 11:58:03 +00005594 set_length(Smi::FromInt(0));
Steve Blocka7e24c12009-10-30 11:49:00 +00005595 FixedArray* new_elements;
5596 if (capacity == 0) {
5597 new_elements = Heap::empty_fixed_array();
5598 } else {
5599 Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
5600 if (obj->IsFailure()) return obj;
5601 new_elements = FixedArray::cast(obj);
5602 }
5603 set_elements(new_elements);
5604 return this;
5605}
5606
5607
5608void JSArray::Expand(int required_size) {
5609 Handle<JSArray> self(this);
5610 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
5611 int old_size = old_backing->length();
Steve Blockd0582a62009-12-15 09:54:21 +00005612 int new_size = required_size > old_size ? required_size : old_size;
Steve Blocka7e24c12009-10-30 11:49:00 +00005613 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
5614 // Can't use this any more now because we may have had a GC!
5615 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
5616 self->SetContent(*new_backing);
5617}
5618
5619
5620// Computes the new capacity when expanding the elements of a JSObject.
5621static int NewElementsCapacity(int old_capacity) {
5622 // (old_capacity + 50%) + 16
5623 return old_capacity + (old_capacity >> 1) + 16;
5624}
5625
5626
5627static Object* ArrayLengthRangeError() {
5628 HandleScope scope;
5629 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
5630 HandleVector<Object>(NULL, 0)));
5631}
5632
5633
5634Object* JSObject::SetElementsLength(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00005635 // We should never end in here with a pixel or external array.
Steve Block6ded16b2010-05-10 14:33:55 +01005636 ASSERT(AllowsSetElementsLength());
Steve Blocka7e24c12009-10-30 11:49:00 +00005637
5638 Object* smi_length = len->ToSmi();
5639 if (smi_length->IsSmi()) {
Steve Block8defd9f2010-07-08 12:39:36 +01005640 const int value = Smi::cast(smi_length)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00005641 if (value < 0) return ArrayLengthRangeError();
5642 switch (GetElementsKind()) {
5643 case FAST_ELEMENTS: {
5644 int old_capacity = FixedArray::cast(elements())->length();
5645 if (value <= old_capacity) {
5646 if (IsJSArray()) {
5647 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
5648 // NOTE: We may be able to optimize this by removing the
5649 // last part of the elements backing storage array and
5650 // setting the capacity to the new size.
5651 for (int i = value; i < old_length; i++) {
5652 FixedArray::cast(elements())->set_the_hole(i);
5653 }
Leon Clarke4515c472010-02-03 11:58:03 +00005654 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00005655 }
5656 return this;
5657 }
5658 int min = NewElementsCapacity(old_capacity);
5659 int new_capacity = value > min ? value : min;
5660 if (new_capacity <= kMaxFastElementsLength ||
5661 !ShouldConvertToSlowElements(new_capacity)) {
Steve Block8defd9f2010-07-08 12:39:36 +01005662 Object* obj = SetFastElementsCapacityAndLength(new_capacity, value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005663 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00005664 return this;
5665 }
5666 break;
5667 }
5668 case DICTIONARY_ELEMENTS: {
5669 if (IsJSArray()) {
5670 if (value == 0) {
5671 // If the length of a slow array is reset to zero, we clear
5672 // the array and flush backing storage. This has the added
5673 // benefit that the array returns to fast mode.
Steve Block8defd9f2010-07-08 12:39:36 +01005674 Object* obj = ResetElements();
5675 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00005676 } else {
5677 // Remove deleted elements.
5678 uint32_t old_length =
5679 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
5680 element_dictionary()->RemoveNumberEntries(value, old_length);
5681 }
Leon Clarke4515c472010-02-03 11:58:03 +00005682 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00005683 }
5684 return this;
5685 }
5686 default:
5687 UNREACHABLE();
5688 break;
5689 }
5690 }
5691
5692 // General slow case.
5693 if (len->IsNumber()) {
5694 uint32_t length;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005695 if (len->ToArrayIndex(&length)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005696 return SetSlowElements(len);
5697 } else {
5698 return ArrayLengthRangeError();
5699 }
5700 }
5701
5702 // len is not a number so make the array size one and
5703 // set only element to len.
5704 Object* obj = Heap::AllocateFixedArray(1);
5705 if (obj->IsFailure()) return obj;
5706 FixedArray::cast(obj)->set(0, len);
Leon Clarke4515c472010-02-03 11:58:03 +00005707 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
Steve Blocka7e24c12009-10-30 11:49:00 +00005708 set_elements(FixedArray::cast(obj));
5709 return this;
5710}
5711
5712
Andrei Popescu402d9372010-02-26 13:31:12 +00005713Object* JSObject::SetPrototype(Object* value,
5714 bool skip_hidden_prototypes) {
5715 // Silently ignore the change if value is not a JSObject or null.
5716 // SpiderMonkey behaves this way.
5717 if (!value->IsJSObject() && !value->IsNull()) return value;
5718
5719 // Before we can set the prototype we need to be sure
5720 // prototype cycles are prevented.
5721 // It is sufficient to validate that the receiver is not in the new prototype
5722 // chain.
5723 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
5724 if (JSObject::cast(pt) == this) {
5725 // Cycle detected.
5726 HandleScope scope;
5727 return Top::Throw(*Factory::NewError("cyclic_proto",
5728 HandleVector<Object>(NULL, 0)));
5729 }
5730 }
5731
5732 JSObject* real_receiver = this;
5733
5734 if (skip_hidden_prototypes) {
5735 // Find the first object in the chain whose prototype object is not
5736 // hidden and set the new prototype on that object.
5737 Object* current_proto = real_receiver->GetPrototype();
5738 while (current_proto->IsJSObject() &&
5739 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
5740 real_receiver = JSObject::cast(current_proto);
5741 current_proto = current_proto->GetPrototype();
5742 }
5743 }
5744
5745 // Set the new prototype of the object.
5746 Object* new_map = real_receiver->map()->CopyDropTransitions();
5747 if (new_map->IsFailure()) return new_map;
5748 Map::cast(new_map)->set_prototype(value);
5749 real_receiver->set_map(Map::cast(new_map));
5750
Kristian Monsen25f61362010-05-21 11:50:48 +01005751 Heap::ClearInstanceofCache();
5752
Andrei Popescu402d9372010-02-26 13:31:12 +00005753 return value;
5754}
5755
5756
Steve Blocka7e24c12009-10-30 11:49:00 +00005757bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
5758 switch (GetElementsKind()) {
5759 case FAST_ELEMENTS: {
5760 uint32_t length = IsJSArray() ?
5761 static_cast<uint32_t>
5762 (Smi::cast(JSArray::cast(this)->length())->value()) :
5763 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5764 if ((index < length) &&
5765 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
5766 return true;
5767 }
5768 break;
5769 }
5770 case PIXEL_ELEMENTS: {
5771 // TODO(iposva): Add testcase.
5772 PixelArray* pixels = PixelArray::cast(elements());
5773 if (index < static_cast<uint32_t>(pixels->length())) {
5774 return true;
5775 }
5776 break;
5777 }
Steve Block3ce2e202009-11-05 08:53:23 +00005778 case EXTERNAL_BYTE_ELEMENTS:
5779 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5780 case EXTERNAL_SHORT_ELEMENTS:
5781 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5782 case EXTERNAL_INT_ELEMENTS:
5783 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5784 case EXTERNAL_FLOAT_ELEMENTS: {
5785 // TODO(kbr): Add testcase.
5786 ExternalArray* array = ExternalArray::cast(elements());
5787 if (index < static_cast<uint32_t>(array->length())) {
5788 return true;
5789 }
5790 break;
5791 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005792 case DICTIONARY_ELEMENTS: {
5793 if (element_dictionary()->FindEntry(index)
5794 != NumberDictionary::kNotFound) {
5795 return true;
5796 }
5797 break;
5798 }
5799 default:
5800 UNREACHABLE();
5801 break;
5802 }
5803
5804 // Handle [] on String objects.
5805 if (this->IsStringObjectWithCharacterAt(index)) return true;
5806
5807 Object* pt = GetPrototype();
5808 if (pt == Heap::null_value()) return false;
5809 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5810}
5811
5812
5813bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
5814 // Make sure that the top context does not change when doing
5815 // callbacks or interceptor calls.
5816 AssertNoContextChange ncc;
5817 HandleScope scope;
5818 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5819 Handle<JSObject> receiver_handle(receiver);
5820 Handle<JSObject> holder_handle(this);
5821 CustomArguments args(interceptor->data(), receiver, this);
5822 v8::AccessorInfo info(args.end());
5823 if (!interceptor->query()->IsUndefined()) {
5824 v8::IndexedPropertyQuery query =
5825 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
5826 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
5827 v8::Handle<v8::Boolean> result;
5828 {
5829 // Leaving JavaScript.
5830 VMState state(EXTERNAL);
5831 result = query(index, info);
5832 }
5833 if (!result.IsEmpty()) return result->IsTrue();
5834 } else if (!interceptor->getter()->IsUndefined()) {
5835 v8::IndexedPropertyGetter getter =
5836 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
5837 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
5838 v8::Handle<v8::Value> result;
5839 {
5840 // Leaving JavaScript.
5841 VMState state(EXTERNAL);
5842 result = getter(index, info);
5843 }
5844 if (!result.IsEmpty()) return true;
5845 }
5846 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
5847}
5848
5849
5850bool JSObject::HasLocalElement(uint32_t index) {
5851 // Check access rights if needed.
5852 if (IsAccessCheckNeeded() &&
5853 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5854 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5855 return false;
5856 }
5857
5858 // Check for lookup interceptor
5859 if (HasIndexedInterceptor()) {
5860 return HasElementWithInterceptor(this, index);
5861 }
5862
5863 // Handle [] on String objects.
5864 if (this->IsStringObjectWithCharacterAt(index)) return true;
5865
5866 switch (GetElementsKind()) {
5867 case FAST_ELEMENTS: {
5868 uint32_t length = IsJSArray() ?
5869 static_cast<uint32_t>
5870 (Smi::cast(JSArray::cast(this)->length())->value()) :
5871 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5872 return (index < length) &&
5873 !FixedArray::cast(elements())->get(index)->IsTheHole();
5874 }
5875 case PIXEL_ELEMENTS: {
5876 PixelArray* pixels = PixelArray::cast(elements());
5877 return (index < static_cast<uint32_t>(pixels->length()));
5878 }
Steve Block3ce2e202009-11-05 08:53:23 +00005879 case EXTERNAL_BYTE_ELEMENTS:
5880 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5881 case EXTERNAL_SHORT_ELEMENTS:
5882 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5883 case EXTERNAL_INT_ELEMENTS:
5884 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5885 case EXTERNAL_FLOAT_ELEMENTS: {
5886 ExternalArray* array = ExternalArray::cast(elements());
5887 return (index < static_cast<uint32_t>(array->length()));
5888 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005889 case DICTIONARY_ELEMENTS: {
5890 return element_dictionary()->FindEntry(index)
5891 != NumberDictionary::kNotFound;
5892 }
5893 default:
5894 UNREACHABLE();
5895 break;
5896 }
5897 UNREACHABLE();
5898 return Heap::null_value();
5899}
5900
5901
5902bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
5903 // Check access rights if needed.
5904 if (IsAccessCheckNeeded() &&
5905 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5906 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5907 return false;
5908 }
5909
5910 // Check for lookup interceptor
5911 if (HasIndexedInterceptor()) {
5912 return HasElementWithInterceptor(receiver, index);
5913 }
5914
5915 switch (GetElementsKind()) {
5916 case FAST_ELEMENTS: {
5917 uint32_t length = IsJSArray() ?
5918 static_cast<uint32_t>
5919 (Smi::cast(JSArray::cast(this)->length())->value()) :
5920 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5921 if ((index < length) &&
5922 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
5923 break;
5924 }
5925 case PIXEL_ELEMENTS: {
5926 PixelArray* pixels = PixelArray::cast(elements());
5927 if (index < static_cast<uint32_t>(pixels->length())) {
5928 return true;
5929 }
5930 break;
5931 }
Steve Block3ce2e202009-11-05 08:53:23 +00005932 case EXTERNAL_BYTE_ELEMENTS:
5933 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5934 case EXTERNAL_SHORT_ELEMENTS:
5935 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5936 case EXTERNAL_INT_ELEMENTS:
5937 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5938 case EXTERNAL_FLOAT_ELEMENTS: {
5939 ExternalArray* array = ExternalArray::cast(elements());
5940 if (index < static_cast<uint32_t>(array->length())) {
5941 return true;
5942 }
5943 break;
5944 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005945 case DICTIONARY_ELEMENTS: {
5946 if (element_dictionary()->FindEntry(index)
5947 != NumberDictionary::kNotFound) {
5948 return true;
5949 }
5950 break;
5951 }
5952 default:
5953 UNREACHABLE();
5954 break;
5955 }
5956
5957 // Handle [] on String objects.
5958 if (this->IsStringObjectWithCharacterAt(index)) return true;
5959
5960 Object* pt = GetPrototype();
5961 if (pt == Heap::null_value()) return false;
5962 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5963}
5964
5965
5966Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) {
5967 // Make sure that the top context does not change when doing
5968 // callbacks or interceptor calls.
5969 AssertNoContextChange ncc;
5970 HandleScope scope;
5971 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5972 Handle<JSObject> this_handle(this);
5973 Handle<Object> value_handle(value);
5974 if (!interceptor->setter()->IsUndefined()) {
5975 v8::IndexedPropertySetter setter =
5976 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
5977 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
5978 CustomArguments args(interceptor->data(), this, this);
5979 v8::AccessorInfo info(args.end());
5980 v8::Handle<v8::Value> result;
5981 {
5982 // Leaving JavaScript.
5983 VMState state(EXTERNAL);
5984 result = setter(index, v8::Utils::ToLocal(value_handle), info);
5985 }
5986 RETURN_IF_SCHEDULED_EXCEPTION();
5987 if (!result.IsEmpty()) return *value_handle;
5988 }
5989 Object* raw_result =
5990 this_handle->SetElementWithoutInterceptor(index, *value_handle);
5991 RETURN_IF_SCHEDULED_EXCEPTION();
5992 return raw_result;
5993}
5994
5995
Leon Clarkef7060e22010-06-03 12:02:55 +01005996Object* JSObject::GetElementWithCallback(Object* receiver,
5997 Object* structure,
5998 uint32_t index,
5999 Object* holder) {
6000 ASSERT(!structure->IsProxy());
6001
6002 // api style callbacks.
6003 if (structure->IsAccessorInfo()) {
6004 AccessorInfo* data = AccessorInfo::cast(structure);
6005 Object* fun_obj = data->getter();
6006 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
6007 HandleScope scope;
6008 Handle<JSObject> self(JSObject::cast(receiver));
6009 Handle<JSObject> holder_handle(JSObject::cast(holder));
6010 Handle<Object> number = Factory::NewNumberFromUint(index);
6011 Handle<String> key(Factory::NumberToString(number));
6012 LOG(ApiNamedPropertyAccess("load", *self, *key));
6013 CustomArguments args(data->data(), *self, *holder_handle);
6014 v8::AccessorInfo info(args.end());
6015 v8::Handle<v8::Value> result;
6016 {
6017 // Leaving JavaScript.
6018 VMState state(EXTERNAL);
6019 result = call_fun(v8::Utils::ToLocal(key), info);
6020 }
6021 RETURN_IF_SCHEDULED_EXCEPTION();
6022 if (result.IsEmpty()) return Heap::undefined_value();
6023 return *v8::Utils::OpenHandle(*result);
6024 }
6025
6026 // __defineGetter__ callback
6027 if (structure->IsFixedArray()) {
6028 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
6029 if (getter->IsJSFunction()) {
6030 return Object::GetPropertyWithDefinedGetter(receiver,
6031 JSFunction::cast(getter));
6032 }
6033 // Getter is not a function.
6034 return Heap::undefined_value();
6035 }
6036
6037 UNREACHABLE();
6038 return NULL;
6039}
6040
6041
6042Object* JSObject::SetElementWithCallback(Object* structure,
6043 uint32_t index,
6044 Object* value,
6045 JSObject* holder) {
6046 HandleScope scope;
6047
6048 // We should never get here to initialize a const with the hole
6049 // value since a const declaration would conflict with the setter.
6050 ASSERT(!value->IsTheHole());
6051 Handle<Object> value_handle(value);
6052
6053 // To accommodate both the old and the new api we switch on the
6054 // data structure used to store the callbacks. Eventually proxy
6055 // callbacks should be phased out.
6056 ASSERT(!structure->IsProxy());
6057
6058 if (structure->IsAccessorInfo()) {
6059 // api style callbacks
6060 AccessorInfo* data = AccessorInfo::cast(structure);
6061 Object* call_obj = data->setter();
6062 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
6063 if (call_fun == NULL) return value;
6064 Handle<Object> number = Factory::NewNumberFromUint(index);
6065 Handle<String> key(Factory::NumberToString(number));
6066 LOG(ApiNamedPropertyAccess("store", this, *key));
6067 CustomArguments args(data->data(), this, JSObject::cast(holder));
6068 v8::AccessorInfo info(args.end());
6069 {
6070 // Leaving JavaScript.
6071 VMState state(EXTERNAL);
6072 call_fun(v8::Utils::ToLocal(key),
6073 v8::Utils::ToLocal(value_handle),
6074 info);
6075 }
6076 RETURN_IF_SCHEDULED_EXCEPTION();
6077 return *value_handle;
6078 }
6079
6080 if (structure->IsFixedArray()) {
6081 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
6082 if (setter->IsJSFunction()) {
6083 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
6084 } else {
6085 Handle<Object> holder_handle(holder);
6086 Handle<Object> key(Factory::NewNumberFromUint(index));
6087 Handle<Object> args[2] = { key, holder_handle };
6088 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
6089 HandleVector(args, 2)));
6090 }
6091 }
6092
6093 UNREACHABLE();
6094 return NULL;
6095}
6096
6097
Steve Blocka7e24c12009-10-30 11:49:00 +00006098// Adding n elements in fast case is O(n*n).
6099// Note: revisit design to have dual undefined values to capture absent
6100// elements.
6101Object* JSObject::SetFastElement(uint32_t index, Object* value) {
6102 ASSERT(HasFastElements());
6103
6104 FixedArray* elms = FixedArray::cast(elements());
6105 uint32_t elms_length = static_cast<uint32_t>(elms->length());
6106
6107 if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006108 if (SetElementWithCallbackSetterInPrototypes(index, value)) {
6109 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006110 }
6111 }
6112
6113 // Check whether there is extra space in fixed array..
6114 if (index < elms_length) {
6115 elms->set(index, value);
6116 if (IsJSArray()) {
6117 // Update the length of the array if needed.
6118 uint32_t array_length = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006119 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006120 if (index >= array_length) {
Leon Clarke4515c472010-02-03 11:58:03 +00006121 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006122 }
6123 }
6124 return value;
6125 }
6126
6127 // Allow gap in fast case.
6128 if ((index - elms_length) < kMaxGap) {
6129 // Try allocating extra space.
6130 int new_capacity = NewElementsCapacity(index+1);
6131 if (new_capacity <= kMaxFastElementsLength ||
6132 !ShouldConvertToSlowElements(new_capacity)) {
6133 ASSERT(static_cast<uint32_t>(new_capacity) > index);
Steve Block8defd9f2010-07-08 12:39:36 +01006134 Object* obj = SetFastElementsCapacityAndLength(new_capacity, index + 1);
Steve Blocka7e24c12009-10-30 11:49:00 +00006135 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00006136 FixedArray::cast(elements())->set(index, value);
6137 return value;
6138 }
6139 }
6140
6141 // Otherwise default to slow case.
6142 Object* obj = NormalizeElements();
6143 if (obj->IsFailure()) return obj;
6144 ASSERT(HasDictionaryElements());
6145 return SetElement(index, value);
6146}
6147
6148Object* JSObject::SetElement(uint32_t index, Object* value) {
6149 // Check access rights if needed.
6150 if (IsAccessCheckNeeded() &&
6151 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
6152 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
6153 return value;
6154 }
6155
6156 if (IsJSGlobalProxy()) {
6157 Object* proto = GetPrototype();
6158 if (proto->IsNull()) return value;
6159 ASSERT(proto->IsJSGlobalObject());
6160 return JSObject::cast(proto)->SetElement(index, value);
6161 }
6162
6163 // Check for lookup interceptor
6164 if (HasIndexedInterceptor()) {
6165 return SetElementWithInterceptor(index, value);
6166 }
6167
6168 return SetElementWithoutInterceptor(index, value);
6169}
6170
6171
6172Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
6173 switch (GetElementsKind()) {
6174 case FAST_ELEMENTS:
6175 // Fast case.
6176 return SetFastElement(index, value);
6177 case PIXEL_ELEMENTS: {
6178 PixelArray* pixels = PixelArray::cast(elements());
6179 return pixels->SetValue(index, value);
6180 }
Steve Block3ce2e202009-11-05 08:53:23 +00006181 case EXTERNAL_BYTE_ELEMENTS: {
6182 ExternalByteArray* array = ExternalByteArray::cast(elements());
6183 return array->SetValue(index, value);
6184 }
6185 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6186 ExternalUnsignedByteArray* array =
6187 ExternalUnsignedByteArray::cast(elements());
6188 return array->SetValue(index, value);
6189 }
6190 case EXTERNAL_SHORT_ELEMENTS: {
6191 ExternalShortArray* array = ExternalShortArray::cast(elements());
6192 return array->SetValue(index, value);
6193 }
6194 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6195 ExternalUnsignedShortArray* array =
6196 ExternalUnsignedShortArray::cast(elements());
6197 return array->SetValue(index, value);
6198 }
6199 case EXTERNAL_INT_ELEMENTS: {
6200 ExternalIntArray* array = ExternalIntArray::cast(elements());
6201 return array->SetValue(index, value);
6202 }
6203 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6204 ExternalUnsignedIntArray* array =
6205 ExternalUnsignedIntArray::cast(elements());
6206 return array->SetValue(index, value);
6207 }
6208 case EXTERNAL_FLOAT_ELEMENTS: {
6209 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
6210 return array->SetValue(index, value);
6211 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006212 case DICTIONARY_ELEMENTS: {
6213 // Insert element in the dictionary.
6214 FixedArray* elms = FixedArray::cast(elements());
6215 NumberDictionary* dictionary = NumberDictionary::cast(elms);
6216
6217 int entry = dictionary->FindEntry(index);
6218 if (entry != NumberDictionary::kNotFound) {
6219 Object* element = dictionary->ValueAt(entry);
6220 PropertyDetails details = dictionary->DetailsAt(entry);
6221 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006222 return SetElementWithCallback(element, index, value, this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006223 } else {
6224 dictionary->UpdateMaxNumberKey(index);
6225 dictionary->ValueAtPut(entry, value);
6226 }
6227 } else {
6228 // Index not already used. Look for an accessor in the prototype chain.
6229 if (!IsJSArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006230 if (SetElementWithCallbackSetterInPrototypes(index, value)) {
6231 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006232 }
6233 }
Steve Block8defd9f2010-07-08 12:39:36 +01006234 // When we set the is_extensible flag to false we always force
6235 // the element into dictionary mode (and force them to stay there).
6236 if (!map()->is_extensible()) {
6237 Handle<Object> number(Heap::NumberFromUint32(index));
6238 Handle<String> index_string(Factory::NumberToString(number));
6239 Handle<Object> args[1] = { index_string };
6240 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
6241 HandleVector(args, 1)));
6242 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006243 Object* result = dictionary->AtNumberPut(index, value);
6244 if (result->IsFailure()) return result;
6245 if (elms != FixedArray::cast(result)) {
6246 set_elements(FixedArray::cast(result));
6247 }
6248 }
6249
6250 // Update the array length if this JSObject is an array.
6251 if (IsJSArray()) {
6252 JSArray* array = JSArray::cast(this);
6253 Object* return_value = array->JSArrayUpdateLengthFromIndex(index,
6254 value);
6255 if (return_value->IsFailure()) return return_value;
6256 }
6257
6258 // Attempt to put this object back in fast case.
6259 if (ShouldConvertToFastElements()) {
6260 uint32_t new_length = 0;
6261 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006262 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006263 } else {
6264 new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
6265 }
Steve Block8defd9f2010-07-08 12:39:36 +01006266 Object* obj = SetFastElementsCapacityAndLength(new_length, new_length);
Steve Blocka7e24c12009-10-30 11:49:00 +00006267 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00006268#ifdef DEBUG
6269 if (FLAG_trace_normalization) {
6270 PrintF("Object elements are fast case again:\n");
6271 Print();
6272 }
6273#endif
6274 }
6275
6276 return value;
6277 }
6278 default:
6279 UNREACHABLE();
6280 break;
6281 }
6282 // All possible cases have been handled above. Add a return to avoid the
6283 // complaints from the compiler.
6284 UNREACHABLE();
6285 return Heap::null_value();
6286}
6287
6288
6289Object* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index, Object* value) {
6290 uint32_t old_len = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006291 CHECK(length()->ToArrayIndex(&old_len));
Steve Blocka7e24c12009-10-30 11:49:00 +00006292 // Check to see if we need to update the length. For now, we make
6293 // sure that the length stays within 32-bits (unsigned).
6294 if (index >= old_len && index != 0xffffffff) {
6295 Object* len =
6296 Heap::NumberFromDouble(static_cast<double>(index) + 1);
6297 if (len->IsFailure()) return len;
6298 set_length(len);
6299 }
6300 return value;
6301}
6302
6303
6304Object* JSObject::GetElementPostInterceptor(JSObject* receiver,
6305 uint32_t index) {
6306 // Get element works for both JSObject and JSArray since
6307 // JSArray::length cannot change.
6308 switch (GetElementsKind()) {
6309 case FAST_ELEMENTS: {
6310 FixedArray* elms = FixedArray::cast(elements());
6311 if (index < static_cast<uint32_t>(elms->length())) {
6312 Object* value = elms->get(index);
6313 if (!value->IsTheHole()) return value;
6314 }
6315 break;
6316 }
6317 case PIXEL_ELEMENTS: {
6318 // TODO(iposva): Add testcase and implement.
6319 UNIMPLEMENTED();
6320 break;
6321 }
Steve Block3ce2e202009-11-05 08:53:23 +00006322 case EXTERNAL_BYTE_ELEMENTS:
6323 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6324 case EXTERNAL_SHORT_ELEMENTS:
6325 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6326 case EXTERNAL_INT_ELEMENTS:
6327 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6328 case EXTERNAL_FLOAT_ELEMENTS: {
6329 // TODO(kbr): Add testcase and implement.
6330 UNIMPLEMENTED();
6331 break;
6332 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006333 case DICTIONARY_ELEMENTS: {
6334 NumberDictionary* dictionary = element_dictionary();
6335 int entry = dictionary->FindEntry(index);
6336 if (entry != NumberDictionary::kNotFound) {
6337 Object* element = dictionary->ValueAt(entry);
6338 PropertyDetails details = dictionary->DetailsAt(entry);
6339 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006340 return GetElementWithCallback(receiver,
6341 element,
6342 index,
6343 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006344 }
6345 return element;
6346 }
6347 break;
6348 }
6349 default:
6350 UNREACHABLE();
6351 break;
6352 }
6353
6354 // Continue searching via the prototype chain.
6355 Object* pt = GetPrototype();
6356 if (pt == Heap::null_value()) return Heap::undefined_value();
6357 return pt->GetElementWithReceiver(receiver, index);
6358}
6359
6360
6361Object* JSObject::GetElementWithInterceptor(JSObject* receiver,
6362 uint32_t index) {
6363 // Make sure that the top context does not change when doing
6364 // callbacks or interceptor calls.
6365 AssertNoContextChange ncc;
6366 HandleScope scope;
6367 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6368 Handle<JSObject> this_handle(receiver);
6369 Handle<JSObject> holder_handle(this);
6370
6371 if (!interceptor->getter()->IsUndefined()) {
6372 v8::IndexedPropertyGetter getter =
6373 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
6374 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
6375 CustomArguments args(interceptor->data(), receiver, this);
6376 v8::AccessorInfo info(args.end());
6377 v8::Handle<v8::Value> result;
6378 {
6379 // Leaving JavaScript.
6380 VMState state(EXTERNAL);
6381 result = getter(index, info);
6382 }
6383 RETURN_IF_SCHEDULED_EXCEPTION();
6384 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6385 }
6386
6387 Object* raw_result =
6388 holder_handle->GetElementPostInterceptor(*this_handle, index);
6389 RETURN_IF_SCHEDULED_EXCEPTION();
6390 return raw_result;
6391}
6392
6393
6394Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) {
6395 // Check access rights if needed.
6396 if (IsAccessCheckNeeded() &&
6397 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
6398 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
6399 return Heap::undefined_value();
6400 }
6401
6402 if (HasIndexedInterceptor()) {
6403 return GetElementWithInterceptor(receiver, index);
6404 }
6405
6406 // Get element works for both JSObject and JSArray since
6407 // JSArray::length cannot change.
6408 switch (GetElementsKind()) {
6409 case FAST_ELEMENTS: {
6410 FixedArray* elms = FixedArray::cast(elements());
6411 if (index < static_cast<uint32_t>(elms->length())) {
6412 Object* value = elms->get(index);
6413 if (!value->IsTheHole()) return value;
6414 }
6415 break;
6416 }
6417 case PIXEL_ELEMENTS: {
6418 PixelArray* pixels = PixelArray::cast(elements());
6419 if (index < static_cast<uint32_t>(pixels->length())) {
6420 uint8_t value = pixels->get(index);
6421 return Smi::FromInt(value);
6422 }
6423 break;
6424 }
Steve Block3ce2e202009-11-05 08:53:23 +00006425 case EXTERNAL_BYTE_ELEMENTS: {
6426 ExternalByteArray* array = ExternalByteArray::cast(elements());
6427 if (index < static_cast<uint32_t>(array->length())) {
6428 int8_t value = array->get(index);
6429 return Smi::FromInt(value);
6430 }
6431 break;
6432 }
6433 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6434 ExternalUnsignedByteArray* array =
6435 ExternalUnsignedByteArray::cast(elements());
6436 if (index < static_cast<uint32_t>(array->length())) {
6437 uint8_t value = array->get(index);
6438 return Smi::FromInt(value);
6439 }
6440 break;
6441 }
6442 case EXTERNAL_SHORT_ELEMENTS: {
6443 ExternalShortArray* array = ExternalShortArray::cast(elements());
6444 if (index < static_cast<uint32_t>(array->length())) {
6445 int16_t value = array->get(index);
6446 return Smi::FromInt(value);
6447 }
6448 break;
6449 }
6450 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6451 ExternalUnsignedShortArray* array =
6452 ExternalUnsignedShortArray::cast(elements());
6453 if (index < static_cast<uint32_t>(array->length())) {
6454 uint16_t value = array->get(index);
6455 return Smi::FromInt(value);
6456 }
6457 break;
6458 }
6459 case EXTERNAL_INT_ELEMENTS: {
6460 ExternalIntArray* array = ExternalIntArray::cast(elements());
6461 if (index < static_cast<uint32_t>(array->length())) {
6462 int32_t value = array->get(index);
6463 return Heap::NumberFromInt32(value);
6464 }
6465 break;
6466 }
6467 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6468 ExternalUnsignedIntArray* array =
6469 ExternalUnsignedIntArray::cast(elements());
6470 if (index < static_cast<uint32_t>(array->length())) {
6471 uint32_t value = array->get(index);
6472 return Heap::NumberFromUint32(value);
6473 }
6474 break;
6475 }
6476 case EXTERNAL_FLOAT_ELEMENTS: {
6477 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
6478 if (index < static_cast<uint32_t>(array->length())) {
6479 float value = array->get(index);
6480 return Heap::AllocateHeapNumber(value);
6481 }
6482 break;
6483 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006484 case DICTIONARY_ELEMENTS: {
6485 NumberDictionary* dictionary = element_dictionary();
6486 int entry = dictionary->FindEntry(index);
6487 if (entry != NumberDictionary::kNotFound) {
6488 Object* element = dictionary->ValueAt(entry);
6489 PropertyDetails details = dictionary->DetailsAt(entry);
6490 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006491 return GetElementWithCallback(receiver,
6492 element,
6493 index,
6494 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006495 }
6496 return element;
6497 }
6498 break;
6499 }
6500 }
6501
6502 Object* pt = GetPrototype();
6503 if (pt == Heap::null_value()) return Heap::undefined_value();
6504 return pt->GetElementWithReceiver(receiver, index);
6505}
6506
6507
6508bool JSObject::HasDenseElements() {
6509 int capacity = 0;
6510 int number_of_elements = 0;
6511
6512 switch (GetElementsKind()) {
6513 case FAST_ELEMENTS: {
6514 FixedArray* elms = FixedArray::cast(elements());
6515 capacity = elms->length();
6516 for (int i = 0; i < capacity; i++) {
6517 if (!elms->get(i)->IsTheHole()) number_of_elements++;
6518 }
6519 break;
6520 }
Steve Block3ce2e202009-11-05 08:53:23 +00006521 case PIXEL_ELEMENTS:
6522 case EXTERNAL_BYTE_ELEMENTS:
6523 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6524 case EXTERNAL_SHORT_ELEMENTS:
6525 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6526 case EXTERNAL_INT_ELEMENTS:
6527 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6528 case EXTERNAL_FLOAT_ELEMENTS: {
Steve Blocka7e24c12009-10-30 11:49:00 +00006529 return true;
6530 }
6531 case DICTIONARY_ELEMENTS: {
6532 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6533 capacity = dictionary->Capacity();
6534 number_of_elements = dictionary->NumberOfElements();
6535 break;
6536 }
6537 default:
6538 UNREACHABLE();
6539 break;
6540 }
6541
6542 if (capacity == 0) return true;
6543 return (number_of_elements > (capacity / 2));
6544}
6545
6546
6547bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
6548 ASSERT(HasFastElements());
6549 // Keep the array in fast case if the current backing storage is
6550 // almost filled and if the new capacity is no more than twice the
6551 // old capacity.
6552 int elements_length = FixedArray::cast(elements())->length();
6553 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
6554}
6555
6556
6557bool JSObject::ShouldConvertToFastElements() {
6558 ASSERT(HasDictionaryElements());
6559 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6560 // If the elements are sparse, we should not go back to fast case.
6561 if (!HasDenseElements()) return false;
6562 // If an element has been added at a very high index in the elements
6563 // dictionary, we cannot go back to fast case.
6564 if (dictionary->requires_slow_elements()) return false;
6565 // An object requiring access checks is never allowed to have fast
6566 // elements. If it had fast elements we would skip security checks.
6567 if (IsAccessCheckNeeded()) return false;
6568 // If the dictionary backing storage takes up roughly half as much
6569 // space as a fast-case backing storage would the array should have
6570 // fast elements.
6571 uint32_t length = 0;
6572 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006573 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006574 } else {
6575 length = dictionary->max_number_key();
6576 }
6577 return static_cast<uint32_t>(dictionary->Capacity()) >=
6578 (length / (2 * NumberDictionary::kEntrySize));
6579}
6580
6581
6582// Certain compilers request function template instantiation when they
6583// see the definition of the other template functions in the
6584// class. This requires us to have the template functions put
6585// together, so even though this function belongs in objects-debug.cc,
6586// we keep it here instead to satisfy certain compilers.
6587#ifdef DEBUG
6588template<typename Shape, typename Key>
6589void Dictionary<Shape, Key>::Print() {
6590 int capacity = HashTable<Shape, Key>::Capacity();
6591 for (int i = 0; i < capacity; i++) {
6592 Object* k = HashTable<Shape, Key>::KeyAt(i);
6593 if (HashTable<Shape, Key>::IsKey(k)) {
6594 PrintF(" ");
6595 if (k->IsString()) {
6596 String::cast(k)->StringPrint();
6597 } else {
6598 k->ShortPrint();
6599 }
6600 PrintF(": ");
6601 ValueAt(i)->ShortPrint();
6602 PrintF("\n");
6603 }
6604 }
6605}
6606#endif
6607
6608
6609template<typename Shape, typename Key>
6610void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
6611 int pos = 0;
6612 int capacity = HashTable<Shape, Key>::Capacity();
Leon Clarke4515c472010-02-03 11:58:03 +00006613 AssertNoAllocation no_gc;
6614 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00006615 for (int i = 0; i < capacity; i++) {
6616 Object* k = Dictionary<Shape, Key>::KeyAt(i);
6617 if (Dictionary<Shape, Key>::IsKey(k)) {
6618 elements->set(pos++, ValueAt(i), mode);
6619 }
6620 }
6621 ASSERT(pos == elements->length());
6622}
6623
6624
6625InterceptorInfo* JSObject::GetNamedInterceptor() {
6626 ASSERT(map()->has_named_interceptor());
6627 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01006628 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00006629 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01006630 constructor->shared()->get_api_func_data()->named_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00006631 return InterceptorInfo::cast(result);
6632}
6633
6634
6635InterceptorInfo* JSObject::GetIndexedInterceptor() {
6636 ASSERT(map()->has_indexed_interceptor());
6637 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01006638 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00006639 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01006640 constructor->shared()->get_api_func_data()->indexed_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00006641 return InterceptorInfo::cast(result);
6642}
6643
6644
6645Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver,
6646 String* name,
6647 PropertyAttributes* attributes) {
6648 // Check local property in holder, ignore interceptor.
6649 LookupResult result;
6650 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00006651 if (result.IsProperty()) {
6652 return GetProperty(receiver, &result, name, attributes);
6653 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006654 // Continue searching via the prototype chain.
6655 Object* pt = GetPrototype();
6656 *attributes = ABSENT;
6657 if (pt == Heap::null_value()) return Heap::undefined_value();
6658 return pt->GetPropertyWithReceiver(receiver, name, attributes);
6659}
6660
6661
Steve Blockd0582a62009-12-15 09:54:21 +00006662Object* JSObject::GetLocalPropertyPostInterceptor(
6663 JSObject* receiver,
6664 String* name,
6665 PropertyAttributes* attributes) {
6666 // Check local property in holder, ignore interceptor.
6667 LookupResult result;
6668 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00006669 if (result.IsProperty()) {
6670 return GetProperty(receiver, &result, name, attributes);
6671 }
6672 return Heap::undefined_value();
Steve Blockd0582a62009-12-15 09:54:21 +00006673}
6674
6675
Steve Blocka7e24c12009-10-30 11:49:00 +00006676Object* JSObject::GetPropertyWithInterceptor(
6677 JSObject* receiver,
6678 String* name,
6679 PropertyAttributes* attributes) {
6680 InterceptorInfo* interceptor = GetNamedInterceptor();
6681 HandleScope scope;
6682 Handle<JSObject> receiver_handle(receiver);
6683 Handle<JSObject> holder_handle(this);
6684 Handle<String> name_handle(name);
6685
6686 if (!interceptor->getter()->IsUndefined()) {
6687 v8::NamedPropertyGetter getter =
6688 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
6689 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
6690 CustomArguments args(interceptor->data(), receiver, this);
6691 v8::AccessorInfo info(args.end());
6692 v8::Handle<v8::Value> result;
6693 {
6694 // Leaving JavaScript.
6695 VMState state(EXTERNAL);
6696 result = getter(v8::Utils::ToLocal(name_handle), info);
6697 }
6698 RETURN_IF_SCHEDULED_EXCEPTION();
6699 if (!result.IsEmpty()) {
6700 *attributes = NONE;
6701 return *v8::Utils::OpenHandle(*result);
6702 }
6703 }
6704
6705 Object* result = holder_handle->GetPropertyPostInterceptor(
6706 *receiver_handle,
6707 *name_handle,
6708 attributes);
6709 RETURN_IF_SCHEDULED_EXCEPTION();
6710 return result;
6711}
6712
6713
6714bool JSObject::HasRealNamedProperty(String* key) {
6715 // Check access rights if needed.
6716 if (IsAccessCheckNeeded() &&
6717 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
6718 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6719 return false;
6720 }
6721
6722 LookupResult result;
6723 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00006724 return result.IsProperty() && (result.type() != INTERCEPTOR);
Steve Blocka7e24c12009-10-30 11:49:00 +00006725}
6726
6727
6728bool JSObject::HasRealElementProperty(uint32_t index) {
6729 // Check access rights if needed.
6730 if (IsAccessCheckNeeded() &&
6731 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6732 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6733 return false;
6734 }
6735
6736 // Handle [] on String objects.
6737 if (this->IsStringObjectWithCharacterAt(index)) return true;
6738
6739 switch (GetElementsKind()) {
6740 case FAST_ELEMENTS: {
6741 uint32_t length = IsJSArray() ?
6742 static_cast<uint32_t>(
6743 Smi::cast(JSArray::cast(this)->length())->value()) :
6744 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6745 return (index < length) &&
6746 !FixedArray::cast(elements())->get(index)->IsTheHole();
6747 }
6748 case PIXEL_ELEMENTS: {
6749 PixelArray* pixels = PixelArray::cast(elements());
6750 return index < static_cast<uint32_t>(pixels->length());
6751 }
Steve Block3ce2e202009-11-05 08:53:23 +00006752 case EXTERNAL_BYTE_ELEMENTS:
6753 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6754 case EXTERNAL_SHORT_ELEMENTS:
6755 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6756 case EXTERNAL_INT_ELEMENTS:
6757 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6758 case EXTERNAL_FLOAT_ELEMENTS: {
6759 ExternalArray* array = ExternalArray::cast(elements());
6760 return index < static_cast<uint32_t>(array->length());
6761 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006762 case DICTIONARY_ELEMENTS: {
6763 return element_dictionary()->FindEntry(index)
6764 != NumberDictionary::kNotFound;
6765 }
6766 default:
6767 UNREACHABLE();
6768 break;
6769 }
6770 // All possibilities have been handled above already.
6771 UNREACHABLE();
6772 return Heap::null_value();
6773}
6774
6775
6776bool JSObject::HasRealNamedCallbackProperty(String* key) {
6777 // Check access rights if needed.
6778 if (IsAccessCheckNeeded() &&
6779 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
6780 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6781 return false;
6782 }
6783
6784 LookupResult result;
6785 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00006786 return result.IsProperty() && (result.type() == CALLBACKS);
Steve Blocka7e24c12009-10-30 11:49:00 +00006787}
6788
6789
6790int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
6791 if (HasFastProperties()) {
6792 DescriptorArray* descs = map()->instance_descriptors();
6793 int result = 0;
6794 for (int i = 0; i < descs->number_of_descriptors(); i++) {
6795 PropertyDetails details = descs->GetDetails(i);
6796 if (details.IsProperty() && (details.attributes() & filter) == 0) {
6797 result++;
6798 }
6799 }
6800 return result;
6801 } else {
6802 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
6803 }
6804}
6805
6806
6807int JSObject::NumberOfEnumProperties() {
6808 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
6809}
6810
6811
6812void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
6813 Object* temp = get(i);
6814 set(i, get(j));
6815 set(j, temp);
6816 if (this != numbers) {
6817 temp = numbers->get(i);
6818 numbers->set(i, numbers->get(j));
6819 numbers->set(j, temp);
6820 }
6821}
6822
6823
6824static void InsertionSortPairs(FixedArray* content,
6825 FixedArray* numbers,
6826 int len) {
6827 for (int i = 1; i < len; i++) {
6828 int j = i;
6829 while (j > 0 &&
6830 (NumberToUint32(numbers->get(j - 1)) >
6831 NumberToUint32(numbers->get(j)))) {
6832 content->SwapPairs(numbers, j - 1, j);
6833 j--;
6834 }
6835 }
6836}
6837
6838
6839void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
6840 // In-place heap sort.
6841 ASSERT(content->length() == numbers->length());
6842
6843 // Bottom-up max-heap construction.
6844 for (int i = 1; i < len; ++i) {
6845 int child_index = i;
6846 while (child_index > 0) {
6847 int parent_index = ((child_index + 1) >> 1) - 1;
6848 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
6849 uint32_t child_value = NumberToUint32(numbers->get(child_index));
6850 if (parent_value < child_value) {
6851 content->SwapPairs(numbers, parent_index, child_index);
6852 } else {
6853 break;
6854 }
6855 child_index = parent_index;
6856 }
6857 }
6858
6859 // Extract elements and create sorted array.
6860 for (int i = len - 1; i > 0; --i) {
6861 // Put max element at the back of the array.
6862 content->SwapPairs(numbers, 0, i);
6863 // Sift down the new top element.
6864 int parent_index = 0;
6865 while (true) {
6866 int child_index = ((parent_index + 1) << 1) - 1;
6867 if (child_index >= i) break;
6868 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
6869 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
6870 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
6871 if (child_index + 1 >= i || child1_value > child2_value) {
6872 if (parent_value > child1_value) break;
6873 content->SwapPairs(numbers, parent_index, child_index);
6874 parent_index = child_index;
6875 } else {
6876 if (parent_value > child2_value) break;
6877 content->SwapPairs(numbers, parent_index, child_index + 1);
6878 parent_index = child_index + 1;
6879 }
6880 }
6881 }
6882}
6883
6884
6885// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
6886void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
6887 ASSERT(this->length() == numbers->length());
6888 // For small arrays, simply use insertion sort.
6889 if (len <= 10) {
6890 InsertionSortPairs(this, numbers, len);
6891 return;
6892 }
6893 // Check the range of indices.
6894 uint32_t min_index = NumberToUint32(numbers->get(0));
6895 uint32_t max_index = min_index;
6896 uint32_t i;
6897 for (i = 1; i < len; i++) {
6898 if (NumberToUint32(numbers->get(i)) < min_index) {
6899 min_index = NumberToUint32(numbers->get(i));
6900 } else if (NumberToUint32(numbers->get(i)) > max_index) {
6901 max_index = NumberToUint32(numbers->get(i));
6902 }
6903 }
6904 if (max_index - min_index + 1 == len) {
6905 // Indices form a contiguous range, unless there are duplicates.
6906 // Do an in-place linear time sort assuming distinct numbers, but
6907 // avoid hanging in case they are not.
6908 for (i = 0; i < len; i++) {
6909 uint32_t p;
6910 uint32_t j = 0;
6911 // While the current element at i is not at its correct position p,
6912 // swap the elements at these two positions.
6913 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
6914 j++ < len) {
6915 SwapPairs(numbers, i, p);
6916 }
6917 }
6918 } else {
6919 HeapSortPairs(this, numbers, len);
6920 return;
6921 }
6922}
6923
6924
6925// Fill in the names of local properties into the supplied storage. The main
6926// purpose of this function is to provide reflection information for the object
6927// mirrors.
6928void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
6929 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
6930 if (HasFastProperties()) {
6931 DescriptorArray* descs = map()->instance_descriptors();
6932 for (int i = 0; i < descs->number_of_descriptors(); i++) {
6933 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
6934 }
6935 ASSERT(storage->length() >= index);
6936 } else {
6937 property_dictionary()->CopyKeysTo(storage);
6938 }
6939}
6940
6941
6942int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
6943 return GetLocalElementKeys(NULL, filter);
6944}
6945
6946
6947int JSObject::NumberOfEnumElements() {
Steve Blockd0582a62009-12-15 09:54:21 +00006948 // Fast case for objects with no elements.
6949 if (!IsJSValue() && HasFastElements()) {
6950 uint32_t length = IsJSArray() ?
6951 static_cast<uint32_t>(
6952 Smi::cast(JSArray::cast(this)->length())->value()) :
6953 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6954 if (length == 0) return 0;
6955 }
6956 // Compute the number of enumerable elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00006957 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
6958}
6959
6960
6961int JSObject::GetLocalElementKeys(FixedArray* storage,
6962 PropertyAttributes filter) {
6963 int counter = 0;
6964 switch (GetElementsKind()) {
6965 case FAST_ELEMENTS: {
6966 int length = IsJSArray() ?
6967 Smi::cast(JSArray::cast(this)->length())->value() :
6968 FixedArray::cast(elements())->length();
6969 for (int i = 0; i < length; i++) {
6970 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
6971 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00006972 storage->set(counter, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00006973 }
6974 counter++;
6975 }
6976 }
6977 ASSERT(!storage || storage->length() >= counter);
6978 break;
6979 }
6980 case PIXEL_ELEMENTS: {
6981 int length = PixelArray::cast(elements())->length();
6982 while (counter < length) {
6983 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00006984 storage->set(counter, Smi::FromInt(counter));
Steve Blocka7e24c12009-10-30 11:49:00 +00006985 }
6986 counter++;
6987 }
6988 ASSERT(!storage || storage->length() >= counter);
6989 break;
6990 }
Steve Block3ce2e202009-11-05 08:53:23 +00006991 case EXTERNAL_BYTE_ELEMENTS:
6992 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6993 case EXTERNAL_SHORT_ELEMENTS:
6994 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6995 case EXTERNAL_INT_ELEMENTS:
6996 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6997 case EXTERNAL_FLOAT_ELEMENTS: {
6998 int length = ExternalArray::cast(elements())->length();
6999 while (counter < length) {
7000 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007001 storage->set(counter, Smi::FromInt(counter));
Steve Block3ce2e202009-11-05 08:53:23 +00007002 }
7003 counter++;
7004 }
7005 ASSERT(!storage || storage->length() >= counter);
7006 break;
7007 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007008 case DICTIONARY_ELEMENTS: {
7009 if (storage != NULL) {
7010 element_dictionary()->CopyKeysTo(storage, filter);
7011 }
7012 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
7013 break;
7014 }
7015 default:
7016 UNREACHABLE();
7017 break;
7018 }
7019
7020 if (this->IsJSValue()) {
7021 Object* val = JSValue::cast(this)->value();
7022 if (val->IsString()) {
7023 String* str = String::cast(val);
7024 if (storage) {
7025 for (int i = 0; i < str->length(); i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00007026 storage->set(counter + i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00007027 }
7028 }
7029 counter += str->length();
7030 }
7031 }
7032 ASSERT(!storage || storage->length() == counter);
7033 return counter;
7034}
7035
7036
7037int JSObject::GetEnumElementKeys(FixedArray* storage) {
7038 return GetLocalElementKeys(storage,
7039 static_cast<PropertyAttributes>(DONT_ENUM));
7040}
7041
7042
7043bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
7044 ASSERT(other->IsNumber());
7045 return key == static_cast<uint32_t>(other->Number());
7046}
7047
7048
7049uint32_t NumberDictionaryShape::Hash(uint32_t key) {
7050 return ComputeIntegerHash(key);
7051}
7052
7053
7054uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) {
7055 ASSERT(other->IsNumber());
7056 return ComputeIntegerHash(static_cast<uint32_t>(other->Number()));
7057}
7058
7059
7060Object* NumberDictionaryShape::AsObject(uint32_t key) {
7061 return Heap::NumberFromUint32(key);
7062}
7063
7064
7065bool StringDictionaryShape::IsMatch(String* key, Object* other) {
7066 // We know that all entries in a hash table had their hash keys created.
7067 // Use that knowledge to have fast failure.
7068 if (key->Hash() != String::cast(other)->Hash()) return false;
7069 return key->Equals(String::cast(other));
7070}
7071
7072
7073uint32_t StringDictionaryShape::Hash(String* key) {
7074 return key->Hash();
7075}
7076
7077
7078uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) {
7079 return String::cast(other)->Hash();
7080}
7081
7082
7083Object* StringDictionaryShape::AsObject(String* key) {
7084 return key;
7085}
7086
7087
7088// StringKey simply carries a string object as key.
7089class StringKey : public HashTableKey {
7090 public:
7091 explicit StringKey(String* string) :
7092 string_(string),
7093 hash_(HashForObject(string)) { }
7094
7095 bool IsMatch(Object* string) {
7096 // We know that all entries in a hash table had their hash keys created.
7097 // Use that knowledge to have fast failure.
7098 if (hash_ != HashForObject(string)) {
7099 return false;
7100 }
7101 return string_->Equals(String::cast(string));
7102 }
7103
7104 uint32_t Hash() { return hash_; }
7105
7106 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
7107
7108 Object* AsObject() { return string_; }
7109
7110 String* string_;
7111 uint32_t hash_;
7112};
7113
7114
7115// StringSharedKeys are used as keys in the eval cache.
7116class StringSharedKey : public HashTableKey {
7117 public:
7118 StringSharedKey(String* source, SharedFunctionInfo* shared)
7119 : source_(source), shared_(shared) { }
7120
7121 bool IsMatch(Object* other) {
7122 if (!other->IsFixedArray()) return false;
7123 FixedArray* pair = FixedArray::cast(other);
7124 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7125 if (shared != shared_) return false;
7126 String* source = String::cast(pair->get(1));
7127 return source->Equals(source_);
7128 }
7129
7130 static uint32_t StringSharedHashHelper(String* source,
7131 SharedFunctionInfo* shared) {
7132 uint32_t hash = source->Hash();
7133 if (shared->HasSourceCode()) {
7134 // Instead of using the SharedFunctionInfo pointer in the hash
7135 // code computation, we use a combination of the hash of the
7136 // script source code and the start and end positions. We do
7137 // this to ensure that the cache entries can survive garbage
7138 // collection.
7139 Script* script = Script::cast(shared->script());
7140 hash ^= String::cast(script->source())->Hash();
7141 hash += shared->start_position();
7142 }
7143 return hash;
7144 }
7145
7146 uint32_t Hash() {
7147 return StringSharedHashHelper(source_, shared_);
7148 }
7149
7150 uint32_t HashForObject(Object* obj) {
7151 FixedArray* pair = FixedArray::cast(obj);
7152 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7153 String* source = String::cast(pair->get(1));
7154 return StringSharedHashHelper(source, shared);
7155 }
7156
7157 Object* AsObject() {
7158 Object* obj = Heap::AllocateFixedArray(2);
7159 if (obj->IsFailure()) return obj;
7160 FixedArray* pair = FixedArray::cast(obj);
7161 pair->set(0, shared_);
7162 pair->set(1, source_);
7163 return pair;
7164 }
7165
7166 private:
7167 String* source_;
7168 SharedFunctionInfo* shared_;
7169};
7170
7171
7172// RegExpKey carries the source and flags of a regular expression as key.
7173class RegExpKey : public HashTableKey {
7174 public:
7175 RegExpKey(String* string, JSRegExp::Flags flags)
7176 : string_(string),
7177 flags_(Smi::FromInt(flags.value())) { }
7178
Steve Block3ce2e202009-11-05 08:53:23 +00007179 // Rather than storing the key in the hash table, a pointer to the
7180 // stored value is stored where the key should be. IsMatch then
7181 // compares the search key to the found object, rather than comparing
7182 // a key to a key.
Steve Blocka7e24c12009-10-30 11:49:00 +00007183 bool IsMatch(Object* obj) {
7184 FixedArray* val = FixedArray::cast(obj);
7185 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
7186 && (flags_ == val->get(JSRegExp::kFlagsIndex));
7187 }
7188
7189 uint32_t Hash() { return RegExpHash(string_, flags_); }
7190
7191 Object* AsObject() {
7192 // Plain hash maps, which is where regexp keys are used, don't
7193 // use this function.
7194 UNREACHABLE();
7195 return NULL;
7196 }
7197
7198 uint32_t HashForObject(Object* obj) {
7199 FixedArray* val = FixedArray::cast(obj);
7200 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
7201 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
7202 }
7203
7204 static uint32_t RegExpHash(String* string, Smi* flags) {
7205 return string->Hash() + flags->value();
7206 }
7207
7208 String* string_;
7209 Smi* flags_;
7210};
7211
7212// Utf8SymbolKey carries a vector of chars as key.
7213class Utf8SymbolKey : public HashTableKey {
7214 public:
7215 explicit Utf8SymbolKey(Vector<const char> string)
Steve Blockd0582a62009-12-15 09:54:21 +00007216 : string_(string), hash_field_(0) { }
Steve Blocka7e24c12009-10-30 11:49:00 +00007217
7218 bool IsMatch(Object* string) {
7219 return String::cast(string)->IsEqualTo(string_);
7220 }
7221
7222 uint32_t Hash() {
Steve Blockd0582a62009-12-15 09:54:21 +00007223 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00007224 unibrow::Utf8InputBuffer<> buffer(string_.start(),
7225 static_cast<unsigned>(string_.length()));
7226 chars_ = buffer.Length();
Steve Blockd0582a62009-12-15 09:54:21 +00007227 hash_field_ = String::ComputeHashField(&buffer, chars_);
7228 uint32_t result = hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00007229 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
7230 return result;
7231 }
7232
7233 uint32_t HashForObject(Object* other) {
7234 return String::cast(other)->Hash();
7235 }
7236
7237 Object* AsObject() {
Steve Blockd0582a62009-12-15 09:54:21 +00007238 if (hash_field_ == 0) Hash();
7239 return Heap::AllocateSymbol(string_, chars_, hash_field_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007240 }
7241
7242 Vector<const char> string_;
Steve Blockd0582a62009-12-15 09:54:21 +00007243 uint32_t hash_field_;
Steve Blocka7e24c12009-10-30 11:49:00 +00007244 int chars_; // Caches the number of characters when computing the hash code.
7245};
7246
7247
7248// SymbolKey carries a string/symbol object as key.
7249class SymbolKey : public HashTableKey {
7250 public:
7251 explicit SymbolKey(String* string) : string_(string) { }
7252
7253 bool IsMatch(Object* string) {
7254 return String::cast(string)->Equals(string_);
7255 }
7256
7257 uint32_t Hash() { return string_->Hash(); }
7258
7259 uint32_t HashForObject(Object* other) {
7260 return String::cast(other)->Hash();
7261 }
7262
7263 Object* AsObject() {
Leon Clarkef7060e22010-06-03 12:02:55 +01007264 // Attempt to flatten the string, so that symbols will most often
7265 // be flat strings.
7266 string_ = string_->TryFlattenGetString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007267 // Transform string to symbol if possible.
7268 Map* map = Heap::SymbolMapForString(string_);
7269 if (map != NULL) {
7270 string_->set_map(map);
7271 ASSERT(string_->IsSymbol());
7272 return string_;
7273 }
7274 // Otherwise allocate a new symbol.
7275 StringInputBuffer buffer(string_);
7276 return Heap::AllocateInternalSymbol(&buffer,
7277 string_->length(),
Steve Blockd0582a62009-12-15 09:54:21 +00007278 string_->hash_field());
Steve Blocka7e24c12009-10-30 11:49:00 +00007279 }
7280
7281 static uint32_t StringHash(Object* obj) {
7282 return String::cast(obj)->Hash();
7283 }
7284
7285 String* string_;
7286};
7287
7288
7289template<typename Shape, typename Key>
7290void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
7291 IteratePointers(v, 0, kElementsStartOffset);
7292}
7293
7294
7295template<typename Shape, typename Key>
7296void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
7297 IteratePointers(v,
7298 kElementsStartOffset,
7299 kHeaderSize + length() * kPointerSize);
7300}
7301
7302
7303template<typename Shape, typename Key>
Steve Block6ded16b2010-05-10 14:33:55 +01007304Object* HashTable<Shape, Key>::Allocate(int at_least_space_for,
7305 PretenureFlag pretenure) {
7306 const int kMinCapacity = 32;
7307 int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
7308 if (capacity < kMinCapacity) {
7309 capacity = kMinCapacity; // Guarantee min capacity.
Leon Clarkee46be812010-01-19 14:06:41 +00007310 } else if (capacity > HashTable::kMaxCapacity) {
7311 return Failure::OutOfMemoryException();
7312 }
7313
Steve Block6ded16b2010-05-10 14:33:55 +01007314 Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity), pretenure);
Steve Blocka7e24c12009-10-30 11:49:00 +00007315 if (!obj->IsFailure()) {
7316 HashTable::cast(obj)->SetNumberOfElements(0);
Leon Clarkee46be812010-01-19 14:06:41 +00007317 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007318 HashTable::cast(obj)->SetCapacity(capacity);
7319 }
7320 return obj;
7321}
7322
7323
Leon Clarkee46be812010-01-19 14:06:41 +00007324// Find entry for key otherwise return kNotFound.
Steve Blocka7e24c12009-10-30 11:49:00 +00007325template<typename Shape, typename Key>
7326int HashTable<Shape, Key>::FindEntry(Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007327 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007328 uint32_t entry = FirstProbe(Shape::Hash(key), capacity);
7329 uint32_t count = 1;
7330 // EnsureCapacity will guarantee the hash table is never full.
7331 while (true) {
7332 Object* element = KeyAt(entry);
7333 if (element->IsUndefined()) break; // Empty entry.
7334 if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
7335 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007336 }
7337 return kNotFound;
7338}
7339
7340
7341template<typename Shape, typename Key>
7342Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
7343 int capacity = Capacity();
7344 int nof = NumberOfElements() + n;
Leon Clarkee46be812010-01-19 14:06:41 +00007345 int nod = NumberOfDeletedElements();
7346 // Return if:
7347 // 50% is still free after adding n elements and
7348 // at most 50% of the free elements are deleted elements.
Steve Block6ded16b2010-05-10 14:33:55 +01007349 if (nod <= (capacity - nof) >> 1) {
7350 int needed_free = nof >> 1;
7351 if (nof + needed_free <= capacity) return this;
7352 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007353
Steve Block6ded16b2010-05-10 14:33:55 +01007354 const int kMinCapacityForPretenure = 256;
7355 bool pretenure =
7356 (capacity > kMinCapacityForPretenure) && !Heap::InNewSpace(this);
7357 Object* obj = Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
Steve Blocka7e24c12009-10-30 11:49:00 +00007358 if (obj->IsFailure()) return obj;
Leon Clarke4515c472010-02-03 11:58:03 +00007359
7360 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00007361 HashTable* table = HashTable::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00007362 WriteBarrierMode mode = table->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007363
7364 // Copy prefix to new array.
7365 for (int i = kPrefixStartIndex;
7366 i < kPrefixStartIndex + Shape::kPrefixSize;
7367 i++) {
7368 table->set(i, get(i), mode);
7369 }
7370 // Rehash the elements.
7371 for (int i = 0; i < capacity; i++) {
7372 uint32_t from_index = EntryToIndex(i);
7373 Object* k = get(from_index);
7374 if (IsKey(k)) {
7375 uint32_t hash = Shape::HashForObject(key, k);
7376 uint32_t insertion_index =
7377 EntryToIndex(table->FindInsertionEntry(hash));
7378 for (int j = 0; j < Shape::kEntrySize; j++) {
7379 table->set(insertion_index + j, get(from_index + j), mode);
7380 }
7381 }
7382 }
7383 table->SetNumberOfElements(NumberOfElements());
Leon Clarkee46be812010-01-19 14:06:41 +00007384 table->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007385 return table;
7386}
7387
7388
7389template<typename Shape, typename Key>
7390uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
7391 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007392 uint32_t entry = FirstProbe(hash, capacity);
7393 uint32_t count = 1;
7394 // EnsureCapacity will guarantee the hash table is never full.
7395 while (true) {
7396 Object* element = KeyAt(entry);
7397 if (element->IsUndefined() || element->IsNull()) break;
7398 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007399 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007400 return entry;
7401}
7402
7403// Force instantiation of template instances class.
7404// Please note this list is compiler dependent.
7405
7406template class HashTable<SymbolTableShape, HashTableKey*>;
7407
7408template class HashTable<CompilationCacheShape, HashTableKey*>;
7409
7410template class HashTable<MapCacheShape, HashTableKey*>;
7411
7412template class Dictionary<StringDictionaryShape, String*>;
7413
7414template class Dictionary<NumberDictionaryShape, uint32_t>;
7415
7416template Object* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
7417 int);
7418
7419template Object* Dictionary<StringDictionaryShape, String*>::Allocate(
7420 int);
7421
7422template Object* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
7423 uint32_t, Object*);
7424
7425template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
7426 Object*);
7427
7428template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
7429 Object*);
7430
7431template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
7432 FixedArray*, PropertyAttributes);
7433
7434template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
7435 int, JSObject::DeleteMode);
7436
7437template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
7438 int, JSObject::DeleteMode);
7439
7440template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
7441 FixedArray*);
7442
7443template int
7444Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
7445 PropertyAttributes);
7446
7447template Object* Dictionary<StringDictionaryShape, String*>::Add(
7448 String*, Object*, PropertyDetails);
7449
7450template Object*
7451Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
7452
7453template int
7454Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
7455 PropertyAttributes);
7456
7457template Object* Dictionary<NumberDictionaryShape, uint32_t>::Add(
7458 uint32_t, Object*, PropertyDetails);
7459
7460template Object* Dictionary<NumberDictionaryShape, uint32_t>::EnsureCapacity(
7461 int, uint32_t);
7462
7463template Object* Dictionary<StringDictionaryShape, String*>::EnsureCapacity(
7464 int, String*);
7465
7466template Object* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
7467 uint32_t, Object*, PropertyDetails, uint32_t);
7468
7469template Object* Dictionary<StringDictionaryShape, String*>::AddEntry(
7470 String*, Object*, PropertyDetails, uint32_t);
7471
7472template
7473int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
7474
7475template
7476int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
7477
Leon Clarkee46be812010-01-19 14:06:41 +00007478template
7479int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
7480
7481
Steve Blocka7e24c12009-10-30 11:49:00 +00007482// Collates undefined and unexisting elements below limit from position
7483// zero of the elements. The object stays in Dictionary mode.
7484Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
7485 ASSERT(HasDictionaryElements());
7486 // Must stay in dictionary mode, either because of requires_slow_elements,
7487 // or because we are not going to sort (and therefore compact) all of the
7488 // elements.
7489 NumberDictionary* dict = element_dictionary();
7490 HeapNumber* result_double = NULL;
7491 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7492 // Allocate space for result before we start mutating the object.
7493 Object* new_double = Heap::AllocateHeapNumber(0.0);
7494 if (new_double->IsFailure()) return new_double;
7495 result_double = HeapNumber::cast(new_double);
7496 }
7497
Steve Block6ded16b2010-05-10 14:33:55 +01007498 Object* obj = NumberDictionary::Allocate(dict->NumberOfElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00007499 if (obj->IsFailure()) return obj;
7500 NumberDictionary* new_dict = NumberDictionary::cast(obj);
7501
7502 AssertNoAllocation no_alloc;
7503
7504 uint32_t pos = 0;
7505 uint32_t undefs = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01007506 int capacity = dict->Capacity();
Steve Blocka7e24c12009-10-30 11:49:00 +00007507 for (int i = 0; i < capacity; i++) {
7508 Object* k = dict->KeyAt(i);
7509 if (dict->IsKey(k)) {
7510 ASSERT(k->IsNumber());
7511 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
7512 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
7513 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
7514 Object* value = dict->ValueAt(i);
7515 PropertyDetails details = dict->DetailsAt(i);
7516 if (details.type() == CALLBACKS) {
7517 // Bail out and do the sorting of undefineds and array holes in JS.
7518 return Smi::FromInt(-1);
7519 }
7520 uint32_t key = NumberToUint32(k);
7521 if (key < limit) {
7522 if (value->IsUndefined()) {
7523 undefs++;
7524 } else {
7525 new_dict->AddNumberEntry(pos, value, details);
7526 pos++;
7527 }
7528 } else {
7529 new_dict->AddNumberEntry(key, value, details);
7530 }
7531 }
7532 }
7533
7534 uint32_t result = pos;
7535 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
7536 while (undefs > 0) {
7537 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details);
7538 pos++;
7539 undefs--;
7540 }
7541
7542 set_elements(new_dict);
7543
7544 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7545 return Smi::FromInt(static_cast<int>(result));
7546 }
7547
7548 ASSERT_NE(NULL, result_double);
7549 result_double->set_value(static_cast<double>(result));
7550 return result_double;
7551}
7552
7553
7554// Collects all defined (non-hole) and non-undefined (array) elements at
7555// the start of the elements array.
7556// If the object is in dictionary mode, it is converted to fast elements
7557// mode.
7558Object* JSObject::PrepareElementsForSort(uint32_t limit) {
Steve Block3ce2e202009-11-05 08:53:23 +00007559 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00007560
7561 if (HasDictionaryElements()) {
7562 // Convert to fast elements containing only the existing properties.
7563 // Ordering is irrelevant, since we are going to sort anyway.
7564 NumberDictionary* dict = element_dictionary();
7565 if (IsJSArray() || dict->requires_slow_elements() ||
7566 dict->max_number_key() >= limit) {
7567 return PrepareSlowElementsForSort(limit);
7568 }
7569 // Convert to fast elements.
7570
Steve Block8defd9f2010-07-08 12:39:36 +01007571 Object* obj = map()->GetFastElementsMap();
7572 if (obj->IsFailure()) return obj;
7573 Map* new_map = Map::cast(obj);
7574
Steve Blocka7e24c12009-10-30 11:49:00 +00007575 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
7576 Object* new_array =
7577 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
Steve Block8defd9f2010-07-08 12:39:36 +01007578 if (new_array->IsFailure()) return new_array;
Steve Blocka7e24c12009-10-30 11:49:00 +00007579 FixedArray* fast_elements = FixedArray::cast(new_array);
7580 dict->CopyValuesTo(fast_elements);
Steve Block8defd9f2010-07-08 12:39:36 +01007581
7582 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00007583 set_elements(fast_elements);
7584 }
7585 ASSERT(HasFastElements());
7586
7587 // Collect holes at the end, undefined before that and the rest at the
7588 // start, and return the number of non-hole, non-undefined values.
7589
7590 FixedArray* elements = FixedArray::cast(this->elements());
7591 uint32_t elements_length = static_cast<uint32_t>(elements->length());
7592 if (limit > elements_length) {
7593 limit = elements_length ;
7594 }
7595 if (limit == 0) {
7596 return Smi::FromInt(0);
7597 }
7598
7599 HeapNumber* result_double = NULL;
7600 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7601 // Pessimistically allocate space for return value before
7602 // we start mutating the array.
7603 Object* new_double = Heap::AllocateHeapNumber(0.0);
7604 if (new_double->IsFailure()) return new_double;
7605 result_double = HeapNumber::cast(new_double);
7606 }
7607
7608 AssertNoAllocation no_alloc;
7609
7610 // Split elements into defined, undefined and the_hole, in that order.
7611 // Only count locations for undefined and the hole, and fill them afterwards.
Leon Clarke4515c472010-02-03 11:58:03 +00007612 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007613 unsigned int undefs = limit;
7614 unsigned int holes = limit;
7615 // Assume most arrays contain no holes and undefined values, so minimize the
7616 // number of stores of non-undefined, non-the-hole values.
7617 for (unsigned int i = 0; i < undefs; i++) {
7618 Object* current = elements->get(i);
7619 if (current->IsTheHole()) {
7620 holes--;
7621 undefs--;
7622 } else if (current->IsUndefined()) {
7623 undefs--;
7624 } else {
7625 continue;
7626 }
7627 // Position i needs to be filled.
7628 while (undefs > i) {
7629 current = elements->get(undefs);
7630 if (current->IsTheHole()) {
7631 holes--;
7632 undefs--;
7633 } else if (current->IsUndefined()) {
7634 undefs--;
7635 } else {
7636 elements->set(i, current, write_barrier);
7637 break;
7638 }
7639 }
7640 }
7641 uint32_t result = undefs;
7642 while (undefs < holes) {
7643 elements->set_undefined(undefs);
7644 undefs++;
7645 }
7646 while (holes < limit) {
7647 elements->set_the_hole(holes);
7648 holes++;
7649 }
7650
7651 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7652 return Smi::FromInt(static_cast<int>(result));
7653 }
7654 ASSERT_NE(NULL, result_double);
7655 result_double->set_value(static_cast<double>(result));
7656 return result_double;
7657}
7658
7659
7660Object* PixelArray::SetValue(uint32_t index, Object* value) {
7661 uint8_t clamped_value = 0;
7662 if (index < static_cast<uint32_t>(length())) {
7663 if (value->IsSmi()) {
7664 int int_value = Smi::cast(value)->value();
7665 if (int_value < 0) {
7666 clamped_value = 0;
7667 } else if (int_value > 255) {
7668 clamped_value = 255;
7669 } else {
7670 clamped_value = static_cast<uint8_t>(int_value);
7671 }
7672 } else if (value->IsHeapNumber()) {
7673 double double_value = HeapNumber::cast(value)->value();
7674 if (!(double_value > 0)) {
7675 // NaN and less than zero clamp to zero.
7676 clamped_value = 0;
7677 } else if (double_value > 255) {
7678 // Greater than 255 clamp to 255.
7679 clamped_value = 255;
7680 } else {
7681 // Other doubles are rounded to the nearest integer.
7682 clamped_value = static_cast<uint8_t>(double_value + 0.5);
7683 }
7684 } else {
7685 // Clamp undefined to zero (default). All other types have been
7686 // converted to a number type further up in the call chain.
7687 ASSERT(value->IsUndefined());
7688 }
7689 set(index, clamped_value);
7690 }
7691 return Smi::FromInt(clamped_value);
7692}
7693
7694
Steve Block3ce2e202009-11-05 08:53:23 +00007695template<typename ExternalArrayClass, typename ValueType>
7696static Object* ExternalArrayIntSetter(ExternalArrayClass* receiver,
7697 uint32_t index,
7698 Object* value) {
7699 ValueType cast_value = 0;
7700 if (index < static_cast<uint32_t>(receiver->length())) {
7701 if (value->IsSmi()) {
7702 int int_value = Smi::cast(value)->value();
7703 cast_value = static_cast<ValueType>(int_value);
7704 } else if (value->IsHeapNumber()) {
7705 double double_value = HeapNumber::cast(value)->value();
7706 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
7707 } else {
7708 // Clamp undefined to zero (default). All other types have been
7709 // converted to a number type further up in the call chain.
7710 ASSERT(value->IsUndefined());
7711 }
7712 receiver->set(index, cast_value);
7713 }
7714 return Heap::NumberFromInt32(cast_value);
7715}
7716
7717
7718Object* ExternalByteArray::SetValue(uint32_t index, Object* value) {
7719 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
7720 (this, index, value);
7721}
7722
7723
7724Object* ExternalUnsignedByteArray::SetValue(uint32_t index, Object* value) {
7725 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
7726 (this, index, value);
7727}
7728
7729
7730Object* ExternalShortArray::SetValue(uint32_t index, Object* value) {
7731 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
7732 (this, index, value);
7733}
7734
7735
7736Object* ExternalUnsignedShortArray::SetValue(uint32_t index, Object* value) {
7737 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
7738 (this, index, value);
7739}
7740
7741
7742Object* ExternalIntArray::SetValue(uint32_t index, Object* value) {
7743 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
7744 (this, index, value);
7745}
7746
7747
7748Object* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
7749 uint32_t cast_value = 0;
7750 if (index < static_cast<uint32_t>(length())) {
7751 if (value->IsSmi()) {
7752 int int_value = Smi::cast(value)->value();
7753 cast_value = static_cast<uint32_t>(int_value);
7754 } else if (value->IsHeapNumber()) {
7755 double double_value = HeapNumber::cast(value)->value();
7756 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
7757 } else {
7758 // Clamp undefined to zero (default). All other types have been
7759 // converted to a number type further up in the call chain.
7760 ASSERT(value->IsUndefined());
7761 }
7762 set(index, cast_value);
7763 }
7764 return Heap::NumberFromUint32(cast_value);
7765}
7766
7767
7768Object* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
7769 float cast_value = 0;
7770 if (index < static_cast<uint32_t>(length())) {
7771 if (value->IsSmi()) {
7772 int int_value = Smi::cast(value)->value();
7773 cast_value = static_cast<float>(int_value);
7774 } else if (value->IsHeapNumber()) {
7775 double double_value = HeapNumber::cast(value)->value();
7776 cast_value = static_cast<float>(double_value);
7777 } else {
7778 // Clamp undefined to zero (default). All other types have been
7779 // converted to a number type further up in the call chain.
7780 ASSERT(value->IsUndefined());
7781 }
7782 set(index, cast_value);
7783 }
7784 return Heap::AllocateHeapNumber(cast_value);
7785}
7786
7787
Steve Blocka7e24c12009-10-30 11:49:00 +00007788Object* GlobalObject::GetPropertyCell(LookupResult* result) {
7789 ASSERT(!HasFastProperties());
7790 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
7791 ASSERT(value->IsJSGlobalPropertyCell());
7792 return value;
7793}
7794
7795
7796Object* GlobalObject::EnsurePropertyCell(String* name) {
7797 ASSERT(!HasFastProperties());
7798 int entry = property_dictionary()->FindEntry(name);
7799 if (entry == StringDictionary::kNotFound) {
7800 Object* cell = Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
7801 if (cell->IsFailure()) return cell;
7802 PropertyDetails details(NONE, NORMAL);
7803 details = details.AsDeleted();
7804 Object* dictionary = property_dictionary()->Add(name, cell, details);
7805 if (dictionary->IsFailure()) return dictionary;
7806 set_properties(StringDictionary::cast(dictionary));
7807 return cell;
7808 } else {
7809 Object* value = property_dictionary()->ValueAt(entry);
7810 ASSERT(value->IsJSGlobalPropertyCell());
7811 return value;
7812 }
7813}
7814
7815
7816Object* SymbolTable::LookupString(String* string, Object** s) {
7817 SymbolKey key(string);
7818 return LookupKey(&key, s);
7819}
7820
7821
Steve Blockd0582a62009-12-15 09:54:21 +00007822// This class is used for looking up two character strings in the symbol table.
7823// If we don't have a hit we don't want to waste much time so we unroll the
7824// string hash calculation loop here for speed. Doesn't work if the two
7825// characters form a decimal integer, since such strings have a different hash
7826// algorithm.
7827class TwoCharHashTableKey : public HashTableKey {
7828 public:
7829 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
7830 : c1_(c1), c2_(c2) {
7831 // Char 1.
7832 uint32_t hash = c1 + (c1 << 10);
7833 hash ^= hash >> 6;
7834 // Char 2.
7835 hash += c2;
7836 hash += hash << 10;
7837 hash ^= hash >> 6;
7838 // GetHash.
7839 hash += hash << 3;
7840 hash ^= hash >> 11;
7841 hash += hash << 15;
7842 if (hash == 0) hash = 27;
7843#ifdef DEBUG
7844 StringHasher hasher(2);
7845 hasher.AddCharacter(c1);
7846 hasher.AddCharacter(c2);
7847 // If this assert fails then we failed to reproduce the two-character
7848 // version of the string hashing algorithm above. One reason could be
7849 // that we were passed two digits as characters, since the hash
7850 // algorithm is different in that case.
7851 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
7852#endif
7853 hash_ = hash;
7854 }
7855
7856 bool IsMatch(Object* o) {
7857 if (!o->IsString()) return false;
7858 String* other = String::cast(o);
7859 if (other->length() != 2) return false;
7860 if (other->Get(0) != c1_) return false;
7861 return other->Get(1) == c2_;
7862 }
7863
7864 uint32_t Hash() { return hash_; }
7865 uint32_t HashForObject(Object* key) {
7866 if (!key->IsString()) return 0;
7867 return String::cast(key)->Hash();
7868 }
7869
7870 Object* AsObject() {
7871 // The TwoCharHashTableKey is only used for looking in the symbol
7872 // table, not for adding to it.
7873 UNREACHABLE();
7874 return NULL;
7875 }
7876 private:
7877 uint32_t c1_;
7878 uint32_t c2_;
7879 uint32_t hash_;
7880};
7881
7882
Steve Blocka7e24c12009-10-30 11:49:00 +00007883bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
7884 SymbolKey key(string);
7885 int entry = FindEntry(&key);
7886 if (entry == kNotFound) {
7887 return false;
7888 } else {
7889 String* result = String::cast(KeyAt(entry));
7890 ASSERT(StringShape(result).IsSymbol());
7891 *symbol = result;
7892 return true;
7893 }
7894}
7895
7896
Steve Blockd0582a62009-12-15 09:54:21 +00007897bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
7898 uint32_t c2,
7899 String** symbol) {
7900 TwoCharHashTableKey key(c1, c2);
7901 int entry = FindEntry(&key);
7902 if (entry == kNotFound) {
7903 return false;
7904 } else {
7905 String* result = String::cast(KeyAt(entry));
7906 ASSERT(StringShape(result).IsSymbol());
7907 *symbol = result;
7908 return true;
7909 }
7910}
7911
7912
Steve Blocka7e24c12009-10-30 11:49:00 +00007913Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
7914 Utf8SymbolKey key(str);
7915 return LookupKey(&key, s);
7916}
7917
7918
7919Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
7920 int entry = FindEntry(key);
7921
7922 // Symbol already in table.
7923 if (entry != kNotFound) {
7924 *s = KeyAt(entry);
7925 return this;
7926 }
7927
7928 // Adding new symbol. Grow table if needed.
7929 Object* obj = EnsureCapacity(1, key);
7930 if (obj->IsFailure()) return obj;
7931
7932 // Create symbol object.
7933 Object* symbol = key->AsObject();
7934 if (symbol->IsFailure()) return symbol;
7935
7936 // If the symbol table grew as part of EnsureCapacity, obj is not
7937 // the current symbol table and therefore we cannot use
7938 // SymbolTable::cast here.
7939 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
7940
7941 // Add the new symbol and return it along with the symbol table.
7942 entry = table->FindInsertionEntry(key->Hash());
7943 table->set(EntryToIndex(entry), symbol);
7944 table->ElementAdded();
7945 *s = symbol;
7946 return table;
7947}
7948
7949
7950Object* CompilationCacheTable::Lookup(String* src) {
7951 StringKey key(src);
7952 int entry = FindEntry(&key);
7953 if (entry == kNotFound) return Heap::undefined_value();
7954 return get(EntryToIndex(entry) + 1);
7955}
7956
7957
7958Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
7959 StringSharedKey key(src, context->closure()->shared());
7960 int entry = FindEntry(&key);
7961 if (entry == kNotFound) return Heap::undefined_value();
7962 return get(EntryToIndex(entry) + 1);
7963}
7964
7965
7966Object* CompilationCacheTable::LookupRegExp(String* src,
7967 JSRegExp::Flags flags) {
7968 RegExpKey key(src, flags);
7969 int entry = FindEntry(&key);
7970 if (entry == kNotFound) return Heap::undefined_value();
7971 return get(EntryToIndex(entry) + 1);
7972}
7973
7974
7975Object* CompilationCacheTable::Put(String* src, Object* value) {
7976 StringKey key(src);
7977 Object* obj = EnsureCapacity(1, &key);
7978 if (obj->IsFailure()) return obj;
7979
7980 CompilationCacheTable* cache =
7981 reinterpret_cast<CompilationCacheTable*>(obj);
7982 int entry = cache->FindInsertionEntry(key.Hash());
7983 cache->set(EntryToIndex(entry), src);
7984 cache->set(EntryToIndex(entry) + 1, value);
7985 cache->ElementAdded();
7986 return cache;
7987}
7988
7989
7990Object* CompilationCacheTable::PutEval(String* src,
7991 Context* context,
7992 Object* value) {
7993 StringSharedKey key(src, context->closure()->shared());
7994 Object* obj = EnsureCapacity(1, &key);
7995 if (obj->IsFailure()) return obj;
7996
7997 CompilationCacheTable* cache =
7998 reinterpret_cast<CompilationCacheTable*>(obj);
7999 int entry = cache->FindInsertionEntry(key.Hash());
8000
8001 Object* k = key.AsObject();
8002 if (k->IsFailure()) return k;
8003
8004 cache->set(EntryToIndex(entry), k);
8005 cache->set(EntryToIndex(entry) + 1, value);
8006 cache->ElementAdded();
8007 return cache;
8008}
8009
8010
8011Object* CompilationCacheTable::PutRegExp(String* src,
8012 JSRegExp::Flags flags,
8013 FixedArray* value) {
8014 RegExpKey key(src, flags);
8015 Object* obj = EnsureCapacity(1, &key);
8016 if (obj->IsFailure()) return obj;
8017
8018 CompilationCacheTable* cache =
8019 reinterpret_cast<CompilationCacheTable*>(obj);
8020 int entry = cache->FindInsertionEntry(key.Hash());
Steve Block3ce2e202009-11-05 08:53:23 +00008021 // We store the value in the key slot, and compare the search key
8022 // to the stored value with a custon IsMatch function during lookups.
Steve Blocka7e24c12009-10-30 11:49:00 +00008023 cache->set(EntryToIndex(entry), value);
8024 cache->set(EntryToIndex(entry) + 1, value);
8025 cache->ElementAdded();
8026 return cache;
8027}
8028
8029
8030// SymbolsKey used for HashTable where key is array of symbols.
8031class SymbolsKey : public HashTableKey {
8032 public:
8033 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
8034
8035 bool IsMatch(Object* symbols) {
8036 FixedArray* o = FixedArray::cast(symbols);
8037 int len = symbols_->length();
8038 if (o->length() != len) return false;
8039 for (int i = 0; i < len; i++) {
8040 if (o->get(i) != symbols_->get(i)) return false;
8041 }
8042 return true;
8043 }
8044
8045 uint32_t Hash() { return HashForObject(symbols_); }
8046
8047 uint32_t HashForObject(Object* obj) {
8048 FixedArray* symbols = FixedArray::cast(obj);
8049 int len = symbols->length();
8050 uint32_t hash = 0;
8051 for (int i = 0; i < len; i++) {
8052 hash ^= String::cast(symbols->get(i))->Hash();
8053 }
8054 return hash;
8055 }
8056
8057 Object* AsObject() { return symbols_; }
8058
8059 private:
8060 FixedArray* symbols_;
8061};
8062
8063
8064Object* MapCache::Lookup(FixedArray* array) {
8065 SymbolsKey key(array);
8066 int entry = FindEntry(&key);
8067 if (entry == kNotFound) return Heap::undefined_value();
8068 return get(EntryToIndex(entry) + 1);
8069}
8070
8071
8072Object* MapCache::Put(FixedArray* array, Map* value) {
8073 SymbolsKey key(array);
8074 Object* obj = EnsureCapacity(1, &key);
8075 if (obj->IsFailure()) return obj;
8076
8077 MapCache* cache = reinterpret_cast<MapCache*>(obj);
8078 int entry = cache->FindInsertionEntry(key.Hash());
8079 cache->set(EntryToIndex(entry), array);
8080 cache->set(EntryToIndex(entry) + 1, value);
8081 cache->ElementAdded();
8082 return cache;
8083}
8084
8085
8086template<typename Shape, typename Key>
8087Object* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
8088 Object* obj = HashTable<Shape, Key>::Allocate(at_least_space_for);
8089 // Initialize the next enumeration index.
8090 if (!obj->IsFailure()) {
8091 Dictionary<Shape, Key>::cast(obj)->
8092 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
8093 }
8094 return obj;
8095}
8096
8097
8098template<typename Shape, typename Key>
8099Object* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
8100 int length = HashTable<Shape, Key>::NumberOfElements();
8101
8102 // Allocate and initialize iteration order array.
8103 Object* obj = Heap::AllocateFixedArray(length);
8104 if (obj->IsFailure()) return obj;
8105 FixedArray* iteration_order = FixedArray::cast(obj);
8106 for (int i = 0; i < length; i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00008107 iteration_order->set(i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00008108 }
8109
8110 // Allocate array with enumeration order.
8111 obj = Heap::AllocateFixedArray(length);
8112 if (obj->IsFailure()) return obj;
8113 FixedArray* enumeration_order = FixedArray::cast(obj);
8114
8115 // Fill the enumeration order array with property details.
8116 int capacity = HashTable<Shape, Key>::Capacity();
8117 int pos = 0;
8118 for (int i = 0; i < capacity; i++) {
8119 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
Leon Clarke4515c472010-02-03 11:58:03 +00008120 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008121 }
8122 }
8123
8124 // Sort the arrays wrt. enumeration order.
8125 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
8126
8127 // Overwrite the enumeration_order with the enumeration indices.
8128 for (int i = 0; i < length; i++) {
8129 int index = Smi::cast(iteration_order->get(i))->value();
8130 int enum_index = PropertyDetails::kInitialIndex + i;
Leon Clarke4515c472010-02-03 11:58:03 +00008131 enumeration_order->set(index, Smi::FromInt(enum_index));
Steve Blocka7e24c12009-10-30 11:49:00 +00008132 }
8133
8134 // Update the dictionary with new indices.
8135 capacity = HashTable<Shape, Key>::Capacity();
8136 pos = 0;
8137 for (int i = 0; i < capacity; i++) {
8138 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
8139 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
8140 PropertyDetails details = DetailsAt(i);
8141 PropertyDetails new_details =
8142 PropertyDetails(details.attributes(), details.type(), enum_index);
8143 DetailsAtPut(i, new_details);
8144 }
8145 }
8146
8147 // Set the next enumeration index.
8148 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
8149 return this;
8150}
8151
8152template<typename Shape, typename Key>
8153Object* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
8154 // Check whether there are enough enumeration indices to add n elements.
8155 if (Shape::kIsEnumerable &&
8156 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
8157 // If not, we generate new indices for the properties.
8158 Object* result = GenerateNewEnumerationIndices();
8159 if (result->IsFailure()) return result;
8160 }
8161 return HashTable<Shape, Key>::EnsureCapacity(n, key);
8162}
8163
8164
8165void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
8166 // Do nothing if the interval [from, to) is empty.
8167 if (from >= to) return;
8168
8169 int removed_entries = 0;
8170 Object* sentinel = Heap::null_value();
8171 int capacity = Capacity();
8172 for (int i = 0; i < capacity; i++) {
8173 Object* key = KeyAt(i);
8174 if (key->IsNumber()) {
8175 uint32_t number = static_cast<uint32_t>(key->Number());
8176 if (from <= number && number < to) {
8177 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
8178 removed_entries++;
8179 }
8180 }
8181 }
8182
8183 // Update the number of elements.
Leon Clarkee46be812010-01-19 14:06:41 +00008184 ElementsRemoved(removed_entries);
Steve Blocka7e24c12009-10-30 11:49:00 +00008185}
8186
8187
8188template<typename Shape, typename Key>
8189Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
8190 JSObject::DeleteMode mode) {
8191 PropertyDetails details = DetailsAt(entry);
8192 // Ignore attributes if forcing a deletion.
8193 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
8194 return Heap::false_value();
8195 }
8196 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
8197 HashTable<Shape, Key>::ElementRemoved();
8198 return Heap::true_value();
8199}
8200
8201
8202template<typename Shape, typename Key>
8203Object* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008204 int entry = this->FindEntry(key);
Steve Blocka7e24c12009-10-30 11:49:00 +00008205
8206 // If the entry is present set the value;
8207 if (entry != Dictionary<Shape, Key>::kNotFound) {
8208 ValueAtPut(entry, value);
8209 return this;
8210 }
8211
8212 // Check whether the dictionary should be extended.
8213 Object* obj = EnsureCapacity(1, key);
8214 if (obj->IsFailure()) return obj;
8215
8216 Object* k = Shape::AsObject(key);
8217 if (k->IsFailure()) return k;
8218 PropertyDetails details = PropertyDetails(NONE, NORMAL);
8219 return Dictionary<Shape, Key>::cast(obj)->
8220 AddEntry(key, value, details, Shape::Hash(key));
8221}
8222
8223
8224template<typename Shape, typename Key>
8225Object* Dictionary<Shape, Key>::Add(Key key,
8226 Object* value,
8227 PropertyDetails details) {
8228 // Valdate key is absent.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008229 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
Steve Blocka7e24c12009-10-30 11:49:00 +00008230 // Check whether the dictionary should be extended.
8231 Object* obj = EnsureCapacity(1, key);
8232 if (obj->IsFailure()) return obj;
8233 return Dictionary<Shape, Key>::cast(obj)->
8234 AddEntry(key, value, details, Shape::Hash(key));
8235}
8236
8237
8238// Add a key, value pair to the dictionary.
8239template<typename Shape, typename Key>
8240Object* Dictionary<Shape, Key>::AddEntry(Key key,
8241 Object* value,
8242 PropertyDetails details,
8243 uint32_t hash) {
8244 // Compute the key object.
8245 Object* k = Shape::AsObject(key);
8246 if (k->IsFailure()) return k;
8247
8248 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
8249 // Insert element at empty or deleted entry
8250 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
8251 // Assign an enumeration index to the property and update
8252 // SetNextEnumerationIndex.
8253 int index = NextEnumerationIndex();
8254 details = PropertyDetails(details.attributes(), details.type(), index);
8255 SetNextEnumerationIndex(index + 1);
8256 }
8257 SetEntry(entry, k, value, details);
8258 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
8259 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
8260 HashTable<Shape, Key>::ElementAdded();
8261 return this;
8262}
8263
8264
8265void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
8266 // If the dictionary requires slow elements an element has already
8267 // been added at a high index.
8268 if (requires_slow_elements()) return;
8269 // Check if this index is high enough that we should require slow
8270 // elements.
8271 if (key > kRequiresSlowElementsLimit) {
8272 set_requires_slow_elements();
8273 return;
8274 }
8275 // Update max key value.
8276 Object* max_index_object = get(kMaxNumberKeyIndex);
8277 if (!max_index_object->IsSmi() || max_number_key() < key) {
8278 FixedArray::set(kMaxNumberKeyIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00008279 Smi::FromInt(key << kRequiresSlowElementsTagSize));
Steve Blocka7e24c12009-10-30 11:49:00 +00008280 }
8281}
8282
8283
8284Object* NumberDictionary::AddNumberEntry(uint32_t key,
8285 Object* value,
8286 PropertyDetails details) {
8287 UpdateMaxNumberKey(key);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008288 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
Steve Blocka7e24c12009-10-30 11:49:00 +00008289 return Add(key, value, details);
8290}
8291
8292
8293Object* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
8294 UpdateMaxNumberKey(key);
8295 return AtPut(key, value);
8296}
8297
8298
8299Object* NumberDictionary::Set(uint32_t key,
8300 Object* value,
8301 PropertyDetails details) {
8302 int entry = FindEntry(key);
8303 if (entry == kNotFound) return AddNumberEntry(key, value, details);
8304 // Preserve enumeration index.
8305 details = PropertyDetails(details.attributes(),
8306 details.type(),
8307 DetailsAt(entry).index());
8308 SetEntry(entry, NumberDictionaryShape::AsObject(key), value, details);
8309 return this;
8310}
8311
8312
8313
8314template<typename Shape, typename Key>
8315int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
8316 PropertyAttributes filter) {
8317 int capacity = HashTable<Shape, Key>::Capacity();
8318 int result = 0;
8319 for (int i = 0; i < capacity; i++) {
8320 Object* k = HashTable<Shape, Key>::KeyAt(i);
8321 if (HashTable<Shape, Key>::IsKey(k)) {
8322 PropertyDetails details = DetailsAt(i);
8323 if (details.IsDeleted()) continue;
8324 PropertyAttributes attr = details.attributes();
8325 if ((attr & filter) == 0) result++;
8326 }
8327 }
8328 return result;
8329}
8330
8331
8332template<typename Shape, typename Key>
8333int Dictionary<Shape, Key>::NumberOfEnumElements() {
8334 return NumberOfElementsFilterAttributes(
8335 static_cast<PropertyAttributes>(DONT_ENUM));
8336}
8337
8338
8339template<typename Shape, typename Key>
8340void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
8341 PropertyAttributes filter) {
8342 ASSERT(storage->length() >= NumberOfEnumElements());
8343 int capacity = HashTable<Shape, Key>::Capacity();
8344 int index = 0;
8345 for (int i = 0; i < capacity; i++) {
8346 Object* k = HashTable<Shape, Key>::KeyAt(i);
8347 if (HashTable<Shape, Key>::IsKey(k)) {
8348 PropertyDetails details = DetailsAt(i);
8349 if (details.IsDeleted()) continue;
8350 PropertyAttributes attr = details.attributes();
8351 if ((attr & filter) == 0) storage->set(index++, k);
8352 }
8353 }
8354 storage->SortPairs(storage, index);
8355 ASSERT(storage->length() >= index);
8356}
8357
8358
8359void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
8360 FixedArray* sort_array) {
8361 ASSERT(storage->length() >= NumberOfEnumElements());
8362 int capacity = Capacity();
8363 int index = 0;
8364 for (int i = 0; i < capacity; i++) {
8365 Object* k = KeyAt(i);
8366 if (IsKey(k)) {
8367 PropertyDetails details = DetailsAt(i);
8368 if (details.IsDeleted() || details.IsDontEnum()) continue;
8369 storage->set(index, k);
Leon Clarke4515c472010-02-03 11:58:03 +00008370 sort_array->set(index, Smi::FromInt(details.index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008371 index++;
8372 }
8373 }
8374 storage->SortPairs(sort_array, sort_array->length());
8375 ASSERT(storage->length() >= index);
8376}
8377
8378
8379template<typename Shape, typename Key>
8380void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
8381 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
8382 static_cast<PropertyAttributes>(NONE)));
8383 int capacity = HashTable<Shape, Key>::Capacity();
8384 int index = 0;
8385 for (int i = 0; i < capacity; i++) {
8386 Object* k = HashTable<Shape, Key>::KeyAt(i);
8387 if (HashTable<Shape, Key>::IsKey(k)) {
8388 PropertyDetails details = DetailsAt(i);
8389 if (details.IsDeleted()) continue;
8390 storage->set(index++, k);
8391 }
8392 }
8393 ASSERT(storage->length() >= index);
8394}
8395
8396
8397// Backwards lookup (slow).
8398template<typename Shape, typename Key>
8399Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
8400 int capacity = HashTable<Shape, Key>::Capacity();
8401 for (int i = 0; i < capacity; i++) {
8402 Object* k = HashTable<Shape, Key>::KeyAt(i);
8403 if (Dictionary<Shape, Key>::IsKey(k)) {
8404 Object* e = ValueAt(i);
8405 if (e->IsJSGlobalPropertyCell()) {
8406 e = JSGlobalPropertyCell::cast(e)->value();
8407 }
8408 if (e == value) return k;
8409 }
8410 }
8411 return Heap::undefined_value();
8412}
8413
8414
8415Object* StringDictionary::TransformPropertiesToFastFor(
8416 JSObject* obj, int unused_property_fields) {
8417 // Make sure we preserve dictionary representation if there are too many
8418 // descriptors.
8419 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
8420
8421 // Figure out if it is necessary to generate new enumeration indices.
8422 int max_enumeration_index =
8423 NextEnumerationIndex() +
8424 (DescriptorArray::kMaxNumberOfDescriptors -
8425 NumberOfElements());
8426 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
8427 Object* result = GenerateNewEnumerationIndices();
8428 if (result->IsFailure()) return result;
8429 }
8430
8431 int instance_descriptor_length = 0;
8432 int number_of_fields = 0;
8433
8434 // Compute the length of the instance descriptor.
8435 int capacity = Capacity();
8436 for (int i = 0; i < capacity; i++) {
8437 Object* k = KeyAt(i);
8438 if (IsKey(k)) {
8439 Object* value = ValueAt(i);
8440 PropertyType type = DetailsAt(i).type();
8441 ASSERT(type != FIELD);
8442 instance_descriptor_length++;
Leon Clarkee46be812010-01-19 14:06:41 +00008443 if (type == NORMAL &&
8444 (!value->IsJSFunction() || Heap::InNewSpace(value))) {
8445 number_of_fields += 1;
8446 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008447 }
8448 }
8449
8450 // Allocate the instance descriptor.
8451 Object* descriptors_unchecked =
8452 DescriptorArray::Allocate(instance_descriptor_length);
8453 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
8454 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
8455
8456 int inobject_props = obj->map()->inobject_properties();
8457 int number_of_allocated_fields =
8458 number_of_fields + unused_property_fields - inobject_props;
8459
8460 // Allocate the fixed array for the fields.
8461 Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields);
8462 if (fields->IsFailure()) return fields;
8463
8464 // Fill in the instance descriptor and the fields.
8465 int next_descriptor = 0;
8466 int current_offset = 0;
8467 for (int i = 0; i < capacity; i++) {
8468 Object* k = KeyAt(i);
8469 if (IsKey(k)) {
8470 Object* value = ValueAt(i);
8471 // Ensure the key is a symbol before writing into the instance descriptor.
8472 Object* key = Heap::LookupSymbol(String::cast(k));
8473 if (key->IsFailure()) return key;
8474 PropertyDetails details = DetailsAt(i);
8475 PropertyType type = details.type();
8476
Leon Clarkee46be812010-01-19 14:06:41 +00008477 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008478 ConstantFunctionDescriptor d(String::cast(key),
8479 JSFunction::cast(value),
8480 details.attributes(),
8481 details.index());
8482 descriptors->Set(next_descriptor++, &d);
8483 } else if (type == NORMAL) {
8484 if (current_offset < inobject_props) {
8485 obj->InObjectPropertyAtPut(current_offset,
8486 value,
8487 UPDATE_WRITE_BARRIER);
8488 } else {
8489 int offset = current_offset - inobject_props;
8490 FixedArray::cast(fields)->set(offset, value);
8491 }
8492 FieldDescriptor d(String::cast(key),
8493 current_offset++,
8494 details.attributes(),
8495 details.index());
8496 descriptors->Set(next_descriptor++, &d);
8497 } else if (type == CALLBACKS) {
8498 CallbacksDescriptor d(String::cast(key),
8499 value,
8500 details.attributes(),
8501 details.index());
8502 descriptors->Set(next_descriptor++, &d);
8503 } else {
8504 UNREACHABLE();
8505 }
8506 }
8507 }
8508 ASSERT(current_offset == number_of_fields);
8509
8510 descriptors->Sort();
8511 // Allocate new map.
8512 Object* new_map = obj->map()->CopyDropDescriptors();
8513 if (new_map->IsFailure()) return new_map;
8514
8515 // Transform the object.
8516 obj->set_map(Map::cast(new_map));
8517 obj->map()->set_instance_descriptors(descriptors);
8518 obj->map()->set_unused_property_fields(unused_property_fields);
8519
8520 obj->set_properties(FixedArray::cast(fields));
8521 ASSERT(obj->IsJSObject());
8522
8523 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
8524 // Check that it really works.
8525 ASSERT(obj->HasFastProperties());
8526
8527 return obj;
8528}
8529
8530
8531#ifdef ENABLE_DEBUGGER_SUPPORT
8532// Check if there is a break point at this code position.
8533bool DebugInfo::HasBreakPoint(int code_position) {
8534 // Get the break point info object for this code position.
8535 Object* break_point_info = GetBreakPointInfo(code_position);
8536
8537 // If there is no break point info object or no break points in the break
8538 // point info object there is no break point at this code position.
8539 if (break_point_info->IsUndefined()) return false;
8540 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
8541}
8542
8543
8544// Get the break point info object for this code position.
8545Object* DebugInfo::GetBreakPointInfo(int code_position) {
8546 // Find the index of the break point info object for this code position.
8547 int index = GetBreakPointInfoIndex(code_position);
8548
8549 // Return the break point info object if any.
8550 if (index == kNoBreakPointInfo) return Heap::undefined_value();
8551 return BreakPointInfo::cast(break_points()->get(index));
8552}
8553
8554
8555// Clear a break point at the specified code position.
8556void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
8557 int code_position,
8558 Handle<Object> break_point_object) {
8559 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
8560 if (break_point_info->IsUndefined()) return;
8561 BreakPointInfo::ClearBreakPoint(
8562 Handle<BreakPointInfo>::cast(break_point_info),
8563 break_point_object);
8564}
8565
8566
8567void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
8568 int code_position,
8569 int source_position,
8570 int statement_position,
8571 Handle<Object> break_point_object) {
8572 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
8573 if (!break_point_info->IsUndefined()) {
8574 BreakPointInfo::SetBreakPoint(
8575 Handle<BreakPointInfo>::cast(break_point_info),
8576 break_point_object);
8577 return;
8578 }
8579
8580 // Adding a new break point for a code position which did not have any
8581 // break points before. Try to find a free slot.
8582 int index = kNoBreakPointInfo;
8583 for (int i = 0; i < debug_info->break_points()->length(); i++) {
8584 if (debug_info->break_points()->get(i)->IsUndefined()) {
8585 index = i;
8586 break;
8587 }
8588 }
8589 if (index == kNoBreakPointInfo) {
8590 // No free slot - extend break point info array.
8591 Handle<FixedArray> old_break_points =
8592 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
8593 debug_info->set_break_points(*Factory::NewFixedArray(
8594 old_break_points->length() +
8595 Debug::kEstimatedNofBreakPointsInFunction));
8596 Handle<FixedArray> new_break_points =
8597 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
8598 for (int i = 0; i < old_break_points->length(); i++) {
8599 new_break_points->set(i, old_break_points->get(i));
8600 }
8601 index = old_break_points->length();
8602 }
8603 ASSERT(index != kNoBreakPointInfo);
8604
8605 // Allocate new BreakPointInfo object and set the break point.
8606 Handle<BreakPointInfo> new_break_point_info =
8607 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
8608 new_break_point_info->set_code_position(Smi::FromInt(code_position));
8609 new_break_point_info->set_source_position(Smi::FromInt(source_position));
8610 new_break_point_info->
8611 set_statement_position(Smi::FromInt(statement_position));
8612 new_break_point_info->set_break_point_objects(Heap::undefined_value());
8613 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
8614 debug_info->break_points()->set(index, *new_break_point_info);
8615}
8616
8617
8618// Get the break point objects for a code position.
8619Object* DebugInfo::GetBreakPointObjects(int code_position) {
8620 Object* break_point_info = GetBreakPointInfo(code_position);
8621 if (break_point_info->IsUndefined()) {
8622 return Heap::undefined_value();
8623 }
8624 return BreakPointInfo::cast(break_point_info)->break_point_objects();
8625}
8626
8627
8628// Get the total number of break points.
8629int DebugInfo::GetBreakPointCount() {
8630 if (break_points()->IsUndefined()) return 0;
8631 int count = 0;
8632 for (int i = 0; i < break_points()->length(); i++) {
8633 if (!break_points()->get(i)->IsUndefined()) {
8634 BreakPointInfo* break_point_info =
8635 BreakPointInfo::cast(break_points()->get(i));
8636 count += break_point_info->GetBreakPointCount();
8637 }
8638 }
8639 return count;
8640}
8641
8642
8643Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
8644 Handle<Object> break_point_object) {
8645 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
8646 for (int i = 0; i < debug_info->break_points()->length(); i++) {
8647 if (!debug_info->break_points()->get(i)->IsUndefined()) {
8648 Handle<BreakPointInfo> break_point_info =
8649 Handle<BreakPointInfo>(BreakPointInfo::cast(
8650 debug_info->break_points()->get(i)));
8651 if (BreakPointInfo::HasBreakPointObject(break_point_info,
8652 break_point_object)) {
8653 return *break_point_info;
8654 }
8655 }
8656 }
8657 return Heap::undefined_value();
8658}
8659
8660
8661// Find the index of the break point info object for the specified code
8662// position.
8663int DebugInfo::GetBreakPointInfoIndex(int code_position) {
8664 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
8665 for (int i = 0; i < break_points()->length(); i++) {
8666 if (!break_points()->get(i)->IsUndefined()) {
8667 BreakPointInfo* break_point_info =
8668 BreakPointInfo::cast(break_points()->get(i));
8669 if (break_point_info->code_position()->value() == code_position) {
8670 return i;
8671 }
8672 }
8673 }
8674 return kNoBreakPointInfo;
8675}
8676
8677
8678// Remove the specified break point object.
8679void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
8680 Handle<Object> break_point_object) {
8681 // If there are no break points just ignore.
8682 if (break_point_info->break_point_objects()->IsUndefined()) return;
8683 // If there is a single break point clear it if it is the same.
8684 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8685 if (break_point_info->break_point_objects() == *break_point_object) {
8686 break_point_info->set_break_point_objects(Heap::undefined_value());
8687 }
8688 return;
8689 }
8690 // If there are multiple break points shrink the array
8691 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
8692 Handle<FixedArray> old_array =
8693 Handle<FixedArray>(
8694 FixedArray::cast(break_point_info->break_point_objects()));
8695 Handle<FixedArray> new_array =
8696 Factory::NewFixedArray(old_array->length() - 1);
8697 int found_count = 0;
8698 for (int i = 0; i < old_array->length(); i++) {
8699 if (old_array->get(i) == *break_point_object) {
8700 ASSERT(found_count == 0);
8701 found_count++;
8702 } else {
8703 new_array->set(i - found_count, old_array->get(i));
8704 }
8705 }
8706 // If the break point was found in the list change it.
8707 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
8708}
8709
8710
8711// Add the specified break point object.
8712void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
8713 Handle<Object> break_point_object) {
8714 // If there was no break point objects before just set it.
8715 if (break_point_info->break_point_objects()->IsUndefined()) {
8716 break_point_info->set_break_point_objects(*break_point_object);
8717 return;
8718 }
8719 // If the break point object is the same as before just ignore.
8720 if (break_point_info->break_point_objects() == *break_point_object) return;
8721 // If there was one break point object before replace with array.
8722 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8723 Handle<FixedArray> array = Factory::NewFixedArray(2);
8724 array->set(0, break_point_info->break_point_objects());
8725 array->set(1, *break_point_object);
8726 break_point_info->set_break_point_objects(*array);
8727 return;
8728 }
8729 // If there was more than one break point before extend array.
8730 Handle<FixedArray> old_array =
8731 Handle<FixedArray>(
8732 FixedArray::cast(break_point_info->break_point_objects()));
8733 Handle<FixedArray> new_array =
8734 Factory::NewFixedArray(old_array->length() + 1);
8735 for (int i = 0; i < old_array->length(); i++) {
8736 // If the break point was there before just ignore.
8737 if (old_array->get(i) == *break_point_object) return;
8738 new_array->set(i, old_array->get(i));
8739 }
8740 // Add the new break point.
8741 new_array->set(old_array->length(), *break_point_object);
8742 break_point_info->set_break_point_objects(*new_array);
8743}
8744
8745
8746bool BreakPointInfo::HasBreakPointObject(
8747 Handle<BreakPointInfo> break_point_info,
8748 Handle<Object> break_point_object) {
8749 // No break point.
8750 if (break_point_info->break_point_objects()->IsUndefined()) return false;
8751 // Single beak point.
8752 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8753 return break_point_info->break_point_objects() == *break_point_object;
8754 }
8755 // Multiple break points.
8756 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
8757 for (int i = 0; i < array->length(); i++) {
8758 if (array->get(i) == *break_point_object) {
8759 return true;
8760 }
8761 }
8762 return false;
8763}
8764
8765
8766// Get the number of break points.
8767int BreakPointInfo::GetBreakPointCount() {
8768 // No break point.
8769 if (break_point_objects()->IsUndefined()) return 0;
8770 // Single beak point.
8771 if (!break_point_objects()->IsFixedArray()) return 1;
8772 // Multiple break points.
8773 return FixedArray::cast(break_point_objects())->length();
8774}
8775#endif
8776
8777
8778} } // namespace v8::internal