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