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