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