blob: 8f668fb3b142570207cc07080e5c9b1f71966d51 [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);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01002193 new_map->set_scavenger(Heap::GetScavenger(new_map->instance_type(),
2194 new_map->instance_size()));
Steve Blocka7e24c12009-10-30 11:49:00 +00002195 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2196 instance_size_delta);
2197 }
2198 new_map->set_unused_property_fields(0);
2199
2200 // We have now successfully allocated all the necessary objects.
2201 // Changes can now be made with the guarantee that all of them take effect.
2202 set_map(new_map);
2203 map()->set_instance_descriptors(Heap::empty_descriptor_array());
2204
2205 set_properties(dictionary);
2206
2207 Counters::props_to_dictionary.Increment();
2208
2209#ifdef DEBUG
2210 if (FLAG_trace_normalization) {
2211 PrintF("Object properties have been normalized:\n");
2212 Print();
2213 }
2214#endif
2215 return this;
2216}
2217
2218
2219Object* JSObject::TransformToFastProperties(int unused_property_fields) {
2220 if (HasFastProperties()) return this;
2221 ASSERT(!IsGlobalObject());
2222 return property_dictionary()->
2223 TransformPropertiesToFastFor(this, unused_property_fields);
2224}
2225
2226
2227Object* JSObject::NormalizeElements() {
Steve Block3ce2e202009-11-05 08:53:23 +00002228 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002229 if (HasDictionaryElements()) return this;
Steve Block8defd9f2010-07-08 12:39:36 +01002230 ASSERT(map()->has_fast_elements());
2231
2232 Object* obj = map()->GetSlowElementsMap();
2233 if (obj->IsFailure()) return obj;
2234 Map* new_map = Map::cast(obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00002235
2236 // Get number of entries.
2237 FixedArray* array = FixedArray::cast(elements());
2238
2239 // Compute the effective length.
2240 int length = IsJSArray() ?
2241 Smi::cast(JSArray::cast(this)->length())->value() :
2242 array->length();
Steve Block8defd9f2010-07-08 12:39:36 +01002243 obj = NumberDictionary::Allocate(length);
Steve Blocka7e24c12009-10-30 11:49:00 +00002244 if (obj->IsFailure()) return obj;
2245 NumberDictionary* dictionary = NumberDictionary::cast(obj);
2246 // Copy entries.
2247 for (int i = 0; i < length; i++) {
2248 Object* value = array->get(i);
2249 if (!value->IsTheHole()) {
2250 PropertyDetails details = PropertyDetails(NONE, NORMAL);
2251 Object* result = dictionary->AddNumberEntry(i, array->get(i), details);
2252 if (result->IsFailure()) return result;
2253 dictionary = NumberDictionary::cast(result);
2254 }
2255 }
Steve Block8defd9f2010-07-08 12:39:36 +01002256 // Switch to using the dictionary as the backing storage for
2257 // elements. Set the new map first to satify the elements type
2258 // assert in set_elements().
2259 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002260 set_elements(dictionary);
2261
2262 Counters::elements_to_dictionary.Increment();
2263
2264#ifdef DEBUG
2265 if (FLAG_trace_normalization) {
2266 PrintF("Object elements have been normalized:\n");
2267 Print();
2268 }
2269#endif
2270
2271 return this;
2272}
2273
2274
2275Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) {
2276 // Check local property, ignore interceptor.
2277 LookupResult result;
2278 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002279 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002280
2281 // Normalize object if needed.
2282 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2283 if (obj->IsFailure()) return obj;
2284
2285 return DeleteNormalizedProperty(name, mode);
2286}
2287
2288
2289Object* JSObject::DeletePropertyWithInterceptor(String* name) {
2290 HandleScope scope;
2291 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2292 Handle<String> name_handle(name);
2293 Handle<JSObject> this_handle(this);
2294 if (!interceptor->deleter()->IsUndefined()) {
2295 v8::NamedPropertyDeleter deleter =
2296 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2297 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2298 CustomArguments args(interceptor->data(), this, this);
2299 v8::AccessorInfo info(args.end());
2300 v8::Handle<v8::Boolean> result;
2301 {
2302 // Leaving JavaScript.
2303 VMState state(EXTERNAL);
2304 result = deleter(v8::Utils::ToLocal(name_handle), info);
2305 }
2306 RETURN_IF_SCHEDULED_EXCEPTION();
2307 if (!result.IsEmpty()) {
2308 ASSERT(result->IsBoolean());
2309 return *v8::Utils::OpenHandle(*result);
2310 }
2311 }
2312 Object* raw_result =
2313 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
2314 RETURN_IF_SCHEDULED_EXCEPTION();
2315 return raw_result;
2316}
2317
2318
2319Object* JSObject::DeleteElementPostInterceptor(uint32_t index,
2320 DeleteMode mode) {
Steve Block3ce2e202009-11-05 08:53:23 +00002321 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002322 switch (GetElementsKind()) {
2323 case FAST_ELEMENTS: {
2324 uint32_t length = IsJSArray() ?
2325 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2326 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2327 if (index < length) {
2328 FixedArray::cast(elements())->set_the_hole(index);
2329 }
2330 break;
2331 }
2332 case DICTIONARY_ELEMENTS: {
2333 NumberDictionary* dictionary = element_dictionary();
2334 int entry = dictionary->FindEntry(index);
2335 if (entry != NumberDictionary::kNotFound) {
2336 return dictionary->DeleteProperty(entry, mode);
2337 }
2338 break;
2339 }
2340 default:
2341 UNREACHABLE();
2342 break;
2343 }
2344 return Heap::true_value();
2345}
2346
2347
2348Object* JSObject::DeleteElementWithInterceptor(uint32_t index) {
2349 // Make sure that the top context does not change when doing
2350 // callbacks or interceptor calls.
2351 AssertNoContextChange ncc;
2352 HandleScope scope;
2353 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2354 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2355 v8::IndexedPropertyDeleter deleter =
2356 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2357 Handle<JSObject> this_handle(this);
2358 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2359 CustomArguments args(interceptor->data(), this, this);
2360 v8::AccessorInfo info(args.end());
2361 v8::Handle<v8::Boolean> result;
2362 {
2363 // Leaving JavaScript.
2364 VMState state(EXTERNAL);
2365 result = deleter(index, info);
2366 }
2367 RETURN_IF_SCHEDULED_EXCEPTION();
2368 if (!result.IsEmpty()) {
2369 ASSERT(result->IsBoolean());
2370 return *v8::Utils::OpenHandle(*result);
2371 }
2372 Object* raw_result =
2373 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
2374 RETURN_IF_SCHEDULED_EXCEPTION();
2375 return raw_result;
2376}
2377
2378
2379Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
2380 // Check access rights if needed.
2381 if (IsAccessCheckNeeded() &&
2382 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
2383 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2384 return Heap::false_value();
2385 }
2386
2387 if (IsJSGlobalProxy()) {
2388 Object* proto = GetPrototype();
2389 if (proto->IsNull()) return Heap::false_value();
2390 ASSERT(proto->IsJSGlobalObject());
2391 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
2392 }
2393
2394 if (HasIndexedInterceptor()) {
2395 // Skip interceptor if forcing deletion.
2396 if (mode == FORCE_DELETION) {
2397 return DeleteElementPostInterceptor(index, mode);
2398 }
2399 return DeleteElementWithInterceptor(index);
2400 }
2401
2402 switch (GetElementsKind()) {
2403 case FAST_ELEMENTS: {
2404 uint32_t length = IsJSArray() ?
2405 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2406 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2407 if (index < length) {
2408 FixedArray::cast(elements())->set_the_hole(index);
2409 }
2410 break;
2411 }
Steve Block3ce2e202009-11-05 08:53:23 +00002412 case PIXEL_ELEMENTS:
2413 case EXTERNAL_BYTE_ELEMENTS:
2414 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2415 case EXTERNAL_SHORT_ELEMENTS:
2416 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2417 case EXTERNAL_INT_ELEMENTS:
2418 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2419 case EXTERNAL_FLOAT_ELEMENTS:
2420 // Pixel and external array elements cannot be deleted. Just
2421 // silently ignore here.
Steve Blocka7e24c12009-10-30 11:49:00 +00002422 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00002423 case DICTIONARY_ELEMENTS: {
2424 NumberDictionary* dictionary = element_dictionary();
2425 int entry = dictionary->FindEntry(index);
2426 if (entry != NumberDictionary::kNotFound) {
2427 return dictionary->DeleteProperty(entry, mode);
2428 }
2429 break;
2430 }
2431 default:
2432 UNREACHABLE();
2433 break;
2434 }
2435 return Heap::true_value();
2436}
2437
2438
2439Object* JSObject::DeleteProperty(String* name, DeleteMode mode) {
2440 // ECMA-262, 3rd, 8.6.2.5
2441 ASSERT(name->IsString());
2442
2443 // Check access rights if needed.
2444 if (IsAccessCheckNeeded() &&
2445 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2446 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2447 return Heap::false_value();
2448 }
2449
2450 if (IsJSGlobalProxy()) {
2451 Object* proto = GetPrototype();
2452 if (proto->IsNull()) return Heap::false_value();
2453 ASSERT(proto->IsJSGlobalObject());
2454 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
2455 }
2456
2457 uint32_t index = 0;
2458 if (name->AsArrayIndex(&index)) {
2459 return DeleteElement(index, mode);
2460 } else {
2461 LookupResult result;
2462 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002463 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002464 // Ignore attributes if forcing a deletion.
2465 if (result.IsDontDelete() && mode != FORCE_DELETION) {
2466 return Heap::false_value();
2467 }
2468 // Check for interceptor.
2469 if (result.type() == INTERCEPTOR) {
2470 // Skip interceptor if forcing a deletion.
2471 if (mode == FORCE_DELETION) {
2472 return DeletePropertyPostInterceptor(name, mode);
2473 }
2474 return DeletePropertyWithInterceptor(name);
2475 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002476 // Normalize object if needed.
2477 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2478 if (obj->IsFailure()) return obj;
2479 // Make sure the properties are normalized before removing the entry.
2480 return DeleteNormalizedProperty(name, mode);
2481 }
2482}
2483
2484
2485// Check whether this object references another object.
2486bool JSObject::ReferencesObject(Object* obj) {
2487 AssertNoAllocation no_alloc;
2488
2489 // Is the object the constructor for this object?
2490 if (map()->constructor() == obj) {
2491 return true;
2492 }
2493
2494 // Is the object the prototype for this object?
2495 if (map()->prototype() == obj) {
2496 return true;
2497 }
2498
2499 // Check if the object is among the named properties.
2500 Object* key = SlowReverseLookup(obj);
2501 if (key != Heap::undefined_value()) {
2502 return true;
2503 }
2504
2505 // Check if the object is among the indexed properties.
2506 switch (GetElementsKind()) {
2507 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002508 case EXTERNAL_BYTE_ELEMENTS:
2509 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2510 case EXTERNAL_SHORT_ELEMENTS:
2511 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2512 case EXTERNAL_INT_ELEMENTS:
2513 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2514 case EXTERNAL_FLOAT_ELEMENTS:
2515 // Raw pixels and external arrays do not reference other
2516 // objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00002517 break;
2518 case FAST_ELEMENTS: {
2519 int length = IsJSArray() ?
2520 Smi::cast(JSArray::cast(this)->length())->value() :
2521 FixedArray::cast(elements())->length();
2522 for (int i = 0; i < length; i++) {
2523 Object* element = FixedArray::cast(elements())->get(i);
2524 if (!element->IsTheHole() && element == obj) {
2525 return true;
2526 }
2527 }
2528 break;
2529 }
2530 case DICTIONARY_ELEMENTS: {
2531 key = element_dictionary()->SlowReverseLookup(obj);
2532 if (key != Heap::undefined_value()) {
2533 return true;
2534 }
2535 break;
2536 }
2537 default:
2538 UNREACHABLE();
2539 break;
2540 }
2541
Steve Block6ded16b2010-05-10 14:33:55 +01002542 // For functions check the context.
2543 if (IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002544 // Get the constructor function for arguments array.
2545 JSObject* arguments_boilerplate =
2546 Top::context()->global_context()->arguments_boilerplate();
2547 JSFunction* arguments_function =
2548 JSFunction::cast(arguments_boilerplate->map()->constructor());
2549
2550 // Get the context and don't check if it is the global context.
2551 JSFunction* f = JSFunction::cast(this);
2552 Context* context = f->context();
2553 if (context->IsGlobalContext()) {
2554 return false;
2555 }
2556
2557 // Check the non-special context slots.
2558 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2559 // Only check JS objects.
2560 if (context->get(i)->IsJSObject()) {
2561 JSObject* ctxobj = JSObject::cast(context->get(i));
2562 // If it is an arguments array check the content.
2563 if (ctxobj->map()->constructor() == arguments_function) {
2564 if (ctxobj->ReferencesObject(obj)) {
2565 return true;
2566 }
2567 } else if (ctxobj == obj) {
2568 return true;
2569 }
2570 }
2571 }
2572
2573 // Check the context extension if any.
2574 if (context->has_extension()) {
2575 return context->extension()->ReferencesObject(obj);
2576 }
2577 }
2578
2579 // No references to object.
2580 return false;
2581}
2582
2583
Steve Block8defd9f2010-07-08 12:39:36 +01002584Object* JSObject::PreventExtensions() {
2585 // If there are fast elements we normalize.
2586 if (HasFastElements()) {
2587 NormalizeElements();
2588 }
2589 // Make sure that we never go back to fast case.
2590 element_dictionary()->set_requires_slow_elements();
2591
2592 // Do a map transition, other objects with this map may still
2593 // be extensible.
2594 Object* new_map = map()->CopyDropTransitions();
2595 if (new_map->IsFailure()) return new_map;
2596 Map::cast(new_map)->set_is_extensible(false);
2597 set_map(Map::cast(new_map));
2598 ASSERT(!map()->is_extensible());
2599 return new_map;
2600}
2601
2602
Steve Blocka7e24c12009-10-30 11:49:00 +00002603// Tests for the fast common case for property enumeration:
Steve Blockd0582a62009-12-15 09:54:21 +00002604// - This object and all prototypes has an enum cache (which means that it has
2605// no interceptors and needs no access checks).
2606// - This object has no elements.
2607// - No prototype has enumerable properties/elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002608bool JSObject::IsSimpleEnum() {
Steve Blocka7e24c12009-10-30 11:49:00 +00002609 for (Object* o = this;
2610 o != Heap::null_value();
2611 o = JSObject::cast(o)->GetPrototype()) {
2612 JSObject* curr = JSObject::cast(o);
Steve Blocka7e24c12009-10-30 11:49:00 +00002613 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00002614 ASSERT(!curr->HasNamedInterceptor());
2615 ASSERT(!curr->HasIndexedInterceptor());
2616 ASSERT(!curr->IsAccessCheckNeeded());
Steve Blocka7e24c12009-10-30 11:49:00 +00002617 if (curr->NumberOfEnumElements() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002618 if (curr != this) {
2619 FixedArray* curr_fixed_array =
2620 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
Steve Blockd0582a62009-12-15 09:54:21 +00002621 if (curr_fixed_array->length() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002622 }
2623 }
2624 return true;
2625}
2626
2627
2628int Map::NumberOfDescribedProperties() {
2629 int result = 0;
2630 DescriptorArray* descs = instance_descriptors();
2631 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2632 if (descs->IsProperty(i)) result++;
2633 }
2634 return result;
2635}
2636
2637
2638int Map::PropertyIndexFor(String* name) {
2639 DescriptorArray* descs = instance_descriptors();
2640 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2641 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
2642 return descs->GetFieldIndex(i);
2643 }
2644 }
2645 return -1;
2646}
2647
2648
2649int Map::NextFreePropertyIndex() {
2650 int max_index = -1;
2651 DescriptorArray* descs = instance_descriptors();
2652 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2653 if (descs->GetType(i) == FIELD) {
2654 int current_index = descs->GetFieldIndex(i);
2655 if (current_index > max_index) max_index = current_index;
2656 }
2657 }
2658 return max_index + 1;
2659}
2660
2661
2662AccessorDescriptor* Map::FindAccessor(String* name) {
2663 DescriptorArray* descs = instance_descriptors();
2664 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2665 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
2666 return descs->GetCallbacks(i);
2667 }
2668 }
2669 return NULL;
2670}
2671
2672
2673void JSObject::LocalLookup(String* name, LookupResult* result) {
2674 ASSERT(name->IsString());
2675
2676 if (IsJSGlobalProxy()) {
2677 Object* proto = GetPrototype();
2678 if (proto->IsNull()) return result->NotFound();
2679 ASSERT(proto->IsJSGlobalObject());
2680 return JSObject::cast(proto)->LocalLookup(name, result);
2681 }
2682
2683 // Do not use inline caching if the object is a non-global object
2684 // that requires access checks.
2685 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
2686 result->DisallowCaching();
2687 }
2688
2689 // Check __proto__ before interceptor.
2690 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
2691 result->ConstantResult(this);
2692 return;
2693 }
2694
2695 // Check for lookup interceptor except when bootstrapping.
2696 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2697 result->InterceptorResult(this);
2698 return;
2699 }
2700
2701 LocalLookupRealNamedProperty(name, result);
2702}
2703
2704
2705void JSObject::Lookup(String* name, LookupResult* result) {
2706 // Ecma-262 3rd 8.6.2.4
2707 for (Object* current = this;
2708 current != Heap::null_value();
2709 current = JSObject::cast(current)->GetPrototype()) {
2710 JSObject::cast(current)->LocalLookup(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002711 if (result->IsProperty()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002712 }
2713 result->NotFound();
2714}
2715
2716
2717// Search object and it's prototype chain for callback properties.
2718void JSObject::LookupCallback(String* name, LookupResult* result) {
2719 for (Object* current = this;
2720 current != Heap::null_value();
2721 current = JSObject::cast(current)->GetPrototype()) {
2722 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002723 if (result->IsProperty() && result->type() == CALLBACKS) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002724 }
2725 result->NotFound();
2726}
2727
2728
2729Object* JSObject::DefineGetterSetter(String* name,
2730 PropertyAttributes attributes) {
2731 // Make sure that the top context does not change when doing callbacks or
2732 // interceptor calls.
2733 AssertNoContextChange ncc;
2734
Steve Blocka7e24c12009-10-30 11:49:00 +00002735 // Try to flatten before operating on the string.
Steve Block6ded16b2010-05-10 14:33:55 +01002736 name->TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00002737
Leon Clarkef7060e22010-06-03 12:02:55 +01002738 if (!CanSetCallback(name)) {
2739 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002740 }
2741
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002742 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00002743 bool is_element = name->AsArrayIndex(&index);
2744 if (is_element && IsJSArray()) return Heap::undefined_value();
2745
2746 if (is_element) {
2747 switch (GetElementsKind()) {
2748 case FAST_ELEMENTS:
2749 break;
2750 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002751 case EXTERNAL_BYTE_ELEMENTS:
2752 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2753 case EXTERNAL_SHORT_ELEMENTS:
2754 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2755 case EXTERNAL_INT_ELEMENTS:
2756 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2757 case EXTERNAL_FLOAT_ELEMENTS:
2758 // Ignore getters and setters on pixel and external array
2759 // elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002760 return Heap::undefined_value();
2761 case DICTIONARY_ELEMENTS: {
2762 // Lookup the index.
2763 NumberDictionary* dictionary = element_dictionary();
2764 int entry = dictionary->FindEntry(index);
2765 if (entry != NumberDictionary::kNotFound) {
2766 Object* result = dictionary->ValueAt(entry);
2767 PropertyDetails details = dictionary->DetailsAt(entry);
2768 if (details.IsReadOnly()) return Heap::undefined_value();
2769 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002770 if (result->IsFixedArray()) {
2771 return result;
2772 }
2773 // Otherwise allow to override it.
Steve Blocka7e24c12009-10-30 11:49:00 +00002774 }
2775 }
2776 break;
2777 }
2778 default:
2779 UNREACHABLE();
2780 break;
2781 }
2782 } else {
2783 // Lookup the name.
2784 LookupResult result;
2785 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002786 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002787 if (result.IsReadOnly()) return Heap::undefined_value();
2788 if (result.type() == CALLBACKS) {
2789 Object* obj = result.GetCallbackObject();
Leon Clarkef7060e22010-06-03 12:02:55 +01002790 // Need to preserve old getters/setters.
Leon Clarked91b9f72010-01-27 17:25:45 +00002791 if (obj->IsFixedArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002792 // Use set to update attributes.
2793 return SetPropertyCallback(name, obj, attributes);
Leon Clarked91b9f72010-01-27 17:25:45 +00002794 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002795 }
2796 }
2797 }
2798
2799 // Allocate the fixed array to hold getter and setter.
2800 Object* structure = Heap::AllocateFixedArray(2, TENURED);
2801 if (structure->IsFailure()) return structure;
Steve Blocka7e24c12009-10-30 11:49:00 +00002802
2803 if (is_element) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002804 return SetElementCallback(index, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002805 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01002806 return SetPropertyCallback(name, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002807 }
Leon Clarkef7060e22010-06-03 12:02:55 +01002808}
2809
2810
2811bool JSObject::CanSetCallback(String* name) {
2812 ASSERT(!IsAccessCheckNeeded()
2813 || Top::MayNamedAccess(this, name, v8::ACCESS_SET));
2814
2815 // Check if there is an API defined callback object which prohibits
2816 // callback overwriting in this object or it's prototype chain.
2817 // This mechanism is needed for instance in a browser setting, where
2818 // certain accessors such as window.location should not be allowed
2819 // to be overwritten because allowing overwriting could potentially
2820 // cause security problems.
2821 LookupResult callback_result;
2822 LookupCallback(name, &callback_result);
2823 if (callback_result.IsProperty()) {
2824 Object* obj = callback_result.GetCallbackObject();
2825 if (obj->IsAccessorInfo() &&
2826 AccessorInfo::cast(obj)->prohibits_overwriting()) {
2827 return false;
2828 }
2829 }
2830
2831 return true;
2832}
2833
2834
2835Object* JSObject::SetElementCallback(uint32_t index,
2836 Object* structure,
2837 PropertyAttributes attributes) {
2838 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
2839
2840 // Normalize elements to make this operation simple.
2841 Object* ok = NormalizeElements();
2842 if (ok->IsFailure()) return ok;
2843
2844 // Update the dictionary with the new CALLBACKS property.
2845 Object* dict =
2846 element_dictionary()->Set(index, structure, details);
2847 if (dict->IsFailure()) return dict;
2848
2849 NumberDictionary* elements = NumberDictionary::cast(dict);
2850 elements->set_requires_slow_elements();
2851 // Set the potential new dictionary on the object.
2852 set_elements(elements);
Steve Blocka7e24c12009-10-30 11:49:00 +00002853
2854 return structure;
2855}
2856
2857
Leon Clarkef7060e22010-06-03 12:02:55 +01002858Object* JSObject::SetPropertyCallback(String* name,
2859 Object* structure,
2860 PropertyAttributes attributes) {
2861 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
2862
2863 bool convert_back_to_fast = HasFastProperties() &&
2864 (map()->instance_descriptors()->number_of_descriptors()
2865 < DescriptorArray::kMaxNumberOfDescriptors);
2866
2867 // Normalize object to make this operation simple.
2868 Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2869 if (ok->IsFailure()) return ok;
2870
2871 // For the global object allocate a new map to invalidate the global inline
2872 // caches which have a global property cell reference directly in the code.
2873 if (IsGlobalObject()) {
2874 Object* new_map = map()->CopyDropDescriptors();
2875 if (new_map->IsFailure()) return new_map;
2876 set_map(Map::cast(new_map));
2877 }
2878
2879 // Update the dictionary with the new CALLBACKS property.
2880 Object* result = SetNormalizedProperty(name, structure, details);
2881 if (result->IsFailure()) return result;
2882
2883 if (convert_back_to_fast) {
2884 ok = TransformToFastProperties(0);
2885 if (ok->IsFailure()) return ok;
2886 }
2887 return result;
2888}
2889
Steve Blocka7e24c12009-10-30 11:49:00 +00002890Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
2891 PropertyAttributes attributes) {
2892 // Check access rights if needed.
2893 if (IsAccessCheckNeeded() &&
Leon Clarkef7060e22010-06-03 12:02:55 +01002894 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
2895 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Steve Blocka7e24c12009-10-30 11:49:00 +00002896 return Heap::undefined_value();
2897 }
2898
2899 if (IsJSGlobalProxy()) {
2900 Object* proto = GetPrototype();
2901 if (proto->IsNull()) return this;
2902 ASSERT(proto->IsJSGlobalObject());
2903 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
2904 fun, attributes);
2905 }
2906
2907 Object* array = DefineGetterSetter(name, attributes);
2908 if (array->IsFailure() || array->IsUndefined()) return array;
2909 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
2910 return this;
2911}
2912
2913
Leon Clarkef7060e22010-06-03 12:02:55 +01002914Object* JSObject::DefineAccessor(AccessorInfo* info) {
2915 String* name = String::cast(info->name());
2916 // Check access rights if needed.
2917 if (IsAccessCheckNeeded() &&
2918 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
2919 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
2920 return Heap::undefined_value();
2921 }
2922
2923 if (IsJSGlobalProxy()) {
2924 Object* proto = GetPrototype();
2925 if (proto->IsNull()) return this;
2926 ASSERT(proto->IsJSGlobalObject());
2927 return JSObject::cast(proto)->DefineAccessor(info);
2928 }
2929
2930 // Make sure that the top context does not change when doing callbacks or
2931 // interceptor calls.
2932 AssertNoContextChange ncc;
2933
2934 // Try to flatten before operating on the string.
2935 name->TryFlatten();
2936
2937 if (!CanSetCallback(name)) {
2938 return Heap::undefined_value();
2939 }
2940
2941 uint32_t index = 0;
2942 bool is_element = name->AsArrayIndex(&index);
2943
2944 if (is_element) {
2945 if (IsJSArray()) return Heap::undefined_value();
2946
2947 // Accessors overwrite previous callbacks (cf. with getters/setters).
2948 switch (GetElementsKind()) {
2949 case FAST_ELEMENTS:
2950 break;
2951 case PIXEL_ELEMENTS:
2952 case EXTERNAL_BYTE_ELEMENTS:
2953 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2954 case EXTERNAL_SHORT_ELEMENTS:
2955 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2956 case EXTERNAL_INT_ELEMENTS:
2957 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2958 case EXTERNAL_FLOAT_ELEMENTS:
2959 // Ignore getters and setters on pixel and external array
2960 // elements.
2961 return Heap::undefined_value();
2962 case DICTIONARY_ELEMENTS:
2963 break;
2964 default:
2965 UNREACHABLE();
2966 break;
2967 }
2968
2969 SetElementCallback(index, info, info->property_attributes());
2970 } else {
2971 // Lookup the name.
2972 LookupResult result;
2973 LocalLookup(name, &result);
2974 // ES5 forbids turning a property into an accessor if it's not
2975 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
2976 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
2977 return Heap::undefined_value();
2978 }
2979 SetPropertyCallback(name, info, info->property_attributes());
2980 }
2981
2982 return this;
2983}
2984
2985
Steve Blocka7e24c12009-10-30 11:49:00 +00002986Object* JSObject::LookupAccessor(String* name, bool is_getter) {
2987 // Make sure that the top context does not change when doing callbacks or
2988 // interceptor calls.
2989 AssertNoContextChange ncc;
2990
2991 // Check access rights if needed.
2992 if (IsAccessCheckNeeded() &&
2993 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2994 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2995 return Heap::undefined_value();
2996 }
2997
2998 // Make the lookup and include prototypes.
2999 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01003000 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00003001 if (name->AsArrayIndex(&index)) {
3002 for (Object* obj = this;
3003 obj != Heap::null_value();
3004 obj = JSObject::cast(obj)->GetPrototype()) {
3005 JSObject* js_object = JSObject::cast(obj);
3006 if (js_object->HasDictionaryElements()) {
3007 NumberDictionary* dictionary = js_object->element_dictionary();
3008 int entry = dictionary->FindEntry(index);
3009 if (entry != NumberDictionary::kNotFound) {
3010 Object* element = dictionary->ValueAt(entry);
3011 PropertyDetails details = dictionary->DetailsAt(entry);
3012 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003013 if (element->IsFixedArray()) {
3014 return FixedArray::cast(element)->get(accessor_index);
3015 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003016 }
3017 }
3018 }
3019 }
3020 } else {
3021 for (Object* obj = this;
3022 obj != Heap::null_value();
3023 obj = JSObject::cast(obj)->GetPrototype()) {
3024 LookupResult result;
3025 JSObject::cast(obj)->LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00003026 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003027 if (result.IsReadOnly()) return Heap::undefined_value();
3028 if (result.type() == CALLBACKS) {
3029 Object* obj = result.GetCallbackObject();
3030 if (obj->IsFixedArray()) {
3031 return FixedArray::cast(obj)->get(accessor_index);
3032 }
3033 }
3034 }
3035 }
3036 }
3037 return Heap::undefined_value();
3038}
3039
3040
3041Object* JSObject::SlowReverseLookup(Object* value) {
3042 if (HasFastProperties()) {
3043 DescriptorArray* descs = map()->instance_descriptors();
3044 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3045 if (descs->GetType(i) == FIELD) {
3046 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3047 return descs->GetKey(i);
3048 }
3049 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3050 if (descs->GetConstantFunction(i) == value) {
3051 return descs->GetKey(i);
3052 }
3053 }
3054 }
3055 return Heap::undefined_value();
3056 } else {
3057 return property_dictionary()->SlowReverseLookup(value);
3058 }
3059}
3060
3061
3062Object* Map::CopyDropDescriptors() {
3063 Object* result = Heap::AllocateMap(instance_type(), instance_size());
3064 if (result->IsFailure()) return result;
3065 Map::cast(result)->set_prototype(prototype());
3066 Map::cast(result)->set_constructor(constructor());
3067 // Don't copy descriptors, so map transitions always remain a forest.
3068 // If we retained the same descriptors we would have two maps
3069 // pointing to the same transition which is bad because the garbage
3070 // collector relies on being able to reverse pointers from transitions
3071 // to maps. If properties need to be retained use CopyDropTransitions.
3072 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
3073 // Please note instance_type and instance_size are set when allocated.
3074 Map::cast(result)->set_inobject_properties(inobject_properties());
3075 Map::cast(result)->set_unused_property_fields(unused_property_fields());
3076
3077 // If the map has pre-allocated properties always start out with a descriptor
3078 // array describing these properties.
3079 if (pre_allocated_property_fields() > 0) {
3080 ASSERT(constructor()->IsJSFunction());
3081 JSFunction* ctor = JSFunction::cast(constructor());
3082 Object* descriptors =
3083 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
3084 if (descriptors->IsFailure()) return descriptors;
3085 Map::cast(result)->set_instance_descriptors(
3086 DescriptorArray::cast(descriptors));
3087 Map::cast(result)->set_pre_allocated_property_fields(
3088 pre_allocated_property_fields());
3089 }
3090 Map::cast(result)->set_bit_field(bit_field());
3091 Map::cast(result)->set_bit_field2(bit_field2());
3092 Map::cast(result)->ClearCodeCache();
3093 return result;
3094}
3095
3096
3097Object* Map::CopyDropTransitions() {
3098 Object* new_map = CopyDropDescriptors();
3099 if (new_map->IsFailure()) return new_map;
3100 Object* descriptors = instance_descriptors()->RemoveTransitions();
3101 if (descriptors->IsFailure()) return descriptors;
3102 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
Steve Block8defd9f2010-07-08 12:39:36 +01003103 return new_map;
Steve Blocka7e24c12009-10-30 11:49:00 +00003104}
3105
3106
3107Object* Map::UpdateCodeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003108 // Allocate the code cache if not present.
3109 if (code_cache()->IsFixedArray()) {
3110 Object* result = Heap::AllocateCodeCache();
3111 if (result->IsFailure()) return result;
3112 set_code_cache(result);
3113 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003114
Steve Block6ded16b2010-05-10 14:33:55 +01003115 // Update the code cache.
3116 return CodeCache::cast(code_cache())->Update(name, code);
3117}
3118
3119
3120Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3121 // Do a lookup if a code cache exists.
3122 if (!code_cache()->IsFixedArray()) {
3123 return CodeCache::cast(code_cache())->Lookup(name, flags);
3124 } else {
3125 return Heap::undefined_value();
3126 }
3127}
3128
3129
3130int Map::IndexInCodeCache(Object* name, Code* code) {
3131 // Get the internal index if a code cache exists.
3132 if (!code_cache()->IsFixedArray()) {
3133 return CodeCache::cast(code_cache())->GetIndex(name, code);
3134 }
3135 return -1;
3136}
3137
3138
3139void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
3140 // No GC is supposed to happen between a call to IndexInCodeCache and
3141 // RemoveFromCodeCache so the code cache must be there.
3142 ASSERT(!code_cache()->IsFixedArray());
3143 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
3144}
3145
3146
3147Object* CodeCache::Update(String* name, Code* code) {
3148 ASSERT(code->ic_state() == MONOMORPHIC);
3149
3150 // The number of monomorphic stubs for normal load/store/call IC's can grow to
3151 // a large number and therefore they need to go into a hash table. They are
3152 // used to load global properties from cells.
3153 if (code->type() == NORMAL) {
3154 // Make sure that a hash table is allocated for the normal load code cache.
3155 if (normal_type_cache()->IsUndefined()) {
3156 Object* result =
3157 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
3158 if (result->IsFailure()) return result;
3159 set_normal_type_cache(result);
3160 }
3161 return UpdateNormalTypeCache(name, code);
3162 } else {
3163 ASSERT(default_cache()->IsFixedArray());
3164 return UpdateDefaultCache(name, code);
3165 }
3166}
3167
3168
3169Object* CodeCache::UpdateDefaultCache(String* name, Code* code) {
3170 // When updating the default code cache we disregard the type encoded in the
Steve Blocka7e24c12009-10-30 11:49:00 +00003171 // flags. This allows call constant stubs to overwrite call field
3172 // stubs, etc.
3173 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
3174
3175 // First check whether we can update existing code cache without
3176 // extending it.
Steve Block6ded16b2010-05-10 14:33:55 +01003177 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003178 int length = cache->length();
3179 int deleted_index = -1;
Steve Block6ded16b2010-05-10 14:33:55 +01003180 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003181 Object* key = cache->get(i);
3182 if (key->IsNull()) {
3183 if (deleted_index < 0) deleted_index = i;
3184 continue;
3185 }
3186 if (key->IsUndefined()) {
3187 if (deleted_index >= 0) i = deleted_index;
Steve Block6ded16b2010-05-10 14:33:55 +01003188 cache->set(i + kCodeCacheEntryNameOffset, name);
3189 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003190 return this;
3191 }
3192 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003193 Code::Flags found =
3194 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
Steve Blocka7e24c12009-10-30 11:49:00 +00003195 if (Code::RemoveTypeFromFlags(found) == flags) {
Steve Block6ded16b2010-05-10 14:33:55 +01003196 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003197 return this;
3198 }
3199 }
3200 }
3201
3202 // Reached the end of the code cache. If there were deleted
3203 // elements, reuse the space for the first of them.
3204 if (deleted_index >= 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01003205 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
3206 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003207 return this;
3208 }
3209
Steve Block6ded16b2010-05-10 14:33:55 +01003210 // Extend the code cache with some new entries (at least one). Must be a
3211 // multiple of the entry size.
3212 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
3213 new_length = new_length - new_length % kCodeCacheEntrySize;
3214 ASSERT((new_length % kCodeCacheEntrySize) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00003215 Object* result = cache->CopySize(new_length);
3216 if (result->IsFailure()) return result;
3217
3218 // Add the (name, code) pair to the new cache.
3219 cache = FixedArray::cast(result);
Steve Block6ded16b2010-05-10 14:33:55 +01003220 cache->set(length + kCodeCacheEntryNameOffset, name);
3221 cache->set(length + kCodeCacheEntryCodeOffset, code);
3222 set_default_cache(cache);
Steve Blocka7e24c12009-10-30 11:49:00 +00003223 return this;
3224}
3225
3226
Steve Block6ded16b2010-05-10 14:33:55 +01003227Object* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
3228 // Adding a new entry can cause a new cache to be allocated.
3229 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3230 Object* new_cache = cache->Put(name, code);
3231 if (new_cache->IsFailure()) return new_cache;
3232 set_normal_type_cache(new_cache);
3233 return this;
3234}
3235
3236
3237Object* CodeCache::Lookup(String* name, Code::Flags flags) {
3238 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
3239 return LookupNormalTypeCache(name, flags);
3240 } else {
3241 return LookupDefaultCache(name, flags);
3242 }
3243}
3244
3245
3246Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
3247 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003248 int length = cache->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003249 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
3250 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
Steve Blocka7e24c12009-10-30 11:49:00 +00003251 // Skip deleted elements.
3252 if (key->IsNull()) continue;
3253 if (key->IsUndefined()) return key;
3254 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003255 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
3256 if (code->flags() == flags) {
3257 return code;
3258 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003259 }
3260 }
3261 return Heap::undefined_value();
3262}
3263
3264
Steve Block6ded16b2010-05-10 14:33:55 +01003265Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
3266 if (!normal_type_cache()->IsUndefined()) {
3267 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3268 return cache->Lookup(name, flags);
3269 } else {
3270 return Heap::undefined_value();
3271 }
3272}
3273
3274
3275int CodeCache::GetIndex(Object* name, Code* code) {
3276 if (code->type() == NORMAL) {
3277 if (normal_type_cache()->IsUndefined()) return -1;
3278 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3279 return cache->GetIndex(String::cast(name), code->flags());
3280 }
3281
3282 FixedArray* array = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003283 int len = array->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003284 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
3285 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003286 }
3287 return -1;
3288}
3289
3290
Steve Block6ded16b2010-05-10 14:33:55 +01003291void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
3292 if (code->type() == NORMAL) {
3293 ASSERT(!normal_type_cache()->IsUndefined());
3294 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3295 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
3296 cache->RemoveByIndex(index);
3297 } else {
3298 FixedArray* array = default_cache();
3299 ASSERT(array->length() >= index && array->get(index)->IsCode());
3300 // Use null instead of undefined for deleted elements to distinguish
3301 // deleted elements from unused elements. This distinction is used
3302 // when looking up in the cache and when updating the cache.
3303 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
3304 array->set_null(index - 1); // Name.
3305 array->set_null(index); // Code.
3306 }
3307}
3308
3309
3310// The key in the code cache hash table consists of the property name and the
3311// code object. The actual match is on the name and the code flags. If a key
3312// is created using the flags and not a code object it can only be used for
3313// lookup not to create a new entry.
3314class CodeCacheHashTableKey : public HashTableKey {
3315 public:
3316 CodeCacheHashTableKey(String* name, Code::Flags flags)
3317 : name_(name), flags_(flags), code_(NULL) { }
3318
3319 CodeCacheHashTableKey(String* name, Code* code)
3320 : name_(name),
3321 flags_(code->flags()),
3322 code_(code) { }
3323
3324
3325 bool IsMatch(Object* other) {
3326 if (!other->IsFixedArray()) return false;
3327 FixedArray* pair = FixedArray::cast(other);
3328 String* name = String::cast(pair->get(0));
3329 Code::Flags flags = Code::cast(pair->get(1))->flags();
3330 if (flags != flags_) {
3331 return false;
3332 }
3333 return name_->Equals(name);
3334 }
3335
3336 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
3337 return name->Hash() ^ flags;
3338 }
3339
3340 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
3341
3342 uint32_t HashForObject(Object* obj) {
3343 FixedArray* pair = FixedArray::cast(obj);
3344 String* name = String::cast(pair->get(0));
3345 Code* code = Code::cast(pair->get(1));
3346 return NameFlagsHashHelper(name, code->flags());
3347 }
3348
3349 Object* AsObject() {
3350 ASSERT(code_ != NULL);
3351 Object* obj = Heap::AllocateFixedArray(2);
3352 if (obj->IsFailure()) return obj;
3353 FixedArray* pair = FixedArray::cast(obj);
3354 pair->set(0, name_);
3355 pair->set(1, code_);
3356 return pair;
3357 }
3358
3359 private:
3360 String* name_;
3361 Code::Flags flags_;
3362 Code* code_;
3363};
3364
3365
3366Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
3367 CodeCacheHashTableKey key(name, flags);
3368 int entry = FindEntry(&key);
3369 if (entry == kNotFound) return Heap::undefined_value();
3370 return get(EntryToIndex(entry) + 1);
3371}
3372
3373
3374Object* CodeCacheHashTable::Put(String* name, Code* code) {
3375 CodeCacheHashTableKey key(name, code);
3376 Object* obj = EnsureCapacity(1, &key);
3377 if (obj->IsFailure()) return obj;
3378
3379 // Don't use this, as the table might have grown.
3380 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
3381
3382 int entry = cache->FindInsertionEntry(key.Hash());
3383 Object* k = key.AsObject();
3384 if (k->IsFailure()) return k;
3385
3386 cache->set(EntryToIndex(entry), k);
3387 cache->set(EntryToIndex(entry) + 1, code);
3388 cache->ElementAdded();
3389 return cache;
3390}
3391
3392
3393int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
3394 CodeCacheHashTableKey key(name, flags);
3395 int entry = FindEntry(&key);
3396 return (entry == kNotFound) ? -1 : entry;
3397}
3398
3399
3400void CodeCacheHashTable::RemoveByIndex(int index) {
3401 ASSERT(index >= 0);
3402 set(EntryToIndex(index), Heap::null_value());
3403 set(EntryToIndex(index) + 1, Heap::null_value());
3404 ElementRemoved();
Steve Blocka7e24c12009-10-30 11:49:00 +00003405}
3406
3407
3408void FixedArray::FixedArrayIterateBody(ObjectVisitor* v) {
3409 IteratePointers(v, kHeaderSize, kHeaderSize + length() * kPointerSize);
3410}
3411
3412
3413static bool HasKey(FixedArray* array, Object* key) {
3414 int len0 = array->length();
3415 for (int i = 0; i < len0; i++) {
3416 Object* element = array->get(i);
3417 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3418 if (element->IsString() &&
3419 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3420 return true;
3421 }
3422 }
3423 return false;
3424}
3425
3426
3427Object* FixedArray::AddKeysFromJSArray(JSArray* array) {
Steve Block3ce2e202009-11-05 08:53:23 +00003428 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00003429 switch (array->GetElementsKind()) {
3430 case JSObject::FAST_ELEMENTS:
3431 return UnionOfKeys(FixedArray::cast(array->elements()));
3432 case JSObject::DICTIONARY_ELEMENTS: {
3433 NumberDictionary* dict = array->element_dictionary();
3434 int size = dict->NumberOfElements();
3435
3436 // Allocate a temporary fixed array.
3437 Object* object = Heap::AllocateFixedArray(size);
3438 if (object->IsFailure()) return object;
3439 FixedArray* key_array = FixedArray::cast(object);
3440
3441 int capacity = dict->Capacity();
3442 int pos = 0;
3443 // Copy the elements from the JSArray to the temporary fixed array.
3444 for (int i = 0; i < capacity; i++) {
3445 if (dict->IsKey(dict->KeyAt(i))) {
3446 key_array->set(pos++, dict->ValueAt(i));
3447 }
3448 }
3449 // Compute the union of this and the temporary fixed array.
3450 return UnionOfKeys(key_array);
3451 }
3452 default:
3453 UNREACHABLE();
3454 }
3455 UNREACHABLE();
3456 return Heap::null_value(); // Failure case needs to "return" a value.
3457}
3458
3459
3460Object* FixedArray::UnionOfKeys(FixedArray* other) {
3461 int len0 = length();
3462 int len1 = other->length();
3463 // Optimize if either is empty.
3464 if (len0 == 0) return other;
3465 if (len1 == 0) return this;
3466
3467 // Compute how many elements are not in this.
3468 int extra = 0;
3469 for (int y = 0; y < len1; y++) {
3470 Object* value = other->get(y);
3471 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3472 }
3473
3474 if (extra == 0) return this;
3475
3476 // Allocate the result
3477 Object* obj = Heap::AllocateFixedArray(len0 + extra);
3478 if (obj->IsFailure()) return obj;
3479 // Fill in the content
Leon Clarke4515c472010-02-03 11:58:03 +00003480 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003481 FixedArray* result = FixedArray::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00003482 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003483 for (int i = 0; i < len0; i++) {
3484 result->set(i, get(i), mode);
3485 }
3486 // Fill in the extra keys.
3487 int index = 0;
3488 for (int y = 0; y < len1; y++) {
3489 Object* value = other->get(y);
3490 if (!value->IsTheHole() && !HasKey(this, value)) {
3491 result->set(len0 + index, other->get(y), mode);
3492 index++;
3493 }
3494 }
3495 ASSERT(extra == index);
3496 return result;
3497}
3498
3499
3500Object* FixedArray::CopySize(int new_length) {
3501 if (new_length == 0) return Heap::empty_fixed_array();
3502 Object* obj = Heap::AllocateFixedArray(new_length);
3503 if (obj->IsFailure()) return obj;
3504 FixedArray* result = FixedArray::cast(obj);
3505 // Copy the content
Leon Clarke4515c472010-02-03 11:58:03 +00003506 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003507 int len = length();
3508 if (new_length < len) len = new_length;
3509 result->set_map(map());
Leon Clarke4515c472010-02-03 11:58:03 +00003510 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003511 for (int i = 0; i < len; i++) {
3512 result->set(i, get(i), mode);
3513 }
3514 return result;
3515}
3516
3517
3518void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
Leon Clarke4515c472010-02-03 11:58:03 +00003519 AssertNoAllocation no_gc;
3520 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003521 for (int index = 0; index < len; index++) {
3522 dest->set(dest_pos+index, get(pos+index), mode);
3523 }
3524}
3525
3526
3527#ifdef DEBUG
3528bool FixedArray::IsEqualTo(FixedArray* other) {
3529 if (length() != other->length()) return false;
3530 for (int i = 0 ; i < length(); ++i) {
3531 if (get(i) != other->get(i)) return false;
3532 }
3533 return true;
3534}
3535#endif
3536
3537
3538Object* DescriptorArray::Allocate(int number_of_descriptors) {
3539 if (number_of_descriptors == 0) {
3540 return Heap::empty_descriptor_array();
3541 }
3542 // Allocate the array of keys.
Leon Clarkee46be812010-01-19 14:06:41 +00003543 Object* array =
3544 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
Steve Blocka7e24c12009-10-30 11:49:00 +00003545 if (array->IsFailure()) return array;
3546 // Do not use DescriptorArray::cast on incomplete object.
3547 FixedArray* result = FixedArray::cast(array);
3548
3549 // Allocate the content array and set it in the descriptor array.
3550 array = Heap::AllocateFixedArray(number_of_descriptors << 1);
3551 if (array->IsFailure()) return array;
3552 result->set(kContentArrayIndex, array);
3553 result->set(kEnumerationIndexIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00003554 Smi::FromInt(PropertyDetails::kInitialIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +00003555 return result;
3556}
3557
3558
3559void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3560 FixedArray* new_cache) {
3561 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3562 if (HasEnumCache()) {
3563 FixedArray::cast(get(kEnumerationIndexIndex))->
3564 set(kEnumCacheBridgeCacheIndex, new_cache);
3565 } else {
3566 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3567 FixedArray::cast(bridge_storage)->
3568 set(kEnumCacheBridgeCacheIndex, new_cache);
3569 fast_set(FixedArray::cast(bridge_storage),
3570 kEnumCacheBridgeEnumIndex,
3571 get(kEnumerationIndexIndex));
3572 set(kEnumerationIndexIndex, bridge_storage);
3573 }
3574}
3575
3576
3577Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
3578 TransitionFlag transition_flag) {
3579 // Transitions are only kept when inserting another transition.
3580 // This precondition is not required by this function's implementation, but
3581 // is currently required by the semantics of maps, so we check it.
3582 // Conversely, we filter after replacing, so replacing a transition and
3583 // removing all other transitions is not supported.
3584 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3585 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
3586 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
3587
3588 // Ensure the key is a symbol.
3589 Object* result = descriptor->KeyToSymbol();
3590 if (result->IsFailure()) return result;
3591
3592 int transitions = 0;
3593 int null_descriptors = 0;
3594 if (remove_transitions) {
3595 for (int i = 0; i < number_of_descriptors(); i++) {
3596 if (IsTransition(i)) transitions++;
3597 if (IsNullDescriptor(i)) null_descriptors++;
3598 }
3599 } else {
3600 for (int i = 0; i < number_of_descriptors(); i++) {
3601 if (IsNullDescriptor(i)) null_descriptors++;
3602 }
3603 }
3604 int new_size = number_of_descriptors() - transitions - null_descriptors;
3605
3606 // If key is in descriptor, we replace it in-place when filtering.
3607 // Count a null descriptor for key as inserted, not replaced.
3608 int index = Search(descriptor->GetKey());
3609 const bool inserting = (index == kNotFound);
3610 const bool replacing = !inserting;
3611 bool keep_enumeration_index = false;
3612 if (inserting) {
3613 ++new_size;
3614 }
3615 if (replacing) {
3616 // We are replacing an existing descriptor. We keep the enumeration
3617 // index of a visible property.
3618 PropertyType t = PropertyDetails(GetDetails(index)).type();
3619 if (t == CONSTANT_FUNCTION ||
3620 t == FIELD ||
3621 t == CALLBACKS ||
3622 t == INTERCEPTOR) {
3623 keep_enumeration_index = true;
3624 } else if (remove_transitions) {
3625 // Replaced descriptor has been counted as removed if it is
3626 // a transition that will be replaced. Adjust count in this case.
3627 ++new_size;
3628 }
3629 }
3630 result = Allocate(new_size);
3631 if (result->IsFailure()) return result;
3632 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3633 // Set the enumeration index in the descriptors and set the enumeration index
3634 // in the result.
3635 int enumeration_index = NextEnumerationIndex();
3636 if (!descriptor->GetDetails().IsTransition()) {
3637 if (keep_enumeration_index) {
3638 descriptor->SetEnumerationIndex(
3639 PropertyDetails(GetDetails(index)).index());
3640 } else {
3641 descriptor->SetEnumerationIndex(enumeration_index);
3642 ++enumeration_index;
3643 }
3644 }
3645 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3646
3647 // Copy the descriptors, filtering out transitions and null descriptors,
3648 // and inserting or replacing a descriptor.
3649 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
3650 int from_index = 0;
3651 int to_index = 0;
3652
3653 for (; from_index < number_of_descriptors(); from_index++) {
3654 String* key = GetKey(from_index);
3655 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
3656 break;
3657 }
3658 if (IsNullDescriptor(from_index)) continue;
3659 if (remove_transitions && IsTransition(from_index)) continue;
3660 new_descriptors->CopyFrom(to_index++, this, from_index);
3661 }
3662
3663 new_descriptors->Set(to_index++, descriptor);
3664 if (replacing) from_index++;
3665
3666 for (; from_index < number_of_descriptors(); from_index++) {
3667 if (IsNullDescriptor(from_index)) continue;
3668 if (remove_transitions && IsTransition(from_index)) continue;
3669 new_descriptors->CopyFrom(to_index++, this, from_index);
3670 }
3671
3672 ASSERT(to_index == new_descriptors->number_of_descriptors());
3673 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
3674
3675 return new_descriptors;
3676}
3677
3678
3679Object* DescriptorArray::RemoveTransitions() {
3680 // Remove all transitions and null descriptors. Return a copy of the array
3681 // with all transitions removed, or a Failure object if the new array could
3682 // not be allocated.
3683
3684 // Compute the size of the map transition entries to be removed.
3685 int num_removed = 0;
3686 for (int i = 0; i < number_of_descriptors(); i++) {
3687 if (!IsProperty(i)) num_removed++;
3688 }
3689
3690 // Allocate the new descriptor array.
3691 Object* result = Allocate(number_of_descriptors() - num_removed);
3692 if (result->IsFailure()) return result;
3693 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3694
3695 // Copy the content.
3696 int next_descriptor = 0;
3697 for (int i = 0; i < number_of_descriptors(); i++) {
3698 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
3699 }
3700 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
3701
3702 return new_descriptors;
3703}
3704
3705
3706void DescriptorArray::Sort() {
3707 // In-place heap sort.
3708 int len = number_of_descriptors();
3709
3710 // Bottom-up max-heap construction.
Steve Block6ded16b2010-05-10 14:33:55 +01003711 // Index of the last node with children
3712 const int max_parent_index = (len / 2) - 1;
3713 for (int i = max_parent_index; i >= 0; --i) {
3714 int parent_index = i;
3715 const uint32_t parent_hash = GetKey(i)->Hash();
3716 while (parent_index <= max_parent_index) {
3717 int child_index = 2 * parent_index + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003718 uint32_t child_hash = GetKey(child_index)->Hash();
Steve Block6ded16b2010-05-10 14:33:55 +01003719 if (child_index + 1 < len) {
3720 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
3721 if (right_child_hash > child_hash) {
3722 child_index++;
3723 child_hash = right_child_hash;
3724 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003725 }
Steve Block6ded16b2010-05-10 14:33:55 +01003726 if (child_hash <= parent_hash) break;
3727 Swap(parent_index, child_index);
3728 // Now element at child_index could be < its children.
3729 parent_index = child_index; // parent_hash remains correct.
Steve Blocka7e24c12009-10-30 11:49:00 +00003730 }
3731 }
3732
3733 // Extract elements and create sorted array.
3734 for (int i = len - 1; i > 0; --i) {
3735 // Put max element at the back of the array.
3736 Swap(0, i);
3737 // Sift down the new top element.
3738 int parent_index = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01003739 const uint32_t parent_hash = GetKey(parent_index)->Hash();
3740 const int max_parent_index = (i / 2) - 1;
3741 while (parent_index <= max_parent_index) {
3742 int child_index = parent_index * 2 + 1;
3743 uint32_t child_hash = GetKey(child_index)->Hash();
3744 if (child_index + 1 < i) {
3745 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
3746 if (right_child_hash > child_hash) {
3747 child_index++;
3748 child_hash = right_child_hash;
3749 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003750 }
Steve Block6ded16b2010-05-10 14:33:55 +01003751 if (child_hash <= parent_hash) break;
3752 Swap(parent_index, child_index);
3753 parent_index = child_index;
Steve Blocka7e24c12009-10-30 11:49:00 +00003754 }
3755 }
3756
3757 SLOW_ASSERT(IsSortedNoDuplicates());
3758}
3759
3760
3761int DescriptorArray::BinarySearch(String* name, int low, int high) {
3762 uint32_t hash = name->Hash();
3763
3764 while (low <= high) {
3765 int mid = (low + high) / 2;
3766 String* mid_name = GetKey(mid);
3767 uint32_t mid_hash = mid_name->Hash();
3768
3769 if (mid_hash > hash) {
3770 high = mid - 1;
3771 continue;
3772 }
3773 if (mid_hash < hash) {
3774 low = mid + 1;
3775 continue;
3776 }
3777 // Found an element with the same hash-code.
3778 ASSERT(hash == mid_hash);
3779 // There might be more, so we find the first one and
3780 // check them all to see if we have a match.
3781 if (name == mid_name && !is_null_descriptor(mid)) return mid;
3782 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
3783 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
3784 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
3785 }
3786 break;
3787 }
3788 return kNotFound;
3789}
3790
3791
3792int DescriptorArray::LinearSearch(String* name, int len) {
3793 uint32_t hash = name->Hash();
3794 for (int number = 0; number < len; number++) {
3795 String* entry = GetKey(number);
3796 if ((entry->Hash() == hash) &&
3797 name->Equals(entry) &&
3798 !is_null_descriptor(number)) {
3799 return number;
3800 }
3801 }
3802 return kNotFound;
3803}
3804
3805
3806#ifdef DEBUG
3807bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
3808 if (IsEmpty()) return other->IsEmpty();
3809 if (other->IsEmpty()) return false;
3810 if (length() != other->length()) return false;
3811 for (int i = 0; i < length(); ++i) {
3812 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
3813 }
3814 return GetContentArray()->IsEqualTo(other->GetContentArray());
3815}
3816#endif
3817
3818
3819static StaticResource<StringInputBuffer> string_input_buffer;
3820
3821
3822bool String::LooksValid() {
3823 if (!Heap::Contains(this)) return false;
3824 return true;
3825}
3826
3827
3828int String::Utf8Length() {
3829 if (IsAsciiRepresentation()) return length();
3830 // Attempt to flatten before accessing the string. It probably
3831 // doesn't make Utf8Length faster, but it is very likely that
3832 // the string will be accessed later (for example by WriteUtf8)
3833 // so it's still a good idea.
Steve Block6ded16b2010-05-10 14:33:55 +01003834 TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00003835 Access<StringInputBuffer> buffer(&string_input_buffer);
3836 buffer->Reset(0, this);
3837 int result = 0;
3838 while (buffer->has_more())
3839 result += unibrow::Utf8::Length(buffer->GetNext());
3840 return result;
3841}
3842
3843
3844Vector<const char> String::ToAsciiVector() {
3845 ASSERT(IsAsciiRepresentation());
3846 ASSERT(IsFlat());
3847
3848 int offset = 0;
3849 int length = this->length();
3850 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3851 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00003852 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003853 ConsString* cons = ConsString::cast(string);
3854 ASSERT(cons->second()->length() == 0);
3855 string = cons->first();
3856 string_tag = StringShape(string).representation_tag();
3857 }
3858 if (string_tag == kSeqStringTag) {
3859 SeqAsciiString* seq = SeqAsciiString::cast(string);
3860 char* start = seq->GetChars();
3861 return Vector<const char>(start + offset, length);
3862 }
3863 ASSERT(string_tag == kExternalStringTag);
3864 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
3865 const char* start = ext->resource()->data();
3866 return Vector<const char>(start + offset, length);
3867}
3868
3869
3870Vector<const uc16> String::ToUC16Vector() {
3871 ASSERT(IsTwoByteRepresentation());
3872 ASSERT(IsFlat());
3873
3874 int offset = 0;
3875 int length = this->length();
3876 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3877 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00003878 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003879 ConsString* cons = ConsString::cast(string);
3880 ASSERT(cons->second()->length() == 0);
3881 string = cons->first();
3882 string_tag = StringShape(string).representation_tag();
3883 }
3884 if (string_tag == kSeqStringTag) {
3885 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
3886 return Vector<const uc16>(seq->GetChars() + offset, length);
3887 }
3888 ASSERT(string_tag == kExternalStringTag);
3889 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
3890 const uc16* start =
3891 reinterpret_cast<const uc16*>(ext->resource()->data());
3892 return Vector<const uc16>(start + offset, length);
3893}
3894
3895
3896SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3897 RobustnessFlag robust_flag,
3898 int offset,
3899 int length,
3900 int* length_return) {
3901 ASSERT(NativeAllocationChecker::allocation_allowed());
3902 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3903 return SmartPointer<char>(NULL);
3904 }
3905
3906 // Negative length means the to the end of the string.
3907 if (length < 0) length = kMaxInt - offset;
3908
3909 // Compute the size of the UTF-8 string. Start at the specified offset.
3910 Access<StringInputBuffer> buffer(&string_input_buffer);
3911 buffer->Reset(offset, this);
3912 int character_position = offset;
3913 int utf8_bytes = 0;
3914 while (buffer->has_more()) {
3915 uint16_t character = buffer->GetNext();
3916 if (character_position < offset + length) {
3917 utf8_bytes += unibrow::Utf8::Length(character);
3918 }
3919 character_position++;
3920 }
3921
3922 if (length_return) {
3923 *length_return = utf8_bytes;
3924 }
3925
3926 char* result = NewArray<char>(utf8_bytes + 1);
3927
3928 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
3929 buffer->Rewind();
3930 buffer->Seek(offset);
3931 character_position = offset;
3932 int utf8_byte_position = 0;
3933 while (buffer->has_more()) {
3934 uint16_t character = buffer->GetNext();
3935 if (character_position < offset + length) {
3936 if (allow_nulls == DISALLOW_NULLS && character == 0) {
3937 character = ' ';
3938 }
3939 utf8_byte_position +=
3940 unibrow::Utf8::Encode(result + utf8_byte_position, character);
3941 }
3942 character_position++;
3943 }
3944 result[utf8_byte_position] = 0;
3945 return SmartPointer<char>(result);
3946}
3947
3948
3949SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3950 RobustnessFlag robust_flag,
3951 int* length_return) {
3952 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
3953}
3954
3955
3956const uc16* String::GetTwoByteData() {
3957 return GetTwoByteData(0);
3958}
3959
3960
3961const uc16* String::GetTwoByteData(unsigned start) {
3962 ASSERT(!IsAsciiRepresentation());
3963 switch (StringShape(this).representation_tag()) {
3964 case kSeqStringTag:
3965 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
3966 case kExternalStringTag:
3967 return ExternalTwoByteString::cast(this)->
3968 ExternalTwoByteStringGetData(start);
Steve Blocka7e24c12009-10-30 11:49:00 +00003969 case kConsStringTag:
3970 UNREACHABLE();
3971 return NULL;
3972 }
3973 UNREACHABLE();
3974 return NULL;
3975}
3976
3977
3978SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
3979 ASSERT(NativeAllocationChecker::allocation_allowed());
3980
3981 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3982 return SmartPointer<uc16>();
3983 }
3984
3985 Access<StringInputBuffer> buffer(&string_input_buffer);
3986 buffer->Reset(this);
3987
3988 uc16* result = NewArray<uc16>(length() + 1);
3989
3990 int i = 0;
3991 while (buffer->has_more()) {
3992 uint16_t character = buffer->GetNext();
3993 result[i++] = character;
3994 }
3995 result[i] = 0;
3996 return SmartPointer<uc16>(result);
3997}
3998
3999
4000const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
4001 return reinterpret_cast<uc16*>(
4002 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
4003}
4004
4005
4006void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4007 unsigned* offset_ptr,
4008 unsigned max_chars) {
4009 unsigned chars_read = 0;
4010 unsigned offset = *offset_ptr;
4011 while (chars_read < max_chars) {
4012 uint16_t c = *reinterpret_cast<uint16_t*>(
4013 reinterpret_cast<char*>(this) -
4014 kHeapObjectTag + kHeaderSize + offset * kShortSize);
4015 if (c <= kMaxAsciiCharCode) {
4016 // Fast case for ASCII characters. Cursor is an input output argument.
4017 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4018 rbb->util_buffer,
4019 rbb->capacity,
4020 rbb->cursor)) {
4021 break;
4022 }
4023 } else {
4024 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4025 rbb->util_buffer,
4026 rbb->capacity,
4027 rbb->cursor)) {
4028 break;
4029 }
4030 }
4031 offset++;
4032 chars_read++;
4033 }
4034 *offset_ptr = offset;
4035 rbb->remaining += chars_read;
4036}
4037
4038
4039const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
4040 unsigned* remaining,
4041 unsigned* offset_ptr,
4042 unsigned max_chars) {
4043 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
4044 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
4045 *remaining = max_chars;
4046 *offset_ptr += max_chars;
4047 return b;
4048}
4049
4050
4051// This will iterate unless the block of string data spans two 'halves' of
4052// a ConsString, in which case it will recurse. Since the block of string
4053// data to be read has a maximum size this limits the maximum recursion
4054// depth to something sane. Since C++ does not have tail call recursion
4055// elimination, the iteration must be explicit. Since this is not an
4056// -IntoBuffer method it can delegate to one of the efficient
4057// *AsciiStringReadBlock routines.
4058const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
4059 unsigned* offset_ptr,
4060 unsigned max_chars) {
4061 ConsString* current = this;
4062 unsigned offset = *offset_ptr;
4063 int offset_correction = 0;
4064
4065 while (true) {
4066 String* left = current->first();
4067 unsigned left_length = (unsigned)left->length();
4068 if (left_length > offset &&
4069 (max_chars <= left_length - offset ||
4070 (rbb->capacity <= left_length - offset &&
4071 (max_chars = left_length - offset, true)))) { // comma operator!
4072 // Left hand side only - iterate unless we have reached the bottom of
4073 // the cons tree. The assignment on the left of the comma operator is
4074 // in order to make use of the fact that the -IntoBuffer routines can
4075 // produce at most 'capacity' characters. This enables us to postpone
4076 // the point where we switch to the -IntoBuffer routines (below) in order
4077 // to maximize the chances of delegating a big chunk of work to the
4078 // efficient *AsciiStringReadBlock routines.
4079 if (StringShape(left).IsCons()) {
4080 current = ConsString::cast(left);
4081 continue;
4082 } else {
4083 const unibrow::byte* answer =
4084 String::ReadBlock(left, rbb, &offset, max_chars);
4085 *offset_ptr = offset + offset_correction;
4086 return answer;
4087 }
4088 } else if (left_length <= offset) {
4089 // Right hand side only - iterate unless we have reached the bottom of
4090 // the cons tree.
4091 String* right = current->second();
4092 offset -= left_length;
4093 offset_correction += left_length;
4094 if (StringShape(right).IsCons()) {
4095 current = ConsString::cast(right);
4096 continue;
4097 } else {
4098 const unibrow::byte* answer =
4099 String::ReadBlock(right, rbb, &offset, max_chars);
4100 *offset_ptr = offset + offset_correction;
4101 return answer;
4102 }
4103 } else {
4104 // The block to be read spans two sides of the ConsString, so we call the
4105 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
4106 // are able to assemble data from several part strings because they use
4107 // the util_buffer to store their data and never return direct pointers
4108 // to their storage. We don't try to read more than the buffer capacity
4109 // here or we can get too much recursion.
4110 ASSERT(rbb->remaining == 0);
4111 ASSERT(rbb->cursor == 0);
4112 current->ConsStringReadBlockIntoBuffer(
4113 rbb,
4114 &offset,
4115 max_chars > rbb->capacity ? rbb->capacity : max_chars);
4116 *offset_ptr = offset + offset_correction;
4117 return rbb->util_buffer;
4118 }
4119 }
4120}
4121
4122
Steve Blocka7e24c12009-10-30 11:49:00 +00004123uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
4124 ASSERT(index >= 0 && index < length());
4125 return resource()->data()[index];
4126}
4127
4128
4129const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
4130 unsigned* remaining,
4131 unsigned* offset_ptr,
4132 unsigned max_chars) {
4133 // Cast const char* to unibrow::byte* (signedness difference).
4134 const unibrow::byte* b =
4135 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
4136 *remaining = max_chars;
4137 *offset_ptr += max_chars;
4138 return b;
4139}
4140
4141
4142const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
4143 unsigned start) {
4144 return resource()->data() + start;
4145}
4146
4147
4148uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
4149 ASSERT(index >= 0 && index < length());
4150 return resource()->data()[index];
4151}
4152
4153
4154void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
4155 ReadBlockBuffer* rbb,
4156 unsigned* offset_ptr,
4157 unsigned max_chars) {
4158 unsigned chars_read = 0;
4159 unsigned offset = *offset_ptr;
4160 const uint16_t* data = resource()->data();
4161 while (chars_read < max_chars) {
4162 uint16_t c = data[offset];
4163 if (c <= kMaxAsciiCharCode) {
4164 // Fast case for ASCII characters. Cursor is an input output argument.
4165 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4166 rbb->util_buffer,
4167 rbb->capacity,
4168 rbb->cursor))
4169 break;
4170 } else {
4171 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4172 rbb->util_buffer,
4173 rbb->capacity,
4174 rbb->cursor))
4175 break;
4176 }
4177 offset++;
4178 chars_read++;
4179 }
4180 *offset_ptr = offset;
4181 rbb->remaining += chars_read;
4182}
4183
4184
4185void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4186 unsigned* offset_ptr,
4187 unsigned max_chars) {
4188 unsigned capacity = rbb->capacity - rbb->cursor;
4189 if (max_chars > capacity) max_chars = capacity;
4190 memcpy(rbb->util_buffer + rbb->cursor,
4191 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
4192 *offset_ptr * kCharSize,
4193 max_chars);
4194 rbb->remaining += max_chars;
4195 *offset_ptr += max_chars;
4196 rbb->cursor += max_chars;
4197}
4198
4199
4200void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
4201 ReadBlockBuffer* rbb,
4202 unsigned* offset_ptr,
4203 unsigned max_chars) {
4204 unsigned capacity = rbb->capacity - rbb->cursor;
4205 if (max_chars > capacity) max_chars = capacity;
4206 memcpy(rbb->util_buffer + rbb->cursor,
4207 resource()->data() + *offset_ptr,
4208 max_chars);
4209 rbb->remaining += max_chars;
4210 *offset_ptr += max_chars;
4211 rbb->cursor += max_chars;
4212}
4213
4214
4215// This method determines the type of string involved and then copies
4216// a whole chunk of characters into a buffer, or returns a pointer to a buffer
4217// where they can be found. The pointer is not necessarily valid across a GC
4218// (see AsciiStringReadBlock).
4219const unibrow::byte* String::ReadBlock(String* input,
4220 ReadBlockBuffer* rbb,
4221 unsigned* offset_ptr,
4222 unsigned max_chars) {
4223 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
4224 if (max_chars == 0) {
4225 rbb->remaining = 0;
4226 return NULL;
4227 }
4228 switch (StringShape(input).representation_tag()) {
4229 case kSeqStringTag:
4230 if (input->IsAsciiRepresentation()) {
4231 SeqAsciiString* str = SeqAsciiString::cast(input);
4232 return str->SeqAsciiStringReadBlock(&rbb->remaining,
4233 offset_ptr,
4234 max_chars);
4235 } else {
4236 SeqTwoByteString* str = SeqTwoByteString::cast(input);
4237 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4238 offset_ptr,
4239 max_chars);
4240 return rbb->util_buffer;
4241 }
4242 case kConsStringTag:
4243 return ConsString::cast(input)->ConsStringReadBlock(rbb,
4244 offset_ptr,
4245 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004246 case kExternalStringTag:
4247 if (input->IsAsciiRepresentation()) {
4248 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
4249 &rbb->remaining,
4250 offset_ptr,
4251 max_chars);
4252 } else {
4253 ExternalTwoByteString::cast(input)->
4254 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4255 offset_ptr,
4256 max_chars);
4257 return rbb->util_buffer;
4258 }
4259 default:
4260 break;
4261 }
4262
4263 UNREACHABLE();
4264 return 0;
4265}
4266
4267
4268Relocatable* Relocatable::top_ = NULL;
4269
4270
4271void Relocatable::PostGarbageCollectionProcessing() {
4272 Relocatable* current = top_;
4273 while (current != NULL) {
4274 current->PostGarbageCollection();
4275 current = current->prev_;
4276 }
4277}
4278
4279
4280// Reserve space for statics needing saving and restoring.
4281int Relocatable::ArchiveSpacePerThread() {
4282 return sizeof(top_);
4283}
4284
4285
4286// Archive statics that are thread local.
4287char* Relocatable::ArchiveState(char* to) {
4288 *reinterpret_cast<Relocatable**>(to) = top_;
4289 top_ = NULL;
4290 return to + ArchiveSpacePerThread();
4291}
4292
4293
4294// Restore statics that are thread local.
4295char* Relocatable::RestoreState(char* from) {
4296 top_ = *reinterpret_cast<Relocatable**>(from);
4297 return from + ArchiveSpacePerThread();
4298}
4299
4300
4301char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
4302 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
4303 Iterate(v, top);
4304 return thread_storage + ArchiveSpacePerThread();
4305}
4306
4307
4308void Relocatable::Iterate(ObjectVisitor* v) {
4309 Iterate(v, top_);
4310}
4311
4312
4313void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
4314 Relocatable* current = top;
4315 while (current != NULL) {
4316 current->IterateInstance(v);
4317 current = current->prev_;
4318 }
4319}
4320
4321
4322FlatStringReader::FlatStringReader(Handle<String> str)
4323 : str_(str.location()),
4324 length_(str->length()) {
4325 PostGarbageCollection();
4326}
4327
4328
4329FlatStringReader::FlatStringReader(Vector<const char> input)
4330 : str_(0),
4331 is_ascii_(true),
4332 length_(input.length()),
4333 start_(input.start()) { }
4334
4335
4336void FlatStringReader::PostGarbageCollection() {
4337 if (str_ == NULL) return;
4338 Handle<String> str(str_);
4339 ASSERT(str->IsFlat());
4340 is_ascii_ = str->IsAsciiRepresentation();
4341 if (is_ascii_) {
4342 start_ = str->ToAsciiVector().start();
4343 } else {
4344 start_ = str->ToUC16Vector().start();
4345 }
4346}
4347
4348
4349void StringInputBuffer::Seek(unsigned pos) {
4350 Reset(pos, input_);
4351}
4352
4353
4354void SafeStringInputBuffer::Seek(unsigned pos) {
4355 Reset(pos, input_);
4356}
4357
4358
4359// This method determines the type of string involved and then copies
4360// a whole chunk of characters into a buffer. It can be used with strings
4361// that have been glued together to form a ConsString and which must cooperate
4362// to fill up a buffer.
4363void String::ReadBlockIntoBuffer(String* input,
4364 ReadBlockBuffer* rbb,
4365 unsigned* offset_ptr,
4366 unsigned max_chars) {
4367 ASSERT(*offset_ptr <= (unsigned)input->length());
4368 if (max_chars == 0) return;
4369
4370 switch (StringShape(input).representation_tag()) {
4371 case kSeqStringTag:
4372 if (input->IsAsciiRepresentation()) {
4373 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4374 offset_ptr,
4375 max_chars);
4376 return;
4377 } else {
4378 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4379 offset_ptr,
4380 max_chars);
4381 return;
4382 }
4383 case kConsStringTag:
4384 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4385 offset_ptr,
4386 max_chars);
4387 return;
Steve Blocka7e24c12009-10-30 11:49:00 +00004388 case kExternalStringTag:
4389 if (input->IsAsciiRepresentation()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004390 ExternalAsciiString::cast(input)->
4391 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4392 } else {
4393 ExternalTwoByteString::cast(input)->
4394 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4395 offset_ptr,
4396 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004397 }
4398 return;
4399 default:
4400 break;
4401 }
4402
4403 UNREACHABLE();
4404 return;
4405}
4406
4407
4408const unibrow::byte* String::ReadBlock(String* input,
4409 unibrow::byte* util_buffer,
4410 unsigned capacity,
4411 unsigned* remaining,
4412 unsigned* offset_ptr) {
4413 ASSERT(*offset_ptr <= (unsigned)input->length());
4414 unsigned chars = input->length() - *offset_ptr;
4415 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4416 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4417 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4418 *remaining = rbb.remaining;
4419 return answer;
4420}
4421
4422
4423const unibrow::byte* String::ReadBlock(String** raw_input,
4424 unibrow::byte* util_buffer,
4425 unsigned capacity,
4426 unsigned* remaining,
4427 unsigned* offset_ptr) {
4428 Handle<String> input(raw_input);
4429 ASSERT(*offset_ptr <= (unsigned)input->length());
4430 unsigned chars = input->length() - *offset_ptr;
4431 if (chars > capacity) chars = capacity;
4432 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4433 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4434 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4435 *remaining = rbb.remaining;
4436 return rbb.util_buffer;
4437}
4438
4439
4440// This will iterate unless the block of string data spans two 'halves' of
4441// a ConsString, in which case it will recurse. Since the block of string
4442// data to be read has a maximum size this limits the maximum recursion
4443// depth to something sane. Since C++ does not have tail call recursion
4444// elimination, the iteration must be explicit.
4445void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4446 unsigned* offset_ptr,
4447 unsigned max_chars) {
4448 ConsString* current = this;
4449 unsigned offset = *offset_ptr;
4450 int offset_correction = 0;
4451
4452 while (true) {
4453 String* left = current->first();
4454 unsigned left_length = (unsigned)left->length();
4455 if (left_length > offset &&
4456 max_chars <= left_length - offset) {
4457 // Left hand side only - iterate unless we have reached the bottom of
4458 // the cons tree.
4459 if (StringShape(left).IsCons()) {
4460 current = ConsString::cast(left);
4461 continue;
4462 } else {
4463 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4464 *offset_ptr = offset + offset_correction;
4465 return;
4466 }
4467 } else if (left_length <= offset) {
4468 // Right hand side only - iterate unless we have reached the bottom of
4469 // the cons tree.
4470 offset -= left_length;
4471 offset_correction += left_length;
4472 String* right = current->second();
4473 if (StringShape(right).IsCons()) {
4474 current = ConsString::cast(right);
4475 continue;
4476 } else {
4477 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4478 *offset_ptr = offset + offset_correction;
4479 return;
4480 }
4481 } else {
4482 // The block to be read spans two sides of the ConsString, so we recurse.
4483 // First recurse on the left.
4484 max_chars -= left_length - offset;
4485 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4486 // We may have reached the max or there may not have been enough space
4487 // in the buffer for the characters in the left hand side.
4488 if (offset == left_length) {
4489 // Recurse on the right.
4490 String* right = String::cast(current->second());
4491 offset -= left_length;
4492 offset_correction += left_length;
4493 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4494 }
4495 *offset_ptr = offset + offset_correction;
4496 return;
4497 }
4498 }
4499}
4500
4501
Steve Blocka7e24c12009-10-30 11:49:00 +00004502void ConsString::ConsStringIterateBody(ObjectVisitor* v) {
4503 IteratePointers(v, kFirstOffset, kSecondOffset + kPointerSize);
4504}
4505
4506
4507void JSGlobalPropertyCell::JSGlobalPropertyCellIterateBody(ObjectVisitor* v) {
4508 IteratePointers(v, kValueOffset, kValueOffset + kPointerSize);
4509}
4510
4511
4512uint16_t ConsString::ConsStringGet(int index) {
4513 ASSERT(index >= 0 && index < this->length());
4514
4515 // Check for a flattened cons string
4516 if (second()->length() == 0) {
4517 String* left = first();
4518 return left->Get(index);
4519 }
4520
4521 String* string = String::cast(this);
4522
4523 while (true) {
4524 if (StringShape(string).IsCons()) {
4525 ConsString* cons_string = ConsString::cast(string);
4526 String* left = cons_string->first();
4527 if (left->length() > index) {
4528 string = left;
4529 } else {
4530 index -= left->length();
4531 string = cons_string->second();
4532 }
4533 } else {
4534 return string->Get(index);
4535 }
4536 }
4537
4538 UNREACHABLE();
4539 return 0;
4540}
4541
4542
4543template <typename sinkchar>
4544void String::WriteToFlat(String* src,
4545 sinkchar* sink,
4546 int f,
4547 int t) {
4548 String* source = src;
4549 int from = f;
4550 int to = t;
4551 while (true) {
4552 ASSERT(0 <= from && from <= to && to <= source->length());
4553 switch (StringShape(source).full_representation_tag()) {
4554 case kAsciiStringTag | kExternalStringTag: {
4555 CopyChars(sink,
4556 ExternalAsciiString::cast(source)->resource()->data() + from,
4557 to - from);
4558 return;
4559 }
4560 case kTwoByteStringTag | kExternalStringTag: {
4561 const uc16* data =
4562 ExternalTwoByteString::cast(source)->resource()->data();
4563 CopyChars(sink,
4564 data + from,
4565 to - from);
4566 return;
4567 }
4568 case kAsciiStringTag | kSeqStringTag: {
4569 CopyChars(sink,
4570 SeqAsciiString::cast(source)->GetChars() + from,
4571 to - from);
4572 return;
4573 }
4574 case kTwoByteStringTag | kSeqStringTag: {
4575 CopyChars(sink,
4576 SeqTwoByteString::cast(source)->GetChars() + from,
4577 to - from);
4578 return;
4579 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004580 case kAsciiStringTag | kConsStringTag:
4581 case kTwoByteStringTag | kConsStringTag: {
4582 ConsString* cons_string = ConsString::cast(source);
4583 String* first = cons_string->first();
4584 int boundary = first->length();
4585 if (to - boundary >= boundary - from) {
4586 // Right hand side is longer. Recurse over left.
4587 if (from < boundary) {
4588 WriteToFlat(first, sink, from, boundary);
4589 sink += boundary - from;
4590 from = 0;
4591 } else {
4592 from -= boundary;
4593 }
4594 to -= boundary;
4595 source = cons_string->second();
4596 } else {
4597 // Left hand side is longer. Recurse over right.
4598 if (to > boundary) {
4599 String* second = cons_string->second();
4600 WriteToFlat(second,
4601 sink + boundary - from,
4602 0,
4603 to - boundary);
4604 to = boundary;
4605 }
4606 source = first;
4607 }
4608 break;
4609 }
4610 }
4611 }
4612}
4613
4614
Steve Blockd0582a62009-12-15 09:54:21 +00004615#define FIELD_ADDR(p, offset) \
4616 (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
4617
4618void ExternalAsciiString::ExternalAsciiStringIterateBody(ObjectVisitor* v) {
4619 typedef v8::String::ExternalAsciiStringResource Resource;
4620 v->VisitExternalAsciiString(
4621 reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
Steve Blocka7e24c12009-10-30 11:49:00 +00004622}
4623
4624
Steve Blockd0582a62009-12-15 09:54:21 +00004625void ExternalTwoByteString::ExternalTwoByteStringIterateBody(ObjectVisitor* v) {
4626 typedef v8::String::ExternalStringResource Resource;
4627 v->VisitExternalTwoByteString(
4628 reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
Steve Blocka7e24c12009-10-30 11:49:00 +00004629}
4630
Steve Blockd0582a62009-12-15 09:54:21 +00004631#undef FIELD_ADDR
Steve Blocka7e24c12009-10-30 11:49:00 +00004632
4633template <typename IteratorA, typename IteratorB>
4634static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4635 // General slow case check. We know that the ia and ib iterators
4636 // have the same length.
4637 while (ia->has_more()) {
4638 uc32 ca = ia->GetNext();
4639 uc32 cb = ib->GetNext();
4640 if (ca != cb)
4641 return false;
4642 }
4643 return true;
4644}
4645
4646
4647// Compares the contents of two strings by reading and comparing
4648// int-sized blocks of characters.
4649template <typename Char>
4650static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4651 int length = a.length();
4652 ASSERT_EQ(length, b.length());
4653 const Char* pa = a.start();
4654 const Char* pb = b.start();
4655 int i = 0;
4656#ifndef V8_HOST_CAN_READ_UNALIGNED
4657 // If this architecture isn't comfortable reading unaligned ints
4658 // then we have to check that the strings are aligned before
4659 // comparing them blockwise.
4660 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4661 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4662 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
4663 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
4664#endif
4665 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
4666 int endpoint = length - kStepSize;
4667 // Compare blocks until we reach near the end of the string.
4668 for (; i <= endpoint; i += kStepSize) {
4669 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
4670 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
4671 if (wa != wb) {
4672 return false;
4673 }
4674 }
4675#ifndef V8_HOST_CAN_READ_UNALIGNED
4676 }
4677#endif
4678 // Compare the remaining characters that didn't fit into a block.
4679 for (; i < length; i++) {
4680 if (a[i] != b[i]) {
4681 return false;
4682 }
4683 }
4684 return true;
4685}
4686
4687
4688static StringInputBuffer string_compare_buffer_b;
4689
4690
4691template <typename IteratorA>
4692static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
4693 if (b->IsFlat()) {
4694 if (b->IsAsciiRepresentation()) {
4695 VectorIterator<char> ib(b->ToAsciiVector());
4696 return CompareStringContents(ia, &ib);
4697 } else {
4698 VectorIterator<uc16> ib(b->ToUC16Vector());
4699 return CompareStringContents(ia, &ib);
4700 }
4701 } else {
4702 string_compare_buffer_b.Reset(0, b);
4703 return CompareStringContents(ia, &string_compare_buffer_b);
4704 }
4705}
4706
4707
4708static StringInputBuffer string_compare_buffer_a;
4709
4710
4711bool String::SlowEquals(String* other) {
4712 // Fast check: negative check with lengths.
4713 int len = length();
4714 if (len != other->length()) return false;
4715 if (len == 0) return true;
4716
4717 // Fast check: if hash code is computed for both strings
4718 // a fast negative check can be performed.
4719 if (HasHashCode() && other->HasHashCode()) {
4720 if (Hash() != other->Hash()) return false;
4721 }
4722
Leon Clarkef7060e22010-06-03 12:02:55 +01004723 // We know the strings are both non-empty. Compare the first chars
4724 // before we try to flatten the strings.
4725 if (this->Get(0) != other->Get(0)) return false;
4726
4727 String* lhs = this->TryFlattenGetString();
4728 String* rhs = other->TryFlattenGetString();
4729
4730 if (StringShape(lhs).IsSequentialAscii() &&
4731 StringShape(rhs).IsSequentialAscii()) {
4732 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
4733 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
Steve Blocka7e24c12009-10-30 11:49:00 +00004734 return CompareRawStringContents(Vector<const char>(str1, len),
4735 Vector<const char>(str2, len));
4736 }
4737
Leon Clarkef7060e22010-06-03 12:02:55 +01004738 if (lhs->IsFlat()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004739 if (IsAsciiRepresentation()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01004740 Vector<const char> vec1 = lhs->ToAsciiVector();
4741 if (rhs->IsFlat()) {
4742 if (rhs->IsAsciiRepresentation()) {
4743 Vector<const char> vec2 = rhs->ToAsciiVector();
Steve Blocka7e24c12009-10-30 11:49:00 +00004744 return CompareRawStringContents(vec1, vec2);
4745 } else {
4746 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004747 VectorIterator<uc16> ib(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004748 return CompareStringContents(&buf1, &ib);
4749 }
4750 } else {
4751 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004752 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004753 return CompareStringContents(&buf1, &string_compare_buffer_b);
4754 }
4755 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004756 Vector<const uc16> vec1 = lhs->ToUC16Vector();
4757 if (rhs->IsFlat()) {
4758 if (rhs->IsAsciiRepresentation()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004759 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004760 VectorIterator<char> ib(rhs->ToAsciiVector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004761 return CompareStringContents(&buf1, &ib);
4762 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004763 Vector<const uc16> vec2(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004764 return CompareRawStringContents(vec1, vec2);
4765 }
4766 } else {
4767 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004768 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004769 return CompareStringContents(&buf1, &string_compare_buffer_b);
4770 }
4771 }
4772 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004773 string_compare_buffer_a.Reset(0, lhs);
4774 return CompareStringContentsPartial(&string_compare_buffer_a, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004775 }
4776}
4777
4778
4779bool String::MarkAsUndetectable() {
4780 if (StringShape(this).IsSymbol()) return false;
4781
4782 Map* map = this->map();
Steve Blockd0582a62009-12-15 09:54:21 +00004783 if (map == Heap::string_map()) {
4784 this->set_map(Heap::undetectable_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00004785 return true;
Steve Blockd0582a62009-12-15 09:54:21 +00004786 } else if (map == Heap::ascii_string_map()) {
4787 this->set_map(Heap::undetectable_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00004788 return true;
4789 }
4790 // Rest cannot be marked as undetectable
4791 return false;
4792}
4793
4794
4795bool String::IsEqualTo(Vector<const char> str) {
4796 int slen = length();
4797 Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
4798 decoder->Reset(str.start(), str.length());
4799 int i;
4800 for (i = 0; i < slen && decoder->has_more(); i++) {
4801 uc32 r = decoder->GetNext();
4802 if (Get(i) != r) return false;
4803 }
4804 return i == slen && !decoder->has_more();
4805}
4806
4807
Steve Block6ded16b2010-05-10 14:33:55 +01004808template <typename schar>
4809static inline uint32_t HashSequentialString(const schar* chars, int length) {
4810 StringHasher hasher(length);
4811 if (!hasher.has_trivial_hash()) {
4812 int i;
4813 for (i = 0; hasher.is_array_index() && (i < length); i++) {
4814 hasher.AddCharacter(chars[i]);
4815 }
4816 for (; i < length; i++) {
4817 hasher.AddCharacterNoIndex(chars[i]);
4818 }
4819 }
4820 return hasher.GetHashField();
4821}
4822
4823
Steve Blocka7e24c12009-10-30 11:49:00 +00004824uint32_t String::ComputeAndSetHash() {
4825 // Should only be called if hash code has not yet been computed.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004826 ASSERT(!HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00004827
Steve Block6ded16b2010-05-10 14:33:55 +01004828 const int len = length();
4829
Steve Blocka7e24c12009-10-30 11:49:00 +00004830 // Compute the hash code.
Steve Block6ded16b2010-05-10 14:33:55 +01004831 uint32_t field = 0;
4832 if (StringShape(this).IsSequentialAscii()) {
4833 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
4834 } else if (StringShape(this).IsSequentialTwoByte()) {
4835 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
4836 } else {
4837 StringInputBuffer buffer(this);
4838 field = ComputeHashField(&buffer, len);
4839 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004840
4841 // Store the hash code in the object.
Steve Blockd0582a62009-12-15 09:54:21 +00004842 set_hash_field(field);
Steve Blocka7e24c12009-10-30 11:49:00 +00004843
4844 // Check the hash code is there.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004845 ASSERT(HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00004846 uint32_t result = field >> kHashShift;
4847 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
4848 return result;
4849}
4850
4851
4852bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
4853 uint32_t* index,
4854 int length) {
4855 if (length == 0 || length > kMaxArrayIndexSize) return false;
4856 uc32 ch = buffer->GetNext();
4857
4858 // If the string begins with a '0' character, it must only consist
4859 // of it to be a legal array index.
4860 if (ch == '0') {
4861 *index = 0;
4862 return length == 1;
4863 }
4864
4865 // Convert string to uint32 array index; character by character.
4866 int d = ch - '0';
4867 if (d < 0 || d > 9) return false;
4868 uint32_t result = d;
4869 while (buffer->has_more()) {
4870 d = buffer->GetNext() - '0';
4871 if (d < 0 || d > 9) return false;
4872 // Check that the new result is below the 32 bit limit.
4873 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
4874 result = (result * 10) + d;
4875 }
4876
4877 *index = result;
4878 return true;
4879}
4880
4881
4882bool String::SlowAsArrayIndex(uint32_t* index) {
4883 if (length() <= kMaxCachedArrayIndexLength) {
4884 Hash(); // force computation of hash code
Steve Blockd0582a62009-12-15 09:54:21 +00004885 uint32_t field = hash_field();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004886 if ((field & kIsNotArrayIndexMask) != 0) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00004887 // Isolate the array index form the full hash field.
4888 *index = (kArrayIndexHashMask & field) >> kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00004889 return true;
4890 } else {
4891 StringInputBuffer buffer(this);
4892 return ComputeArrayIndex(&buffer, index, length());
4893 }
4894}
4895
4896
Steve Blockd0582a62009-12-15 09:54:21 +00004897static inline uint32_t HashField(uint32_t hash,
4898 bool is_array_index,
4899 int length = -1) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004900 uint32_t result = (hash << String::kHashShift);
Steve Blockd0582a62009-12-15 09:54:21 +00004901 if (is_array_index) {
4902 // For array indexes mix the length into the hash as an array index could
4903 // be zero.
4904 ASSERT(length > 0);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004905 ASSERT(length <= String::kMaxArrayIndexSize);
Steve Blockd0582a62009-12-15 09:54:21 +00004906 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
4907 (1 << String::kArrayIndexValueBits));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004908 ASSERT(String::kMaxArrayIndexSize < (1 << String::kArrayIndexValueBits));
4909 result &= ~String::kIsNotArrayIndexMask;
Steve Blockd0582a62009-12-15 09:54:21 +00004910 result |= length << String::kArrayIndexHashLengthShift;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004911 } else {
4912 result |= String::kIsNotArrayIndexMask;
Steve Blockd0582a62009-12-15 09:54:21 +00004913 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004914 return result;
4915}
4916
4917
4918uint32_t StringHasher::GetHashField() {
4919 ASSERT(is_valid());
Steve Blockd0582a62009-12-15 09:54:21 +00004920 if (length_ <= String::kMaxHashCalcLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004921 if (is_array_index()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004922 return v8::internal::HashField(array_index(), true, length_);
Steve Blocka7e24c12009-10-30 11:49:00 +00004923 } else {
Steve Blockd0582a62009-12-15 09:54:21 +00004924 return v8::internal::HashField(GetHash(), false);
Steve Blocka7e24c12009-10-30 11:49:00 +00004925 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004926 uint32_t payload = v8::internal::HashField(GetHash(), false);
Steve Blockd0582a62009-12-15 09:54:21 +00004927 return payload;
Steve Blocka7e24c12009-10-30 11:49:00 +00004928 } else {
4929 return v8::internal::HashField(length_, false);
4930 }
4931}
4932
4933
Steve Blockd0582a62009-12-15 09:54:21 +00004934uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
4935 int length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004936 StringHasher hasher(length);
4937
4938 // Very long strings have a trivial hash that doesn't inspect the
4939 // string contents.
4940 if (hasher.has_trivial_hash()) {
4941 return hasher.GetHashField();
4942 }
4943
4944 // Do the iterative array index computation as long as there is a
4945 // chance this is an array index.
4946 while (buffer->has_more() && hasher.is_array_index()) {
4947 hasher.AddCharacter(buffer->GetNext());
4948 }
4949
4950 // Process the remaining characters without updating the array
4951 // index.
4952 while (buffer->has_more()) {
4953 hasher.AddCharacterNoIndex(buffer->GetNext());
4954 }
4955
4956 return hasher.GetHashField();
4957}
4958
4959
Steve Block6ded16b2010-05-10 14:33:55 +01004960Object* String::SubString(int start, int end, PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004961 if (start == 0 && end == length()) return this;
Steve Block6ded16b2010-05-10 14:33:55 +01004962 Object* result = Heap::AllocateSubString(this, start, end, pretenure);
Steve Blockd0582a62009-12-15 09:54:21 +00004963 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00004964}
4965
4966
4967void String::PrintOn(FILE* file) {
4968 int length = this->length();
4969 for (int i = 0; i < length; i++) {
4970 fprintf(file, "%c", Get(i));
4971 }
4972}
4973
4974
4975void Map::CreateBackPointers() {
4976 DescriptorArray* descriptors = instance_descriptors();
4977 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
4978 if (descriptors->GetType(i) == MAP_TRANSITION) {
4979 // Get target.
4980 Map* target = Map::cast(descriptors->GetValue(i));
4981#ifdef DEBUG
4982 // Verify target.
4983 Object* source_prototype = prototype();
4984 Object* target_prototype = target->prototype();
4985 ASSERT(source_prototype->IsJSObject() ||
4986 source_prototype->IsMap() ||
4987 source_prototype->IsNull());
4988 ASSERT(target_prototype->IsJSObject() ||
4989 target_prototype->IsNull());
4990 ASSERT(source_prototype->IsMap() ||
4991 source_prototype == target_prototype);
4992#endif
4993 // Point target back to source. set_prototype() will not let us set
4994 // the prototype to a map, as we do here.
4995 *RawField(target, kPrototypeOffset) = this;
4996 }
4997 }
4998}
4999
5000
5001void Map::ClearNonLiveTransitions(Object* real_prototype) {
5002 // Live DescriptorArray objects will be marked, so we must use
5003 // low-level accessors to get and modify their data.
5004 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
5005 *RawField(this, Map::kInstanceDescriptorsOffset));
5006 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
5007 Smi* NullDescriptorDetails =
5008 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
5009 FixedArray* contents = reinterpret_cast<FixedArray*>(
5010 d->get(DescriptorArray::kContentArrayIndex));
5011 ASSERT(contents->length() >= 2);
5012 for (int i = 0; i < contents->length(); i += 2) {
5013 // If the pair (value, details) is a map transition,
5014 // check if the target is live. If not, null the descriptor.
5015 // Also drop the back pointer for that map transition, so that this
5016 // map is not reached again by following a back pointer from a
5017 // non-live object.
5018 PropertyDetails details(Smi::cast(contents->get(i + 1)));
5019 if (details.type() == MAP_TRANSITION) {
5020 Map* target = reinterpret_cast<Map*>(contents->get(i));
5021 ASSERT(target->IsHeapObject());
5022 if (!target->IsMarked()) {
5023 ASSERT(target->IsMap());
Leon Clarke4515c472010-02-03 11:58:03 +00005024 contents->set(i + 1, NullDescriptorDetails);
5025 contents->set_null(i);
Steve Blocka7e24c12009-10-30 11:49:00 +00005026 ASSERT(target->prototype() == this ||
5027 target->prototype() == real_prototype);
5028 // Getter prototype() is read-only, set_prototype() has side effects.
5029 *RawField(target, Map::kPrototypeOffset) = real_prototype;
5030 }
5031 }
5032 }
5033}
5034
5035
5036void Map::MapIterateBody(ObjectVisitor* v) {
5037 // Assumes all Object* members are contiguously allocated!
Ben Murdoch3bec4d22010-07-22 14:51:16 +01005038 IteratePointers(v, kPointerFieldsBeginOffset, kPointerFieldsEndOffset);
Steve Blocka7e24c12009-10-30 11:49:00 +00005039}
5040
5041
5042Object* JSFunction::SetInstancePrototype(Object* value) {
5043 ASSERT(value->IsJSObject());
5044
5045 if (has_initial_map()) {
5046 initial_map()->set_prototype(value);
5047 } else {
5048 // Put the value in the initial map field until an initial map is
5049 // needed. At that point, a new initial map is created and the
5050 // prototype is put into the initial map where it belongs.
5051 set_prototype_or_initial_map(value);
5052 }
Kristian Monsen25f61362010-05-21 11:50:48 +01005053 Heap::ClearInstanceofCache();
Steve Blocka7e24c12009-10-30 11:49:00 +00005054 return value;
5055}
5056
5057
5058
5059Object* JSFunction::SetPrototype(Object* value) {
Steve Block6ded16b2010-05-10 14:33:55 +01005060 ASSERT(should_have_prototype());
Steve Blocka7e24c12009-10-30 11:49:00 +00005061 Object* construct_prototype = value;
5062
5063 // If the value is not a JSObject, store the value in the map's
5064 // constructor field so it can be accessed. Also, set the prototype
5065 // used for constructing objects to the original object prototype.
5066 // See ECMA-262 13.2.2.
5067 if (!value->IsJSObject()) {
5068 // Copy the map so this does not affect unrelated functions.
5069 // Remove map transitions because they point to maps with a
5070 // different prototype.
5071 Object* new_map = map()->CopyDropTransitions();
5072 if (new_map->IsFailure()) return new_map;
5073 set_map(Map::cast(new_map));
5074 map()->set_constructor(value);
5075 map()->set_non_instance_prototype(true);
5076 construct_prototype =
5077 Top::context()->global_context()->initial_object_prototype();
5078 } else {
5079 map()->set_non_instance_prototype(false);
5080 }
5081
5082 return SetInstancePrototype(construct_prototype);
5083}
5084
5085
Steve Block6ded16b2010-05-10 14:33:55 +01005086Object* JSFunction::RemovePrototype() {
5087 ASSERT(map() == context()->global_context()->function_map());
5088 set_map(context()->global_context()->function_without_prototype_map());
5089 set_prototype_or_initial_map(Heap::the_hole_value());
5090 return this;
5091}
5092
5093
Steve Blocka7e24c12009-10-30 11:49:00 +00005094Object* JSFunction::SetInstanceClassName(String* name) {
5095 shared()->set_instance_class_name(name);
5096 return this;
5097}
5098
5099
5100Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
5101 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
5102}
5103
5104
5105void Oddball::OddballIterateBody(ObjectVisitor* v) {
5106 // Assumes all Object* members are contiguously allocated!
5107 IteratePointers(v, kToStringOffset, kToNumberOffset + kPointerSize);
5108}
5109
5110
5111Object* Oddball::Initialize(const char* to_string, Object* to_number) {
5112 Object* symbol = Heap::LookupAsciiSymbol(to_string);
5113 if (symbol->IsFailure()) return symbol;
5114 set_to_string(String::cast(symbol));
5115 set_to_number(to_number);
5116 return this;
5117}
5118
5119
5120bool SharedFunctionInfo::HasSourceCode() {
5121 return !script()->IsUndefined() &&
5122 !Script::cast(script())->source()->IsUndefined();
5123}
5124
5125
5126Object* SharedFunctionInfo::GetSourceCode() {
5127 HandleScope scope;
5128 if (script()->IsUndefined()) return Heap::undefined_value();
5129 Object* source = Script::cast(script())->source();
5130 if (source->IsUndefined()) return Heap::undefined_value();
5131 return *SubString(Handle<String>(String::cast(source)),
5132 start_position(), end_position());
5133}
5134
5135
5136int SharedFunctionInfo::CalculateInstanceSize() {
5137 int instance_size =
5138 JSObject::kHeaderSize +
5139 expected_nof_properties() * kPointerSize;
5140 if (instance_size > JSObject::kMaxInstanceSize) {
5141 instance_size = JSObject::kMaxInstanceSize;
5142 }
5143 return instance_size;
5144}
5145
5146
5147int SharedFunctionInfo::CalculateInObjectProperties() {
5148 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
5149}
5150
5151
Andrei Popescu402d9372010-02-26 13:31:12 +00005152bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
5153 // Check the basic conditions for generating inline constructor code.
5154 if (!FLAG_inline_new
5155 || !has_only_simple_this_property_assignments()
5156 || this_property_assignments_count() == 0) {
5157 return false;
5158 }
5159
5160 // If the prototype is null inline constructors cause no problems.
5161 if (!prototype->IsJSObject()) {
5162 ASSERT(prototype->IsNull());
5163 return true;
5164 }
5165
5166 // Traverse the proposed prototype chain looking for setters for properties of
5167 // the same names as are set by the inline constructor.
5168 for (Object* obj = prototype;
5169 obj != Heap::null_value();
5170 obj = obj->GetPrototype()) {
5171 JSObject* js_object = JSObject::cast(obj);
5172 for (int i = 0; i < this_property_assignments_count(); i++) {
5173 LookupResult result;
5174 String* name = GetThisPropertyAssignmentName(i);
5175 js_object->LocalLookupRealNamedProperty(name, &result);
5176 if (result.IsProperty() && result.type() == CALLBACKS) {
5177 return false;
5178 }
5179 }
5180 }
5181
5182 return true;
5183}
5184
5185
Steve Blocka7e24c12009-10-30 11:49:00 +00005186void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +00005187 bool only_simple_this_property_assignments,
5188 FixedArray* assignments) {
5189 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005190 kHasOnlySimpleThisPropertyAssignments,
5191 only_simple_this_property_assignments));
5192 set_this_property_assignments(assignments);
5193 set_this_property_assignments_count(assignments->length() / 3);
5194}
5195
5196
5197void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
5198 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005199 kHasOnlySimpleThisPropertyAssignments,
5200 false));
5201 set_this_property_assignments(Heap::undefined_value());
5202 set_this_property_assignments_count(0);
5203}
5204
5205
5206String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
5207 Object* obj = this_property_assignments();
5208 ASSERT(obj->IsFixedArray());
5209 ASSERT(index < this_property_assignments_count());
5210 obj = FixedArray::cast(obj)->get(index * 3);
5211 ASSERT(obj->IsString());
5212 return String::cast(obj);
5213}
5214
5215
5216bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
5217 Object* obj = this_property_assignments();
5218 ASSERT(obj->IsFixedArray());
5219 ASSERT(index < this_property_assignments_count());
5220 obj = FixedArray::cast(obj)->get(index * 3 + 1);
5221 return Smi::cast(obj)->value() != -1;
5222}
5223
5224
5225int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
5226 ASSERT(IsThisPropertyAssignmentArgument(index));
5227 Object* obj =
5228 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
5229 return Smi::cast(obj)->value();
5230}
5231
5232
5233Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
5234 ASSERT(!IsThisPropertyAssignmentArgument(index));
5235 Object* obj =
5236 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
5237 return obj;
5238}
5239
5240
Steve Blocka7e24c12009-10-30 11:49:00 +00005241// Support function for printing the source code to a StringStream
5242// without any allocation in the heap.
5243void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
5244 int max_length) {
5245 // For some native functions there is no source.
5246 if (script()->IsUndefined() ||
5247 Script::cast(script())->source()->IsUndefined()) {
5248 accumulator->Add("<No Source>");
5249 return;
5250 }
5251
Steve Blockd0582a62009-12-15 09:54:21 +00005252 // Get the source for the script which this function came from.
Steve Blocka7e24c12009-10-30 11:49:00 +00005253 // Don't use String::cast because we don't want more assertion errors while
5254 // we are already creating a stack dump.
5255 String* script_source =
5256 reinterpret_cast<String*>(Script::cast(script())->source());
5257
5258 if (!script_source->LooksValid()) {
5259 accumulator->Add("<Invalid Source>");
5260 return;
5261 }
5262
5263 if (!is_toplevel()) {
5264 accumulator->Add("function ");
5265 Object* name = this->name();
5266 if (name->IsString() && String::cast(name)->length() > 0) {
5267 accumulator->PrintName(name);
5268 }
5269 }
5270
5271 int len = end_position() - start_position();
5272 if (len > max_length) {
5273 accumulator->Put(script_source,
5274 start_position(),
5275 start_position() + max_length);
5276 accumulator->Add("...\n");
5277 } else {
5278 accumulator->Put(script_source, start_position(), end_position());
5279 }
5280}
5281
5282
5283void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
Steve Block6ded16b2010-05-10 14:33:55 +01005284 IteratePointers(v,
5285 kNameOffset,
5286 kThisPropertyAssignmentsOffset + kPointerSize);
Steve Blocka7e24c12009-10-30 11:49:00 +00005287}
5288
5289
5290void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
5291 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
5292 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
5293 Object* old_target = target;
5294 VisitPointer(&target);
5295 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5296}
5297
5298
5299void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005300 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
5301 rinfo->IsPatchedReturnSequence()) ||
5302 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
5303 rinfo->IsPatchedDebugBreakSlotSequence()));
Steve Blocka7e24c12009-10-30 11:49:00 +00005304 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
5305 Object* old_target = target;
5306 VisitPointer(&target);
5307 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5308}
5309
5310
5311void Code::CodeIterateBody(ObjectVisitor* v) {
5312 int mode_mask = RelocInfo::kCodeTargetMask |
5313 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
5314 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
5315 RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005316 RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) |
Steve Blocka7e24c12009-10-30 11:49:00 +00005317 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
5318
Leon Clarkeac952652010-07-15 11:15:24 +01005319 // Use the relocation info pointer before it is visited by
5320 // the heap compaction in the next statement.
5321 RelocIterator it(this, mode_mask);
5322
5323 IteratePointers(v,
5324 kRelocationInfoOffset,
5325 kRelocationInfoOffset + kPointerSize);
5326
5327 for (; !it.done(); it.next()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01005328 it.rinfo()->Visit(v);
Steve Blocka7e24c12009-10-30 11:49:00 +00005329 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005330}
5331
5332
Steve Blockd0582a62009-12-15 09:54:21 +00005333void Code::Relocate(intptr_t delta) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005334 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
5335 it.rinfo()->apply(delta);
5336 }
5337 CPU::FlushICache(instruction_start(), instruction_size());
5338}
5339
5340
5341void Code::CopyFrom(const CodeDesc& desc) {
5342 // copy code
5343 memmove(instruction_start(), desc.buffer, desc.instr_size);
5344
Steve Blocka7e24c12009-10-30 11:49:00 +00005345 // 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
Ben Murdoch3bec4d22010-07-22 14:51:16 +01007341// Find entry for key otherwise return kNotFound.
7342int StringDictionary::FindEntry(String* key) {
7343 if (!key->IsSymbol()) {
7344 return HashTable<StringDictionaryShape, String*>::FindEntry(key);
7345 }
7346
7347 // Optimized for symbol key. Knowledge of the key type allows:
7348 // 1. Move the check if the key is a symbol out of the loop.
7349 // 2. Avoid comparing hash codes in symbol to symbol comparision.
7350 // 3. Detect a case when a dictionary key is not a symbol but the key is.
7351 // In case of positive result the dictionary key may be replaced by
7352 // the symbol with minimal performance penalty. It gives a chance to
7353 // perform further lookups in code stubs (and significant performance boost
7354 // a certain style of code).
7355
7356 // EnsureCapacity will guarantee the hash table is never full.
7357 uint32_t capacity = Capacity();
7358 uint32_t entry = FirstProbe(key->Hash(), capacity);
7359 uint32_t count = 1;
7360
7361 while (true) {
7362 int index = EntryToIndex(entry);
7363 Object* element = get(index);
7364 if (element->IsUndefined()) break; // Empty entry.
7365 if (key == element) return entry;
7366 if (!element->IsSymbol() &&
7367 !element->IsNull() &&
7368 String::cast(element)->Equals(key)) {
7369 // Replace a non-symbol key by the equivalent symbol for faster further
7370 // lookups.
7371 set(index, key);
7372 return entry;
7373 }
7374 ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
7375 entry = NextProbe(entry, count++, capacity);
7376 }
7377 return kNotFound;
7378}
7379
7380
Steve Blocka7e24c12009-10-30 11:49:00 +00007381template<typename Shape, typename Key>
7382Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
7383 int capacity = Capacity();
7384 int nof = NumberOfElements() + n;
Leon Clarkee46be812010-01-19 14:06:41 +00007385 int nod = NumberOfDeletedElements();
7386 // Return if:
7387 // 50% is still free after adding n elements and
7388 // at most 50% of the free elements are deleted elements.
Steve Block6ded16b2010-05-10 14:33:55 +01007389 if (nod <= (capacity - nof) >> 1) {
7390 int needed_free = nof >> 1;
7391 if (nof + needed_free <= capacity) return this;
7392 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007393
Steve Block6ded16b2010-05-10 14:33:55 +01007394 const int kMinCapacityForPretenure = 256;
7395 bool pretenure =
7396 (capacity > kMinCapacityForPretenure) && !Heap::InNewSpace(this);
7397 Object* obj = Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
Steve Blocka7e24c12009-10-30 11:49:00 +00007398 if (obj->IsFailure()) return obj;
Leon Clarke4515c472010-02-03 11:58:03 +00007399
7400 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00007401 HashTable* table = HashTable::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00007402 WriteBarrierMode mode = table->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007403
7404 // Copy prefix to new array.
7405 for (int i = kPrefixStartIndex;
7406 i < kPrefixStartIndex + Shape::kPrefixSize;
7407 i++) {
7408 table->set(i, get(i), mode);
7409 }
7410 // Rehash the elements.
7411 for (int i = 0; i < capacity; i++) {
7412 uint32_t from_index = EntryToIndex(i);
7413 Object* k = get(from_index);
7414 if (IsKey(k)) {
7415 uint32_t hash = Shape::HashForObject(key, k);
7416 uint32_t insertion_index =
7417 EntryToIndex(table->FindInsertionEntry(hash));
7418 for (int j = 0; j < Shape::kEntrySize; j++) {
7419 table->set(insertion_index + j, get(from_index + j), mode);
7420 }
7421 }
7422 }
7423 table->SetNumberOfElements(NumberOfElements());
Leon Clarkee46be812010-01-19 14:06:41 +00007424 table->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007425 return table;
7426}
7427
7428
7429template<typename Shape, typename Key>
7430uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
7431 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007432 uint32_t entry = FirstProbe(hash, capacity);
7433 uint32_t count = 1;
7434 // EnsureCapacity will guarantee the hash table is never full.
7435 while (true) {
7436 Object* element = KeyAt(entry);
7437 if (element->IsUndefined() || element->IsNull()) break;
7438 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007439 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007440 return entry;
7441}
7442
7443// Force instantiation of template instances class.
7444// Please note this list is compiler dependent.
7445
7446template class HashTable<SymbolTableShape, HashTableKey*>;
7447
7448template class HashTable<CompilationCacheShape, HashTableKey*>;
7449
7450template class HashTable<MapCacheShape, HashTableKey*>;
7451
7452template class Dictionary<StringDictionaryShape, String*>;
7453
7454template class Dictionary<NumberDictionaryShape, uint32_t>;
7455
7456template Object* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
7457 int);
7458
7459template Object* Dictionary<StringDictionaryShape, String*>::Allocate(
7460 int);
7461
7462template Object* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
7463 uint32_t, Object*);
7464
7465template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
7466 Object*);
7467
7468template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
7469 Object*);
7470
7471template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
7472 FixedArray*, PropertyAttributes);
7473
7474template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
7475 int, JSObject::DeleteMode);
7476
7477template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
7478 int, JSObject::DeleteMode);
7479
7480template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
7481 FixedArray*);
7482
7483template int
7484Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
7485 PropertyAttributes);
7486
7487template Object* Dictionary<StringDictionaryShape, String*>::Add(
7488 String*, Object*, PropertyDetails);
7489
7490template Object*
7491Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
7492
7493template int
7494Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
7495 PropertyAttributes);
7496
7497template Object* Dictionary<NumberDictionaryShape, uint32_t>::Add(
7498 uint32_t, Object*, PropertyDetails);
7499
7500template Object* Dictionary<NumberDictionaryShape, uint32_t>::EnsureCapacity(
7501 int, uint32_t);
7502
7503template Object* Dictionary<StringDictionaryShape, String*>::EnsureCapacity(
7504 int, String*);
7505
7506template Object* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
7507 uint32_t, Object*, PropertyDetails, uint32_t);
7508
7509template Object* Dictionary<StringDictionaryShape, String*>::AddEntry(
7510 String*, Object*, PropertyDetails, uint32_t);
7511
7512template
7513int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
7514
7515template
7516int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
7517
Leon Clarkee46be812010-01-19 14:06:41 +00007518template
7519int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
7520
7521
Steve Blocka7e24c12009-10-30 11:49:00 +00007522// Collates undefined and unexisting elements below limit from position
7523// zero of the elements. The object stays in Dictionary mode.
7524Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
7525 ASSERT(HasDictionaryElements());
7526 // Must stay in dictionary mode, either because of requires_slow_elements,
7527 // or because we are not going to sort (and therefore compact) all of the
7528 // elements.
7529 NumberDictionary* dict = element_dictionary();
7530 HeapNumber* result_double = NULL;
7531 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7532 // Allocate space for result before we start mutating the object.
7533 Object* new_double = Heap::AllocateHeapNumber(0.0);
7534 if (new_double->IsFailure()) return new_double;
7535 result_double = HeapNumber::cast(new_double);
7536 }
7537
Steve Block6ded16b2010-05-10 14:33:55 +01007538 Object* obj = NumberDictionary::Allocate(dict->NumberOfElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00007539 if (obj->IsFailure()) return obj;
7540 NumberDictionary* new_dict = NumberDictionary::cast(obj);
7541
7542 AssertNoAllocation no_alloc;
7543
7544 uint32_t pos = 0;
7545 uint32_t undefs = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01007546 int capacity = dict->Capacity();
Steve Blocka7e24c12009-10-30 11:49:00 +00007547 for (int i = 0; i < capacity; i++) {
7548 Object* k = dict->KeyAt(i);
7549 if (dict->IsKey(k)) {
7550 ASSERT(k->IsNumber());
7551 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
7552 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
7553 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
7554 Object* value = dict->ValueAt(i);
7555 PropertyDetails details = dict->DetailsAt(i);
7556 if (details.type() == CALLBACKS) {
7557 // Bail out and do the sorting of undefineds and array holes in JS.
7558 return Smi::FromInt(-1);
7559 }
7560 uint32_t key = NumberToUint32(k);
7561 if (key < limit) {
7562 if (value->IsUndefined()) {
7563 undefs++;
7564 } else {
7565 new_dict->AddNumberEntry(pos, value, details);
7566 pos++;
7567 }
7568 } else {
7569 new_dict->AddNumberEntry(key, value, details);
7570 }
7571 }
7572 }
7573
7574 uint32_t result = pos;
7575 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
7576 while (undefs > 0) {
7577 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details);
7578 pos++;
7579 undefs--;
7580 }
7581
7582 set_elements(new_dict);
7583
7584 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7585 return Smi::FromInt(static_cast<int>(result));
7586 }
7587
7588 ASSERT_NE(NULL, result_double);
7589 result_double->set_value(static_cast<double>(result));
7590 return result_double;
7591}
7592
7593
7594// Collects all defined (non-hole) and non-undefined (array) elements at
7595// the start of the elements array.
7596// If the object is in dictionary mode, it is converted to fast elements
7597// mode.
7598Object* JSObject::PrepareElementsForSort(uint32_t limit) {
Steve Block3ce2e202009-11-05 08:53:23 +00007599 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00007600
7601 if (HasDictionaryElements()) {
7602 // Convert to fast elements containing only the existing properties.
7603 // Ordering is irrelevant, since we are going to sort anyway.
7604 NumberDictionary* dict = element_dictionary();
7605 if (IsJSArray() || dict->requires_slow_elements() ||
7606 dict->max_number_key() >= limit) {
7607 return PrepareSlowElementsForSort(limit);
7608 }
7609 // Convert to fast elements.
7610
Steve Block8defd9f2010-07-08 12:39:36 +01007611 Object* obj = map()->GetFastElementsMap();
7612 if (obj->IsFailure()) return obj;
7613 Map* new_map = Map::cast(obj);
7614
Steve Blocka7e24c12009-10-30 11:49:00 +00007615 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
7616 Object* new_array =
7617 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
Steve Block8defd9f2010-07-08 12:39:36 +01007618 if (new_array->IsFailure()) return new_array;
Steve Blocka7e24c12009-10-30 11:49:00 +00007619 FixedArray* fast_elements = FixedArray::cast(new_array);
7620 dict->CopyValuesTo(fast_elements);
Steve Block8defd9f2010-07-08 12:39:36 +01007621
7622 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00007623 set_elements(fast_elements);
7624 }
7625 ASSERT(HasFastElements());
7626
7627 // Collect holes at the end, undefined before that and the rest at the
7628 // start, and return the number of non-hole, non-undefined values.
7629
7630 FixedArray* elements = FixedArray::cast(this->elements());
7631 uint32_t elements_length = static_cast<uint32_t>(elements->length());
7632 if (limit > elements_length) {
7633 limit = elements_length ;
7634 }
7635 if (limit == 0) {
7636 return Smi::FromInt(0);
7637 }
7638
7639 HeapNumber* result_double = NULL;
7640 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7641 // Pessimistically allocate space for return value before
7642 // we start mutating the array.
7643 Object* new_double = Heap::AllocateHeapNumber(0.0);
7644 if (new_double->IsFailure()) return new_double;
7645 result_double = HeapNumber::cast(new_double);
7646 }
7647
7648 AssertNoAllocation no_alloc;
7649
7650 // Split elements into defined, undefined and the_hole, in that order.
7651 // Only count locations for undefined and the hole, and fill them afterwards.
Leon Clarke4515c472010-02-03 11:58:03 +00007652 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007653 unsigned int undefs = limit;
7654 unsigned int holes = limit;
7655 // Assume most arrays contain no holes and undefined values, so minimize the
7656 // number of stores of non-undefined, non-the-hole values.
7657 for (unsigned int i = 0; i < undefs; i++) {
7658 Object* current = elements->get(i);
7659 if (current->IsTheHole()) {
7660 holes--;
7661 undefs--;
7662 } else if (current->IsUndefined()) {
7663 undefs--;
7664 } else {
7665 continue;
7666 }
7667 // Position i needs to be filled.
7668 while (undefs > i) {
7669 current = elements->get(undefs);
7670 if (current->IsTheHole()) {
7671 holes--;
7672 undefs--;
7673 } else if (current->IsUndefined()) {
7674 undefs--;
7675 } else {
7676 elements->set(i, current, write_barrier);
7677 break;
7678 }
7679 }
7680 }
7681 uint32_t result = undefs;
7682 while (undefs < holes) {
7683 elements->set_undefined(undefs);
7684 undefs++;
7685 }
7686 while (holes < limit) {
7687 elements->set_the_hole(holes);
7688 holes++;
7689 }
7690
7691 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7692 return Smi::FromInt(static_cast<int>(result));
7693 }
7694 ASSERT_NE(NULL, result_double);
7695 result_double->set_value(static_cast<double>(result));
7696 return result_double;
7697}
7698
7699
7700Object* PixelArray::SetValue(uint32_t index, Object* value) {
7701 uint8_t clamped_value = 0;
7702 if (index < static_cast<uint32_t>(length())) {
7703 if (value->IsSmi()) {
7704 int int_value = Smi::cast(value)->value();
7705 if (int_value < 0) {
7706 clamped_value = 0;
7707 } else if (int_value > 255) {
7708 clamped_value = 255;
7709 } else {
7710 clamped_value = static_cast<uint8_t>(int_value);
7711 }
7712 } else if (value->IsHeapNumber()) {
7713 double double_value = HeapNumber::cast(value)->value();
7714 if (!(double_value > 0)) {
7715 // NaN and less than zero clamp to zero.
7716 clamped_value = 0;
7717 } else if (double_value > 255) {
7718 // Greater than 255 clamp to 255.
7719 clamped_value = 255;
7720 } else {
7721 // Other doubles are rounded to the nearest integer.
7722 clamped_value = static_cast<uint8_t>(double_value + 0.5);
7723 }
7724 } else {
7725 // Clamp undefined to zero (default). All other types have been
7726 // converted to a number type further up in the call chain.
7727 ASSERT(value->IsUndefined());
7728 }
7729 set(index, clamped_value);
7730 }
7731 return Smi::FromInt(clamped_value);
7732}
7733
7734
Steve Block3ce2e202009-11-05 08:53:23 +00007735template<typename ExternalArrayClass, typename ValueType>
7736static Object* ExternalArrayIntSetter(ExternalArrayClass* receiver,
7737 uint32_t index,
7738 Object* value) {
7739 ValueType cast_value = 0;
7740 if (index < static_cast<uint32_t>(receiver->length())) {
7741 if (value->IsSmi()) {
7742 int int_value = Smi::cast(value)->value();
7743 cast_value = static_cast<ValueType>(int_value);
7744 } else if (value->IsHeapNumber()) {
7745 double double_value = HeapNumber::cast(value)->value();
7746 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
7747 } else {
7748 // Clamp undefined to zero (default). All other types have been
7749 // converted to a number type further up in the call chain.
7750 ASSERT(value->IsUndefined());
7751 }
7752 receiver->set(index, cast_value);
7753 }
7754 return Heap::NumberFromInt32(cast_value);
7755}
7756
7757
7758Object* ExternalByteArray::SetValue(uint32_t index, Object* value) {
7759 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
7760 (this, index, value);
7761}
7762
7763
7764Object* ExternalUnsignedByteArray::SetValue(uint32_t index, Object* value) {
7765 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
7766 (this, index, value);
7767}
7768
7769
7770Object* ExternalShortArray::SetValue(uint32_t index, Object* value) {
7771 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
7772 (this, index, value);
7773}
7774
7775
7776Object* ExternalUnsignedShortArray::SetValue(uint32_t index, Object* value) {
7777 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
7778 (this, index, value);
7779}
7780
7781
7782Object* ExternalIntArray::SetValue(uint32_t index, Object* value) {
7783 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
7784 (this, index, value);
7785}
7786
7787
7788Object* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
7789 uint32_t cast_value = 0;
7790 if (index < static_cast<uint32_t>(length())) {
7791 if (value->IsSmi()) {
7792 int int_value = Smi::cast(value)->value();
7793 cast_value = static_cast<uint32_t>(int_value);
7794 } else if (value->IsHeapNumber()) {
7795 double double_value = HeapNumber::cast(value)->value();
7796 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
7797 } else {
7798 // Clamp undefined to zero (default). All other types have been
7799 // converted to a number type further up in the call chain.
7800 ASSERT(value->IsUndefined());
7801 }
7802 set(index, cast_value);
7803 }
7804 return Heap::NumberFromUint32(cast_value);
7805}
7806
7807
7808Object* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
7809 float cast_value = 0;
7810 if (index < static_cast<uint32_t>(length())) {
7811 if (value->IsSmi()) {
7812 int int_value = Smi::cast(value)->value();
7813 cast_value = static_cast<float>(int_value);
7814 } else if (value->IsHeapNumber()) {
7815 double double_value = HeapNumber::cast(value)->value();
7816 cast_value = static_cast<float>(double_value);
7817 } else {
7818 // Clamp undefined to zero (default). All other types have been
7819 // converted to a number type further up in the call chain.
7820 ASSERT(value->IsUndefined());
7821 }
7822 set(index, cast_value);
7823 }
7824 return Heap::AllocateHeapNumber(cast_value);
7825}
7826
7827
Steve Blocka7e24c12009-10-30 11:49:00 +00007828Object* GlobalObject::GetPropertyCell(LookupResult* result) {
7829 ASSERT(!HasFastProperties());
7830 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
7831 ASSERT(value->IsJSGlobalPropertyCell());
7832 return value;
7833}
7834
7835
7836Object* GlobalObject::EnsurePropertyCell(String* name) {
7837 ASSERT(!HasFastProperties());
7838 int entry = property_dictionary()->FindEntry(name);
7839 if (entry == StringDictionary::kNotFound) {
7840 Object* cell = Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
7841 if (cell->IsFailure()) return cell;
7842 PropertyDetails details(NONE, NORMAL);
7843 details = details.AsDeleted();
7844 Object* dictionary = property_dictionary()->Add(name, cell, details);
7845 if (dictionary->IsFailure()) return dictionary;
7846 set_properties(StringDictionary::cast(dictionary));
7847 return cell;
7848 } else {
7849 Object* value = property_dictionary()->ValueAt(entry);
7850 ASSERT(value->IsJSGlobalPropertyCell());
7851 return value;
7852 }
7853}
7854
7855
7856Object* SymbolTable::LookupString(String* string, Object** s) {
7857 SymbolKey key(string);
7858 return LookupKey(&key, s);
7859}
7860
7861
Steve Blockd0582a62009-12-15 09:54:21 +00007862// This class is used for looking up two character strings in the symbol table.
7863// If we don't have a hit we don't want to waste much time so we unroll the
7864// string hash calculation loop here for speed. Doesn't work if the two
7865// characters form a decimal integer, since such strings have a different hash
7866// algorithm.
7867class TwoCharHashTableKey : public HashTableKey {
7868 public:
7869 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
7870 : c1_(c1), c2_(c2) {
7871 // Char 1.
7872 uint32_t hash = c1 + (c1 << 10);
7873 hash ^= hash >> 6;
7874 // Char 2.
7875 hash += c2;
7876 hash += hash << 10;
7877 hash ^= hash >> 6;
7878 // GetHash.
7879 hash += hash << 3;
7880 hash ^= hash >> 11;
7881 hash += hash << 15;
7882 if (hash == 0) hash = 27;
7883#ifdef DEBUG
7884 StringHasher hasher(2);
7885 hasher.AddCharacter(c1);
7886 hasher.AddCharacter(c2);
7887 // If this assert fails then we failed to reproduce the two-character
7888 // version of the string hashing algorithm above. One reason could be
7889 // that we were passed two digits as characters, since the hash
7890 // algorithm is different in that case.
7891 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
7892#endif
7893 hash_ = hash;
7894 }
7895
7896 bool IsMatch(Object* o) {
7897 if (!o->IsString()) return false;
7898 String* other = String::cast(o);
7899 if (other->length() != 2) return false;
7900 if (other->Get(0) != c1_) return false;
7901 return other->Get(1) == c2_;
7902 }
7903
7904 uint32_t Hash() { return hash_; }
7905 uint32_t HashForObject(Object* key) {
7906 if (!key->IsString()) return 0;
7907 return String::cast(key)->Hash();
7908 }
7909
7910 Object* AsObject() {
7911 // The TwoCharHashTableKey is only used for looking in the symbol
7912 // table, not for adding to it.
7913 UNREACHABLE();
7914 return NULL;
7915 }
7916 private:
7917 uint32_t c1_;
7918 uint32_t c2_;
7919 uint32_t hash_;
7920};
7921
7922
Steve Blocka7e24c12009-10-30 11:49:00 +00007923bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
7924 SymbolKey key(string);
7925 int entry = FindEntry(&key);
7926 if (entry == kNotFound) {
7927 return false;
7928 } else {
7929 String* result = String::cast(KeyAt(entry));
7930 ASSERT(StringShape(result).IsSymbol());
7931 *symbol = result;
7932 return true;
7933 }
7934}
7935
7936
Steve Blockd0582a62009-12-15 09:54:21 +00007937bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
7938 uint32_t c2,
7939 String** symbol) {
7940 TwoCharHashTableKey key(c1, c2);
7941 int entry = FindEntry(&key);
7942 if (entry == kNotFound) {
7943 return false;
7944 } else {
7945 String* result = String::cast(KeyAt(entry));
7946 ASSERT(StringShape(result).IsSymbol());
7947 *symbol = result;
7948 return true;
7949 }
7950}
7951
7952
Steve Blocka7e24c12009-10-30 11:49:00 +00007953Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
7954 Utf8SymbolKey key(str);
7955 return LookupKey(&key, s);
7956}
7957
7958
7959Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
7960 int entry = FindEntry(key);
7961
7962 // Symbol already in table.
7963 if (entry != kNotFound) {
7964 *s = KeyAt(entry);
7965 return this;
7966 }
7967
7968 // Adding new symbol. Grow table if needed.
7969 Object* obj = EnsureCapacity(1, key);
7970 if (obj->IsFailure()) return obj;
7971
7972 // Create symbol object.
7973 Object* symbol = key->AsObject();
7974 if (symbol->IsFailure()) return symbol;
7975
7976 // If the symbol table grew as part of EnsureCapacity, obj is not
7977 // the current symbol table and therefore we cannot use
7978 // SymbolTable::cast here.
7979 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
7980
7981 // Add the new symbol and return it along with the symbol table.
7982 entry = table->FindInsertionEntry(key->Hash());
7983 table->set(EntryToIndex(entry), symbol);
7984 table->ElementAdded();
7985 *s = symbol;
7986 return table;
7987}
7988
7989
7990Object* CompilationCacheTable::Lookup(String* src) {
7991 StringKey key(src);
7992 int entry = FindEntry(&key);
7993 if (entry == kNotFound) return Heap::undefined_value();
7994 return get(EntryToIndex(entry) + 1);
7995}
7996
7997
7998Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
7999 StringSharedKey key(src, context->closure()->shared());
8000 int entry = FindEntry(&key);
8001 if (entry == kNotFound) return Heap::undefined_value();
8002 return get(EntryToIndex(entry) + 1);
8003}
8004
8005
8006Object* CompilationCacheTable::LookupRegExp(String* src,
8007 JSRegExp::Flags flags) {
8008 RegExpKey key(src, flags);
8009 int entry = FindEntry(&key);
8010 if (entry == kNotFound) return Heap::undefined_value();
8011 return get(EntryToIndex(entry) + 1);
8012}
8013
8014
8015Object* CompilationCacheTable::Put(String* src, Object* value) {
8016 StringKey key(src);
8017 Object* obj = EnsureCapacity(1, &key);
8018 if (obj->IsFailure()) return obj;
8019
8020 CompilationCacheTable* cache =
8021 reinterpret_cast<CompilationCacheTable*>(obj);
8022 int entry = cache->FindInsertionEntry(key.Hash());
8023 cache->set(EntryToIndex(entry), src);
8024 cache->set(EntryToIndex(entry) + 1, value);
8025 cache->ElementAdded();
8026 return cache;
8027}
8028
8029
8030Object* CompilationCacheTable::PutEval(String* src,
8031 Context* context,
8032 Object* value) {
8033 StringSharedKey key(src, context->closure()->shared());
8034 Object* obj = EnsureCapacity(1, &key);
8035 if (obj->IsFailure()) return obj;
8036
8037 CompilationCacheTable* cache =
8038 reinterpret_cast<CompilationCacheTable*>(obj);
8039 int entry = cache->FindInsertionEntry(key.Hash());
8040
8041 Object* k = key.AsObject();
8042 if (k->IsFailure()) return k;
8043
8044 cache->set(EntryToIndex(entry), k);
8045 cache->set(EntryToIndex(entry) + 1, value);
8046 cache->ElementAdded();
8047 return cache;
8048}
8049
8050
8051Object* CompilationCacheTable::PutRegExp(String* src,
8052 JSRegExp::Flags flags,
8053 FixedArray* value) {
8054 RegExpKey key(src, flags);
8055 Object* obj = EnsureCapacity(1, &key);
8056 if (obj->IsFailure()) return obj;
8057
8058 CompilationCacheTable* cache =
8059 reinterpret_cast<CompilationCacheTable*>(obj);
8060 int entry = cache->FindInsertionEntry(key.Hash());
Steve Block3ce2e202009-11-05 08:53:23 +00008061 // We store the value in the key slot, and compare the search key
8062 // to the stored value with a custon IsMatch function during lookups.
Steve Blocka7e24c12009-10-30 11:49:00 +00008063 cache->set(EntryToIndex(entry), value);
8064 cache->set(EntryToIndex(entry) + 1, value);
8065 cache->ElementAdded();
8066 return cache;
8067}
8068
8069
8070// SymbolsKey used for HashTable where key is array of symbols.
8071class SymbolsKey : public HashTableKey {
8072 public:
8073 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
8074
8075 bool IsMatch(Object* symbols) {
8076 FixedArray* o = FixedArray::cast(symbols);
8077 int len = symbols_->length();
8078 if (o->length() != len) return false;
8079 for (int i = 0; i < len; i++) {
8080 if (o->get(i) != symbols_->get(i)) return false;
8081 }
8082 return true;
8083 }
8084
8085 uint32_t Hash() { return HashForObject(symbols_); }
8086
8087 uint32_t HashForObject(Object* obj) {
8088 FixedArray* symbols = FixedArray::cast(obj);
8089 int len = symbols->length();
8090 uint32_t hash = 0;
8091 for (int i = 0; i < len; i++) {
8092 hash ^= String::cast(symbols->get(i))->Hash();
8093 }
8094 return hash;
8095 }
8096
8097 Object* AsObject() { return symbols_; }
8098
8099 private:
8100 FixedArray* symbols_;
8101};
8102
8103
8104Object* MapCache::Lookup(FixedArray* array) {
8105 SymbolsKey key(array);
8106 int entry = FindEntry(&key);
8107 if (entry == kNotFound) return Heap::undefined_value();
8108 return get(EntryToIndex(entry) + 1);
8109}
8110
8111
8112Object* MapCache::Put(FixedArray* array, Map* value) {
8113 SymbolsKey key(array);
8114 Object* obj = EnsureCapacity(1, &key);
8115 if (obj->IsFailure()) return obj;
8116
8117 MapCache* cache = reinterpret_cast<MapCache*>(obj);
8118 int entry = cache->FindInsertionEntry(key.Hash());
8119 cache->set(EntryToIndex(entry), array);
8120 cache->set(EntryToIndex(entry) + 1, value);
8121 cache->ElementAdded();
8122 return cache;
8123}
8124
8125
8126template<typename Shape, typename Key>
8127Object* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
8128 Object* obj = HashTable<Shape, Key>::Allocate(at_least_space_for);
8129 // Initialize the next enumeration index.
8130 if (!obj->IsFailure()) {
8131 Dictionary<Shape, Key>::cast(obj)->
8132 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
8133 }
8134 return obj;
8135}
8136
8137
8138template<typename Shape, typename Key>
8139Object* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
8140 int length = HashTable<Shape, Key>::NumberOfElements();
8141
8142 // Allocate and initialize iteration order array.
8143 Object* obj = Heap::AllocateFixedArray(length);
8144 if (obj->IsFailure()) return obj;
8145 FixedArray* iteration_order = FixedArray::cast(obj);
8146 for (int i = 0; i < length; i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00008147 iteration_order->set(i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00008148 }
8149
8150 // Allocate array with enumeration order.
8151 obj = Heap::AllocateFixedArray(length);
8152 if (obj->IsFailure()) return obj;
8153 FixedArray* enumeration_order = FixedArray::cast(obj);
8154
8155 // Fill the enumeration order array with property details.
8156 int capacity = HashTable<Shape, Key>::Capacity();
8157 int pos = 0;
8158 for (int i = 0; i < capacity; i++) {
8159 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
Leon Clarke4515c472010-02-03 11:58:03 +00008160 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008161 }
8162 }
8163
8164 // Sort the arrays wrt. enumeration order.
8165 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
8166
8167 // Overwrite the enumeration_order with the enumeration indices.
8168 for (int i = 0; i < length; i++) {
8169 int index = Smi::cast(iteration_order->get(i))->value();
8170 int enum_index = PropertyDetails::kInitialIndex + i;
Leon Clarke4515c472010-02-03 11:58:03 +00008171 enumeration_order->set(index, Smi::FromInt(enum_index));
Steve Blocka7e24c12009-10-30 11:49:00 +00008172 }
8173
8174 // Update the dictionary with new indices.
8175 capacity = HashTable<Shape, Key>::Capacity();
8176 pos = 0;
8177 for (int i = 0; i < capacity; i++) {
8178 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
8179 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
8180 PropertyDetails details = DetailsAt(i);
8181 PropertyDetails new_details =
8182 PropertyDetails(details.attributes(), details.type(), enum_index);
8183 DetailsAtPut(i, new_details);
8184 }
8185 }
8186
8187 // Set the next enumeration index.
8188 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
8189 return this;
8190}
8191
8192template<typename Shape, typename Key>
8193Object* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
8194 // Check whether there are enough enumeration indices to add n elements.
8195 if (Shape::kIsEnumerable &&
8196 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
8197 // If not, we generate new indices for the properties.
8198 Object* result = GenerateNewEnumerationIndices();
8199 if (result->IsFailure()) return result;
8200 }
8201 return HashTable<Shape, Key>::EnsureCapacity(n, key);
8202}
8203
8204
8205void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
8206 // Do nothing if the interval [from, to) is empty.
8207 if (from >= to) return;
8208
8209 int removed_entries = 0;
8210 Object* sentinel = Heap::null_value();
8211 int capacity = Capacity();
8212 for (int i = 0; i < capacity; i++) {
8213 Object* key = KeyAt(i);
8214 if (key->IsNumber()) {
8215 uint32_t number = static_cast<uint32_t>(key->Number());
8216 if (from <= number && number < to) {
8217 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
8218 removed_entries++;
8219 }
8220 }
8221 }
8222
8223 // Update the number of elements.
Leon Clarkee46be812010-01-19 14:06:41 +00008224 ElementsRemoved(removed_entries);
Steve Blocka7e24c12009-10-30 11:49:00 +00008225}
8226
8227
8228template<typename Shape, typename Key>
8229Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
8230 JSObject::DeleteMode mode) {
8231 PropertyDetails details = DetailsAt(entry);
8232 // Ignore attributes if forcing a deletion.
8233 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
8234 return Heap::false_value();
8235 }
8236 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
8237 HashTable<Shape, Key>::ElementRemoved();
8238 return Heap::true_value();
8239}
8240
8241
8242template<typename Shape, typename Key>
8243Object* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008244 int entry = this->FindEntry(key);
Steve Blocka7e24c12009-10-30 11:49:00 +00008245
8246 // If the entry is present set the value;
8247 if (entry != Dictionary<Shape, Key>::kNotFound) {
8248 ValueAtPut(entry, value);
8249 return this;
8250 }
8251
8252 // Check whether the dictionary should be extended.
8253 Object* obj = EnsureCapacity(1, key);
8254 if (obj->IsFailure()) return obj;
8255
8256 Object* k = Shape::AsObject(key);
8257 if (k->IsFailure()) return k;
8258 PropertyDetails details = PropertyDetails(NONE, NORMAL);
8259 return Dictionary<Shape, Key>::cast(obj)->
8260 AddEntry(key, value, details, Shape::Hash(key));
8261}
8262
8263
8264template<typename Shape, typename Key>
8265Object* Dictionary<Shape, Key>::Add(Key key,
8266 Object* value,
8267 PropertyDetails details) {
8268 // Valdate key is absent.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008269 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
Steve Blocka7e24c12009-10-30 11:49:00 +00008270 // Check whether the dictionary should be extended.
8271 Object* obj = EnsureCapacity(1, key);
8272 if (obj->IsFailure()) return obj;
8273 return Dictionary<Shape, Key>::cast(obj)->
8274 AddEntry(key, value, details, Shape::Hash(key));
8275}
8276
8277
8278// Add a key, value pair to the dictionary.
8279template<typename Shape, typename Key>
8280Object* Dictionary<Shape, Key>::AddEntry(Key key,
8281 Object* value,
8282 PropertyDetails details,
8283 uint32_t hash) {
8284 // Compute the key object.
8285 Object* k = Shape::AsObject(key);
8286 if (k->IsFailure()) return k;
8287
8288 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
8289 // Insert element at empty or deleted entry
8290 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
8291 // Assign an enumeration index to the property and update
8292 // SetNextEnumerationIndex.
8293 int index = NextEnumerationIndex();
8294 details = PropertyDetails(details.attributes(), details.type(), index);
8295 SetNextEnumerationIndex(index + 1);
8296 }
8297 SetEntry(entry, k, value, details);
8298 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
8299 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
8300 HashTable<Shape, Key>::ElementAdded();
8301 return this;
8302}
8303
8304
8305void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
8306 // If the dictionary requires slow elements an element has already
8307 // been added at a high index.
8308 if (requires_slow_elements()) return;
8309 // Check if this index is high enough that we should require slow
8310 // elements.
8311 if (key > kRequiresSlowElementsLimit) {
8312 set_requires_slow_elements();
8313 return;
8314 }
8315 // Update max key value.
8316 Object* max_index_object = get(kMaxNumberKeyIndex);
8317 if (!max_index_object->IsSmi() || max_number_key() < key) {
8318 FixedArray::set(kMaxNumberKeyIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00008319 Smi::FromInt(key << kRequiresSlowElementsTagSize));
Steve Blocka7e24c12009-10-30 11:49:00 +00008320 }
8321}
8322
8323
8324Object* NumberDictionary::AddNumberEntry(uint32_t key,
8325 Object* value,
8326 PropertyDetails details) {
8327 UpdateMaxNumberKey(key);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008328 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
Steve Blocka7e24c12009-10-30 11:49:00 +00008329 return Add(key, value, details);
8330}
8331
8332
8333Object* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
8334 UpdateMaxNumberKey(key);
8335 return AtPut(key, value);
8336}
8337
8338
8339Object* NumberDictionary::Set(uint32_t key,
8340 Object* value,
8341 PropertyDetails details) {
8342 int entry = FindEntry(key);
8343 if (entry == kNotFound) return AddNumberEntry(key, value, details);
8344 // Preserve enumeration index.
8345 details = PropertyDetails(details.attributes(),
8346 details.type(),
8347 DetailsAt(entry).index());
8348 SetEntry(entry, NumberDictionaryShape::AsObject(key), value, details);
8349 return this;
8350}
8351
8352
8353
8354template<typename Shape, typename Key>
8355int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
8356 PropertyAttributes filter) {
8357 int capacity = HashTable<Shape, Key>::Capacity();
8358 int result = 0;
8359 for (int i = 0; i < capacity; i++) {
8360 Object* k = HashTable<Shape, Key>::KeyAt(i);
8361 if (HashTable<Shape, Key>::IsKey(k)) {
8362 PropertyDetails details = DetailsAt(i);
8363 if (details.IsDeleted()) continue;
8364 PropertyAttributes attr = details.attributes();
8365 if ((attr & filter) == 0) result++;
8366 }
8367 }
8368 return result;
8369}
8370
8371
8372template<typename Shape, typename Key>
8373int Dictionary<Shape, Key>::NumberOfEnumElements() {
8374 return NumberOfElementsFilterAttributes(
8375 static_cast<PropertyAttributes>(DONT_ENUM));
8376}
8377
8378
8379template<typename Shape, typename Key>
8380void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
8381 PropertyAttributes filter) {
8382 ASSERT(storage->length() >= NumberOfEnumElements());
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 PropertyAttributes attr = details.attributes();
8391 if ((attr & filter) == 0) storage->set(index++, k);
8392 }
8393 }
8394 storage->SortPairs(storage, index);
8395 ASSERT(storage->length() >= index);
8396}
8397
8398
8399void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
8400 FixedArray* sort_array) {
8401 ASSERT(storage->length() >= NumberOfEnumElements());
8402 int capacity = Capacity();
8403 int index = 0;
8404 for (int i = 0; i < capacity; i++) {
8405 Object* k = KeyAt(i);
8406 if (IsKey(k)) {
8407 PropertyDetails details = DetailsAt(i);
8408 if (details.IsDeleted() || details.IsDontEnum()) continue;
8409 storage->set(index, k);
Leon Clarke4515c472010-02-03 11:58:03 +00008410 sort_array->set(index, Smi::FromInt(details.index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008411 index++;
8412 }
8413 }
8414 storage->SortPairs(sort_array, sort_array->length());
8415 ASSERT(storage->length() >= index);
8416}
8417
8418
8419template<typename Shape, typename Key>
8420void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
8421 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
8422 static_cast<PropertyAttributes>(NONE)));
8423 int capacity = HashTable<Shape, Key>::Capacity();
8424 int index = 0;
8425 for (int i = 0; i < capacity; i++) {
8426 Object* k = HashTable<Shape, Key>::KeyAt(i);
8427 if (HashTable<Shape, Key>::IsKey(k)) {
8428 PropertyDetails details = DetailsAt(i);
8429 if (details.IsDeleted()) continue;
8430 storage->set(index++, k);
8431 }
8432 }
8433 ASSERT(storage->length() >= index);
8434}
8435
8436
8437// Backwards lookup (slow).
8438template<typename Shape, typename Key>
8439Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
8440 int capacity = HashTable<Shape, Key>::Capacity();
8441 for (int i = 0; i < capacity; i++) {
8442 Object* k = HashTable<Shape, Key>::KeyAt(i);
8443 if (Dictionary<Shape, Key>::IsKey(k)) {
8444 Object* e = ValueAt(i);
8445 if (e->IsJSGlobalPropertyCell()) {
8446 e = JSGlobalPropertyCell::cast(e)->value();
8447 }
8448 if (e == value) return k;
8449 }
8450 }
8451 return Heap::undefined_value();
8452}
8453
8454
8455Object* StringDictionary::TransformPropertiesToFastFor(
8456 JSObject* obj, int unused_property_fields) {
8457 // Make sure we preserve dictionary representation if there are too many
8458 // descriptors.
8459 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
8460
8461 // Figure out if it is necessary to generate new enumeration indices.
8462 int max_enumeration_index =
8463 NextEnumerationIndex() +
8464 (DescriptorArray::kMaxNumberOfDescriptors -
8465 NumberOfElements());
8466 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
8467 Object* result = GenerateNewEnumerationIndices();
8468 if (result->IsFailure()) return result;
8469 }
8470
8471 int instance_descriptor_length = 0;
8472 int number_of_fields = 0;
8473
8474 // Compute the length of the instance descriptor.
8475 int capacity = Capacity();
8476 for (int i = 0; i < capacity; i++) {
8477 Object* k = KeyAt(i);
8478 if (IsKey(k)) {
8479 Object* value = ValueAt(i);
8480 PropertyType type = DetailsAt(i).type();
8481 ASSERT(type != FIELD);
8482 instance_descriptor_length++;
Leon Clarkee46be812010-01-19 14:06:41 +00008483 if (type == NORMAL &&
8484 (!value->IsJSFunction() || Heap::InNewSpace(value))) {
8485 number_of_fields += 1;
8486 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008487 }
8488 }
8489
8490 // Allocate the instance descriptor.
8491 Object* descriptors_unchecked =
8492 DescriptorArray::Allocate(instance_descriptor_length);
8493 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
8494 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
8495
8496 int inobject_props = obj->map()->inobject_properties();
8497 int number_of_allocated_fields =
8498 number_of_fields + unused_property_fields - inobject_props;
8499
8500 // Allocate the fixed array for the fields.
8501 Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields);
8502 if (fields->IsFailure()) return fields;
8503
8504 // Fill in the instance descriptor and the fields.
8505 int next_descriptor = 0;
8506 int current_offset = 0;
8507 for (int i = 0; i < capacity; i++) {
8508 Object* k = KeyAt(i);
8509 if (IsKey(k)) {
8510 Object* value = ValueAt(i);
8511 // Ensure the key is a symbol before writing into the instance descriptor.
8512 Object* key = Heap::LookupSymbol(String::cast(k));
8513 if (key->IsFailure()) return key;
8514 PropertyDetails details = DetailsAt(i);
8515 PropertyType type = details.type();
8516
Leon Clarkee46be812010-01-19 14:06:41 +00008517 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008518 ConstantFunctionDescriptor d(String::cast(key),
8519 JSFunction::cast(value),
8520 details.attributes(),
8521 details.index());
8522 descriptors->Set(next_descriptor++, &d);
8523 } else if (type == NORMAL) {
8524 if (current_offset < inobject_props) {
8525 obj->InObjectPropertyAtPut(current_offset,
8526 value,
8527 UPDATE_WRITE_BARRIER);
8528 } else {
8529 int offset = current_offset - inobject_props;
8530 FixedArray::cast(fields)->set(offset, value);
8531 }
8532 FieldDescriptor d(String::cast(key),
8533 current_offset++,
8534 details.attributes(),
8535 details.index());
8536 descriptors->Set(next_descriptor++, &d);
8537 } else if (type == CALLBACKS) {
8538 CallbacksDescriptor d(String::cast(key),
8539 value,
8540 details.attributes(),
8541 details.index());
8542 descriptors->Set(next_descriptor++, &d);
8543 } else {
8544 UNREACHABLE();
8545 }
8546 }
8547 }
8548 ASSERT(current_offset == number_of_fields);
8549
8550 descriptors->Sort();
8551 // Allocate new map.
8552 Object* new_map = obj->map()->CopyDropDescriptors();
8553 if (new_map->IsFailure()) return new_map;
8554
8555 // Transform the object.
8556 obj->set_map(Map::cast(new_map));
8557 obj->map()->set_instance_descriptors(descriptors);
8558 obj->map()->set_unused_property_fields(unused_property_fields);
8559
8560 obj->set_properties(FixedArray::cast(fields));
8561 ASSERT(obj->IsJSObject());
8562
8563 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
8564 // Check that it really works.
8565 ASSERT(obj->HasFastProperties());
8566
8567 return obj;
8568}
8569
8570
8571#ifdef ENABLE_DEBUGGER_SUPPORT
8572// Check if there is a break point at this code position.
8573bool DebugInfo::HasBreakPoint(int code_position) {
8574 // Get the break point info object for this code position.
8575 Object* break_point_info = GetBreakPointInfo(code_position);
8576
8577 // If there is no break point info object or no break points in the break
8578 // point info object there is no break point at this code position.
8579 if (break_point_info->IsUndefined()) return false;
8580 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
8581}
8582
8583
8584// Get the break point info object for this code position.
8585Object* DebugInfo::GetBreakPointInfo(int code_position) {
8586 // Find the index of the break point info object for this code position.
8587 int index = GetBreakPointInfoIndex(code_position);
8588
8589 // Return the break point info object if any.
8590 if (index == kNoBreakPointInfo) return Heap::undefined_value();
8591 return BreakPointInfo::cast(break_points()->get(index));
8592}
8593
8594
8595// Clear a break point at the specified code position.
8596void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
8597 int code_position,
8598 Handle<Object> break_point_object) {
8599 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
8600 if (break_point_info->IsUndefined()) return;
8601 BreakPointInfo::ClearBreakPoint(
8602 Handle<BreakPointInfo>::cast(break_point_info),
8603 break_point_object);
8604}
8605
8606
8607void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
8608 int code_position,
8609 int source_position,
8610 int statement_position,
8611 Handle<Object> break_point_object) {
8612 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
8613 if (!break_point_info->IsUndefined()) {
8614 BreakPointInfo::SetBreakPoint(
8615 Handle<BreakPointInfo>::cast(break_point_info),
8616 break_point_object);
8617 return;
8618 }
8619
8620 // Adding a new break point for a code position which did not have any
8621 // break points before. Try to find a free slot.
8622 int index = kNoBreakPointInfo;
8623 for (int i = 0; i < debug_info->break_points()->length(); i++) {
8624 if (debug_info->break_points()->get(i)->IsUndefined()) {
8625 index = i;
8626 break;
8627 }
8628 }
8629 if (index == kNoBreakPointInfo) {
8630 // No free slot - extend break point info array.
8631 Handle<FixedArray> old_break_points =
8632 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
8633 debug_info->set_break_points(*Factory::NewFixedArray(
8634 old_break_points->length() +
8635 Debug::kEstimatedNofBreakPointsInFunction));
8636 Handle<FixedArray> new_break_points =
8637 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
8638 for (int i = 0; i < old_break_points->length(); i++) {
8639 new_break_points->set(i, old_break_points->get(i));
8640 }
8641 index = old_break_points->length();
8642 }
8643 ASSERT(index != kNoBreakPointInfo);
8644
8645 // Allocate new BreakPointInfo object and set the break point.
8646 Handle<BreakPointInfo> new_break_point_info =
8647 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
8648 new_break_point_info->set_code_position(Smi::FromInt(code_position));
8649 new_break_point_info->set_source_position(Smi::FromInt(source_position));
8650 new_break_point_info->
8651 set_statement_position(Smi::FromInt(statement_position));
8652 new_break_point_info->set_break_point_objects(Heap::undefined_value());
8653 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
8654 debug_info->break_points()->set(index, *new_break_point_info);
8655}
8656
8657
8658// Get the break point objects for a code position.
8659Object* DebugInfo::GetBreakPointObjects(int code_position) {
8660 Object* break_point_info = GetBreakPointInfo(code_position);
8661 if (break_point_info->IsUndefined()) {
8662 return Heap::undefined_value();
8663 }
8664 return BreakPointInfo::cast(break_point_info)->break_point_objects();
8665}
8666
8667
8668// Get the total number of break points.
8669int DebugInfo::GetBreakPointCount() {
8670 if (break_points()->IsUndefined()) return 0;
8671 int count = 0;
8672 for (int i = 0; i < break_points()->length(); i++) {
8673 if (!break_points()->get(i)->IsUndefined()) {
8674 BreakPointInfo* break_point_info =
8675 BreakPointInfo::cast(break_points()->get(i));
8676 count += break_point_info->GetBreakPointCount();
8677 }
8678 }
8679 return count;
8680}
8681
8682
8683Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
8684 Handle<Object> break_point_object) {
8685 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
8686 for (int i = 0; i < debug_info->break_points()->length(); i++) {
8687 if (!debug_info->break_points()->get(i)->IsUndefined()) {
8688 Handle<BreakPointInfo> break_point_info =
8689 Handle<BreakPointInfo>(BreakPointInfo::cast(
8690 debug_info->break_points()->get(i)));
8691 if (BreakPointInfo::HasBreakPointObject(break_point_info,
8692 break_point_object)) {
8693 return *break_point_info;
8694 }
8695 }
8696 }
8697 return Heap::undefined_value();
8698}
8699
8700
8701// Find the index of the break point info object for the specified code
8702// position.
8703int DebugInfo::GetBreakPointInfoIndex(int code_position) {
8704 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
8705 for (int i = 0; i < break_points()->length(); i++) {
8706 if (!break_points()->get(i)->IsUndefined()) {
8707 BreakPointInfo* break_point_info =
8708 BreakPointInfo::cast(break_points()->get(i));
8709 if (break_point_info->code_position()->value() == code_position) {
8710 return i;
8711 }
8712 }
8713 }
8714 return kNoBreakPointInfo;
8715}
8716
8717
8718// Remove the specified break point object.
8719void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
8720 Handle<Object> break_point_object) {
8721 // If there are no break points just ignore.
8722 if (break_point_info->break_point_objects()->IsUndefined()) return;
8723 // If there is a single break point clear it if it is the same.
8724 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8725 if (break_point_info->break_point_objects() == *break_point_object) {
8726 break_point_info->set_break_point_objects(Heap::undefined_value());
8727 }
8728 return;
8729 }
8730 // If there are multiple break points shrink the array
8731 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
8732 Handle<FixedArray> old_array =
8733 Handle<FixedArray>(
8734 FixedArray::cast(break_point_info->break_point_objects()));
8735 Handle<FixedArray> new_array =
8736 Factory::NewFixedArray(old_array->length() - 1);
8737 int found_count = 0;
8738 for (int i = 0; i < old_array->length(); i++) {
8739 if (old_array->get(i) == *break_point_object) {
8740 ASSERT(found_count == 0);
8741 found_count++;
8742 } else {
8743 new_array->set(i - found_count, old_array->get(i));
8744 }
8745 }
8746 // If the break point was found in the list change it.
8747 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
8748}
8749
8750
8751// Add the specified break point object.
8752void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
8753 Handle<Object> break_point_object) {
8754 // If there was no break point objects before just set it.
8755 if (break_point_info->break_point_objects()->IsUndefined()) {
8756 break_point_info->set_break_point_objects(*break_point_object);
8757 return;
8758 }
8759 // If the break point object is the same as before just ignore.
8760 if (break_point_info->break_point_objects() == *break_point_object) return;
8761 // If there was one break point object before replace with array.
8762 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8763 Handle<FixedArray> array = Factory::NewFixedArray(2);
8764 array->set(0, break_point_info->break_point_objects());
8765 array->set(1, *break_point_object);
8766 break_point_info->set_break_point_objects(*array);
8767 return;
8768 }
8769 // If there was more than one break point before extend array.
8770 Handle<FixedArray> old_array =
8771 Handle<FixedArray>(
8772 FixedArray::cast(break_point_info->break_point_objects()));
8773 Handle<FixedArray> new_array =
8774 Factory::NewFixedArray(old_array->length() + 1);
8775 for (int i = 0; i < old_array->length(); i++) {
8776 // If the break point was there before just ignore.
8777 if (old_array->get(i) == *break_point_object) return;
8778 new_array->set(i, old_array->get(i));
8779 }
8780 // Add the new break point.
8781 new_array->set(old_array->length(), *break_point_object);
8782 break_point_info->set_break_point_objects(*new_array);
8783}
8784
8785
8786bool BreakPointInfo::HasBreakPointObject(
8787 Handle<BreakPointInfo> break_point_info,
8788 Handle<Object> break_point_object) {
8789 // No break point.
8790 if (break_point_info->break_point_objects()->IsUndefined()) return false;
8791 // Single beak point.
8792 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8793 return break_point_info->break_point_objects() == *break_point_object;
8794 }
8795 // Multiple break points.
8796 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
8797 for (int i = 0; i < array->length(); i++) {
8798 if (array->get(i) == *break_point_object) {
8799 return true;
8800 }
8801 }
8802 return false;
8803}
8804
8805
8806// Get the number of break points.
8807int BreakPointInfo::GetBreakPointCount() {
8808 // No break point.
8809 if (break_point_objects()->IsUndefined()) return 0;
8810 // Single beak point.
8811 if (!break_point_objects()->IsFixedArray()) return 1;
8812 // Multiple break points.
8813 return FixedArray::cast(break_point_objects())->length();
8814}
8815#endif
8816
8817
8818} } // namespace v8::internal