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