blob: 1a4ed0559cd90ec662693755ac904f02104a4481 [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"
Iain Merrick75681382010-08-19 15:07:18 +010036#include "objects-visiting.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000037#include "macro-assembler.h"
38#include "scanner.h"
39#include "scopeinfo.h"
40#include "string-stream.h"
Steve Blockd0582a62009-12-15 09:54:21 +000041#include "utils.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000042
43#ifdef ENABLE_DISASSEMBLER
44#include "disassembler.h"
45#endif
46
47
48namespace v8 {
49namespace internal {
50
51// Getters and setters are stored in a fixed array property. These are
52// constants for their indices.
53const int kGetterIndex = 0;
54const int kSetterIndex = 1;
55
56
57static Object* CreateJSValue(JSFunction* constructor, Object* value) {
58 Object* result = Heap::AllocateJSObject(constructor);
59 if (result->IsFailure()) return result;
60 JSValue::cast(result)->set_value(value);
61 return result;
62}
63
64
65Object* Object::ToObject(Context* global_context) {
66 if (IsNumber()) {
67 return CreateJSValue(global_context->number_function(), this);
68 } else if (IsBoolean()) {
69 return CreateJSValue(global_context->boolean_function(), this);
70 } else if (IsString()) {
71 return CreateJSValue(global_context->string_function(), this);
72 }
73 ASSERT(IsJSObject());
74 return this;
75}
76
77
78Object* Object::ToObject() {
79 Context* global_context = Top::context()->global_context();
80 if (IsJSObject()) {
81 return this;
82 } else if (IsNumber()) {
83 return CreateJSValue(global_context->number_function(), this);
84 } else if (IsBoolean()) {
85 return CreateJSValue(global_context->boolean_function(), this);
86 } else if (IsString()) {
87 return CreateJSValue(global_context->string_function(), this);
88 }
89
90 // Throw a type error.
91 return Failure::InternalError();
92}
93
94
95Object* Object::ToBoolean() {
96 if (IsTrue()) return Heap::true_value();
97 if (IsFalse()) return Heap::false_value();
98 if (IsSmi()) {
99 return Heap::ToBoolean(Smi::cast(this)->value() != 0);
100 }
101 if (IsUndefined() || IsNull()) return Heap::false_value();
102 // Undetectable object is false
103 if (IsUndetectableObject()) {
104 return Heap::false_value();
105 }
106 if (IsString()) {
107 return Heap::ToBoolean(String::cast(this)->length() != 0);
108 }
109 if (IsHeapNumber()) {
110 return HeapNumber::cast(this)->HeapNumberToBoolean();
111 }
112 return Heap::true_value();
113}
114
115
116void Object::Lookup(String* name, LookupResult* result) {
117 if (IsJSObject()) return JSObject::cast(this)->Lookup(name, result);
118 Object* holder = NULL;
119 Context* global_context = Top::context()->global_context();
120 if (IsString()) {
121 holder = global_context->string_function()->instance_prototype();
122 } else if (IsNumber()) {
123 holder = global_context->number_function()->instance_prototype();
124 } else if (IsBoolean()) {
125 holder = global_context->boolean_function()->instance_prototype();
126 }
127 ASSERT(holder != NULL); // Cannot handle null or undefined.
128 JSObject::cast(holder)->Lookup(name, result);
129}
130
131
132Object* Object::GetPropertyWithReceiver(Object* receiver,
133 String* name,
134 PropertyAttributes* attributes) {
135 LookupResult result;
136 Lookup(name, &result);
137 Object* value = GetProperty(receiver, &result, name, attributes);
138 ASSERT(*attributes <= ABSENT);
139 return value;
140}
141
142
143Object* Object::GetPropertyWithCallback(Object* receiver,
144 Object* structure,
145 String* name,
146 Object* holder) {
147 // To accommodate both the old and the new api we switch on the
148 // data structure used to store the callbacks. Eventually proxy
149 // callbacks should be phased out.
150 if (structure->IsProxy()) {
151 AccessorDescriptor* callback =
152 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
153 Object* value = (callback->getter)(receiver, callback->data);
154 RETURN_IF_SCHEDULED_EXCEPTION();
155 return value;
156 }
157
158 // api style callbacks.
159 if (structure->IsAccessorInfo()) {
160 AccessorInfo* data = AccessorInfo::cast(structure);
161 Object* fun_obj = data->getter();
162 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
163 HandleScope scope;
164 JSObject* self = JSObject::cast(receiver);
165 JSObject* holder_handle = JSObject::cast(holder);
166 Handle<String> key(name);
167 LOG(ApiNamedPropertyAccess("load", self, name));
168 CustomArguments args(data->data(), self, holder_handle);
169 v8::AccessorInfo info(args.end());
170 v8::Handle<v8::Value> result;
171 {
172 // Leaving JavaScript.
173 VMState state(EXTERNAL);
174 result = call_fun(v8::Utils::ToLocal(key), info);
175 }
176 RETURN_IF_SCHEDULED_EXCEPTION();
177 if (result.IsEmpty()) return Heap::undefined_value();
178 return *v8::Utils::OpenHandle(*result);
179 }
180
181 // __defineGetter__ callback
182 if (structure->IsFixedArray()) {
183 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
184 if (getter->IsJSFunction()) {
185 return Object::GetPropertyWithDefinedGetter(receiver,
186 JSFunction::cast(getter));
187 }
188 // Getter is not a function.
189 return Heap::undefined_value();
190 }
191
192 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +0100193 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000194}
195
196
197Object* Object::GetPropertyWithDefinedGetter(Object* receiver,
198 JSFunction* getter) {
199 HandleScope scope;
200 Handle<JSFunction> fun(JSFunction::cast(getter));
201 Handle<Object> self(receiver);
202#ifdef ENABLE_DEBUGGER_SUPPORT
203 // Handle stepping into a getter if step into is active.
204 if (Debug::StepInActive()) {
205 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
206 }
207#endif
208 bool has_pending_exception;
209 Handle<Object> result =
210 Execution::Call(fun, self, 0, NULL, &has_pending_exception);
211 // Check for pending exception and return the result.
212 if (has_pending_exception) return Failure::Exception();
213 return *result;
214}
215
216
217// Only deal with CALLBACKS and INTERCEPTOR
218Object* JSObject::GetPropertyWithFailedAccessCheck(
219 Object* receiver,
220 LookupResult* result,
221 String* name,
222 PropertyAttributes* attributes) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000223 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000224 switch (result->type()) {
225 case CALLBACKS: {
226 // Only allow API accessors.
227 Object* obj = result->GetCallbackObject();
228 if (obj->IsAccessorInfo()) {
229 AccessorInfo* info = AccessorInfo::cast(obj);
230 if (info->all_can_read()) {
231 *attributes = result->GetAttributes();
232 return GetPropertyWithCallback(receiver,
233 result->GetCallbackObject(),
234 name,
235 result->holder());
236 }
237 }
238 break;
239 }
240 case NORMAL:
241 case FIELD:
242 case CONSTANT_FUNCTION: {
243 // Search ALL_CAN_READ accessors in prototype chain.
244 LookupResult r;
245 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000246 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000247 return GetPropertyWithFailedAccessCheck(receiver,
248 &r,
249 name,
250 attributes);
251 }
252 break;
253 }
254 case INTERCEPTOR: {
255 // If the object has an interceptor, try real named properties.
256 // No access check in GetPropertyAttributeWithInterceptor.
257 LookupResult r;
258 result->holder()->LookupRealNamedProperty(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000259 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000260 return GetPropertyWithFailedAccessCheck(receiver,
261 &r,
262 name,
263 attributes);
264 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000265 break;
266 }
Andrei Popescu402d9372010-02-26 13:31:12 +0000267 default:
268 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +0000269 }
270 }
271
272 // No accessible property found.
273 *attributes = ABSENT;
274 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
275 return Heap::undefined_value();
276}
277
278
279PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
280 Object* receiver,
281 LookupResult* result,
282 String* name,
283 bool continue_search) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000284 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000285 switch (result->type()) {
286 case CALLBACKS: {
287 // Only allow API accessors.
288 Object* obj = result->GetCallbackObject();
289 if (obj->IsAccessorInfo()) {
290 AccessorInfo* info = AccessorInfo::cast(obj);
291 if (info->all_can_read()) {
292 return result->GetAttributes();
293 }
294 }
295 break;
296 }
297
298 case NORMAL:
299 case FIELD:
300 case CONSTANT_FUNCTION: {
301 if (!continue_search) break;
302 // Search ALL_CAN_READ accessors in prototype chain.
303 LookupResult r;
304 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000305 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000306 return GetPropertyAttributeWithFailedAccessCheck(receiver,
307 &r,
308 name,
309 continue_search);
310 }
311 break;
312 }
313
314 case INTERCEPTOR: {
315 // If the object has an interceptor, try real named properties.
316 // No access check in GetPropertyAttributeWithInterceptor.
317 LookupResult r;
318 if (continue_search) {
319 result->holder()->LookupRealNamedProperty(name, &r);
320 } else {
321 result->holder()->LocalLookupRealNamedProperty(name, &r);
322 }
Andrei Popescu402d9372010-02-26 13:31:12 +0000323 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000324 return GetPropertyAttributeWithFailedAccessCheck(receiver,
325 &r,
326 name,
327 continue_search);
328 }
329 break;
330 }
331
Andrei Popescu402d9372010-02-26 13:31:12 +0000332 default:
333 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +0000334 }
335 }
336
337 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
338 return ABSENT;
339}
340
341
Steve Blocka7e24c12009-10-30 11:49:00 +0000342Object* JSObject::GetNormalizedProperty(LookupResult* result) {
343 ASSERT(!HasFastProperties());
344 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
345 if (IsGlobalObject()) {
346 value = JSGlobalPropertyCell::cast(value)->value();
347 }
348 ASSERT(!value->IsJSGlobalPropertyCell());
349 return value;
350}
351
352
353Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) {
354 ASSERT(!HasFastProperties());
355 if (IsGlobalObject()) {
356 JSGlobalPropertyCell* cell =
357 JSGlobalPropertyCell::cast(
358 property_dictionary()->ValueAt(result->GetDictionaryEntry()));
359 cell->set_value(value);
360 } else {
361 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
362 }
363 return value;
364}
365
366
367Object* JSObject::SetNormalizedProperty(String* name,
368 Object* value,
369 PropertyDetails details) {
370 ASSERT(!HasFastProperties());
371 int entry = property_dictionary()->FindEntry(name);
372 if (entry == StringDictionary::kNotFound) {
373 Object* store_value = value;
374 if (IsGlobalObject()) {
375 store_value = Heap::AllocateJSGlobalPropertyCell(value);
376 if (store_value->IsFailure()) return store_value;
377 }
378 Object* dict = property_dictionary()->Add(name, store_value, details);
379 if (dict->IsFailure()) return dict;
380 set_properties(StringDictionary::cast(dict));
381 return value;
382 }
383 // Preserve enumeration index.
384 details = PropertyDetails(details.attributes(),
385 details.type(),
386 property_dictionary()->DetailsAt(entry).index());
387 if (IsGlobalObject()) {
388 JSGlobalPropertyCell* cell =
389 JSGlobalPropertyCell::cast(property_dictionary()->ValueAt(entry));
390 cell->set_value(value);
391 // Please note we have to update the property details.
392 property_dictionary()->DetailsAtPut(entry, details);
393 } else {
394 property_dictionary()->SetEntry(entry, name, value, details);
395 }
396 return value;
397}
398
399
400Object* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) {
401 ASSERT(!HasFastProperties());
402 StringDictionary* dictionary = property_dictionary();
403 int entry = dictionary->FindEntry(name);
404 if (entry != StringDictionary::kNotFound) {
405 // If we have a global object set the cell to the hole.
406 if (IsGlobalObject()) {
407 PropertyDetails details = dictionary->DetailsAt(entry);
408 if (details.IsDontDelete()) {
409 if (mode != FORCE_DELETION) return Heap::false_value();
410 // When forced to delete global properties, we have to make a
411 // map change to invalidate any ICs that think they can load
412 // from the DontDelete cell without checking if it contains
413 // the hole value.
414 Object* new_map = map()->CopyDropDescriptors();
415 if (new_map->IsFailure()) return new_map;
416 set_map(Map::cast(new_map));
417 }
418 JSGlobalPropertyCell* cell =
419 JSGlobalPropertyCell::cast(dictionary->ValueAt(entry));
420 cell->set_value(Heap::the_hole_value());
421 dictionary->DetailsAtPut(entry, details.AsDeleted());
422 } else {
423 return dictionary->DeleteProperty(entry, mode);
424 }
425 }
426 return Heap::true_value();
427}
428
429
430bool JSObject::IsDirty() {
431 Object* cons_obj = map()->constructor();
432 if (!cons_obj->IsJSFunction())
433 return true;
434 JSFunction* fun = JSFunction::cast(cons_obj);
Steve Block6ded16b2010-05-10 14:33:55 +0100435 if (!fun->shared()->IsApiFunction())
Steve Blocka7e24c12009-10-30 11:49:00 +0000436 return true;
437 // If the object is fully fast case and has the same map it was
438 // created with then no changes can have been made to it.
439 return map() != fun->initial_map()
440 || !HasFastElements()
441 || !HasFastProperties();
442}
443
444
445Object* Object::GetProperty(Object* receiver,
446 LookupResult* result,
447 String* name,
448 PropertyAttributes* attributes) {
449 // Make sure that the top context does not change when doing
450 // callbacks or interceptor calls.
451 AssertNoContextChange ncc;
452
453 // Traverse the prototype chain from the current object (this) to
454 // the holder and check for access rights. This avoid traversing the
455 // objects more than once in case of interceptors, because the
456 // holder will always be the interceptor holder and the search may
457 // only continue with a current object just after the interceptor
458 // holder in the prototype chain.
Andrei Popescu402d9372010-02-26 13:31:12 +0000459 Object* last = result->IsProperty() ? result->holder() : Heap::null_value();
Steve Blocka7e24c12009-10-30 11:49:00 +0000460 for (Object* current = this; true; current = current->GetPrototype()) {
461 if (current->IsAccessCheckNeeded()) {
462 // Check if we're allowed to read from the current object. Note
463 // that even though we may not actually end up loading the named
464 // property from the current object, we still check that we have
465 // access to it.
466 JSObject* checked = JSObject::cast(current);
467 if (!Top::MayNamedAccess(checked, name, v8::ACCESS_GET)) {
468 return checked->GetPropertyWithFailedAccessCheck(receiver,
469 result,
470 name,
471 attributes);
472 }
473 }
474 // Stop traversing the chain once we reach the last object in the
475 // chain; either the holder of the result or null in case of an
476 // absent property.
477 if (current == last) break;
478 }
479
480 if (!result->IsProperty()) {
481 *attributes = ABSENT;
482 return Heap::undefined_value();
483 }
484 *attributes = result->GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +0000485 Object* value;
486 JSObject* holder = result->holder();
487 switch (result->type()) {
488 case NORMAL:
489 value = holder->GetNormalizedProperty(result);
490 ASSERT(!value->IsTheHole() || result->IsReadOnly());
491 return value->IsTheHole() ? Heap::undefined_value() : value;
492 case FIELD:
493 value = holder->FastPropertyAt(result->GetFieldIndex());
494 ASSERT(!value->IsTheHole() || result->IsReadOnly());
495 return value->IsTheHole() ? Heap::undefined_value() : value;
496 case CONSTANT_FUNCTION:
497 return result->GetConstantFunction();
498 case CALLBACKS:
499 return GetPropertyWithCallback(receiver,
500 result->GetCallbackObject(),
501 name,
502 holder);
503 case INTERCEPTOR: {
504 JSObject* recvr = JSObject::cast(receiver);
505 return holder->GetPropertyWithInterceptor(recvr, name, attributes);
506 }
507 default:
508 UNREACHABLE();
509 return NULL;
510 }
511}
512
513
514Object* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
515 // Non-JS objects do not have integer indexed properties.
516 if (!IsJSObject()) return Heap::undefined_value();
517 return JSObject::cast(this)->GetElementWithReceiver(JSObject::cast(receiver),
518 index);
519}
520
521
522Object* Object::GetPrototype() {
523 // The object is either a number, a string, a boolean, or a real JS object.
524 if (IsJSObject()) return JSObject::cast(this)->map()->prototype();
525 Context* context = Top::context()->global_context();
526
527 if (IsNumber()) return context->number_function()->instance_prototype();
528 if (IsString()) return context->string_function()->instance_prototype();
529 if (IsBoolean()) {
530 return context->boolean_function()->instance_prototype();
531 } else {
532 return Heap::null_value();
533 }
534}
535
536
537void Object::ShortPrint() {
538 HeapStringAllocator allocator;
539 StringStream accumulator(&allocator);
540 ShortPrint(&accumulator);
541 accumulator.OutputToStdOut();
542}
543
544
545void Object::ShortPrint(StringStream* accumulator) {
546 if (IsSmi()) {
547 Smi::cast(this)->SmiPrint(accumulator);
548 } else if (IsFailure()) {
549 Failure::cast(this)->FailurePrint(accumulator);
550 } else {
551 HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
552 }
553}
554
555
556void Smi::SmiPrint() {
557 PrintF("%d", value());
558}
559
560
561void Smi::SmiPrint(StringStream* accumulator) {
562 accumulator->Add("%d", value());
563}
564
565
566void Failure::FailurePrint(StringStream* accumulator) {
Steve Block3ce2e202009-11-05 08:53:23 +0000567 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000568}
569
570
571void Failure::FailurePrint() {
Steve Block3ce2e202009-11-05 08:53:23 +0000572 PrintF("Failure(%p)", reinterpret_cast<void*>(value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000573}
574
575
576Failure* Failure::RetryAfterGC(int requested_bytes, AllocationSpace space) {
577 ASSERT((space & ~kSpaceTagMask) == 0);
578 // TODO(X64): Stop using Smi validation for non-smi checks, even if they
579 // happen to be identical at the moment.
580
581 int requested = requested_bytes >> kObjectAlignmentBits;
582 int value = (requested << kSpaceTagSize) | space;
583 // We can't very well allocate a heap number in this situation, and if the
584 // requested memory is so large it seems reasonable to say that this is an
585 // out of memory situation. This fixes a crash in
586 // js1_5/Regress/regress-303213.js.
587 if (value >> kSpaceTagSize != requested ||
588 !Smi::IsValid(value) ||
589 value != ((value << kFailureTypeTagSize) >> kFailureTypeTagSize) ||
590 !Smi::IsValid(value << kFailureTypeTagSize)) {
591 Top::context()->mark_out_of_memory();
592 return Failure::OutOfMemoryException();
593 }
594 return Construct(RETRY_AFTER_GC, value);
595}
596
597
598// Should a word be prefixed by 'a' or 'an' in order to read naturally in
599// English? Returns false for non-ASCII or words that don't start with
600// a capital letter. The a/an rule follows pronunciation in English.
601// We don't use the BBC's overcorrect "an historic occasion" though if
602// you speak a dialect you may well say "an 'istoric occasion".
603static bool AnWord(String* str) {
604 if (str->length() == 0) return false; // A nothing.
605 int c0 = str->Get(0);
606 int c1 = str->length() > 1 ? str->Get(1) : 0;
607 if (c0 == 'U') {
608 if (c1 > 'Z') {
609 return true; // An Umpire, but a UTF8String, a U.
610 }
611 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
612 return true; // An Ape, an ABCBook.
613 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
614 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
615 c0 == 'S' || c0 == 'X')) {
616 return true; // An MP3File, an M.
617 }
618 return false;
619}
620
621
Steve Block6ded16b2010-05-10 14:33:55 +0100622Object* String::SlowTryFlatten(PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000623#ifdef DEBUG
624 // Do not attempt to flatten in debug mode when allocation is not
625 // allowed. This is to avoid an assertion failure when allocating.
626 // Flattening strings is the only case where we always allow
627 // allocation because no GC is performed if the allocation fails.
628 if (!Heap::IsAllocationAllowed()) return this;
629#endif
630
631 switch (StringShape(this).representation_tag()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000632 case kConsStringTag: {
633 ConsString* cs = ConsString::cast(this);
634 if (cs->second()->length() == 0) {
Leon Clarkef7060e22010-06-03 12:02:55 +0100635 return cs->first();
Steve Blocka7e24c12009-10-30 11:49:00 +0000636 }
637 // There's little point in putting the flat string in new space if the
638 // cons string is in old space. It can never get GCed until there is
639 // an old space GC.
Steve Block6ded16b2010-05-10 14:33:55 +0100640 PretenureFlag tenure = Heap::InNewSpace(this) ? pretenure : TENURED;
Steve Blocka7e24c12009-10-30 11:49:00 +0000641 int len = length();
642 Object* object;
643 String* result;
644 if (IsAsciiRepresentation()) {
645 object = Heap::AllocateRawAsciiString(len, tenure);
646 if (object->IsFailure()) return object;
647 result = String::cast(object);
648 String* first = cs->first();
649 int first_length = first->length();
650 char* dest = SeqAsciiString::cast(result)->GetChars();
651 WriteToFlat(first, dest, 0, first_length);
652 String* second = cs->second();
653 WriteToFlat(second,
654 dest + first_length,
655 0,
656 len - first_length);
657 } else {
658 object = Heap::AllocateRawTwoByteString(len, tenure);
659 if (object->IsFailure()) return object;
660 result = String::cast(object);
661 uc16* dest = SeqTwoByteString::cast(result)->GetChars();
662 String* first = cs->first();
663 int first_length = first->length();
664 WriteToFlat(first, dest, 0, first_length);
665 String* second = cs->second();
666 WriteToFlat(second,
667 dest + first_length,
668 0,
669 len - first_length);
670 }
671 cs->set_first(result);
672 cs->set_second(Heap::empty_string());
Leon Clarkef7060e22010-06-03 12:02:55 +0100673 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +0000674 }
675 default:
676 return this;
677 }
678}
679
680
681bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
Steve Block8defd9f2010-07-08 12:39:36 +0100682 // Externalizing twice leaks the external resource, so it's
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100683 // prohibited by the API.
684 ASSERT(!this->IsExternalString());
Steve Blocka7e24c12009-10-30 11:49:00 +0000685#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000686 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000687 // Assert that the resource and the string are equivalent.
688 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100689 ScopedVector<uc16> smart_chars(this->length());
690 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
691 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000692 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100693 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000694 }
695#endif // DEBUG
696
697 int size = this->Size(); // Byte size of the original string.
698 if (size < ExternalString::kSize) {
699 // The string is too small to fit an external String in its place. This can
700 // only happen for zero length strings.
701 return false;
702 }
703 ASSERT(size >= ExternalString::kSize);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100704 bool is_ascii = this->IsAsciiRepresentation();
Steve Blocka7e24c12009-10-30 11:49:00 +0000705 bool is_symbol = this->IsSymbol();
706 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000707 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000708
709 // Morph the object to an external string by adjusting the map and
710 // reinitializing the fields.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100711 this->set_map(is_ascii ?
712 Heap::external_string_with_ascii_data_map() :
713 Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000714 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
715 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000716 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000717 self->set_resource(resource);
718 // Additionally make the object into an external symbol if the original string
719 // was a symbol to start with.
720 if (is_symbol) {
721 self->Hash(); // Force regeneration of the hash value.
722 // Now morph this external string into a external symbol.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100723 this->set_map(is_ascii ?
724 Heap::external_symbol_with_ascii_data_map() :
725 Heap::external_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000726 }
727
728 // Fill the remainder of the string with dead wood.
729 int new_size = this->Size(); // Byte size of the external String object.
730 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
731 return true;
732}
733
734
735bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
736#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000737 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000738 // Assert that the resource and the string are equivalent.
739 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100740 ScopedVector<char> smart_chars(this->length());
741 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
742 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000743 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100744 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000745 }
746#endif // DEBUG
747
748 int size = this->Size(); // Byte size of the original string.
749 if (size < ExternalString::kSize) {
750 // The string is too small to fit an external String in its place. This can
751 // only happen for zero length strings.
752 return false;
753 }
754 ASSERT(size >= ExternalString::kSize);
755 bool is_symbol = this->IsSymbol();
756 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000757 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000758
759 // Morph the object to an external string by adjusting the map and
760 // reinitializing the fields.
Steve Blockd0582a62009-12-15 09:54:21 +0000761 this->set_map(Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000762 ExternalAsciiString* self = ExternalAsciiString::cast(this);
763 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000764 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000765 self->set_resource(resource);
766 // Additionally make the object into an external symbol if the original string
767 // was a symbol to start with.
768 if (is_symbol) {
769 self->Hash(); // Force regeneration of the hash value.
770 // Now morph this external string into a external symbol.
Steve Blockd0582a62009-12-15 09:54:21 +0000771 this->set_map(Heap::external_ascii_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000772 }
773
774 // Fill the remainder of the string with dead wood.
775 int new_size = this->Size(); // Byte size of the external String object.
776 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
777 return true;
778}
779
780
781void String::StringShortPrint(StringStream* accumulator) {
782 int len = length();
Steve Blockd0582a62009-12-15 09:54:21 +0000783 if (len > kMaxShortPrintLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000784 accumulator->Add("<Very long string[%u]>", len);
785 return;
786 }
787
788 if (!LooksValid()) {
789 accumulator->Add("<Invalid String>");
790 return;
791 }
792
793 StringInputBuffer buf(this);
794
795 bool truncated = false;
796 if (len > kMaxShortPrintLength) {
797 len = kMaxShortPrintLength;
798 truncated = true;
799 }
800 bool ascii = true;
801 for (int i = 0; i < len; i++) {
802 int c = buf.GetNext();
803
804 if (c < 32 || c >= 127) {
805 ascii = false;
806 }
807 }
808 buf.Reset(this);
809 if (ascii) {
810 accumulator->Add("<String[%u]: ", length());
811 for (int i = 0; i < len; i++) {
812 accumulator->Put(buf.GetNext());
813 }
814 accumulator->Put('>');
815 } else {
816 // Backslash indicates that the string contains control
817 // characters and that backslashes are therefore escaped.
818 accumulator->Add("<String[%u]\\: ", length());
819 for (int i = 0; i < len; i++) {
820 int c = buf.GetNext();
821 if (c == '\n') {
822 accumulator->Add("\\n");
823 } else if (c == '\r') {
824 accumulator->Add("\\r");
825 } else if (c == '\\') {
826 accumulator->Add("\\\\");
827 } else if (c < 32 || c > 126) {
828 accumulator->Add("\\x%02x", c);
829 } else {
830 accumulator->Put(c);
831 }
832 }
833 if (truncated) {
834 accumulator->Put('.');
835 accumulator->Put('.');
836 accumulator->Put('.');
837 }
838 accumulator->Put('>');
839 }
840 return;
841}
842
843
844void JSObject::JSObjectShortPrint(StringStream* accumulator) {
845 switch (map()->instance_type()) {
846 case JS_ARRAY_TYPE: {
847 double length = JSArray::cast(this)->length()->Number();
848 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
849 break;
850 }
851 case JS_REGEXP_TYPE: {
852 accumulator->Add("<JS RegExp>");
853 break;
854 }
855 case JS_FUNCTION_TYPE: {
856 Object* fun_name = JSFunction::cast(this)->shared()->name();
857 bool printed = false;
858 if (fun_name->IsString()) {
859 String* str = String::cast(fun_name);
860 if (str->length() > 0) {
861 accumulator->Add("<JS Function ");
862 accumulator->Put(str);
863 accumulator->Put('>');
864 printed = true;
865 }
866 }
867 if (!printed) {
868 accumulator->Add("<JS Function>");
869 }
870 break;
871 }
872 // All other JSObjects are rather similar to each other (JSObject,
873 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
874 default: {
875 Object* constructor = map()->constructor();
876 bool printed = false;
877 if (constructor->IsHeapObject() &&
878 !Heap::Contains(HeapObject::cast(constructor))) {
879 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
880 } else {
881 bool global_object = IsJSGlobalProxy();
882 if (constructor->IsJSFunction()) {
883 if (!Heap::Contains(JSFunction::cast(constructor)->shared())) {
884 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
885 } else {
886 Object* constructor_name =
887 JSFunction::cast(constructor)->shared()->name();
888 if (constructor_name->IsString()) {
889 String* str = String::cast(constructor_name);
890 if (str->length() > 0) {
891 bool vowel = AnWord(str);
892 accumulator->Add("<%sa%s ",
893 global_object ? "Global Object: " : "",
894 vowel ? "n" : "");
895 accumulator->Put(str);
896 accumulator->Put('>');
897 printed = true;
898 }
899 }
900 }
901 }
902 if (!printed) {
903 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
904 }
905 }
906 if (IsJSValue()) {
907 accumulator->Add(" value = ");
908 JSValue::cast(this)->value()->ShortPrint(accumulator);
909 }
910 accumulator->Put('>');
911 break;
912 }
913 }
914}
915
916
917void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
918 // if (!Heap::InNewSpace(this)) PrintF("*", this);
919 if (!Heap::Contains(this)) {
920 accumulator->Add("!!!INVALID POINTER!!!");
921 return;
922 }
923 if (!Heap::Contains(map())) {
924 accumulator->Add("!!!INVALID MAP!!!");
925 return;
926 }
927
928 accumulator->Add("%p ", this);
929
930 if (IsString()) {
931 String::cast(this)->StringShortPrint(accumulator);
932 return;
933 }
934 if (IsJSObject()) {
935 JSObject::cast(this)->JSObjectShortPrint(accumulator);
936 return;
937 }
938 switch (map()->instance_type()) {
939 case MAP_TYPE:
940 accumulator->Add("<Map>");
941 break;
942 case FIXED_ARRAY_TYPE:
943 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
944 break;
945 case BYTE_ARRAY_TYPE:
946 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
947 break;
948 case PIXEL_ARRAY_TYPE:
949 accumulator->Add("<PixelArray[%u]>", PixelArray::cast(this)->length());
950 break;
Steve Block3ce2e202009-11-05 08:53:23 +0000951 case EXTERNAL_BYTE_ARRAY_TYPE:
952 accumulator->Add("<ExternalByteArray[%u]>",
953 ExternalByteArray::cast(this)->length());
954 break;
955 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
956 accumulator->Add("<ExternalUnsignedByteArray[%u]>",
957 ExternalUnsignedByteArray::cast(this)->length());
958 break;
959 case EXTERNAL_SHORT_ARRAY_TYPE:
960 accumulator->Add("<ExternalShortArray[%u]>",
961 ExternalShortArray::cast(this)->length());
962 break;
963 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
964 accumulator->Add("<ExternalUnsignedShortArray[%u]>",
965 ExternalUnsignedShortArray::cast(this)->length());
966 break;
967 case EXTERNAL_INT_ARRAY_TYPE:
968 accumulator->Add("<ExternalIntArray[%u]>",
969 ExternalIntArray::cast(this)->length());
970 break;
971 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
972 accumulator->Add("<ExternalUnsignedIntArray[%u]>",
973 ExternalUnsignedIntArray::cast(this)->length());
974 break;
975 case EXTERNAL_FLOAT_ARRAY_TYPE:
976 accumulator->Add("<ExternalFloatArray[%u]>",
977 ExternalFloatArray::cast(this)->length());
978 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000979 case SHARED_FUNCTION_INFO_TYPE:
980 accumulator->Add("<SharedFunctionInfo>");
981 break;
982#define MAKE_STRUCT_CASE(NAME, Name, name) \
983 case NAME##_TYPE: \
984 accumulator->Put('<'); \
985 accumulator->Add(#Name); \
986 accumulator->Put('>'); \
987 break;
988 STRUCT_LIST(MAKE_STRUCT_CASE)
989#undef MAKE_STRUCT_CASE
990 case CODE_TYPE:
991 accumulator->Add("<Code>");
992 break;
993 case ODDBALL_TYPE: {
994 if (IsUndefined())
995 accumulator->Add("<undefined>");
996 else if (IsTheHole())
997 accumulator->Add("<the hole>");
998 else if (IsNull())
999 accumulator->Add("<null>");
1000 else if (IsTrue())
1001 accumulator->Add("<true>");
1002 else if (IsFalse())
1003 accumulator->Add("<false>");
1004 else
1005 accumulator->Add("<Odd Oddball>");
1006 break;
1007 }
1008 case HEAP_NUMBER_TYPE:
1009 accumulator->Add("<Number: ");
1010 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
1011 accumulator->Put('>');
1012 break;
1013 case PROXY_TYPE:
1014 accumulator->Add("<Proxy>");
1015 break;
1016 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1017 accumulator->Add("Cell for ");
1018 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator);
1019 break;
1020 default:
1021 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
1022 break;
1023 }
1024}
1025
1026
Steve Blocka7e24c12009-10-30 11:49:00 +00001027void HeapObject::Iterate(ObjectVisitor* v) {
1028 // Handle header
1029 IteratePointer(v, kMapOffset);
1030 // Handle object body
1031 Map* m = map();
1032 IterateBody(m->instance_type(), SizeFromMap(m), v);
1033}
1034
1035
1036void HeapObject::IterateBody(InstanceType type, int object_size,
1037 ObjectVisitor* v) {
1038 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
1039 // During GC, the map pointer field is encoded.
1040 if (type < FIRST_NONSTRING_TYPE) {
1041 switch (type & kStringRepresentationMask) {
1042 case kSeqStringTag:
1043 break;
1044 case kConsStringTag:
Iain Merrick75681382010-08-19 15:07:18 +01001045 ConsString::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001046 break;
Steve Blockd0582a62009-12-15 09:54:21 +00001047 case kExternalStringTag:
1048 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1049 reinterpret_cast<ExternalAsciiString*>(this)->
1050 ExternalAsciiStringIterateBody(v);
1051 } else {
1052 reinterpret_cast<ExternalTwoByteString*>(this)->
1053 ExternalTwoByteStringIterateBody(v);
1054 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001055 break;
1056 }
1057 return;
1058 }
1059
1060 switch (type) {
1061 case FIXED_ARRAY_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001062 FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001063 break;
1064 case JS_OBJECT_TYPE:
1065 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
1066 case JS_VALUE_TYPE:
1067 case JS_ARRAY_TYPE:
1068 case JS_REGEXP_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001069 case JS_GLOBAL_PROXY_TYPE:
1070 case JS_GLOBAL_OBJECT_TYPE:
1071 case JS_BUILTINS_OBJECT_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001072 JSObject::BodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001073 break;
Steve Block791712a2010-08-27 10:21:07 +01001074 case JS_FUNCTION_TYPE:
1075 reinterpret_cast<JSFunction*>(this)
1076 ->JSFunctionIterateBody(object_size, v);
1077 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001078 case ODDBALL_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001079 Oddball::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001080 break;
1081 case PROXY_TYPE:
1082 reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
1083 break;
1084 case MAP_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001085 Map::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001086 break;
1087 case CODE_TYPE:
1088 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1089 break;
1090 case JS_GLOBAL_PROPERTY_CELL_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001091 JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001092 break;
1093 case HEAP_NUMBER_TYPE:
1094 case FILLER_TYPE:
1095 case BYTE_ARRAY_TYPE:
1096 case PIXEL_ARRAY_TYPE:
Steve Block3ce2e202009-11-05 08:53:23 +00001097 case EXTERNAL_BYTE_ARRAY_TYPE:
1098 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1099 case EXTERNAL_SHORT_ARRAY_TYPE:
1100 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1101 case EXTERNAL_INT_ARRAY_TYPE:
1102 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1103 case EXTERNAL_FLOAT_ARRAY_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001104 break;
Iain Merrick75681382010-08-19 15:07:18 +01001105 case SHARED_FUNCTION_INFO_TYPE:
1106 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001107 break;
Iain Merrick75681382010-08-19 15:07:18 +01001108
Steve Blocka7e24c12009-10-30 11:49:00 +00001109#define MAKE_STRUCT_CASE(NAME, Name, name) \
1110 case NAME##_TYPE:
1111 STRUCT_LIST(MAKE_STRUCT_CASE)
1112#undef MAKE_STRUCT_CASE
Iain Merrick75681382010-08-19 15:07:18 +01001113 StructBodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001114 break;
1115 default:
1116 PrintF("Unknown type: %d\n", type);
1117 UNREACHABLE();
1118 }
1119}
1120
1121
Steve Blocka7e24c12009-10-30 11:49:00 +00001122Object* HeapNumber::HeapNumberToBoolean() {
1123 // NaN, +0, and -0 should return the false object
Iain Merrick75681382010-08-19 15:07:18 +01001124#if __BYTE_ORDER == __LITTLE_ENDIAN
1125 union IeeeDoubleLittleEndianArchType u;
1126#elif __BYTE_ORDER == __BIG_ENDIAN
1127 union IeeeDoubleBigEndianArchType u;
1128#endif
1129 u.d = value();
1130 if (u.bits.exp == 2047) {
1131 // Detect NaN for IEEE double precision floating point.
1132 if ((u.bits.man_low | u.bits.man_high) != 0)
1133 return Heap::false_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001134 }
Iain Merrick75681382010-08-19 15:07:18 +01001135 if (u.bits.exp == 0) {
1136 // Detect +0, and -0 for IEEE double precision floating point.
1137 if ((u.bits.man_low | u.bits.man_high) == 0)
1138 return Heap::false_value();
1139 }
1140 return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001141}
1142
1143
1144void HeapNumber::HeapNumberPrint() {
1145 PrintF("%.16g", Number());
1146}
1147
1148
1149void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1150 // The Windows version of vsnprintf can allocate when printing a %g string
1151 // into a buffer that may not be big enough. We don't want random memory
1152 // allocation when producing post-crash stack traces, so we print into a
1153 // buffer that is plenty big enough for any floating point number, then
1154 // print that using vsnprintf (which may truncate but never allocate if
1155 // there is no more space in the buffer).
1156 EmbeddedVector<char, 100> buffer;
1157 OS::SNPrintF(buffer, "%.16g", Number());
1158 accumulator->Add("%s", buffer.start());
1159}
1160
1161
1162String* JSObject::class_name() {
1163 if (IsJSFunction()) {
1164 return Heap::function_class_symbol();
1165 }
1166 if (map()->constructor()->IsJSFunction()) {
1167 JSFunction* constructor = JSFunction::cast(map()->constructor());
1168 return String::cast(constructor->shared()->instance_class_name());
1169 }
1170 // If the constructor is not present, return "Object".
1171 return Heap::Object_symbol();
1172}
1173
1174
1175String* JSObject::constructor_name() {
1176 if (IsJSFunction()) {
Steve Block6ded16b2010-05-10 14:33:55 +01001177 return Heap::closure_symbol();
Steve Blocka7e24c12009-10-30 11:49:00 +00001178 }
1179 if (map()->constructor()->IsJSFunction()) {
1180 JSFunction* constructor = JSFunction::cast(map()->constructor());
1181 String* name = String::cast(constructor->shared()->name());
1182 return name->length() > 0 ? name : constructor->shared()->inferred_name();
1183 }
1184 // If the constructor is not present, return "Object".
1185 return Heap::Object_symbol();
1186}
1187
1188
Steve Blocka7e24c12009-10-30 11:49:00 +00001189Object* JSObject::AddFastPropertyUsingMap(Map* new_map,
1190 String* name,
1191 Object* value) {
1192 int index = new_map->PropertyIndexFor(name);
1193 if (map()->unused_property_fields() == 0) {
1194 ASSERT(map()->unused_property_fields() == 0);
1195 int new_unused = new_map->unused_property_fields();
1196 Object* values =
1197 properties()->CopySize(properties()->length() + new_unused + 1);
1198 if (values->IsFailure()) return values;
1199 set_properties(FixedArray::cast(values));
1200 }
1201 set_map(new_map);
1202 return FastPropertyAtPut(index, value);
1203}
1204
1205
1206Object* JSObject::AddFastProperty(String* name,
1207 Object* value,
1208 PropertyAttributes attributes) {
1209 // Normalize the object if the name is an actual string (not the
1210 // hidden symbols) and is not a real identifier.
1211 StringInputBuffer buffer(name);
1212 if (!Scanner::IsIdentifier(&buffer) && name != Heap::hidden_symbol()) {
1213 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1214 if (obj->IsFailure()) return obj;
1215 return AddSlowProperty(name, value, attributes);
1216 }
1217
1218 DescriptorArray* old_descriptors = map()->instance_descriptors();
1219 // Compute the new index for new field.
1220 int index = map()->NextFreePropertyIndex();
1221
1222 // Allocate new instance descriptors with (name, index) added
1223 FieldDescriptor new_field(name, index, attributes);
1224 Object* new_descriptors =
1225 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1226 if (new_descriptors->IsFailure()) return new_descriptors;
1227
1228 // Only allow map transition if the object's map is NOT equal to the
1229 // global object_function's map and there is not a transition for name.
1230 bool allow_map_transition =
1231 !old_descriptors->Contains(name) &&
1232 (Top::context()->global_context()->object_function()->map() != map());
1233
1234 ASSERT(index < map()->inobject_properties() ||
1235 (index - map()->inobject_properties()) < properties()->length() ||
1236 map()->unused_property_fields() == 0);
1237 // Allocate a new map for the object.
1238 Object* r = map()->CopyDropDescriptors();
1239 if (r->IsFailure()) return r;
1240 Map* new_map = Map::cast(r);
1241 if (allow_map_transition) {
1242 // Allocate new instance descriptors for the old map with map transition.
1243 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
1244 Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1245 if (r->IsFailure()) return r;
1246 old_descriptors = DescriptorArray::cast(r);
1247 }
1248
1249 if (map()->unused_property_fields() == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01001250 if (properties()->length() > MaxFastProperties()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001251 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1252 if (obj->IsFailure()) return obj;
1253 return AddSlowProperty(name, value, attributes);
1254 }
1255 // Make room for the new value
1256 Object* values =
1257 properties()->CopySize(properties()->length() + kFieldsAdded);
1258 if (values->IsFailure()) return values;
1259 set_properties(FixedArray::cast(values));
1260 new_map->set_unused_property_fields(kFieldsAdded - 1);
1261 } else {
1262 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
1263 }
1264 // We have now allocated all the necessary objects.
1265 // All the changes can be applied at once, so they are atomic.
1266 map()->set_instance_descriptors(old_descriptors);
1267 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1268 set_map(new_map);
1269 return FastPropertyAtPut(index, value);
1270}
1271
1272
1273Object* JSObject::AddConstantFunctionProperty(String* name,
1274 JSFunction* function,
1275 PropertyAttributes attributes) {
Leon Clarkee46be812010-01-19 14:06:41 +00001276 ASSERT(!Heap::InNewSpace(function));
1277
Steve Blocka7e24c12009-10-30 11:49:00 +00001278 // Allocate new instance descriptors with (name, function) added
1279 ConstantFunctionDescriptor d(name, function, attributes);
1280 Object* new_descriptors =
1281 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1282 if (new_descriptors->IsFailure()) return new_descriptors;
1283
1284 // Allocate a new map for the object.
1285 Object* new_map = map()->CopyDropDescriptors();
1286 if (new_map->IsFailure()) return new_map;
1287
1288 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1289 Map::cast(new_map)->set_instance_descriptors(descriptors);
1290 Map* old_map = map();
1291 set_map(Map::cast(new_map));
1292
1293 // If the old map is the global object map (from new Object()),
1294 // then transitions are not added to it, so we are done.
1295 if (old_map == Top::context()->global_context()->object_function()->map()) {
1296 return function;
1297 }
1298
1299 // Do not add CONSTANT_TRANSITIONS to global objects
1300 if (IsGlobalObject()) {
1301 return function;
1302 }
1303
1304 // Add a CONSTANT_TRANSITION descriptor to the old map,
1305 // so future assignments to this property on other objects
1306 // of the same type will create a normal field, not a constant function.
1307 // Don't do this for special properties, with non-trival attributes.
1308 if (attributes != NONE) {
1309 return function;
1310 }
Iain Merrick75681382010-08-19 15:07:18 +01001311 ConstTransitionDescriptor mark(name, Map::cast(new_map));
Steve Blocka7e24c12009-10-30 11:49:00 +00001312 new_descriptors =
1313 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1314 if (new_descriptors->IsFailure()) {
1315 return function; // We have accomplished the main goal, so return success.
1316 }
1317 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1318
1319 return function;
1320}
1321
1322
1323// Add property in slow mode
1324Object* JSObject::AddSlowProperty(String* name,
1325 Object* value,
1326 PropertyAttributes attributes) {
1327 ASSERT(!HasFastProperties());
1328 StringDictionary* dict = property_dictionary();
1329 Object* store_value = value;
1330 if (IsGlobalObject()) {
1331 // In case name is an orphaned property reuse the cell.
1332 int entry = dict->FindEntry(name);
1333 if (entry != StringDictionary::kNotFound) {
1334 store_value = dict->ValueAt(entry);
1335 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1336 // Assign an enumeration index to the property and update
1337 // SetNextEnumerationIndex.
1338 int index = dict->NextEnumerationIndex();
1339 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1340 dict->SetNextEnumerationIndex(index + 1);
1341 dict->SetEntry(entry, name, store_value, details);
1342 return value;
1343 }
1344 store_value = Heap::AllocateJSGlobalPropertyCell(value);
1345 if (store_value->IsFailure()) return store_value;
1346 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1347 }
1348 PropertyDetails details = PropertyDetails(attributes, NORMAL);
1349 Object* result = dict->Add(name, store_value, details);
1350 if (result->IsFailure()) return result;
1351 if (dict != result) set_properties(StringDictionary::cast(result));
1352 return value;
1353}
1354
1355
1356Object* JSObject::AddProperty(String* name,
1357 Object* value,
1358 PropertyAttributes attributes) {
1359 ASSERT(!IsJSGlobalProxy());
Steve Block8defd9f2010-07-08 12:39:36 +01001360 if (!map()->is_extensible()) {
1361 Handle<Object> args[1] = {Handle<String>(name)};
1362 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
1363 HandleVector(args, 1)));
1364 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001365 if (HasFastProperties()) {
1366 // Ensure the descriptor array does not get too big.
1367 if (map()->instance_descriptors()->number_of_descriptors() <
1368 DescriptorArray::kMaxNumberOfDescriptors) {
Leon Clarkee46be812010-01-19 14:06:41 +00001369 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001370 return AddConstantFunctionProperty(name,
1371 JSFunction::cast(value),
1372 attributes);
1373 } else {
1374 return AddFastProperty(name, value, attributes);
1375 }
1376 } else {
1377 // Normalize the object to prevent very large instance descriptors.
1378 // This eliminates unwanted N^2 allocation and lookup behavior.
1379 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1380 if (obj->IsFailure()) return obj;
1381 }
1382 }
1383 return AddSlowProperty(name, value, attributes);
1384}
1385
1386
1387Object* JSObject::SetPropertyPostInterceptor(String* name,
1388 Object* value,
1389 PropertyAttributes attributes) {
1390 // Check local property, ignore interceptor.
1391 LookupResult result;
1392 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001393 if (result.IsFound()) {
1394 // An existing property, a map transition or a null descriptor was
1395 // found. Use set property to handle all these cases.
1396 return SetProperty(&result, name, value, attributes);
1397 }
1398 // Add a new real property.
Steve Blocka7e24c12009-10-30 11:49:00 +00001399 return AddProperty(name, value, attributes);
1400}
1401
1402
1403Object* JSObject::ReplaceSlowProperty(String* name,
Steve Blockd0582a62009-12-15 09:54:21 +00001404 Object* value,
1405 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001406 StringDictionary* dictionary = property_dictionary();
1407 int old_index = dictionary->FindEntry(name);
1408 int new_enumeration_index = 0; // 0 means "Use the next available index."
1409 if (old_index != -1) {
1410 // All calls to ReplaceSlowProperty have had all transitions removed.
1411 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1412 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1413 }
1414
1415 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
1416 return SetNormalizedProperty(name, value, new_details);
1417}
1418
Steve Blockd0582a62009-12-15 09:54:21 +00001419
Steve Blocka7e24c12009-10-30 11:49:00 +00001420Object* JSObject::ConvertDescriptorToFieldAndMapTransition(
1421 String* name,
1422 Object* new_value,
1423 PropertyAttributes attributes) {
1424 Map* old_map = map();
1425 Object* result = ConvertDescriptorToField(name, new_value, attributes);
1426 if (result->IsFailure()) return result;
1427 // If we get to this point we have succeeded - do not return failure
1428 // after this point. Later stuff is optional.
1429 if (!HasFastProperties()) {
1430 return result;
1431 }
1432 // Do not add transitions to the map of "new Object()".
1433 if (map() == Top::context()->global_context()->object_function()->map()) {
1434 return result;
1435 }
1436
1437 MapTransitionDescriptor transition(name,
1438 map(),
1439 attributes);
1440 Object* new_descriptors =
1441 old_map->instance_descriptors()->
1442 CopyInsert(&transition, KEEP_TRANSITIONS);
1443 if (new_descriptors->IsFailure()) return result; // Yes, return _result_.
1444 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1445 return result;
1446}
1447
1448
1449Object* JSObject::ConvertDescriptorToField(String* name,
1450 Object* new_value,
1451 PropertyAttributes attributes) {
1452 if (map()->unused_property_fields() == 0 &&
Steve Block8defd9f2010-07-08 12:39:36 +01001453 properties()->length() > MaxFastProperties()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001454 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1455 if (obj->IsFailure()) return obj;
1456 return ReplaceSlowProperty(name, new_value, attributes);
1457 }
1458
1459 int index = map()->NextFreePropertyIndex();
1460 FieldDescriptor new_field(name, index, attributes);
1461 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
1462 Object* descriptors_unchecked = map()->instance_descriptors()->
1463 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1464 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
1465 DescriptorArray* new_descriptors =
1466 DescriptorArray::cast(descriptors_unchecked);
1467
1468 // Make a new map for the object.
1469 Object* new_map_unchecked = map()->CopyDropDescriptors();
1470 if (new_map_unchecked->IsFailure()) return new_map_unchecked;
1471 Map* new_map = Map::cast(new_map_unchecked);
1472 new_map->set_instance_descriptors(new_descriptors);
1473
1474 // Make new properties array if necessary.
1475 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1476 int new_unused_property_fields = map()->unused_property_fields() - 1;
1477 if (map()->unused_property_fields() == 0) {
1478 new_unused_property_fields = kFieldsAdded - 1;
1479 Object* new_properties_unchecked =
1480 properties()->CopySize(properties()->length() + kFieldsAdded);
1481 if (new_properties_unchecked->IsFailure()) return new_properties_unchecked;
1482 new_properties = FixedArray::cast(new_properties_unchecked);
1483 }
1484
1485 // Update pointers to commit changes.
1486 // Object points to the new map.
1487 new_map->set_unused_property_fields(new_unused_property_fields);
1488 set_map(new_map);
1489 if (new_properties) {
1490 set_properties(FixedArray::cast(new_properties));
1491 }
1492 return FastPropertyAtPut(index, new_value);
1493}
1494
1495
1496
1497Object* JSObject::SetPropertyWithInterceptor(String* name,
1498 Object* value,
1499 PropertyAttributes attributes) {
1500 HandleScope scope;
1501 Handle<JSObject> this_handle(this);
1502 Handle<String> name_handle(name);
1503 Handle<Object> value_handle(value);
1504 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1505 if (!interceptor->setter()->IsUndefined()) {
1506 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name));
1507 CustomArguments args(interceptor->data(), this, this);
1508 v8::AccessorInfo info(args.end());
1509 v8::NamedPropertySetter setter =
1510 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1511 v8::Handle<v8::Value> result;
1512 {
1513 // Leaving JavaScript.
1514 VMState state(EXTERNAL);
1515 Handle<Object> value_unhole(value->IsTheHole() ?
1516 Heap::undefined_value() :
1517 value);
1518 result = setter(v8::Utils::ToLocal(name_handle),
1519 v8::Utils::ToLocal(value_unhole),
1520 info);
1521 }
1522 RETURN_IF_SCHEDULED_EXCEPTION();
1523 if (!result.IsEmpty()) return *value_handle;
1524 }
1525 Object* raw_result = this_handle->SetPropertyPostInterceptor(*name_handle,
1526 *value_handle,
1527 attributes);
1528 RETURN_IF_SCHEDULED_EXCEPTION();
1529 return raw_result;
1530}
1531
1532
1533Object* JSObject::SetProperty(String* name,
1534 Object* value,
1535 PropertyAttributes attributes) {
1536 LookupResult result;
1537 LocalLookup(name, &result);
1538 return SetProperty(&result, name, value, attributes);
1539}
1540
1541
1542Object* JSObject::SetPropertyWithCallback(Object* structure,
1543 String* name,
1544 Object* value,
1545 JSObject* holder) {
1546 HandleScope scope;
1547
1548 // We should never get here to initialize a const with the hole
1549 // value since a const declaration would conflict with the setter.
1550 ASSERT(!value->IsTheHole());
1551 Handle<Object> value_handle(value);
1552
1553 // To accommodate both the old and the new api we switch on the
1554 // data structure used to store the callbacks. Eventually proxy
1555 // callbacks should be phased out.
1556 if (structure->IsProxy()) {
1557 AccessorDescriptor* callback =
1558 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
1559 Object* obj = (callback->setter)(this, value, callback->data);
1560 RETURN_IF_SCHEDULED_EXCEPTION();
1561 if (obj->IsFailure()) return obj;
1562 return *value_handle;
1563 }
1564
1565 if (structure->IsAccessorInfo()) {
1566 // api style callbacks
1567 AccessorInfo* data = AccessorInfo::cast(structure);
1568 Object* call_obj = data->setter();
1569 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1570 if (call_fun == NULL) return value;
1571 Handle<String> key(name);
1572 LOG(ApiNamedPropertyAccess("store", this, name));
1573 CustomArguments args(data->data(), this, JSObject::cast(holder));
1574 v8::AccessorInfo info(args.end());
1575 {
1576 // Leaving JavaScript.
1577 VMState state(EXTERNAL);
1578 call_fun(v8::Utils::ToLocal(key),
1579 v8::Utils::ToLocal(value_handle),
1580 info);
1581 }
1582 RETURN_IF_SCHEDULED_EXCEPTION();
1583 return *value_handle;
1584 }
1585
1586 if (structure->IsFixedArray()) {
1587 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1588 if (setter->IsJSFunction()) {
1589 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
1590 } else {
1591 Handle<String> key(name);
1592 Handle<Object> holder_handle(holder);
1593 Handle<Object> args[2] = { key, holder_handle };
1594 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
1595 HandleVector(args, 2)));
1596 }
1597 }
1598
1599 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +01001600 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001601}
1602
1603
1604Object* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1605 Object* value) {
1606 Handle<Object> value_handle(value);
1607 Handle<JSFunction> fun(JSFunction::cast(setter));
1608 Handle<JSObject> self(this);
1609#ifdef ENABLE_DEBUGGER_SUPPORT
1610 // Handle stepping into a setter if step into is active.
1611 if (Debug::StepInActive()) {
1612 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
1613 }
1614#endif
1615 bool has_pending_exception;
1616 Object** argv[] = { value_handle.location() };
1617 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1618 // Check for pending exception and return the result.
1619 if (has_pending_exception) return Failure::Exception();
1620 return *value_handle;
1621}
1622
1623
1624void JSObject::LookupCallbackSetterInPrototypes(String* name,
1625 LookupResult* result) {
1626 for (Object* pt = GetPrototype();
1627 pt != Heap::null_value();
1628 pt = pt->GetPrototype()) {
1629 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001630 if (result->IsProperty()) {
1631 if (result->IsReadOnly()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001632 result->NotFound();
1633 return;
1634 }
1635 if (result->type() == CALLBACKS) {
1636 return;
1637 }
1638 }
1639 }
1640 result->NotFound();
1641}
1642
1643
Leon Clarkef7060e22010-06-03 12:02:55 +01001644bool JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
1645 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001646 for (Object* pt = GetPrototype();
1647 pt != Heap::null_value();
1648 pt = pt->GetPrototype()) {
1649 if (!JSObject::cast(pt)->HasDictionaryElements()) {
1650 continue;
1651 }
1652 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1653 int entry = dictionary->FindEntry(index);
1654 if (entry != NumberDictionary::kNotFound) {
1655 Object* element = dictionary->ValueAt(entry);
1656 PropertyDetails details = dictionary->DetailsAt(entry);
1657 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01001658 SetElementWithCallback(element, index, value, JSObject::cast(pt));
1659 return true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001660 }
1661 }
1662 }
Leon Clarkef7060e22010-06-03 12:02:55 +01001663 return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001664}
1665
1666
1667void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1668 DescriptorArray* descriptors = map()->instance_descriptors();
Iain Merrick75681382010-08-19 15:07:18 +01001669 int number = descriptors->SearchWithCache(name);
Steve Blocka7e24c12009-10-30 11:49:00 +00001670 if (number != DescriptorArray::kNotFound) {
1671 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1672 } else {
1673 result->NotFound();
1674 }
1675}
1676
1677
1678void JSObject::LocalLookupRealNamedProperty(String* name,
1679 LookupResult* result) {
1680 if (IsJSGlobalProxy()) {
1681 Object* proto = GetPrototype();
1682 if (proto->IsNull()) return result->NotFound();
1683 ASSERT(proto->IsJSGlobalObject());
1684 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
1685 }
1686
1687 if (HasFastProperties()) {
1688 LookupInDescriptor(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001689 if (result->IsFound()) {
1690 // A property, a map transition or a null descriptor was found.
1691 // We return all of these result types because
1692 // LocalLookupRealNamedProperty is used when setting properties
1693 // where map transitions and null descriptors are handled.
Steve Blocka7e24c12009-10-30 11:49:00 +00001694 ASSERT(result->holder() == this && result->type() != NORMAL);
1695 // Disallow caching for uninitialized constants. These can only
1696 // occur as fields.
1697 if (result->IsReadOnly() && result->type() == FIELD &&
1698 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
1699 result->DisallowCaching();
1700 }
1701 return;
1702 }
1703 } else {
1704 int entry = property_dictionary()->FindEntry(name);
1705 if (entry != StringDictionary::kNotFound) {
1706 Object* value = property_dictionary()->ValueAt(entry);
1707 if (IsGlobalObject()) {
1708 PropertyDetails d = property_dictionary()->DetailsAt(entry);
1709 if (d.IsDeleted()) {
1710 result->NotFound();
1711 return;
1712 }
1713 value = JSGlobalPropertyCell::cast(value)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001714 }
1715 // Make sure to disallow caching for uninitialized constants
1716 // found in the dictionary-mode objects.
1717 if (value->IsTheHole()) result->DisallowCaching();
1718 result->DictionaryResult(this, entry);
1719 return;
1720 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001721 }
1722 result->NotFound();
1723}
1724
1725
1726void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
1727 LocalLookupRealNamedProperty(name, result);
1728 if (result->IsProperty()) return;
1729
1730 LookupRealNamedPropertyInPrototypes(name, result);
1731}
1732
1733
1734void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
1735 LookupResult* result) {
1736 for (Object* pt = GetPrototype();
1737 pt != Heap::null_value();
1738 pt = JSObject::cast(pt)->GetPrototype()) {
1739 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001740 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001741 }
1742 result->NotFound();
1743}
1744
1745
1746// We only need to deal with CALLBACKS and INTERCEPTORS
1747Object* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
1748 String* name,
1749 Object* value) {
1750 if (!result->IsProperty()) {
1751 LookupCallbackSetterInPrototypes(name, result);
1752 }
1753
1754 if (result->IsProperty()) {
1755 if (!result->IsReadOnly()) {
1756 switch (result->type()) {
1757 case CALLBACKS: {
1758 Object* obj = result->GetCallbackObject();
1759 if (obj->IsAccessorInfo()) {
1760 AccessorInfo* info = AccessorInfo::cast(obj);
1761 if (info->all_can_write()) {
1762 return SetPropertyWithCallback(result->GetCallbackObject(),
1763 name,
1764 value,
1765 result->holder());
1766 }
1767 }
1768 break;
1769 }
1770 case INTERCEPTOR: {
1771 // Try lookup real named properties. Note that only property can be
1772 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
1773 LookupResult r;
1774 LookupRealNamedProperty(name, &r);
1775 if (r.IsProperty()) {
1776 return SetPropertyWithFailedAccessCheck(&r, name, value);
1777 }
1778 break;
1779 }
1780 default: {
1781 break;
1782 }
1783 }
1784 }
1785 }
1786
Iain Merrick75681382010-08-19 15:07:18 +01001787 HandleScope scope;
1788 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001789 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01001790 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00001791}
1792
1793
1794Object* JSObject::SetProperty(LookupResult* result,
1795 String* name,
1796 Object* value,
1797 PropertyAttributes attributes) {
1798 // Make sure that the top context does not change when doing callbacks or
1799 // interceptor calls.
1800 AssertNoContextChange ncc;
1801
Steve Blockd0582a62009-12-15 09:54:21 +00001802 // Optimization for 2-byte strings often used as keys in a decompression
1803 // dictionary. We make these short keys into symbols to avoid constantly
1804 // reallocating them.
1805 if (!name->IsSymbol() && name->length() <= 2) {
1806 Object* symbol_version = Heap::LookupSymbol(name);
1807 if (!symbol_version->IsFailure()) name = String::cast(symbol_version);
1808 }
1809
Steve Blocka7e24c12009-10-30 11:49:00 +00001810 // Check access rights if needed.
1811 if (IsAccessCheckNeeded()
1812 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1813 return SetPropertyWithFailedAccessCheck(result, name, value);
1814 }
1815
1816 if (IsJSGlobalProxy()) {
1817 Object* proto = GetPrototype();
1818 if (proto->IsNull()) return value;
1819 ASSERT(proto->IsJSGlobalObject());
1820 return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
1821 }
1822
1823 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
1824 // We could not find a local property so let's check whether there is an
1825 // accessor that wants to handle the property.
1826 LookupResult accessor_result;
1827 LookupCallbackSetterInPrototypes(name, &accessor_result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001828 if (accessor_result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001829 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
1830 name,
1831 value,
1832 accessor_result.holder());
1833 }
1834 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001835 if (!result->IsFound()) {
1836 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001837 return AddProperty(name, value, attributes);
1838 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001839 if (result->IsReadOnly() && result->IsProperty()) return value;
1840 // This is a real property that is not read-only, or it is a
1841 // transition or null descriptor and there are no setters in the prototypes.
1842 switch (result->type()) {
1843 case NORMAL:
1844 return SetNormalizedProperty(result, value);
1845 case FIELD:
1846 return FastPropertyAtPut(result->GetFieldIndex(), value);
1847 case MAP_TRANSITION:
1848 if (attributes == result->GetAttributes()) {
1849 // Only use map transition if the attributes match.
1850 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1851 name,
1852 value);
1853 }
1854 return ConvertDescriptorToField(name, value, attributes);
1855 case CONSTANT_FUNCTION:
1856 // Only replace the function if necessary.
1857 if (value == result->GetConstantFunction()) return value;
1858 // Preserve the attributes of this existing property.
1859 attributes = result->GetAttributes();
1860 return ConvertDescriptorToField(name, value, attributes);
1861 case CALLBACKS:
1862 return SetPropertyWithCallback(result->GetCallbackObject(),
1863 name,
1864 value,
1865 result->holder());
1866 case INTERCEPTOR:
1867 return SetPropertyWithInterceptor(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01001868 case CONSTANT_TRANSITION: {
1869 // If the same constant function is being added we can simply
1870 // transition to the target map.
1871 Map* target_map = result->GetTransitionMap();
1872 DescriptorArray* target_descriptors = target_map->instance_descriptors();
1873 int number = target_descriptors->SearchWithCache(name);
1874 ASSERT(number != DescriptorArray::kNotFound);
1875 ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
1876 JSFunction* function =
1877 JSFunction::cast(target_descriptors->GetValue(number));
1878 ASSERT(!Heap::InNewSpace(function));
1879 if (value == function) {
1880 set_map(target_map);
1881 return value;
1882 }
1883 // Otherwise, replace with a MAP_TRANSITION to a new map with a
1884 // FIELD, even if the value is a constant function.
Steve Blocka7e24c12009-10-30 11:49:00 +00001885 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01001886 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001887 case NULL_DESCRIPTOR:
1888 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1889 default:
1890 UNREACHABLE();
1891 }
1892 UNREACHABLE();
1893 return value;
1894}
1895
1896
1897// Set a real local property, even if it is READ_ONLY. If the property is not
1898// present, add it with attributes NONE. This code is an exact clone of
1899// SetProperty, with the check for IsReadOnly and the check for a
1900// callback setter removed. The two lines looking up the LookupResult
1901// result are also added. If one of the functions is changed, the other
1902// should be.
1903Object* JSObject::IgnoreAttributesAndSetLocalProperty(
1904 String* name,
1905 Object* value,
1906 PropertyAttributes attributes) {
1907 // Make sure that the top context does not change when doing callbacks or
1908 // interceptor calls.
1909 AssertNoContextChange ncc;
Andrei Popescu402d9372010-02-26 13:31:12 +00001910 LookupResult result;
1911 LocalLookup(name, &result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001912 // Check access rights if needed.
1913 if (IsAccessCheckNeeded()
Andrei Popescu402d9372010-02-26 13:31:12 +00001914 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1915 return SetPropertyWithFailedAccessCheck(&result, name, value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001916 }
1917
1918 if (IsJSGlobalProxy()) {
1919 Object* proto = GetPrototype();
1920 if (proto->IsNull()) return value;
1921 ASSERT(proto->IsJSGlobalObject());
1922 return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
1923 name,
1924 value,
1925 attributes);
1926 }
1927
1928 // Check for accessor in prototype chain removed here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00001929 if (!result.IsFound()) {
1930 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001931 return AddProperty(name, value, attributes);
1932 }
Steve Block6ded16b2010-05-10 14:33:55 +01001933
Andrei Popescu402d9372010-02-26 13:31:12 +00001934 PropertyDetails details = PropertyDetails(attributes, NORMAL);
1935
Steve Blocka7e24c12009-10-30 11:49:00 +00001936 // Check of IsReadOnly removed from here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00001937 switch (result.type()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001938 case NORMAL:
Andrei Popescu402d9372010-02-26 13:31:12 +00001939 return SetNormalizedProperty(name, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00001940 case FIELD:
Andrei Popescu402d9372010-02-26 13:31:12 +00001941 return FastPropertyAtPut(result.GetFieldIndex(), value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001942 case MAP_TRANSITION:
Andrei Popescu402d9372010-02-26 13:31:12 +00001943 if (attributes == result.GetAttributes()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001944 // Only use map transition if the attributes match.
Andrei Popescu402d9372010-02-26 13:31:12 +00001945 return AddFastPropertyUsingMap(result.GetTransitionMap(),
Steve Blocka7e24c12009-10-30 11:49:00 +00001946 name,
1947 value);
1948 }
1949 return ConvertDescriptorToField(name, value, attributes);
1950 case CONSTANT_FUNCTION:
1951 // Only replace the function if necessary.
Andrei Popescu402d9372010-02-26 13:31:12 +00001952 if (value == result.GetConstantFunction()) return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00001953 // Preserve the attributes of this existing property.
Andrei Popescu402d9372010-02-26 13:31:12 +00001954 attributes = result.GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +00001955 return ConvertDescriptorToField(name, value, attributes);
1956 case CALLBACKS:
1957 case INTERCEPTOR:
1958 // Override callback in clone
1959 return ConvertDescriptorToField(name, value, attributes);
1960 case CONSTANT_TRANSITION:
1961 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
1962 // if the value is a function.
1963 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1964 case NULL_DESCRIPTOR:
1965 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1966 default:
1967 UNREACHABLE();
1968 }
1969 UNREACHABLE();
1970 return value;
1971}
1972
1973
1974PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
1975 JSObject* receiver,
1976 String* name,
1977 bool continue_search) {
1978 // Check local property, ignore interceptor.
1979 LookupResult result;
1980 LocalLookupRealNamedProperty(name, &result);
1981 if (result.IsProperty()) return result.GetAttributes();
1982
1983 if (continue_search) {
1984 // Continue searching via the prototype chain.
1985 Object* pt = GetPrototype();
1986 if (pt != Heap::null_value()) {
1987 return JSObject::cast(pt)->
1988 GetPropertyAttributeWithReceiver(receiver, name);
1989 }
1990 }
1991 return ABSENT;
1992}
1993
1994
1995PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
1996 JSObject* receiver,
1997 String* name,
1998 bool continue_search) {
1999 // Make sure that the top context does not change when doing
2000 // callbacks or interceptor calls.
2001 AssertNoContextChange ncc;
2002
2003 HandleScope scope;
2004 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2005 Handle<JSObject> receiver_handle(receiver);
2006 Handle<JSObject> holder_handle(this);
2007 Handle<String> name_handle(name);
2008 CustomArguments args(interceptor->data(), receiver, this);
2009 v8::AccessorInfo info(args.end());
2010 if (!interceptor->query()->IsUndefined()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002011 v8::NamedPropertyQuery query =
2012 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
Steve Blocka7e24c12009-10-30 11:49:00 +00002013 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002014 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00002015 {
2016 // Leaving JavaScript.
2017 VMState state(EXTERNAL);
2018 result = query(v8::Utils::ToLocal(name_handle), info);
2019 }
2020 if (!result.IsEmpty()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002021 ASSERT(result->IsInt32());
2022 return static_cast<PropertyAttributes>(result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002023 }
2024 } else if (!interceptor->getter()->IsUndefined()) {
2025 v8::NamedPropertyGetter getter =
2026 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
2027 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
2028 v8::Handle<v8::Value> result;
2029 {
2030 // Leaving JavaScript.
2031 VMState state(EXTERNAL);
2032 result = getter(v8::Utils::ToLocal(name_handle), info);
2033 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002034 if (!result.IsEmpty()) return DONT_ENUM;
Steve Blocka7e24c12009-10-30 11:49:00 +00002035 }
2036 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2037 *name_handle,
2038 continue_search);
2039}
2040
2041
2042PropertyAttributes JSObject::GetPropertyAttributeWithReceiver(
2043 JSObject* receiver,
2044 String* key) {
2045 uint32_t index = 0;
2046 if (key->AsArrayIndex(&index)) {
2047 if (HasElementWithReceiver(receiver, index)) return NONE;
2048 return ABSENT;
2049 }
2050 // Named property.
2051 LookupResult result;
2052 Lookup(key, &result);
2053 return GetPropertyAttribute(receiver, &result, key, true);
2054}
2055
2056
2057PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
2058 LookupResult* result,
2059 String* name,
2060 bool continue_search) {
2061 // Check access rights if needed.
2062 if (IsAccessCheckNeeded() &&
2063 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2064 return GetPropertyAttributeWithFailedAccessCheck(receiver,
2065 result,
2066 name,
2067 continue_search);
2068 }
Andrei Popescu402d9372010-02-26 13:31:12 +00002069 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002070 switch (result->type()) {
2071 case NORMAL: // fall through
2072 case FIELD:
2073 case CONSTANT_FUNCTION:
2074 case CALLBACKS:
2075 return result->GetAttributes();
2076 case INTERCEPTOR:
2077 return result->holder()->
2078 GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
Steve Blocka7e24c12009-10-30 11:49:00 +00002079 default:
2080 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +00002081 }
2082 }
2083 return ABSENT;
2084}
2085
2086
2087PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) {
2088 // Check whether the name is an array index.
2089 uint32_t index = 0;
2090 if (name->AsArrayIndex(&index)) {
2091 if (HasLocalElement(index)) return NONE;
2092 return ABSENT;
2093 }
2094 // Named property.
2095 LookupResult result;
2096 LocalLookup(name, &result);
2097 return GetPropertyAttribute(this, &result, name, false);
2098}
2099
2100
2101Object* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2102 int expected_additional_properties) {
2103 if (!HasFastProperties()) return this;
2104
2105 // The global object is always normalized.
2106 ASSERT(!IsGlobalObject());
2107
2108 // Allocate new content.
2109 int property_count = map()->NumberOfDescribedProperties();
2110 if (expected_additional_properties > 0) {
2111 property_count += expected_additional_properties;
2112 } else {
2113 property_count += 2; // Make space for two more properties.
2114 }
2115 Object* obj =
Steve Block6ded16b2010-05-10 14:33:55 +01002116 StringDictionary::Allocate(property_count);
Steve Blocka7e24c12009-10-30 11:49:00 +00002117 if (obj->IsFailure()) return obj;
2118 StringDictionary* dictionary = StringDictionary::cast(obj);
2119
2120 DescriptorArray* descs = map()->instance_descriptors();
2121 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2122 PropertyDetails details = descs->GetDetails(i);
2123 switch (details.type()) {
2124 case CONSTANT_FUNCTION: {
2125 PropertyDetails d =
2126 PropertyDetails(details.attributes(), NORMAL, details.index());
2127 Object* value = descs->GetConstantFunction(i);
2128 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2129 if (result->IsFailure()) return result;
2130 dictionary = StringDictionary::cast(result);
2131 break;
2132 }
2133 case FIELD: {
2134 PropertyDetails d =
2135 PropertyDetails(details.attributes(), NORMAL, details.index());
2136 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
2137 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2138 if (result->IsFailure()) return result;
2139 dictionary = StringDictionary::cast(result);
2140 break;
2141 }
2142 case CALLBACKS: {
2143 PropertyDetails d =
2144 PropertyDetails(details.attributes(), CALLBACKS, details.index());
2145 Object* value = descs->GetCallbacksObject(i);
2146 Object* result = dictionary->Add(descs->GetKey(i), value, d);
2147 if (result->IsFailure()) return result;
2148 dictionary = StringDictionary::cast(result);
2149 break;
2150 }
2151 case MAP_TRANSITION:
2152 case CONSTANT_TRANSITION:
2153 case NULL_DESCRIPTOR:
2154 case INTERCEPTOR:
2155 break;
2156 default:
2157 UNREACHABLE();
2158 }
2159 }
2160
2161 // Copy the next enumeration index from instance descriptor.
2162 int index = map()->instance_descriptors()->NextEnumerationIndex();
2163 dictionary->SetNextEnumerationIndex(index);
2164
2165 // Allocate new map.
2166 obj = map()->CopyDropDescriptors();
2167 if (obj->IsFailure()) return obj;
2168 Map* new_map = Map::cast(obj);
2169
2170 // Clear inobject properties if needed by adjusting the instance size and
2171 // putting in a filler object instead of the inobject properties.
2172 if (mode == CLEAR_INOBJECT_PROPERTIES && map()->inobject_properties() > 0) {
2173 int instance_size_delta = map()->inobject_properties() * kPointerSize;
2174 int new_instance_size = map()->instance_size() - instance_size_delta;
2175 new_map->set_inobject_properties(0);
2176 new_map->set_instance_size(new_instance_size);
Iain Merrick75681382010-08-19 15:07:18 +01002177 new_map->set_visitor_id(StaticVisitorBase::GetVisitorId(new_map));
Steve Blocka7e24c12009-10-30 11:49:00 +00002178 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2179 instance_size_delta);
2180 }
2181 new_map->set_unused_property_fields(0);
2182
2183 // We have now successfully allocated all the necessary objects.
2184 // Changes can now be made with the guarantee that all of them take effect.
2185 set_map(new_map);
2186 map()->set_instance_descriptors(Heap::empty_descriptor_array());
2187
2188 set_properties(dictionary);
2189
2190 Counters::props_to_dictionary.Increment();
2191
2192#ifdef DEBUG
2193 if (FLAG_trace_normalization) {
2194 PrintF("Object properties have been normalized:\n");
2195 Print();
2196 }
2197#endif
2198 return this;
2199}
2200
2201
2202Object* JSObject::TransformToFastProperties(int unused_property_fields) {
2203 if (HasFastProperties()) return this;
2204 ASSERT(!IsGlobalObject());
2205 return property_dictionary()->
2206 TransformPropertiesToFastFor(this, unused_property_fields);
2207}
2208
2209
2210Object* JSObject::NormalizeElements() {
Steve Block3ce2e202009-11-05 08:53:23 +00002211 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002212 if (HasDictionaryElements()) return this;
Steve Block8defd9f2010-07-08 12:39:36 +01002213 ASSERT(map()->has_fast_elements());
2214
2215 Object* obj = map()->GetSlowElementsMap();
2216 if (obj->IsFailure()) return obj;
2217 Map* new_map = Map::cast(obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00002218
2219 // Get number of entries.
2220 FixedArray* array = FixedArray::cast(elements());
2221
2222 // Compute the effective length.
2223 int length = IsJSArray() ?
2224 Smi::cast(JSArray::cast(this)->length())->value() :
2225 array->length();
Steve Block8defd9f2010-07-08 12:39:36 +01002226 obj = NumberDictionary::Allocate(length);
Steve Blocka7e24c12009-10-30 11:49:00 +00002227 if (obj->IsFailure()) return obj;
2228 NumberDictionary* dictionary = NumberDictionary::cast(obj);
2229 // Copy entries.
2230 for (int i = 0; i < length; i++) {
2231 Object* value = array->get(i);
2232 if (!value->IsTheHole()) {
2233 PropertyDetails details = PropertyDetails(NONE, NORMAL);
2234 Object* result = dictionary->AddNumberEntry(i, array->get(i), details);
2235 if (result->IsFailure()) return result;
2236 dictionary = NumberDictionary::cast(result);
2237 }
2238 }
Steve Block8defd9f2010-07-08 12:39:36 +01002239 // Switch to using the dictionary as the backing storage for
2240 // elements. Set the new map first to satify the elements type
2241 // assert in set_elements().
2242 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002243 set_elements(dictionary);
2244
2245 Counters::elements_to_dictionary.Increment();
2246
2247#ifdef DEBUG
2248 if (FLAG_trace_normalization) {
2249 PrintF("Object elements have been normalized:\n");
2250 Print();
2251 }
2252#endif
2253
2254 return this;
2255}
2256
2257
2258Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) {
2259 // Check local property, ignore interceptor.
2260 LookupResult result;
2261 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002262 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002263
2264 // Normalize object if needed.
2265 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2266 if (obj->IsFailure()) return obj;
2267
2268 return DeleteNormalizedProperty(name, mode);
2269}
2270
2271
2272Object* JSObject::DeletePropertyWithInterceptor(String* name) {
2273 HandleScope scope;
2274 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2275 Handle<String> name_handle(name);
2276 Handle<JSObject> this_handle(this);
2277 if (!interceptor->deleter()->IsUndefined()) {
2278 v8::NamedPropertyDeleter deleter =
2279 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2280 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2281 CustomArguments args(interceptor->data(), this, this);
2282 v8::AccessorInfo info(args.end());
2283 v8::Handle<v8::Boolean> result;
2284 {
2285 // Leaving JavaScript.
2286 VMState state(EXTERNAL);
2287 result = deleter(v8::Utils::ToLocal(name_handle), info);
2288 }
2289 RETURN_IF_SCHEDULED_EXCEPTION();
2290 if (!result.IsEmpty()) {
2291 ASSERT(result->IsBoolean());
2292 return *v8::Utils::OpenHandle(*result);
2293 }
2294 }
2295 Object* raw_result =
2296 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
2297 RETURN_IF_SCHEDULED_EXCEPTION();
2298 return raw_result;
2299}
2300
2301
2302Object* JSObject::DeleteElementPostInterceptor(uint32_t index,
2303 DeleteMode mode) {
Steve Block3ce2e202009-11-05 08:53:23 +00002304 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002305 switch (GetElementsKind()) {
2306 case FAST_ELEMENTS: {
Iain Merrick75681382010-08-19 15:07:18 +01002307 Object* obj = EnsureWritableFastElements();
2308 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00002309 uint32_t length = IsJSArray() ?
2310 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2311 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2312 if (index < length) {
2313 FixedArray::cast(elements())->set_the_hole(index);
2314 }
2315 break;
2316 }
2317 case DICTIONARY_ELEMENTS: {
2318 NumberDictionary* dictionary = element_dictionary();
2319 int entry = dictionary->FindEntry(index);
2320 if (entry != NumberDictionary::kNotFound) {
2321 return dictionary->DeleteProperty(entry, mode);
2322 }
2323 break;
2324 }
2325 default:
2326 UNREACHABLE();
2327 break;
2328 }
2329 return Heap::true_value();
2330}
2331
2332
2333Object* JSObject::DeleteElementWithInterceptor(uint32_t index) {
2334 // Make sure that the top context does not change when doing
2335 // callbacks or interceptor calls.
2336 AssertNoContextChange ncc;
2337 HandleScope scope;
2338 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2339 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2340 v8::IndexedPropertyDeleter deleter =
2341 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2342 Handle<JSObject> this_handle(this);
2343 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2344 CustomArguments args(interceptor->data(), this, this);
2345 v8::AccessorInfo info(args.end());
2346 v8::Handle<v8::Boolean> result;
2347 {
2348 // Leaving JavaScript.
2349 VMState state(EXTERNAL);
2350 result = deleter(index, info);
2351 }
2352 RETURN_IF_SCHEDULED_EXCEPTION();
2353 if (!result.IsEmpty()) {
2354 ASSERT(result->IsBoolean());
2355 return *v8::Utils::OpenHandle(*result);
2356 }
2357 Object* raw_result =
2358 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
2359 RETURN_IF_SCHEDULED_EXCEPTION();
2360 return raw_result;
2361}
2362
2363
2364Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
2365 // Check access rights if needed.
2366 if (IsAccessCheckNeeded() &&
2367 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
2368 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2369 return Heap::false_value();
2370 }
2371
2372 if (IsJSGlobalProxy()) {
2373 Object* proto = GetPrototype();
2374 if (proto->IsNull()) return Heap::false_value();
2375 ASSERT(proto->IsJSGlobalObject());
2376 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
2377 }
2378
2379 if (HasIndexedInterceptor()) {
2380 // Skip interceptor if forcing deletion.
2381 if (mode == FORCE_DELETION) {
2382 return DeleteElementPostInterceptor(index, mode);
2383 }
2384 return DeleteElementWithInterceptor(index);
2385 }
2386
2387 switch (GetElementsKind()) {
2388 case FAST_ELEMENTS: {
Iain Merrick75681382010-08-19 15:07:18 +01002389 Object* obj = EnsureWritableFastElements();
2390 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00002391 uint32_t length = IsJSArray() ?
2392 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2393 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2394 if (index < length) {
2395 FixedArray::cast(elements())->set_the_hole(index);
2396 }
2397 break;
2398 }
Steve Block3ce2e202009-11-05 08:53:23 +00002399 case PIXEL_ELEMENTS:
2400 case EXTERNAL_BYTE_ELEMENTS:
2401 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2402 case EXTERNAL_SHORT_ELEMENTS:
2403 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2404 case EXTERNAL_INT_ELEMENTS:
2405 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2406 case EXTERNAL_FLOAT_ELEMENTS:
2407 // Pixel and external array elements cannot be deleted. Just
2408 // silently ignore here.
Steve Blocka7e24c12009-10-30 11:49:00 +00002409 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00002410 case DICTIONARY_ELEMENTS: {
2411 NumberDictionary* dictionary = element_dictionary();
2412 int entry = dictionary->FindEntry(index);
2413 if (entry != NumberDictionary::kNotFound) {
2414 return dictionary->DeleteProperty(entry, mode);
2415 }
2416 break;
2417 }
2418 default:
2419 UNREACHABLE();
2420 break;
2421 }
2422 return Heap::true_value();
2423}
2424
2425
2426Object* JSObject::DeleteProperty(String* name, DeleteMode mode) {
2427 // ECMA-262, 3rd, 8.6.2.5
2428 ASSERT(name->IsString());
2429
2430 // Check access rights if needed.
2431 if (IsAccessCheckNeeded() &&
2432 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2433 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2434 return Heap::false_value();
2435 }
2436
2437 if (IsJSGlobalProxy()) {
2438 Object* proto = GetPrototype();
2439 if (proto->IsNull()) return Heap::false_value();
2440 ASSERT(proto->IsJSGlobalObject());
2441 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
2442 }
2443
2444 uint32_t index = 0;
2445 if (name->AsArrayIndex(&index)) {
2446 return DeleteElement(index, mode);
2447 } else {
2448 LookupResult result;
2449 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002450 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002451 // Ignore attributes if forcing a deletion.
2452 if (result.IsDontDelete() && mode != FORCE_DELETION) {
2453 return Heap::false_value();
2454 }
2455 // Check for interceptor.
2456 if (result.type() == INTERCEPTOR) {
2457 // Skip interceptor if forcing a deletion.
2458 if (mode == FORCE_DELETION) {
2459 return DeletePropertyPostInterceptor(name, mode);
2460 }
2461 return DeletePropertyWithInterceptor(name);
2462 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002463 // Normalize object if needed.
2464 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2465 if (obj->IsFailure()) return obj;
2466 // Make sure the properties are normalized before removing the entry.
2467 return DeleteNormalizedProperty(name, mode);
2468 }
2469}
2470
2471
2472// Check whether this object references another object.
2473bool JSObject::ReferencesObject(Object* obj) {
2474 AssertNoAllocation no_alloc;
2475
2476 // Is the object the constructor for this object?
2477 if (map()->constructor() == obj) {
2478 return true;
2479 }
2480
2481 // Is the object the prototype for this object?
2482 if (map()->prototype() == obj) {
2483 return true;
2484 }
2485
2486 // Check if the object is among the named properties.
2487 Object* key = SlowReverseLookup(obj);
2488 if (key != Heap::undefined_value()) {
2489 return true;
2490 }
2491
2492 // Check if the object is among the indexed properties.
2493 switch (GetElementsKind()) {
2494 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002495 case EXTERNAL_BYTE_ELEMENTS:
2496 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2497 case EXTERNAL_SHORT_ELEMENTS:
2498 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2499 case EXTERNAL_INT_ELEMENTS:
2500 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2501 case EXTERNAL_FLOAT_ELEMENTS:
2502 // Raw pixels and external arrays do not reference other
2503 // objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00002504 break;
2505 case FAST_ELEMENTS: {
2506 int length = IsJSArray() ?
2507 Smi::cast(JSArray::cast(this)->length())->value() :
2508 FixedArray::cast(elements())->length();
2509 for (int i = 0; i < length; i++) {
2510 Object* element = FixedArray::cast(elements())->get(i);
2511 if (!element->IsTheHole() && element == obj) {
2512 return true;
2513 }
2514 }
2515 break;
2516 }
2517 case DICTIONARY_ELEMENTS: {
2518 key = element_dictionary()->SlowReverseLookup(obj);
2519 if (key != Heap::undefined_value()) {
2520 return true;
2521 }
2522 break;
2523 }
2524 default:
2525 UNREACHABLE();
2526 break;
2527 }
2528
Steve Block6ded16b2010-05-10 14:33:55 +01002529 // For functions check the context.
2530 if (IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002531 // Get the constructor function for arguments array.
2532 JSObject* arguments_boilerplate =
2533 Top::context()->global_context()->arguments_boilerplate();
2534 JSFunction* arguments_function =
2535 JSFunction::cast(arguments_boilerplate->map()->constructor());
2536
2537 // Get the context and don't check if it is the global context.
2538 JSFunction* f = JSFunction::cast(this);
2539 Context* context = f->context();
2540 if (context->IsGlobalContext()) {
2541 return false;
2542 }
2543
2544 // Check the non-special context slots.
2545 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2546 // Only check JS objects.
2547 if (context->get(i)->IsJSObject()) {
2548 JSObject* ctxobj = JSObject::cast(context->get(i));
2549 // If it is an arguments array check the content.
2550 if (ctxobj->map()->constructor() == arguments_function) {
2551 if (ctxobj->ReferencesObject(obj)) {
2552 return true;
2553 }
2554 } else if (ctxobj == obj) {
2555 return true;
2556 }
2557 }
2558 }
2559
2560 // Check the context extension if any.
2561 if (context->has_extension()) {
2562 return context->extension()->ReferencesObject(obj);
2563 }
2564 }
2565
2566 // No references to object.
2567 return false;
2568}
2569
2570
Steve Block8defd9f2010-07-08 12:39:36 +01002571Object* JSObject::PreventExtensions() {
2572 // If there are fast elements we normalize.
2573 if (HasFastElements()) {
2574 NormalizeElements();
2575 }
2576 // Make sure that we never go back to fast case.
2577 element_dictionary()->set_requires_slow_elements();
2578
2579 // Do a map transition, other objects with this map may still
2580 // be extensible.
2581 Object* new_map = map()->CopyDropTransitions();
2582 if (new_map->IsFailure()) return new_map;
2583 Map::cast(new_map)->set_is_extensible(false);
2584 set_map(Map::cast(new_map));
2585 ASSERT(!map()->is_extensible());
2586 return new_map;
2587}
2588
2589
Steve Blocka7e24c12009-10-30 11:49:00 +00002590// Tests for the fast common case for property enumeration:
Steve Blockd0582a62009-12-15 09:54:21 +00002591// - This object and all prototypes has an enum cache (which means that it has
2592// no interceptors and needs no access checks).
2593// - This object has no elements.
2594// - No prototype has enumerable properties/elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002595bool JSObject::IsSimpleEnum() {
Steve Blocka7e24c12009-10-30 11:49:00 +00002596 for (Object* o = this;
2597 o != Heap::null_value();
2598 o = JSObject::cast(o)->GetPrototype()) {
2599 JSObject* curr = JSObject::cast(o);
Steve Blocka7e24c12009-10-30 11:49:00 +00002600 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00002601 ASSERT(!curr->HasNamedInterceptor());
2602 ASSERT(!curr->HasIndexedInterceptor());
2603 ASSERT(!curr->IsAccessCheckNeeded());
Steve Blocka7e24c12009-10-30 11:49:00 +00002604 if (curr->NumberOfEnumElements() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002605 if (curr != this) {
2606 FixedArray* curr_fixed_array =
2607 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
Steve Blockd0582a62009-12-15 09:54:21 +00002608 if (curr_fixed_array->length() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002609 }
2610 }
2611 return true;
2612}
2613
2614
2615int Map::NumberOfDescribedProperties() {
2616 int result = 0;
2617 DescriptorArray* descs = instance_descriptors();
2618 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2619 if (descs->IsProperty(i)) result++;
2620 }
2621 return result;
2622}
2623
2624
2625int Map::PropertyIndexFor(String* name) {
2626 DescriptorArray* descs = instance_descriptors();
2627 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2628 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
2629 return descs->GetFieldIndex(i);
2630 }
2631 }
2632 return -1;
2633}
2634
2635
2636int Map::NextFreePropertyIndex() {
2637 int max_index = -1;
2638 DescriptorArray* descs = instance_descriptors();
2639 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2640 if (descs->GetType(i) == FIELD) {
2641 int current_index = descs->GetFieldIndex(i);
2642 if (current_index > max_index) max_index = current_index;
2643 }
2644 }
2645 return max_index + 1;
2646}
2647
2648
2649AccessorDescriptor* Map::FindAccessor(String* name) {
2650 DescriptorArray* descs = instance_descriptors();
2651 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2652 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
2653 return descs->GetCallbacks(i);
2654 }
2655 }
2656 return NULL;
2657}
2658
2659
2660void JSObject::LocalLookup(String* name, LookupResult* result) {
2661 ASSERT(name->IsString());
2662
2663 if (IsJSGlobalProxy()) {
2664 Object* proto = GetPrototype();
2665 if (proto->IsNull()) return result->NotFound();
2666 ASSERT(proto->IsJSGlobalObject());
2667 return JSObject::cast(proto)->LocalLookup(name, result);
2668 }
2669
2670 // Do not use inline caching if the object is a non-global object
2671 // that requires access checks.
2672 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
2673 result->DisallowCaching();
2674 }
2675
2676 // Check __proto__ before interceptor.
2677 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
2678 result->ConstantResult(this);
2679 return;
2680 }
2681
2682 // Check for lookup interceptor except when bootstrapping.
2683 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2684 result->InterceptorResult(this);
2685 return;
2686 }
2687
2688 LocalLookupRealNamedProperty(name, result);
2689}
2690
2691
2692void JSObject::Lookup(String* name, LookupResult* result) {
2693 // Ecma-262 3rd 8.6.2.4
2694 for (Object* current = this;
2695 current != Heap::null_value();
2696 current = JSObject::cast(current)->GetPrototype()) {
2697 JSObject::cast(current)->LocalLookup(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002698 if (result->IsProperty()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002699 }
2700 result->NotFound();
2701}
2702
2703
2704// Search object and it's prototype chain for callback properties.
2705void JSObject::LookupCallback(String* name, LookupResult* result) {
2706 for (Object* current = this;
2707 current != Heap::null_value();
2708 current = JSObject::cast(current)->GetPrototype()) {
2709 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002710 if (result->IsProperty() && result->type() == CALLBACKS) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002711 }
2712 result->NotFound();
2713}
2714
2715
2716Object* JSObject::DefineGetterSetter(String* name,
2717 PropertyAttributes attributes) {
2718 // Make sure that the top context does not change when doing callbacks or
2719 // interceptor calls.
2720 AssertNoContextChange ncc;
2721
Steve Blocka7e24c12009-10-30 11:49:00 +00002722 // Try to flatten before operating on the string.
Steve Block6ded16b2010-05-10 14:33:55 +01002723 name->TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00002724
Leon Clarkef7060e22010-06-03 12:02:55 +01002725 if (!CanSetCallback(name)) {
2726 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002727 }
2728
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002729 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00002730 bool is_element = name->AsArrayIndex(&index);
2731 if (is_element && IsJSArray()) return Heap::undefined_value();
2732
2733 if (is_element) {
2734 switch (GetElementsKind()) {
2735 case FAST_ELEMENTS:
2736 break;
2737 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002738 case EXTERNAL_BYTE_ELEMENTS:
2739 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2740 case EXTERNAL_SHORT_ELEMENTS:
2741 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2742 case EXTERNAL_INT_ELEMENTS:
2743 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2744 case EXTERNAL_FLOAT_ELEMENTS:
2745 // Ignore getters and setters on pixel and external array
2746 // elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002747 return Heap::undefined_value();
2748 case DICTIONARY_ELEMENTS: {
2749 // Lookup the index.
2750 NumberDictionary* dictionary = element_dictionary();
2751 int entry = dictionary->FindEntry(index);
2752 if (entry != NumberDictionary::kNotFound) {
2753 Object* result = dictionary->ValueAt(entry);
2754 PropertyDetails details = dictionary->DetailsAt(entry);
2755 if (details.IsReadOnly()) return Heap::undefined_value();
2756 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002757 if (result->IsFixedArray()) {
2758 return result;
2759 }
2760 // Otherwise allow to override it.
Steve Blocka7e24c12009-10-30 11:49:00 +00002761 }
2762 }
2763 break;
2764 }
2765 default:
2766 UNREACHABLE();
2767 break;
2768 }
2769 } else {
2770 // Lookup the name.
2771 LookupResult result;
2772 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002773 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002774 if (result.IsReadOnly()) return Heap::undefined_value();
2775 if (result.type() == CALLBACKS) {
2776 Object* obj = result.GetCallbackObject();
Leon Clarkef7060e22010-06-03 12:02:55 +01002777 // Need to preserve old getters/setters.
Leon Clarked91b9f72010-01-27 17:25:45 +00002778 if (obj->IsFixedArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002779 // Use set to update attributes.
2780 return SetPropertyCallback(name, obj, attributes);
Leon Clarked91b9f72010-01-27 17:25:45 +00002781 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002782 }
2783 }
2784 }
2785
2786 // Allocate the fixed array to hold getter and setter.
2787 Object* structure = Heap::AllocateFixedArray(2, TENURED);
2788 if (structure->IsFailure()) return structure;
Steve Blocka7e24c12009-10-30 11:49:00 +00002789
2790 if (is_element) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002791 return SetElementCallback(index, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002792 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01002793 return SetPropertyCallback(name, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002794 }
Leon Clarkef7060e22010-06-03 12:02:55 +01002795}
2796
2797
2798bool JSObject::CanSetCallback(String* name) {
2799 ASSERT(!IsAccessCheckNeeded()
2800 || Top::MayNamedAccess(this, name, v8::ACCESS_SET));
2801
2802 // Check if there is an API defined callback object which prohibits
2803 // callback overwriting in this object or it's prototype chain.
2804 // This mechanism is needed for instance in a browser setting, where
2805 // certain accessors such as window.location should not be allowed
2806 // to be overwritten because allowing overwriting could potentially
2807 // cause security problems.
2808 LookupResult callback_result;
2809 LookupCallback(name, &callback_result);
2810 if (callback_result.IsProperty()) {
2811 Object* obj = callback_result.GetCallbackObject();
2812 if (obj->IsAccessorInfo() &&
2813 AccessorInfo::cast(obj)->prohibits_overwriting()) {
2814 return false;
2815 }
2816 }
2817
2818 return true;
2819}
2820
2821
2822Object* JSObject::SetElementCallback(uint32_t index,
2823 Object* structure,
2824 PropertyAttributes attributes) {
2825 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
2826
2827 // Normalize elements to make this operation simple.
2828 Object* ok = NormalizeElements();
2829 if (ok->IsFailure()) return ok;
2830
2831 // Update the dictionary with the new CALLBACKS property.
2832 Object* dict =
2833 element_dictionary()->Set(index, structure, details);
2834 if (dict->IsFailure()) return dict;
2835
2836 NumberDictionary* elements = NumberDictionary::cast(dict);
2837 elements->set_requires_slow_elements();
2838 // Set the potential new dictionary on the object.
2839 set_elements(elements);
Steve Blocka7e24c12009-10-30 11:49:00 +00002840
2841 return structure;
2842}
2843
2844
Leon Clarkef7060e22010-06-03 12:02:55 +01002845Object* JSObject::SetPropertyCallback(String* name,
2846 Object* structure,
2847 PropertyAttributes attributes) {
2848 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
2849
2850 bool convert_back_to_fast = HasFastProperties() &&
2851 (map()->instance_descriptors()->number_of_descriptors()
2852 < DescriptorArray::kMaxNumberOfDescriptors);
2853
2854 // Normalize object to make this operation simple.
2855 Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2856 if (ok->IsFailure()) return ok;
2857
2858 // For the global object allocate a new map to invalidate the global inline
2859 // caches which have a global property cell reference directly in the code.
2860 if (IsGlobalObject()) {
2861 Object* new_map = map()->CopyDropDescriptors();
2862 if (new_map->IsFailure()) return new_map;
2863 set_map(Map::cast(new_map));
2864 }
2865
2866 // Update the dictionary with the new CALLBACKS property.
2867 Object* result = SetNormalizedProperty(name, structure, details);
2868 if (result->IsFailure()) return result;
2869
2870 if (convert_back_to_fast) {
2871 ok = TransformToFastProperties(0);
2872 if (ok->IsFailure()) return ok;
2873 }
2874 return result;
2875}
2876
Steve Blocka7e24c12009-10-30 11:49:00 +00002877Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
2878 PropertyAttributes attributes) {
2879 // Check access rights if needed.
2880 if (IsAccessCheckNeeded() &&
Leon Clarkef7060e22010-06-03 12:02:55 +01002881 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
2882 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Steve Blocka7e24c12009-10-30 11:49:00 +00002883 return Heap::undefined_value();
2884 }
2885
2886 if (IsJSGlobalProxy()) {
2887 Object* proto = GetPrototype();
2888 if (proto->IsNull()) return this;
2889 ASSERT(proto->IsJSGlobalObject());
2890 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
2891 fun, attributes);
2892 }
2893
2894 Object* array = DefineGetterSetter(name, attributes);
2895 if (array->IsFailure() || array->IsUndefined()) return array;
2896 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
2897 return this;
2898}
2899
2900
Leon Clarkef7060e22010-06-03 12:02:55 +01002901Object* JSObject::DefineAccessor(AccessorInfo* info) {
2902 String* name = String::cast(info->name());
2903 // Check access rights if needed.
2904 if (IsAccessCheckNeeded() &&
2905 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
2906 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
2907 return Heap::undefined_value();
2908 }
2909
2910 if (IsJSGlobalProxy()) {
2911 Object* proto = GetPrototype();
2912 if (proto->IsNull()) return this;
2913 ASSERT(proto->IsJSGlobalObject());
2914 return JSObject::cast(proto)->DefineAccessor(info);
2915 }
2916
2917 // Make sure that the top context does not change when doing callbacks or
2918 // interceptor calls.
2919 AssertNoContextChange ncc;
2920
2921 // Try to flatten before operating on the string.
2922 name->TryFlatten();
2923
2924 if (!CanSetCallback(name)) {
2925 return Heap::undefined_value();
2926 }
2927
2928 uint32_t index = 0;
2929 bool is_element = name->AsArrayIndex(&index);
2930
2931 if (is_element) {
2932 if (IsJSArray()) return Heap::undefined_value();
2933
2934 // Accessors overwrite previous callbacks (cf. with getters/setters).
2935 switch (GetElementsKind()) {
2936 case FAST_ELEMENTS:
2937 break;
2938 case PIXEL_ELEMENTS:
2939 case EXTERNAL_BYTE_ELEMENTS:
2940 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2941 case EXTERNAL_SHORT_ELEMENTS:
2942 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2943 case EXTERNAL_INT_ELEMENTS:
2944 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2945 case EXTERNAL_FLOAT_ELEMENTS:
2946 // Ignore getters and setters on pixel and external array
2947 // elements.
2948 return Heap::undefined_value();
2949 case DICTIONARY_ELEMENTS:
2950 break;
2951 default:
2952 UNREACHABLE();
2953 break;
2954 }
2955
Kristian Monsen50ef84f2010-07-29 15:18:00 +01002956 Object* ok = SetElementCallback(index, info, info->property_attributes());
2957 if (ok->IsFailure()) return ok;
Leon Clarkef7060e22010-06-03 12:02:55 +01002958 } else {
2959 // Lookup the name.
2960 LookupResult result;
2961 LocalLookup(name, &result);
2962 // ES5 forbids turning a property into an accessor if it's not
2963 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
2964 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
2965 return Heap::undefined_value();
2966 }
Kristian Monsen50ef84f2010-07-29 15:18:00 +01002967 Object* ok = SetPropertyCallback(name, info, info->property_attributes());
2968 if (ok->IsFailure()) return ok;
Leon Clarkef7060e22010-06-03 12:02:55 +01002969 }
2970
2971 return this;
2972}
2973
2974
Steve Blocka7e24c12009-10-30 11:49:00 +00002975Object* JSObject::LookupAccessor(String* name, bool is_getter) {
2976 // Make sure that the top context does not change when doing callbacks or
2977 // interceptor calls.
2978 AssertNoContextChange ncc;
2979
2980 // Check access rights if needed.
2981 if (IsAccessCheckNeeded() &&
2982 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2983 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2984 return Heap::undefined_value();
2985 }
2986
2987 // Make the lookup and include prototypes.
2988 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002989 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00002990 if (name->AsArrayIndex(&index)) {
2991 for (Object* obj = this;
2992 obj != Heap::null_value();
2993 obj = JSObject::cast(obj)->GetPrototype()) {
2994 JSObject* js_object = JSObject::cast(obj);
2995 if (js_object->HasDictionaryElements()) {
2996 NumberDictionary* dictionary = js_object->element_dictionary();
2997 int entry = dictionary->FindEntry(index);
2998 if (entry != NumberDictionary::kNotFound) {
2999 Object* element = dictionary->ValueAt(entry);
3000 PropertyDetails details = dictionary->DetailsAt(entry);
3001 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003002 if (element->IsFixedArray()) {
3003 return FixedArray::cast(element)->get(accessor_index);
3004 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003005 }
3006 }
3007 }
3008 }
3009 } else {
3010 for (Object* obj = this;
3011 obj != Heap::null_value();
3012 obj = JSObject::cast(obj)->GetPrototype()) {
3013 LookupResult result;
3014 JSObject::cast(obj)->LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00003015 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003016 if (result.IsReadOnly()) return Heap::undefined_value();
3017 if (result.type() == CALLBACKS) {
3018 Object* obj = result.GetCallbackObject();
3019 if (obj->IsFixedArray()) {
3020 return FixedArray::cast(obj)->get(accessor_index);
3021 }
3022 }
3023 }
3024 }
3025 }
3026 return Heap::undefined_value();
3027}
3028
3029
3030Object* JSObject::SlowReverseLookup(Object* value) {
3031 if (HasFastProperties()) {
3032 DescriptorArray* descs = map()->instance_descriptors();
3033 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3034 if (descs->GetType(i) == FIELD) {
3035 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3036 return descs->GetKey(i);
3037 }
3038 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3039 if (descs->GetConstantFunction(i) == value) {
3040 return descs->GetKey(i);
3041 }
3042 }
3043 }
3044 return Heap::undefined_value();
3045 } else {
3046 return property_dictionary()->SlowReverseLookup(value);
3047 }
3048}
3049
3050
3051Object* Map::CopyDropDescriptors() {
3052 Object* result = Heap::AllocateMap(instance_type(), instance_size());
3053 if (result->IsFailure()) return result;
3054 Map::cast(result)->set_prototype(prototype());
3055 Map::cast(result)->set_constructor(constructor());
3056 // Don't copy descriptors, so map transitions always remain a forest.
3057 // If we retained the same descriptors we would have two maps
3058 // pointing to the same transition which is bad because the garbage
3059 // collector relies on being able to reverse pointers from transitions
3060 // to maps. If properties need to be retained use CopyDropTransitions.
3061 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
3062 // Please note instance_type and instance_size are set when allocated.
3063 Map::cast(result)->set_inobject_properties(inobject_properties());
3064 Map::cast(result)->set_unused_property_fields(unused_property_fields());
3065
3066 // If the map has pre-allocated properties always start out with a descriptor
3067 // array describing these properties.
3068 if (pre_allocated_property_fields() > 0) {
3069 ASSERT(constructor()->IsJSFunction());
3070 JSFunction* ctor = JSFunction::cast(constructor());
3071 Object* descriptors =
3072 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
3073 if (descriptors->IsFailure()) return descriptors;
3074 Map::cast(result)->set_instance_descriptors(
3075 DescriptorArray::cast(descriptors));
3076 Map::cast(result)->set_pre_allocated_property_fields(
3077 pre_allocated_property_fields());
3078 }
3079 Map::cast(result)->set_bit_field(bit_field());
3080 Map::cast(result)->set_bit_field2(bit_field2());
3081 Map::cast(result)->ClearCodeCache();
3082 return result;
3083}
3084
3085
3086Object* Map::CopyDropTransitions() {
3087 Object* new_map = CopyDropDescriptors();
3088 if (new_map->IsFailure()) return new_map;
3089 Object* descriptors = instance_descriptors()->RemoveTransitions();
3090 if (descriptors->IsFailure()) return descriptors;
3091 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
Steve Block8defd9f2010-07-08 12:39:36 +01003092 return new_map;
Steve Blocka7e24c12009-10-30 11:49:00 +00003093}
3094
3095
3096Object* Map::UpdateCodeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003097 // Allocate the code cache if not present.
3098 if (code_cache()->IsFixedArray()) {
3099 Object* result = Heap::AllocateCodeCache();
3100 if (result->IsFailure()) return result;
3101 set_code_cache(result);
3102 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003103
Steve Block6ded16b2010-05-10 14:33:55 +01003104 // Update the code cache.
3105 return CodeCache::cast(code_cache())->Update(name, code);
3106}
3107
3108
3109Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3110 // Do a lookup if a code cache exists.
3111 if (!code_cache()->IsFixedArray()) {
3112 return CodeCache::cast(code_cache())->Lookup(name, flags);
3113 } else {
3114 return Heap::undefined_value();
3115 }
3116}
3117
3118
3119int Map::IndexInCodeCache(Object* name, Code* code) {
3120 // Get the internal index if a code cache exists.
3121 if (!code_cache()->IsFixedArray()) {
3122 return CodeCache::cast(code_cache())->GetIndex(name, code);
3123 }
3124 return -1;
3125}
3126
3127
3128void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
3129 // No GC is supposed to happen between a call to IndexInCodeCache and
3130 // RemoveFromCodeCache so the code cache must be there.
3131 ASSERT(!code_cache()->IsFixedArray());
3132 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
3133}
3134
3135
3136Object* CodeCache::Update(String* name, Code* code) {
3137 ASSERT(code->ic_state() == MONOMORPHIC);
3138
3139 // The number of monomorphic stubs for normal load/store/call IC's can grow to
3140 // a large number and therefore they need to go into a hash table. They are
3141 // used to load global properties from cells.
3142 if (code->type() == NORMAL) {
3143 // Make sure that a hash table is allocated for the normal load code cache.
3144 if (normal_type_cache()->IsUndefined()) {
3145 Object* result =
3146 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
3147 if (result->IsFailure()) return result;
3148 set_normal_type_cache(result);
3149 }
3150 return UpdateNormalTypeCache(name, code);
3151 } else {
3152 ASSERT(default_cache()->IsFixedArray());
3153 return UpdateDefaultCache(name, code);
3154 }
3155}
3156
3157
3158Object* CodeCache::UpdateDefaultCache(String* name, Code* code) {
3159 // When updating the default code cache we disregard the type encoded in the
Steve Blocka7e24c12009-10-30 11:49:00 +00003160 // flags. This allows call constant stubs to overwrite call field
3161 // stubs, etc.
3162 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
3163
3164 // First check whether we can update existing code cache without
3165 // extending it.
Steve Block6ded16b2010-05-10 14:33:55 +01003166 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003167 int length = cache->length();
3168 int deleted_index = -1;
Steve Block6ded16b2010-05-10 14:33:55 +01003169 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003170 Object* key = cache->get(i);
3171 if (key->IsNull()) {
3172 if (deleted_index < 0) deleted_index = i;
3173 continue;
3174 }
3175 if (key->IsUndefined()) {
3176 if (deleted_index >= 0) i = deleted_index;
Steve Block6ded16b2010-05-10 14:33:55 +01003177 cache->set(i + kCodeCacheEntryNameOffset, name);
3178 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003179 return this;
3180 }
3181 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003182 Code::Flags found =
3183 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
Steve Blocka7e24c12009-10-30 11:49:00 +00003184 if (Code::RemoveTypeFromFlags(found) == flags) {
Steve Block6ded16b2010-05-10 14:33:55 +01003185 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003186 return this;
3187 }
3188 }
3189 }
3190
3191 // Reached the end of the code cache. If there were deleted
3192 // elements, reuse the space for the first of them.
3193 if (deleted_index >= 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01003194 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
3195 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003196 return this;
3197 }
3198
Steve Block6ded16b2010-05-10 14:33:55 +01003199 // Extend the code cache with some new entries (at least one). Must be a
3200 // multiple of the entry size.
3201 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
3202 new_length = new_length - new_length % kCodeCacheEntrySize;
3203 ASSERT((new_length % kCodeCacheEntrySize) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00003204 Object* result = cache->CopySize(new_length);
3205 if (result->IsFailure()) return result;
3206
3207 // Add the (name, code) pair to the new cache.
3208 cache = FixedArray::cast(result);
Steve Block6ded16b2010-05-10 14:33:55 +01003209 cache->set(length + kCodeCacheEntryNameOffset, name);
3210 cache->set(length + kCodeCacheEntryCodeOffset, code);
3211 set_default_cache(cache);
Steve Blocka7e24c12009-10-30 11:49:00 +00003212 return this;
3213}
3214
3215
Steve Block6ded16b2010-05-10 14:33:55 +01003216Object* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
3217 // Adding a new entry can cause a new cache to be allocated.
3218 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3219 Object* new_cache = cache->Put(name, code);
3220 if (new_cache->IsFailure()) return new_cache;
3221 set_normal_type_cache(new_cache);
3222 return this;
3223}
3224
3225
3226Object* CodeCache::Lookup(String* name, Code::Flags flags) {
3227 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
3228 return LookupNormalTypeCache(name, flags);
3229 } else {
3230 return LookupDefaultCache(name, flags);
3231 }
3232}
3233
3234
3235Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
3236 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003237 int length = cache->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003238 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
3239 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
Steve Blocka7e24c12009-10-30 11:49:00 +00003240 // Skip deleted elements.
3241 if (key->IsNull()) continue;
3242 if (key->IsUndefined()) return key;
3243 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003244 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
3245 if (code->flags() == flags) {
3246 return code;
3247 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003248 }
3249 }
3250 return Heap::undefined_value();
3251}
3252
3253
Steve Block6ded16b2010-05-10 14:33:55 +01003254Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
3255 if (!normal_type_cache()->IsUndefined()) {
3256 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3257 return cache->Lookup(name, flags);
3258 } else {
3259 return Heap::undefined_value();
3260 }
3261}
3262
3263
3264int CodeCache::GetIndex(Object* name, Code* code) {
3265 if (code->type() == NORMAL) {
3266 if (normal_type_cache()->IsUndefined()) return -1;
3267 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3268 return cache->GetIndex(String::cast(name), code->flags());
3269 }
3270
3271 FixedArray* array = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003272 int len = array->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003273 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
3274 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003275 }
3276 return -1;
3277}
3278
3279
Steve Block6ded16b2010-05-10 14:33:55 +01003280void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
3281 if (code->type() == NORMAL) {
3282 ASSERT(!normal_type_cache()->IsUndefined());
3283 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3284 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
3285 cache->RemoveByIndex(index);
3286 } else {
3287 FixedArray* array = default_cache();
3288 ASSERT(array->length() >= index && array->get(index)->IsCode());
3289 // Use null instead of undefined for deleted elements to distinguish
3290 // deleted elements from unused elements. This distinction is used
3291 // when looking up in the cache and when updating the cache.
3292 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
3293 array->set_null(index - 1); // Name.
3294 array->set_null(index); // Code.
3295 }
3296}
3297
3298
3299// The key in the code cache hash table consists of the property name and the
3300// code object. The actual match is on the name and the code flags. If a key
3301// is created using the flags and not a code object it can only be used for
3302// lookup not to create a new entry.
3303class CodeCacheHashTableKey : public HashTableKey {
3304 public:
3305 CodeCacheHashTableKey(String* name, Code::Flags flags)
3306 : name_(name), flags_(flags), code_(NULL) { }
3307
3308 CodeCacheHashTableKey(String* name, Code* code)
3309 : name_(name),
3310 flags_(code->flags()),
3311 code_(code) { }
3312
3313
3314 bool IsMatch(Object* other) {
3315 if (!other->IsFixedArray()) return false;
3316 FixedArray* pair = FixedArray::cast(other);
3317 String* name = String::cast(pair->get(0));
3318 Code::Flags flags = Code::cast(pair->get(1))->flags();
3319 if (flags != flags_) {
3320 return false;
3321 }
3322 return name_->Equals(name);
3323 }
3324
3325 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
3326 return name->Hash() ^ flags;
3327 }
3328
3329 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
3330
3331 uint32_t HashForObject(Object* obj) {
3332 FixedArray* pair = FixedArray::cast(obj);
3333 String* name = String::cast(pair->get(0));
3334 Code* code = Code::cast(pair->get(1));
3335 return NameFlagsHashHelper(name, code->flags());
3336 }
3337
3338 Object* AsObject() {
3339 ASSERT(code_ != NULL);
3340 Object* obj = Heap::AllocateFixedArray(2);
3341 if (obj->IsFailure()) return obj;
3342 FixedArray* pair = FixedArray::cast(obj);
3343 pair->set(0, name_);
3344 pair->set(1, code_);
3345 return pair;
3346 }
3347
3348 private:
3349 String* name_;
3350 Code::Flags flags_;
3351 Code* code_;
3352};
3353
3354
3355Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
3356 CodeCacheHashTableKey key(name, flags);
3357 int entry = FindEntry(&key);
3358 if (entry == kNotFound) return Heap::undefined_value();
3359 return get(EntryToIndex(entry) + 1);
3360}
3361
3362
3363Object* CodeCacheHashTable::Put(String* name, Code* code) {
3364 CodeCacheHashTableKey key(name, code);
3365 Object* obj = EnsureCapacity(1, &key);
3366 if (obj->IsFailure()) return obj;
3367
3368 // Don't use this, as the table might have grown.
3369 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
3370
3371 int entry = cache->FindInsertionEntry(key.Hash());
3372 Object* k = key.AsObject();
3373 if (k->IsFailure()) return k;
3374
3375 cache->set(EntryToIndex(entry), k);
3376 cache->set(EntryToIndex(entry) + 1, code);
3377 cache->ElementAdded();
3378 return cache;
3379}
3380
3381
3382int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
3383 CodeCacheHashTableKey key(name, flags);
3384 int entry = FindEntry(&key);
3385 return (entry == kNotFound) ? -1 : entry;
3386}
3387
3388
3389void CodeCacheHashTable::RemoveByIndex(int index) {
3390 ASSERT(index >= 0);
3391 set(EntryToIndex(index), Heap::null_value());
3392 set(EntryToIndex(index) + 1, Heap::null_value());
3393 ElementRemoved();
Steve Blocka7e24c12009-10-30 11:49:00 +00003394}
3395
3396
Steve Blocka7e24c12009-10-30 11:49:00 +00003397static bool HasKey(FixedArray* array, Object* key) {
3398 int len0 = array->length();
3399 for (int i = 0; i < len0; i++) {
3400 Object* element = array->get(i);
3401 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3402 if (element->IsString() &&
3403 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3404 return true;
3405 }
3406 }
3407 return false;
3408}
3409
3410
3411Object* FixedArray::AddKeysFromJSArray(JSArray* array) {
Steve Block3ce2e202009-11-05 08:53:23 +00003412 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00003413 switch (array->GetElementsKind()) {
3414 case JSObject::FAST_ELEMENTS:
3415 return UnionOfKeys(FixedArray::cast(array->elements()));
3416 case JSObject::DICTIONARY_ELEMENTS: {
3417 NumberDictionary* dict = array->element_dictionary();
3418 int size = dict->NumberOfElements();
3419
3420 // Allocate a temporary fixed array.
3421 Object* object = Heap::AllocateFixedArray(size);
3422 if (object->IsFailure()) return object;
3423 FixedArray* key_array = FixedArray::cast(object);
3424
3425 int capacity = dict->Capacity();
3426 int pos = 0;
3427 // Copy the elements from the JSArray to the temporary fixed array.
3428 for (int i = 0; i < capacity; i++) {
3429 if (dict->IsKey(dict->KeyAt(i))) {
3430 key_array->set(pos++, dict->ValueAt(i));
3431 }
3432 }
3433 // Compute the union of this and the temporary fixed array.
3434 return UnionOfKeys(key_array);
3435 }
3436 default:
3437 UNREACHABLE();
3438 }
3439 UNREACHABLE();
3440 return Heap::null_value(); // Failure case needs to "return" a value.
3441}
3442
3443
3444Object* FixedArray::UnionOfKeys(FixedArray* other) {
3445 int len0 = length();
3446 int len1 = other->length();
3447 // Optimize if either is empty.
3448 if (len0 == 0) return other;
3449 if (len1 == 0) return this;
3450
3451 // Compute how many elements are not in this.
3452 int extra = 0;
3453 for (int y = 0; y < len1; y++) {
3454 Object* value = other->get(y);
3455 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3456 }
3457
3458 if (extra == 0) return this;
3459
3460 // Allocate the result
3461 Object* obj = Heap::AllocateFixedArray(len0 + extra);
3462 if (obj->IsFailure()) return obj;
3463 // Fill in the content
Leon Clarke4515c472010-02-03 11:58:03 +00003464 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003465 FixedArray* result = FixedArray::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00003466 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003467 for (int i = 0; i < len0; i++) {
3468 result->set(i, get(i), mode);
3469 }
3470 // Fill in the extra keys.
3471 int index = 0;
3472 for (int y = 0; y < len1; y++) {
3473 Object* value = other->get(y);
3474 if (!value->IsTheHole() && !HasKey(this, value)) {
3475 result->set(len0 + index, other->get(y), mode);
3476 index++;
3477 }
3478 }
3479 ASSERT(extra == index);
3480 return result;
3481}
3482
3483
3484Object* FixedArray::CopySize(int new_length) {
3485 if (new_length == 0) return Heap::empty_fixed_array();
3486 Object* obj = Heap::AllocateFixedArray(new_length);
3487 if (obj->IsFailure()) return obj;
3488 FixedArray* result = FixedArray::cast(obj);
3489 // Copy the content
Leon Clarke4515c472010-02-03 11:58:03 +00003490 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003491 int len = length();
3492 if (new_length < len) len = new_length;
3493 result->set_map(map());
Leon Clarke4515c472010-02-03 11:58:03 +00003494 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003495 for (int i = 0; i < len; i++) {
3496 result->set(i, get(i), mode);
3497 }
3498 return result;
3499}
3500
3501
3502void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
Leon Clarke4515c472010-02-03 11:58:03 +00003503 AssertNoAllocation no_gc;
3504 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003505 for (int index = 0; index < len; index++) {
3506 dest->set(dest_pos+index, get(pos+index), mode);
3507 }
3508}
3509
3510
3511#ifdef DEBUG
3512bool FixedArray::IsEqualTo(FixedArray* other) {
3513 if (length() != other->length()) return false;
3514 for (int i = 0 ; i < length(); ++i) {
3515 if (get(i) != other->get(i)) return false;
3516 }
3517 return true;
3518}
3519#endif
3520
3521
3522Object* DescriptorArray::Allocate(int number_of_descriptors) {
3523 if (number_of_descriptors == 0) {
3524 return Heap::empty_descriptor_array();
3525 }
3526 // Allocate the array of keys.
Leon Clarkee46be812010-01-19 14:06:41 +00003527 Object* array =
3528 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
Steve Blocka7e24c12009-10-30 11:49:00 +00003529 if (array->IsFailure()) return array;
3530 // Do not use DescriptorArray::cast on incomplete object.
3531 FixedArray* result = FixedArray::cast(array);
3532
3533 // Allocate the content array and set it in the descriptor array.
3534 array = Heap::AllocateFixedArray(number_of_descriptors << 1);
3535 if (array->IsFailure()) return array;
3536 result->set(kContentArrayIndex, array);
3537 result->set(kEnumerationIndexIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00003538 Smi::FromInt(PropertyDetails::kInitialIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +00003539 return result;
3540}
3541
3542
3543void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3544 FixedArray* new_cache) {
3545 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3546 if (HasEnumCache()) {
3547 FixedArray::cast(get(kEnumerationIndexIndex))->
3548 set(kEnumCacheBridgeCacheIndex, new_cache);
3549 } else {
3550 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3551 FixedArray::cast(bridge_storage)->
3552 set(kEnumCacheBridgeCacheIndex, new_cache);
3553 fast_set(FixedArray::cast(bridge_storage),
3554 kEnumCacheBridgeEnumIndex,
3555 get(kEnumerationIndexIndex));
3556 set(kEnumerationIndexIndex, bridge_storage);
3557 }
3558}
3559
3560
3561Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
3562 TransitionFlag transition_flag) {
3563 // Transitions are only kept when inserting another transition.
3564 // This precondition is not required by this function's implementation, but
3565 // is currently required by the semantics of maps, so we check it.
3566 // Conversely, we filter after replacing, so replacing a transition and
3567 // removing all other transitions is not supported.
3568 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3569 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
3570 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
3571
3572 // Ensure the key is a symbol.
3573 Object* result = descriptor->KeyToSymbol();
3574 if (result->IsFailure()) return result;
3575
3576 int transitions = 0;
3577 int null_descriptors = 0;
3578 if (remove_transitions) {
3579 for (int i = 0; i < number_of_descriptors(); i++) {
3580 if (IsTransition(i)) transitions++;
3581 if (IsNullDescriptor(i)) null_descriptors++;
3582 }
3583 } else {
3584 for (int i = 0; i < number_of_descriptors(); i++) {
3585 if (IsNullDescriptor(i)) null_descriptors++;
3586 }
3587 }
3588 int new_size = number_of_descriptors() - transitions - null_descriptors;
3589
3590 // If key is in descriptor, we replace it in-place when filtering.
3591 // Count a null descriptor for key as inserted, not replaced.
3592 int index = Search(descriptor->GetKey());
3593 const bool inserting = (index == kNotFound);
3594 const bool replacing = !inserting;
3595 bool keep_enumeration_index = false;
3596 if (inserting) {
3597 ++new_size;
3598 }
3599 if (replacing) {
3600 // We are replacing an existing descriptor. We keep the enumeration
3601 // index of a visible property.
3602 PropertyType t = PropertyDetails(GetDetails(index)).type();
3603 if (t == CONSTANT_FUNCTION ||
3604 t == FIELD ||
3605 t == CALLBACKS ||
3606 t == INTERCEPTOR) {
3607 keep_enumeration_index = true;
3608 } else if (remove_transitions) {
3609 // Replaced descriptor has been counted as removed if it is
3610 // a transition that will be replaced. Adjust count in this case.
3611 ++new_size;
3612 }
3613 }
3614 result = Allocate(new_size);
3615 if (result->IsFailure()) return result;
3616 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3617 // Set the enumeration index in the descriptors and set the enumeration index
3618 // in the result.
3619 int enumeration_index = NextEnumerationIndex();
3620 if (!descriptor->GetDetails().IsTransition()) {
3621 if (keep_enumeration_index) {
3622 descriptor->SetEnumerationIndex(
3623 PropertyDetails(GetDetails(index)).index());
3624 } else {
3625 descriptor->SetEnumerationIndex(enumeration_index);
3626 ++enumeration_index;
3627 }
3628 }
3629 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3630
3631 // Copy the descriptors, filtering out transitions and null descriptors,
3632 // and inserting or replacing a descriptor.
3633 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
3634 int from_index = 0;
3635 int to_index = 0;
3636
3637 for (; from_index < number_of_descriptors(); from_index++) {
3638 String* key = GetKey(from_index);
3639 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
3640 break;
3641 }
3642 if (IsNullDescriptor(from_index)) continue;
3643 if (remove_transitions && IsTransition(from_index)) continue;
3644 new_descriptors->CopyFrom(to_index++, this, from_index);
3645 }
3646
3647 new_descriptors->Set(to_index++, descriptor);
3648 if (replacing) from_index++;
3649
3650 for (; from_index < number_of_descriptors(); from_index++) {
3651 if (IsNullDescriptor(from_index)) continue;
3652 if (remove_transitions && IsTransition(from_index)) continue;
3653 new_descriptors->CopyFrom(to_index++, this, from_index);
3654 }
3655
3656 ASSERT(to_index == new_descriptors->number_of_descriptors());
3657 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
3658
3659 return new_descriptors;
3660}
3661
3662
3663Object* DescriptorArray::RemoveTransitions() {
3664 // Remove all transitions and null descriptors. Return a copy of the array
3665 // with all transitions removed, or a Failure object if the new array could
3666 // not be allocated.
3667
3668 // Compute the size of the map transition entries to be removed.
3669 int num_removed = 0;
3670 for (int i = 0; i < number_of_descriptors(); i++) {
3671 if (!IsProperty(i)) num_removed++;
3672 }
3673
3674 // Allocate the new descriptor array.
3675 Object* result = Allocate(number_of_descriptors() - num_removed);
3676 if (result->IsFailure()) return result;
3677 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3678
3679 // Copy the content.
3680 int next_descriptor = 0;
3681 for (int i = 0; i < number_of_descriptors(); i++) {
3682 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
3683 }
3684 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
3685
3686 return new_descriptors;
3687}
3688
3689
3690void DescriptorArray::Sort() {
3691 // In-place heap sort.
3692 int len = number_of_descriptors();
3693
3694 // Bottom-up max-heap construction.
Steve Block6ded16b2010-05-10 14:33:55 +01003695 // Index of the last node with children
3696 const int max_parent_index = (len / 2) - 1;
3697 for (int i = max_parent_index; i >= 0; --i) {
3698 int parent_index = i;
3699 const uint32_t parent_hash = GetKey(i)->Hash();
3700 while (parent_index <= max_parent_index) {
3701 int child_index = 2 * parent_index + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003702 uint32_t child_hash = GetKey(child_index)->Hash();
Steve Block6ded16b2010-05-10 14:33:55 +01003703 if (child_index + 1 < len) {
3704 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
3705 if (right_child_hash > child_hash) {
3706 child_index++;
3707 child_hash = right_child_hash;
3708 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003709 }
Steve Block6ded16b2010-05-10 14:33:55 +01003710 if (child_hash <= parent_hash) break;
3711 Swap(parent_index, child_index);
3712 // Now element at child_index could be < its children.
3713 parent_index = child_index; // parent_hash remains correct.
Steve Blocka7e24c12009-10-30 11:49:00 +00003714 }
3715 }
3716
3717 // Extract elements and create sorted array.
3718 for (int i = len - 1; i > 0; --i) {
3719 // Put max element at the back of the array.
3720 Swap(0, i);
3721 // Sift down the new top element.
3722 int parent_index = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01003723 const uint32_t parent_hash = GetKey(parent_index)->Hash();
3724 const int max_parent_index = (i / 2) - 1;
3725 while (parent_index <= max_parent_index) {
3726 int child_index = parent_index * 2 + 1;
3727 uint32_t child_hash = GetKey(child_index)->Hash();
3728 if (child_index + 1 < i) {
3729 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
3730 if (right_child_hash > child_hash) {
3731 child_index++;
3732 child_hash = right_child_hash;
3733 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003734 }
Steve Block6ded16b2010-05-10 14:33:55 +01003735 if (child_hash <= parent_hash) break;
3736 Swap(parent_index, child_index);
3737 parent_index = child_index;
Steve Blocka7e24c12009-10-30 11:49:00 +00003738 }
3739 }
3740
3741 SLOW_ASSERT(IsSortedNoDuplicates());
3742}
3743
3744
3745int DescriptorArray::BinarySearch(String* name, int low, int high) {
3746 uint32_t hash = name->Hash();
3747
3748 while (low <= high) {
3749 int mid = (low + high) / 2;
3750 String* mid_name = GetKey(mid);
3751 uint32_t mid_hash = mid_name->Hash();
3752
3753 if (mid_hash > hash) {
3754 high = mid - 1;
3755 continue;
3756 }
3757 if (mid_hash < hash) {
3758 low = mid + 1;
3759 continue;
3760 }
3761 // Found an element with the same hash-code.
3762 ASSERT(hash == mid_hash);
3763 // There might be more, so we find the first one and
3764 // check them all to see if we have a match.
3765 if (name == mid_name && !is_null_descriptor(mid)) return mid;
3766 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
3767 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
3768 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
3769 }
3770 break;
3771 }
3772 return kNotFound;
3773}
3774
3775
3776int DescriptorArray::LinearSearch(String* name, int len) {
3777 uint32_t hash = name->Hash();
3778 for (int number = 0; number < len; number++) {
3779 String* entry = GetKey(number);
3780 if ((entry->Hash() == hash) &&
3781 name->Equals(entry) &&
3782 !is_null_descriptor(number)) {
3783 return number;
3784 }
3785 }
3786 return kNotFound;
3787}
3788
3789
3790#ifdef DEBUG
3791bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
3792 if (IsEmpty()) return other->IsEmpty();
3793 if (other->IsEmpty()) return false;
3794 if (length() != other->length()) return false;
3795 for (int i = 0; i < length(); ++i) {
3796 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
3797 }
3798 return GetContentArray()->IsEqualTo(other->GetContentArray());
3799}
3800#endif
3801
3802
3803static StaticResource<StringInputBuffer> string_input_buffer;
3804
3805
3806bool String::LooksValid() {
3807 if (!Heap::Contains(this)) return false;
3808 return true;
3809}
3810
3811
3812int String::Utf8Length() {
3813 if (IsAsciiRepresentation()) return length();
3814 // Attempt to flatten before accessing the string. It probably
3815 // doesn't make Utf8Length faster, but it is very likely that
3816 // the string will be accessed later (for example by WriteUtf8)
3817 // so it's still a good idea.
Steve Block6ded16b2010-05-10 14:33:55 +01003818 TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00003819 Access<StringInputBuffer> buffer(&string_input_buffer);
3820 buffer->Reset(0, this);
3821 int result = 0;
3822 while (buffer->has_more())
3823 result += unibrow::Utf8::Length(buffer->GetNext());
3824 return result;
3825}
3826
3827
3828Vector<const char> String::ToAsciiVector() {
3829 ASSERT(IsAsciiRepresentation());
3830 ASSERT(IsFlat());
3831
3832 int offset = 0;
3833 int length = this->length();
3834 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3835 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00003836 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003837 ConsString* cons = ConsString::cast(string);
3838 ASSERT(cons->second()->length() == 0);
3839 string = cons->first();
3840 string_tag = StringShape(string).representation_tag();
3841 }
3842 if (string_tag == kSeqStringTag) {
3843 SeqAsciiString* seq = SeqAsciiString::cast(string);
3844 char* start = seq->GetChars();
3845 return Vector<const char>(start + offset, length);
3846 }
3847 ASSERT(string_tag == kExternalStringTag);
3848 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
3849 const char* start = ext->resource()->data();
3850 return Vector<const char>(start + offset, length);
3851}
3852
3853
3854Vector<const uc16> String::ToUC16Vector() {
3855 ASSERT(IsTwoByteRepresentation());
3856 ASSERT(IsFlat());
3857
3858 int offset = 0;
3859 int length = this->length();
3860 StringRepresentationTag string_tag = StringShape(this).representation_tag();
3861 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00003862 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003863 ConsString* cons = ConsString::cast(string);
3864 ASSERT(cons->second()->length() == 0);
3865 string = cons->first();
3866 string_tag = StringShape(string).representation_tag();
3867 }
3868 if (string_tag == kSeqStringTag) {
3869 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
3870 return Vector<const uc16>(seq->GetChars() + offset, length);
3871 }
3872 ASSERT(string_tag == kExternalStringTag);
3873 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
3874 const uc16* start =
3875 reinterpret_cast<const uc16*>(ext->resource()->data());
3876 return Vector<const uc16>(start + offset, length);
3877}
3878
3879
3880SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3881 RobustnessFlag robust_flag,
3882 int offset,
3883 int length,
3884 int* length_return) {
3885 ASSERT(NativeAllocationChecker::allocation_allowed());
3886 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3887 return SmartPointer<char>(NULL);
3888 }
3889
3890 // Negative length means the to the end of the string.
3891 if (length < 0) length = kMaxInt - offset;
3892
3893 // Compute the size of the UTF-8 string. Start at the specified offset.
3894 Access<StringInputBuffer> buffer(&string_input_buffer);
3895 buffer->Reset(offset, this);
3896 int character_position = offset;
3897 int utf8_bytes = 0;
3898 while (buffer->has_more()) {
3899 uint16_t character = buffer->GetNext();
3900 if (character_position < offset + length) {
3901 utf8_bytes += unibrow::Utf8::Length(character);
3902 }
3903 character_position++;
3904 }
3905
3906 if (length_return) {
3907 *length_return = utf8_bytes;
3908 }
3909
3910 char* result = NewArray<char>(utf8_bytes + 1);
3911
3912 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
3913 buffer->Rewind();
3914 buffer->Seek(offset);
3915 character_position = offset;
3916 int utf8_byte_position = 0;
3917 while (buffer->has_more()) {
3918 uint16_t character = buffer->GetNext();
3919 if (character_position < offset + length) {
3920 if (allow_nulls == DISALLOW_NULLS && character == 0) {
3921 character = ' ';
3922 }
3923 utf8_byte_position +=
3924 unibrow::Utf8::Encode(result + utf8_byte_position, character);
3925 }
3926 character_position++;
3927 }
3928 result[utf8_byte_position] = 0;
3929 return SmartPointer<char>(result);
3930}
3931
3932
3933SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3934 RobustnessFlag robust_flag,
3935 int* length_return) {
3936 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
3937}
3938
3939
3940const uc16* String::GetTwoByteData() {
3941 return GetTwoByteData(0);
3942}
3943
3944
3945const uc16* String::GetTwoByteData(unsigned start) {
3946 ASSERT(!IsAsciiRepresentation());
3947 switch (StringShape(this).representation_tag()) {
3948 case kSeqStringTag:
3949 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
3950 case kExternalStringTag:
3951 return ExternalTwoByteString::cast(this)->
3952 ExternalTwoByteStringGetData(start);
Steve Blocka7e24c12009-10-30 11:49:00 +00003953 case kConsStringTag:
3954 UNREACHABLE();
3955 return NULL;
3956 }
3957 UNREACHABLE();
3958 return NULL;
3959}
3960
3961
3962SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
3963 ASSERT(NativeAllocationChecker::allocation_allowed());
3964
3965 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3966 return SmartPointer<uc16>();
3967 }
3968
3969 Access<StringInputBuffer> buffer(&string_input_buffer);
3970 buffer->Reset(this);
3971
3972 uc16* result = NewArray<uc16>(length() + 1);
3973
3974 int i = 0;
3975 while (buffer->has_more()) {
3976 uint16_t character = buffer->GetNext();
3977 result[i++] = character;
3978 }
3979 result[i] = 0;
3980 return SmartPointer<uc16>(result);
3981}
3982
3983
3984const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
3985 return reinterpret_cast<uc16*>(
3986 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
3987}
3988
3989
3990void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
3991 unsigned* offset_ptr,
3992 unsigned max_chars) {
3993 unsigned chars_read = 0;
3994 unsigned offset = *offset_ptr;
3995 while (chars_read < max_chars) {
3996 uint16_t c = *reinterpret_cast<uint16_t*>(
3997 reinterpret_cast<char*>(this) -
3998 kHeapObjectTag + kHeaderSize + offset * kShortSize);
3999 if (c <= kMaxAsciiCharCode) {
4000 // Fast case for ASCII characters. Cursor is an input output argument.
4001 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4002 rbb->util_buffer,
4003 rbb->capacity,
4004 rbb->cursor)) {
4005 break;
4006 }
4007 } else {
4008 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4009 rbb->util_buffer,
4010 rbb->capacity,
4011 rbb->cursor)) {
4012 break;
4013 }
4014 }
4015 offset++;
4016 chars_read++;
4017 }
4018 *offset_ptr = offset;
4019 rbb->remaining += chars_read;
4020}
4021
4022
4023const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
4024 unsigned* remaining,
4025 unsigned* offset_ptr,
4026 unsigned max_chars) {
4027 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
4028 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
4029 *remaining = max_chars;
4030 *offset_ptr += max_chars;
4031 return b;
4032}
4033
4034
4035// This will iterate unless the block of string data spans two 'halves' of
4036// a ConsString, in which case it will recurse. Since the block of string
4037// data to be read has a maximum size this limits the maximum recursion
4038// depth to something sane. Since C++ does not have tail call recursion
4039// elimination, the iteration must be explicit. Since this is not an
4040// -IntoBuffer method it can delegate to one of the efficient
4041// *AsciiStringReadBlock routines.
4042const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
4043 unsigned* offset_ptr,
4044 unsigned max_chars) {
4045 ConsString* current = this;
4046 unsigned offset = *offset_ptr;
4047 int offset_correction = 0;
4048
4049 while (true) {
4050 String* left = current->first();
4051 unsigned left_length = (unsigned)left->length();
4052 if (left_length > offset &&
4053 (max_chars <= left_length - offset ||
4054 (rbb->capacity <= left_length - offset &&
4055 (max_chars = left_length - offset, true)))) { // comma operator!
4056 // Left hand side only - iterate unless we have reached the bottom of
4057 // the cons tree. The assignment on the left of the comma operator is
4058 // in order to make use of the fact that the -IntoBuffer routines can
4059 // produce at most 'capacity' characters. This enables us to postpone
4060 // the point where we switch to the -IntoBuffer routines (below) in order
4061 // to maximize the chances of delegating a big chunk of work to the
4062 // efficient *AsciiStringReadBlock routines.
4063 if (StringShape(left).IsCons()) {
4064 current = ConsString::cast(left);
4065 continue;
4066 } else {
4067 const unibrow::byte* answer =
4068 String::ReadBlock(left, rbb, &offset, max_chars);
4069 *offset_ptr = offset + offset_correction;
4070 return answer;
4071 }
4072 } else if (left_length <= offset) {
4073 // Right hand side only - iterate unless we have reached the bottom of
4074 // the cons tree.
4075 String* right = current->second();
4076 offset -= left_length;
4077 offset_correction += left_length;
4078 if (StringShape(right).IsCons()) {
4079 current = ConsString::cast(right);
4080 continue;
4081 } else {
4082 const unibrow::byte* answer =
4083 String::ReadBlock(right, rbb, &offset, max_chars);
4084 *offset_ptr = offset + offset_correction;
4085 return answer;
4086 }
4087 } else {
4088 // The block to be read spans two sides of the ConsString, so we call the
4089 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
4090 // are able to assemble data from several part strings because they use
4091 // the util_buffer to store their data and never return direct pointers
4092 // to their storage. We don't try to read more than the buffer capacity
4093 // here or we can get too much recursion.
4094 ASSERT(rbb->remaining == 0);
4095 ASSERT(rbb->cursor == 0);
4096 current->ConsStringReadBlockIntoBuffer(
4097 rbb,
4098 &offset,
4099 max_chars > rbb->capacity ? rbb->capacity : max_chars);
4100 *offset_ptr = offset + offset_correction;
4101 return rbb->util_buffer;
4102 }
4103 }
4104}
4105
4106
Steve Blocka7e24c12009-10-30 11:49:00 +00004107uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
4108 ASSERT(index >= 0 && index < length());
4109 return resource()->data()[index];
4110}
4111
4112
4113const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
4114 unsigned* remaining,
4115 unsigned* offset_ptr,
4116 unsigned max_chars) {
4117 // Cast const char* to unibrow::byte* (signedness difference).
4118 const unibrow::byte* b =
4119 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
4120 *remaining = max_chars;
4121 *offset_ptr += max_chars;
4122 return b;
4123}
4124
4125
4126const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
4127 unsigned start) {
4128 return resource()->data() + start;
4129}
4130
4131
4132uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
4133 ASSERT(index >= 0 && index < length());
4134 return resource()->data()[index];
4135}
4136
4137
4138void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
4139 ReadBlockBuffer* rbb,
4140 unsigned* offset_ptr,
4141 unsigned max_chars) {
4142 unsigned chars_read = 0;
4143 unsigned offset = *offset_ptr;
4144 const uint16_t* data = resource()->data();
4145 while (chars_read < max_chars) {
4146 uint16_t c = data[offset];
4147 if (c <= kMaxAsciiCharCode) {
4148 // Fast case for ASCII characters. Cursor is an input output argument.
4149 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4150 rbb->util_buffer,
4151 rbb->capacity,
4152 rbb->cursor))
4153 break;
4154 } else {
4155 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4156 rbb->util_buffer,
4157 rbb->capacity,
4158 rbb->cursor))
4159 break;
4160 }
4161 offset++;
4162 chars_read++;
4163 }
4164 *offset_ptr = offset;
4165 rbb->remaining += chars_read;
4166}
4167
4168
4169void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4170 unsigned* offset_ptr,
4171 unsigned max_chars) {
4172 unsigned capacity = rbb->capacity - rbb->cursor;
4173 if (max_chars > capacity) max_chars = capacity;
4174 memcpy(rbb->util_buffer + rbb->cursor,
4175 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
4176 *offset_ptr * kCharSize,
4177 max_chars);
4178 rbb->remaining += max_chars;
4179 *offset_ptr += max_chars;
4180 rbb->cursor += max_chars;
4181}
4182
4183
4184void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
4185 ReadBlockBuffer* rbb,
4186 unsigned* offset_ptr,
4187 unsigned max_chars) {
4188 unsigned capacity = rbb->capacity - rbb->cursor;
4189 if (max_chars > capacity) max_chars = capacity;
4190 memcpy(rbb->util_buffer + rbb->cursor,
4191 resource()->data() + *offset_ptr,
4192 max_chars);
4193 rbb->remaining += max_chars;
4194 *offset_ptr += max_chars;
4195 rbb->cursor += max_chars;
4196}
4197
4198
4199// This method determines the type of string involved and then copies
4200// a whole chunk of characters into a buffer, or returns a pointer to a buffer
4201// where they can be found. The pointer is not necessarily valid across a GC
4202// (see AsciiStringReadBlock).
4203const unibrow::byte* String::ReadBlock(String* input,
4204 ReadBlockBuffer* rbb,
4205 unsigned* offset_ptr,
4206 unsigned max_chars) {
4207 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
4208 if (max_chars == 0) {
4209 rbb->remaining = 0;
4210 return NULL;
4211 }
4212 switch (StringShape(input).representation_tag()) {
4213 case kSeqStringTag:
4214 if (input->IsAsciiRepresentation()) {
4215 SeqAsciiString* str = SeqAsciiString::cast(input);
4216 return str->SeqAsciiStringReadBlock(&rbb->remaining,
4217 offset_ptr,
4218 max_chars);
4219 } else {
4220 SeqTwoByteString* str = SeqTwoByteString::cast(input);
4221 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4222 offset_ptr,
4223 max_chars);
4224 return rbb->util_buffer;
4225 }
4226 case kConsStringTag:
4227 return ConsString::cast(input)->ConsStringReadBlock(rbb,
4228 offset_ptr,
4229 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004230 case kExternalStringTag:
4231 if (input->IsAsciiRepresentation()) {
4232 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
4233 &rbb->remaining,
4234 offset_ptr,
4235 max_chars);
4236 } else {
4237 ExternalTwoByteString::cast(input)->
4238 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4239 offset_ptr,
4240 max_chars);
4241 return rbb->util_buffer;
4242 }
4243 default:
4244 break;
4245 }
4246
4247 UNREACHABLE();
4248 return 0;
4249}
4250
4251
4252Relocatable* Relocatable::top_ = NULL;
4253
4254
4255void Relocatable::PostGarbageCollectionProcessing() {
4256 Relocatable* current = top_;
4257 while (current != NULL) {
4258 current->PostGarbageCollection();
4259 current = current->prev_;
4260 }
4261}
4262
4263
4264// Reserve space for statics needing saving and restoring.
4265int Relocatable::ArchiveSpacePerThread() {
4266 return sizeof(top_);
4267}
4268
4269
4270// Archive statics that are thread local.
4271char* Relocatable::ArchiveState(char* to) {
4272 *reinterpret_cast<Relocatable**>(to) = top_;
4273 top_ = NULL;
4274 return to + ArchiveSpacePerThread();
4275}
4276
4277
4278// Restore statics that are thread local.
4279char* Relocatable::RestoreState(char* from) {
4280 top_ = *reinterpret_cast<Relocatable**>(from);
4281 return from + ArchiveSpacePerThread();
4282}
4283
4284
4285char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
4286 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
4287 Iterate(v, top);
4288 return thread_storage + ArchiveSpacePerThread();
4289}
4290
4291
4292void Relocatable::Iterate(ObjectVisitor* v) {
4293 Iterate(v, top_);
4294}
4295
4296
4297void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
4298 Relocatable* current = top;
4299 while (current != NULL) {
4300 current->IterateInstance(v);
4301 current = current->prev_;
4302 }
4303}
4304
4305
4306FlatStringReader::FlatStringReader(Handle<String> str)
4307 : str_(str.location()),
4308 length_(str->length()) {
4309 PostGarbageCollection();
4310}
4311
4312
4313FlatStringReader::FlatStringReader(Vector<const char> input)
4314 : str_(0),
4315 is_ascii_(true),
4316 length_(input.length()),
4317 start_(input.start()) { }
4318
4319
4320void FlatStringReader::PostGarbageCollection() {
4321 if (str_ == NULL) return;
4322 Handle<String> str(str_);
4323 ASSERT(str->IsFlat());
4324 is_ascii_ = str->IsAsciiRepresentation();
4325 if (is_ascii_) {
4326 start_ = str->ToAsciiVector().start();
4327 } else {
4328 start_ = str->ToUC16Vector().start();
4329 }
4330}
4331
4332
4333void StringInputBuffer::Seek(unsigned pos) {
4334 Reset(pos, input_);
4335}
4336
4337
4338void SafeStringInputBuffer::Seek(unsigned pos) {
4339 Reset(pos, input_);
4340}
4341
4342
4343// This method determines the type of string involved and then copies
4344// a whole chunk of characters into a buffer. It can be used with strings
4345// that have been glued together to form a ConsString and which must cooperate
4346// to fill up a buffer.
4347void String::ReadBlockIntoBuffer(String* input,
4348 ReadBlockBuffer* rbb,
4349 unsigned* offset_ptr,
4350 unsigned max_chars) {
4351 ASSERT(*offset_ptr <= (unsigned)input->length());
4352 if (max_chars == 0) return;
4353
4354 switch (StringShape(input).representation_tag()) {
4355 case kSeqStringTag:
4356 if (input->IsAsciiRepresentation()) {
4357 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4358 offset_ptr,
4359 max_chars);
4360 return;
4361 } else {
4362 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4363 offset_ptr,
4364 max_chars);
4365 return;
4366 }
4367 case kConsStringTag:
4368 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4369 offset_ptr,
4370 max_chars);
4371 return;
Steve Blocka7e24c12009-10-30 11:49:00 +00004372 case kExternalStringTag:
4373 if (input->IsAsciiRepresentation()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004374 ExternalAsciiString::cast(input)->
4375 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4376 } else {
4377 ExternalTwoByteString::cast(input)->
4378 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4379 offset_ptr,
4380 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004381 }
4382 return;
4383 default:
4384 break;
4385 }
4386
4387 UNREACHABLE();
4388 return;
4389}
4390
4391
4392const unibrow::byte* String::ReadBlock(String* input,
4393 unibrow::byte* util_buffer,
4394 unsigned capacity,
4395 unsigned* remaining,
4396 unsigned* offset_ptr) {
4397 ASSERT(*offset_ptr <= (unsigned)input->length());
4398 unsigned chars = input->length() - *offset_ptr;
4399 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4400 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4401 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4402 *remaining = rbb.remaining;
4403 return answer;
4404}
4405
4406
4407const unibrow::byte* String::ReadBlock(String** raw_input,
4408 unibrow::byte* util_buffer,
4409 unsigned capacity,
4410 unsigned* remaining,
4411 unsigned* offset_ptr) {
4412 Handle<String> input(raw_input);
4413 ASSERT(*offset_ptr <= (unsigned)input->length());
4414 unsigned chars = input->length() - *offset_ptr;
4415 if (chars > capacity) chars = capacity;
4416 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4417 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4418 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4419 *remaining = rbb.remaining;
4420 return rbb.util_buffer;
4421}
4422
4423
4424// This will iterate unless the block of string data spans two 'halves' of
4425// a ConsString, in which case it will recurse. Since the block of string
4426// data to be read has a maximum size this limits the maximum recursion
4427// depth to something sane. Since C++ does not have tail call recursion
4428// elimination, the iteration must be explicit.
4429void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4430 unsigned* offset_ptr,
4431 unsigned max_chars) {
4432 ConsString* current = this;
4433 unsigned offset = *offset_ptr;
4434 int offset_correction = 0;
4435
4436 while (true) {
4437 String* left = current->first();
4438 unsigned left_length = (unsigned)left->length();
4439 if (left_length > offset &&
4440 max_chars <= left_length - offset) {
4441 // Left hand side only - iterate unless we have reached the bottom of
4442 // the cons tree.
4443 if (StringShape(left).IsCons()) {
4444 current = ConsString::cast(left);
4445 continue;
4446 } else {
4447 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4448 *offset_ptr = offset + offset_correction;
4449 return;
4450 }
4451 } else if (left_length <= offset) {
4452 // Right hand side only - iterate unless we have reached the bottom of
4453 // the cons tree.
4454 offset -= left_length;
4455 offset_correction += left_length;
4456 String* right = current->second();
4457 if (StringShape(right).IsCons()) {
4458 current = ConsString::cast(right);
4459 continue;
4460 } else {
4461 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4462 *offset_ptr = offset + offset_correction;
4463 return;
4464 }
4465 } else {
4466 // The block to be read spans two sides of the ConsString, so we recurse.
4467 // First recurse on the left.
4468 max_chars -= left_length - offset;
4469 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4470 // We may have reached the max or there may not have been enough space
4471 // in the buffer for the characters in the left hand side.
4472 if (offset == left_length) {
4473 // Recurse on the right.
4474 String* right = String::cast(current->second());
4475 offset -= left_length;
4476 offset_correction += left_length;
4477 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4478 }
4479 *offset_ptr = offset + offset_correction;
4480 return;
4481 }
4482 }
4483}
4484
4485
Steve Blocka7e24c12009-10-30 11:49:00 +00004486uint16_t ConsString::ConsStringGet(int index) {
4487 ASSERT(index >= 0 && index < this->length());
4488
4489 // Check for a flattened cons string
4490 if (second()->length() == 0) {
4491 String* left = first();
4492 return left->Get(index);
4493 }
4494
4495 String* string = String::cast(this);
4496
4497 while (true) {
4498 if (StringShape(string).IsCons()) {
4499 ConsString* cons_string = ConsString::cast(string);
4500 String* left = cons_string->first();
4501 if (left->length() > index) {
4502 string = left;
4503 } else {
4504 index -= left->length();
4505 string = cons_string->second();
4506 }
4507 } else {
4508 return string->Get(index);
4509 }
4510 }
4511
4512 UNREACHABLE();
4513 return 0;
4514}
4515
4516
4517template <typename sinkchar>
4518void String::WriteToFlat(String* src,
4519 sinkchar* sink,
4520 int f,
4521 int t) {
4522 String* source = src;
4523 int from = f;
4524 int to = t;
4525 while (true) {
4526 ASSERT(0 <= from && from <= to && to <= source->length());
4527 switch (StringShape(source).full_representation_tag()) {
4528 case kAsciiStringTag | kExternalStringTag: {
4529 CopyChars(sink,
4530 ExternalAsciiString::cast(source)->resource()->data() + from,
4531 to - from);
4532 return;
4533 }
4534 case kTwoByteStringTag | kExternalStringTag: {
4535 const uc16* data =
4536 ExternalTwoByteString::cast(source)->resource()->data();
4537 CopyChars(sink,
4538 data + from,
4539 to - from);
4540 return;
4541 }
4542 case kAsciiStringTag | kSeqStringTag: {
4543 CopyChars(sink,
4544 SeqAsciiString::cast(source)->GetChars() + from,
4545 to - from);
4546 return;
4547 }
4548 case kTwoByteStringTag | kSeqStringTag: {
4549 CopyChars(sink,
4550 SeqTwoByteString::cast(source)->GetChars() + from,
4551 to - from);
4552 return;
4553 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004554 case kAsciiStringTag | kConsStringTag:
4555 case kTwoByteStringTag | kConsStringTag: {
4556 ConsString* cons_string = ConsString::cast(source);
4557 String* first = cons_string->first();
4558 int boundary = first->length();
4559 if (to - boundary >= boundary - from) {
4560 // Right hand side is longer. Recurse over left.
4561 if (from < boundary) {
4562 WriteToFlat(first, sink, from, boundary);
4563 sink += boundary - from;
4564 from = 0;
4565 } else {
4566 from -= boundary;
4567 }
4568 to -= boundary;
4569 source = cons_string->second();
4570 } else {
4571 // Left hand side is longer. Recurse over right.
4572 if (to > boundary) {
4573 String* second = cons_string->second();
4574 WriteToFlat(second,
4575 sink + boundary - from,
4576 0,
4577 to - boundary);
4578 to = boundary;
4579 }
4580 source = first;
4581 }
4582 break;
4583 }
4584 }
4585 }
4586}
4587
4588
Steve Blocka7e24c12009-10-30 11:49:00 +00004589template <typename IteratorA, typename IteratorB>
4590static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4591 // General slow case check. We know that the ia and ib iterators
4592 // have the same length.
4593 while (ia->has_more()) {
4594 uc32 ca = ia->GetNext();
4595 uc32 cb = ib->GetNext();
4596 if (ca != cb)
4597 return false;
4598 }
4599 return true;
4600}
4601
4602
4603// Compares the contents of two strings by reading and comparing
4604// int-sized blocks of characters.
4605template <typename Char>
4606static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4607 int length = a.length();
4608 ASSERT_EQ(length, b.length());
4609 const Char* pa = a.start();
4610 const Char* pb = b.start();
4611 int i = 0;
4612#ifndef V8_HOST_CAN_READ_UNALIGNED
4613 // If this architecture isn't comfortable reading unaligned ints
4614 // then we have to check that the strings are aligned before
4615 // comparing them blockwise.
4616 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4617 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4618 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
4619 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
4620#endif
4621 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
4622 int endpoint = length - kStepSize;
4623 // Compare blocks until we reach near the end of the string.
4624 for (; i <= endpoint; i += kStepSize) {
4625 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
4626 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
4627 if (wa != wb) {
4628 return false;
4629 }
4630 }
4631#ifndef V8_HOST_CAN_READ_UNALIGNED
4632 }
4633#endif
4634 // Compare the remaining characters that didn't fit into a block.
4635 for (; i < length; i++) {
4636 if (a[i] != b[i]) {
4637 return false;
4638 }
4639 }
4640 return true;
4641}
4642
4643
4644static StringInputBuffer string_compare_buffer_b;
4645
4646
4647template <typename IteratorA>
4648static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
4649 if (b->IsFlat()) {
4650 if (b->IsAsciiRepresentation()) {
4651 VectorIterator<char> ib(b->ToAsciiVector());
4652 return CompareStringContents(ia, &ib);
4653 } else {
4654 VectorIterator<uc16> ib(b->ToUC16Vector());
4655 return CompareStringContents(ia, &ib);
4656 }
4657 } else {
4658 string_compare_buffer_b.Reset(0, b);
4659 return CompareStringContents(ia, &string_compare_buffer_b);
4660 }
4661}
4662
4663
4664static StringInputBuffer string_compare_buffer_a;
4665
4666
4667bool String::SlowEquals(String* other) {
4668 // Fast check: negative check with lengths.
4669 int len = length();
4670 if (len != other->length()) return false;
4671 if (len == 0) return true;
4672
4673 // Fast check: if hash code is computed for both strings
4674 // a fast negative check can be performed.
4675 if (HasHashCode() && other->HasHashCode()) {
4676 if (Hash() != other->Hash()) return false;
4677 }
4678
Leon Clarkef7060e22010-06-03 12:02:55 +01004679 // We know the strings are both non-empty. Compare the first chars
4680 // before we try to flatten the strings.
4681 if (this->Get(0) != other->Get(0)) return false;
4682
4683 String* lhs = this->TryFlattenGetString();
4684 String* rhs = other->TryFlattenGetString();
4685
4686 if (StringShape(lhs).IsSequentialAscii() &&
4687 StringShape(rhs).IsSequentialAscii()) {
4688 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
4689 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
Steve Blocka7e24c12009-10-30 11:49:00 +00004690 return CompareRawStringContents(Vector<const char>(str1, len),
4691 Vector<const char>(str2, len));
4692 }
4693
Leon Clarkef7060e22010-06-03 12:02:55 +01004694 if (lhs->IsFlat()) {
Ben Murdochbb769b22010-08-11 14:56:33 +01004695 if (lhs->IsAsciiRepresentation()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01004696 Vector<const char> vec1 = lhs->ToAsciiVector();
4697 if (rhs->IsFlat()) {
4698 if (rhs->IsAsciiRepresentation()) {
4699 Vector<const char> vec2 = rhs->ToAsciiVector();
Steve Blocka7e24c12009-10-30 11:49:00 +00004700 return CompareRawStringContents(vec1, vec2);
4701 } else {
4702 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004703 VectorIterator<uc16> ib(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004704 return CompareStringContents(&buf1, &ib);
4705 }
4706 } else {
4707 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004708 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004709 return CompareStringContents(&buf1, &string_compare_buffer_b);
4710 }
4711 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004712 Vector<const uc16> vec1 = lhs->ToUC16Vector();
4713 if (rhs->IsFlat()) {
4714 if (rhs->IsAsciiRepresentation()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004715 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004716 VectorIterator<char> ib(rhs->ToAsciiVector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004717 return CompareStringContents(&buf1, &ib);
4718 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004719 Vector<const uc16> vec2(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00004720 return CompareRawStringContents(vec1, vec2);
4721 }
4722 } else {
4723 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01004724 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004725 return CompareStringContents(&buf1, &string_compare_buffer_b);
4726 }
4727 }
4728 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01004729 string_compare_buffer_a.Reset(0, lhs);
4730 return CompareStringContentsPartial(&string_compare_buffer_a, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00004731 }
4732}
4733
4734
4735bool String::MarkAsUndetectable() {
4736 if (StringShape(this).IsSymbol()) return false;
4737
4738 Map* map = this->map();
Steve Blockd0582a62009-12-15 09:54:21 +00004739 if (map == Heap::string_map()) {
4740 this->set_map(Heap::undetectable_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00004741 return true;
Steve Blockd0582a62009-12-15 09:54:21 +00004742 } else if (map == Heap::ascii_string_map()) {
4743 this->set_map(Heap::undetectable_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00004744 return true;
4745 }
4746 // Rest cannot be marked as undetectable
4747 return false;
4748}
4749
4750
4751bool String::IsEqualTo(Vector<const char> str) {
4752 int slen = length();
4753 Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
4754 decoder->Reset(str.start(), str.length());
4755 int i;
4756 for (i = 0; i < slen && decoder->has_more(); i++) {
4757 uc32 r = decoder->GetNext();
4758 if (Get(i) != r) return false;
4759 }
4760 return i == slen && !decoder->has_more();
4761}
4762
4763
Steve Block6ded16b2010-05-10 14:33:55 +01004764template <typename schar>
4765static inline uint32_t HashSequentialString(const schar* chars, int length) {
4766 StringHasher hasher(length);
4767 if (!hasher.has_trivial_hash()) {
4768 int i;
4769 for (i = 0; hasher.is_array_index() && (i < length); i++) {
4770 hasher.AddCharacter(chars[i]);
4771 }
4772 for (; i < length; i++) {
4773 hasher.AddCharacterNoIndex(chars[i]);
4774 }
4775 }
4776 return hasher.GetHashField();
4777}
4778
4779
Steve Blocka7e24c12009-10-30 11:49:00 +00004780uint32_t String::ComputeAndSetHash() {
4781 // Should only be called if hash code has not yet been computed.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004782 ASSERT(!HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00004783
Steve Block6ded16b2010-05-10 14:33:55 +01004784 const int len = length();
4785
Steve Blocka7e24c12009-10-30 11:49:00 +00004786 // Compute the hash code.
Steve Block6ded16b2010-05-10 14:33:55 +01004787 uint32_t field = 0;
4788 if (StringShape(this).IsSequentialAscii()) {
4789 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
4790 } else if (StringShape(this).IsSequentialTwoByte()) {
4791 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
4792 } else {
4793 StringInputBuffer buffer(this);
4794 field = ComputeHashField(&buffer, len);
4795 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004796
4797 // Store the hash code in the object.
Steve Blockd0582a62009-12-15 09:54:21 +00004798 set_hash_field(field);
Steve Blocka7e24c12009-10-30 11:49:00 +00004799
4800 // Check the hash code is there.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004801 ASSERT(HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00004802 uint32_t result = field >> kHashShift;
4803 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
4804 return result;
4805}
4806
4807
4808bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
4809 uint32_t* index,
4810 int length) {
4811 if (length == 0 || length > kMaxArrayIndexSize) return false;
4812 uc32 ch = buffer->GetNext();
4813
4814 // If the string begins with a '0' character, it must only consist
4815 // of it to be a legal array index.
4816 if (ch == '0') {
4817 *index = 0;
4818 return length == 1;
4819 }
4820
4821 // Convert string to uint32 array index; character by character.
4822 int d = ch - '0';
4823 if (d < 0 || d > 9) return false;
4824 uint32_t result = d;
4825 while (buffer->has_more()) {
4826 d = buffer->GetNext() - '0';
4827 if (d < 0 || d > 9) return false;
4828 // Check that the new result is below the 32 bit limit.
4829 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
4830 result = (result * 10) + d;
4831 }
4832
4833 *index = result;
4834 return true;
4835}
4836
4837
4838bool String::SlowAsArrayIndex(uint32_t* index) {
4839 if (length() <= kMaxCachedArrayIndexLength) {
4840 Hash(); // force computation of hash code
Steve Blockd0582a62009-12-15 09:54:21 +00004841 uint32_t field = hash_field();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004842 if ((field & kIsNotArrayIndexMask) != 0) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00004843 // Isolate the array index form the full hash field.
4844 *index = (kArrayIndexHashMask & field) >> kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00004845 return true;
4846 } else {
4847 StringInputBuffer buffer(this);
4848 return ComputeArrayIndex(&buffer, index, length());
4849 }
4850}
4851
4852
Steve Blockd0582a62009-12-15 09:54:21 +00004853static inline uint32_t HashField(uint32_t hash,
4854 bool is_array_index,
4855 int length = -1) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004856 uint32_t result = (hash << String::kHashShift);
Steve Blockd0582a62009-12-15 09:54:21 +00004857 if (is_array_index) {
4858 // For array indexes mix the length into the hash as an array index could
4859 // be zero.
4860 ASSERT(length > 0);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004861 ASSERT(length <= String::kMaxArrayIndexSize);
Steve Blockd0582a62009-12-15 09:54:21 +00004862 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
4863 (1 << String::kArrayIndexValueBits));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004864 ASSERT(String::kMaxArrayIndexSize < (1 << String::kArrayIndexValueBits));
4865 result &= ~String::kIsNotArrayIndexMask;
Steve Blockd0582a62009-12-15 09:54:21 +00004866 result |= length << String::kArrayIndexHashLengthShift;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01004867 } else {
4868 result |= String::kIsNotArrayIndexMask;
Steve Blockd0582a62009-12-15 09:54:21 +00004869 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004870 return result;
4871}
4872
4873
4874uint32_t StringHasher::GetHashField() {
4875 ASSERT(is_valid());
Steve Blockd0582a62009-12-15 09:54:21 +00004876 if (length_ <= String::kMaxHashCalcLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004877 if (is_array_index()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004878 return v8::internal::HashField(array_index(), true, length_);
Steve Blocka7e24c12009-10-30 11:49:00 +00004879 } else {
Steve Blockd0582a62009-12-15 09:54:21 +00004880 return v8::internal::HashField(GetHash(), false);
Steve Blocka7e24c12009-10-30 11:49:00 +00004881 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004882 uint32_t payload = v8::internal::HashField(GetHash(), false);
Steve Blockd0582a62009-12-15 09:54:21 +00004883 return payload;
Steve Blocka7e24c12009-10-30 11:49:00 +00004884 } else {
4885 return v8::internal::HashField(length_, false);
4886 }
4887}
4888
4889
Steve Blockd0582a62009-12-15 09:54:21 +00004890uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
4891 int length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004892 StringHasher hasher(length);
4893
4894 // Very long strings have a trivial hash that doesn't inspect the
4895 // string contents.
4896 if (hasher.has_trivial_hash()) {
4897 return hasher.GetHashField();
4898 }
4899
4900 // Do the iterative array index computation as long as there is a
4901 // chance this is an array index.
4902 while (buffer->has_more() && hasher.is_array_index()) {
4903 hasher.AddCharacter(buffer->GetNext());
4904 }
4905
4906 // Process the remaining characters without updating the array
4907 // index.
4908 while (buffer->has_more()) {
4909 hasher.AddCharacterNoIndex(buffer->GetNext());
4910 }
4911
4912 return hasher.GetHashField();
4913}
4914
4915
Steve Block6ded16b2010-05-10 14:33:55 +01004916Object* String::SubString(int start, int end, PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004917 if (start == 0 && end == length()) return this;
Steve Block6ded16b2010-05-10 14:33:55 +01004918 Object* result = Heap::AllocateSubString(this, start, end, pretenure);
Steve Blockd0582a62009-12-15 09:54:21 +00004919 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00004920}
4921
4922
4923void String::PrintOn(FILE* file) {
4924 int length = this->length();
4925 for (int i = 0; i < length; i++) {
4926 fprintf(file, "%c", Get(i));
4927 }
4928}
4929
4930
4931void Map::CreateBackPointers() {
4932 DescriptorArray* descriptors = instance_descriptors();
4933 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
Iain Merrick75681382010-08-19 15:07:18 +01004934 if (descriptors->GetType(i) == MAP_TRANSITION ||
4935 descriptors->GetType(i) == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004936 // Get target.
4937 Map* target = Map::cast(descriptors->GetValue(i));
4938#ifdef DEBUG
4939 // Verify target.
4940 Object* source_prototype = prototype();
4941 Object* target_prototype = target->prototype();
4942 ASSERT(source_prototype->IsJSObject() ||
4943 source_prototype->IsMap() ||
4944 source_prototype->IsNull());
4945 ASSERT(target_prototype->IsJSObject() ||
4946 target_prototype->IsNull());
4947 ASSERT(source_prototype->IsMap() ||
4948 source_prototype == target_prototype);
4949#endif
4950 // Point target back to source. set_prototype() will not let us set
4951 // the prototype to a map, as we do here.
4952 *RawField(target, kPrototypeOffset) = this;
4953 }
4954 }
4955}
4956
4957
4958void Map::ClearNonLiveTransitions(Object* real_prototype) {
4959 // Live DescriptorArray objects will be marked, so we must use
4960 // low-level accessors to get and modify their data.
4961 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
4962 *RawField(this, Map::kInstanceDescriptorsOffset));
4963 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
4964 Smi* NullDescriptorDetails =
4965 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
4966 FixedArray* contents = reinterpret_cast<FixedArray*>(
4967 d->get(DescriptorArray::kContentArrayIndex));
4968 ASSERT(contents->length() >= 2);
4969 for (int i = 0; i < contents->length(); i += 2) {
4970 // If the pair (value, details) is a map transition,
4971 // check if the target is live. If not, null the descriptor.
4972 // Also drop the back pointer for that map transition, so that this
4973 // map is not reached again by following a back pointer from a
4974 // non-live object.
4975 PropertyDetails details(Smi::cast(contents->get(i + 1)));
Iain Merrick75681382010-08-19 15:07:18 +01004976 if (details.type() == MAP_TRANSITION ||
4977 details.type() == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004978 Map* target = reinterpret_cast<Map*>(contents->get(i));
4979 ASSERT(target->IsHeapObject());
4980 if (!target->IsMarked()) {
4981 ASSERT(target->IsMap());
Iain Merrick75681382010-08-19 15:07:18 +01004982 contents->set_unchecked(i + 1, NullDescriptorDetails);
4983 contents->set_null_unchecked(i);
Steve Blocka7e24c12009-10-30 11:49:00 +00004984 ASSERT(target->prototype() == this ||
4985 target->prototype() == real_prototype);
4986 // Getter prototype() is read-only, set_prototype() has side effects.
4987 *RawField(target, Map::kPrototypeOffset) = real_prototype;
4988 }
4989 }
4990 }
4991}
4992
4993
Steve Block791712a2010-08-27 10:21:07 +01004994void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
4995 // Iterate over all fields in the body but take care in dealing with
4996 // the code entry.
4997 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
4998 v->VisitCodeEntry(this->address() + kCodeEntryOffset);
4999 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
5000}
5001
5002
Steve Blocka7e24c12009-10-30 11:49:00 +00005003Object* JSFunction::SetInstancePrototype(Object* value) {
5004 ASSERT(value->IsJSObject());
5005
5006 if (has_initial_map()) {
5007 initial_map()->set_prototype(value);
5008 } else {
5009 // Put the value in the initial map field until an initial map is
5010 // needed. At that point, a new initial map is created and the
5011 // prototype is put into the initial map where it belongs.
5012 set_prototype_or_initial_map(value);
5013 }
Kristian Monsen25f61362010-05-21 11:50:48 +01005014 Heap::ClearInstanceofCache();
Steve Blocka7e24c12009-10-30 11:49:00 +00005015 return value;
5016}
5017
5018
Steve Blocka7e24c12009-10-30 11:49:00 +00005019Object* JSFunction::SetPrototype(Object* value) {
Steve Block6ded16b2010-05-10 14:33:55 +01005020 ASSERT(should_have_prototype());
Steve Blocka7e24c12009-10-30 11:49:00 +00005021 Object* construct_prototype = value;
5022
5023 // If the value is not a JSObject, store the value in the map's
5024 // constructor field so it can be accessed. Also, set the prototype
5025 // used for constructing objects to the original object prototype.
5026 // See ECMA-262 13.2.2.
5027 if (!value->IsJSObject()) {
5028 // Copy the map so this does not affect unrelated functions.
5029 // Remove map transitions because they point to maps with a
5030 // different prototype.
5031 Object* new_map = map()->CopyDropTransitions();
5032 if (new_map->IsFailure()) return new_map;
5033 set_map(Map::cast(new_map));
5034 map()->set_constructor(value);
5035 map()->set_non_instance_prototype(true);
5036 construct_prototype =
5037 Top::context()->global_context()->initial_object_prototype();
5038 } else {
5039 map()->set_non_instance_prototype(false);
5040 }
5041
5042 return SetInstancePrototype(construct_prototype);
5043}
5044
5045
Steve Block6ded16b2010-05-10 14:33:55 +01005046Object* JSFunction::RemovePrototype() {
5047 ASSERT(map() == context()->global_context()->function_map());
5048 set_map(context()->global_context()->function_without_prototype_map());
5049 set_prototype_or_initial_map(Heap::the_hole_value());
5050 return this;
5051}
5052
5053
Steve Blocka7e24c12009-10-30 11:49:00 +00005054Object* JSFunction::SetInstanceClassName(String* name) {
5055 shared()->set_instance_class_name(name);
5056 return this;
5057}
5058
5059
5060Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
5061 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
5062}
5063
5064
Steve Blocka7e24c12009-10-30 11:49:00 +00005065Object* Oddball::Initialize(const char* to_string, Object* to_number) {
5066 Object* symbol = Heap::LookupAsciiSymbol(to_string);
5067 if (symbol->IsFailure()) return symbol;
5068 set_to_string(String::cast(symbol));
5069 set_to_number(to_number);
5070 return this;
5071}
5072
5073
5074bool SharedFunctionInfo::HasSourceCode() {
5075 return !script()->IsUndefined() &&
Iain Merrick75681382010-08-19 15:07:18 +01005076 !reinterpret_cast<Script*>(script())->source()->IsUndefined();
Steve Blocka7e24c12009-10-30 11:49:00 +00005077}
5078
5079
5080Object* SharedFunctionInfo::GetSourceCode() {
5081 HandleScope scope;
5082 if (script()->IsUndefined()) return Heap::undefined_value();
5083 Object* source = Script::cast(script())->source();
5084 if (source->IsUndefined()) return Heap::undefined_value();
5085 return *SubString(Handle<String>(String::cast(source)),
5086 start_position(), end_position());
5087}
5088
5089
5090int SharedFunctionInfo::CalculateInstanceSize() {
5091 int instance_size =
5092 JSObject::kHeaderSize +
5093 expected_nof_properties() * kPointerSize;
5094 if (instance_size > JSObject::kMaxInstanceSize) {
5095 instance_size = JSObject::kMaxInstanceSize;
5096 }
5097 return instance_size;
5098}
5099
5100
5101int SharedFunctionInfo::CalculateInObjectProperties() {
5102 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
5103}
5104
5105
Andrei Popescu402d9372010-02-26 13:31:12 +00005106bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
5107 // Check the basic conditions for generating inline constructor code.
5108 if (!FLAG_inline_new
5109 || !has_only_simple_this_property_assignments()
5110 || this_property_assignments_count() == 0) {
5111 return false;
5112 }
5113
5114 // If the prototype is null inline constructors cause no problems.
5115 if (!prototype->IsJSObject()) {
5116 ASSERT(prototype->IsNull());
5117 return true;
5118 }
5119
5120 // Traverse the proposed prototype chain looking for setters for properties of
5121 // the same names as are set by the inline constructor.
5122 for (Object* obj = prototype;
5123 obj != Heap::null_value();
5124 obj = obj->GetPrototype()) {
5125 JSObject* js_object = JSObject::cast(obj);
5126 for (int i = 0; i < this_property_assignments_count(); i++) {
5127 LookupResult result;
5128 String* name = GetThisPropertyAssignmentName(i);
5129 js_object->LocalLookupRealNamedProperty(name, &result);
5130 if (result.IsProperty() && result.type() == CALLBACKS) {
5131 return false;
5132 }
5133 }
5134 }
5135
5136 return true;
5137}
5138
5139
Steve Blocka7e24c12009-10-30 11:49:00 +00005140void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +00005141 bool only_simple_this_property_assignments,
5142 FixedArray* assignments) {
5143 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005144 kHasOnlySimpleThisPropertyAssignments,
5145 only_simple_this_property_assignments));
5146 set_this_property_assignments(assignments);
5147 set_this_property_assignments_count(assignments->length() / 3);
5148}
5149
5150
5151void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
5152 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005153 kHasOnlySimpleThisPropertyAssignments,
5154 false));
5155 set_this_property_assignments(Heap::undefined_value());
5156 set_this_property_assignments_count(0);
5157}
5158
5159
5160String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
5161 Object* obj = this_property_assignments();
5162 ASSERT(obj->IsFixedArray());
5163 ASSERT(index < this_property_assignments_count());
5164 obj = FixedArray::cast(obj)->get(index * 3);
5165 ASSERT(obj->IsString());
5166 return String::cast(obj);
5167}
5168
5169
5170bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
5171 Object* obj = this_property_assignments();
5172 ASSERT(obj->IsFixedArray());
5173 ASSERT(index < this_property_assignments_count());
5174 obj = FixedArray::cast(obj)->get(index * 3 + 1);
5175 return Smi::cast(obj)->value() != -1;
5176}
5177
5178
5179int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
5180 ASSERT(IsThisPropertyAssignmentArgument(index));
5181 Object* obj =
5182 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
5183 return Smi::cast(obj)->value();
5184}
5185
5186
5187Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
5188 ASSERT(!IsThisPropertyAssignmentArgument(index));
5189 Object* obj =
5190 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
5191 return obj;
5192}
5193
5194
Steve Blocka7e24c12009-10-30 11:49:00 +00005195// Support function for printing the source code to a StringStream
5196// without any allocation in the heap.
5197void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
5198 int max_length) {
5199 // For some native functions there is no source.
5200 if (script()->IsUndefined() ||
5201 Script::cast(script())->source()->IsUndefined()) {
5202 accumulator->Add("<No Source>");
5203 return;
5204 }
5205
Steve Blockd0582a62009-12-15 09:54:21 +00005206 // Get the source for the script which this function came from.
Steve Blocka7e24c12009-10-30 11:49:00 +00005207 // Don't use String::cast because we don't want more assertion errors while
5208 // we are already creating a stack dump.
5209 String* script_source =
5210 reinterpret_cast<String*>(Script::cast(script())->source());
5211
5212 if (!script_source->LooksValid()) {
5213 accumulator->Add("<Invalid Source>");
5214 return;
5215 }
5216
5217 if (!is_toplevel()) {
5218 accumulator->Add("function ");
5219 Object* name = this->name();
5220 if (name->IsString() && String::cast(name)->length() > 0) {
5221 accumulator->PrintName(name);
5222 }
5223 }
5224
5225 int len = end_position() - start_position();
5226 if (len > max_length) {
5227 accumulator->Put(script_source,
5228 start_position(),
5229 start_position() + max_length);
5230 accumulator->Add("...\n");
5231 } else {
5232 accumulator->Put(script_source, start_position(), end_position());
5233 }
5234}
5235
5236
Steve Blocka7e24c12009-10-30 11:49:00 +00005237void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
5238 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
5239 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
5240 Object* old_target = target;
5241 VisitPointer(&target);
5242 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5243}
5244
5245
Steve Block791712a2010-08-27 10:21:07 +01005246void ObjectVisitor::VisitCodeEntry(Address entry_address) {
5247 Object* code = Code::GetObjectFromEntryAddress(entry_address);
5248 Object* old_code = code;
5249 VisitPointer(&code);
5250 if (code != old_code) {
5251 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry();
5252 }
5253}
5254
5255
Steve Blocka7e24c12009-10-30 11:49:00 +00005256void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005257 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
5258 rinfo->IsPatchedReturnSequence()) ||
5259 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
5260 rinfo->IsPatchedDebugBreakSlotSequence()));
Steve Blocka7e24c12009-10-30 11:49:00 +00005261 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
5262 Object* old_target = target;
5263 VisitPointer(&target);
5264 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5265}
5266
5267
Steve Blockd0582a62009-12-15 09:54:21 +00005268void Code::Relocate(intptr_t delta) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005269 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
5270 it.rinfo()->apply(delta);
5271 }
5272 CPU::FlushICache(instruction_start(), instruction_size());
5273}
5274
5275
5276void Code::CopyFrom(const CodeDesc& desc) {
5277 // copy code
5278 memmove(instruction_start(), desc.buffer, desc.instr_size);
5279
Steve Blocka7e24c12009-10-30 11:49:00 +00005280 // copy reloc info
5281 memmove(relocation_start(),
5282 desc.buffer + desc.buffer_size - desc.reloc_size,
5283 desc.reloc_size);
5284
5285 // unbox handles and relocate
Steve Block3ce2e202009-11-05 08:53:23 +00005286 intptr_t delta = instruction_start() - desc.buffer;
Steve Blocka7e24c12009-10-30 11:49:00 +00005287 int mode_mask = RelocInfo::kCodeTargetMask |
5288 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
5289 RelocInfo::kApplyMask;
Steve Block3ce2e202009-11-05 08:53:23 +00005290 Assembler* origin = desc.origin; // Needed to find target_object on X64.
Steve Blocka7e24c12009-10-30 11:49:00 +00005291 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
5292 RelocInfo::Mode mode = it.rinfo()->rmode();
5293 if (mode == RelocInfo::EMBEDDED_OBJECT) {
Steve Block3ce2e202009-11-05 08:53:23 +00005294 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005295 it.rinfo()->set_target_object(*p);
5296 } else if (RelocInfo::IsCodeTarget(mode)) {
5297 // rewrite code handles in inline cache targets to direct
5298 // pointers to the first instruction in the code object
Steve Block3ce2e202009-11-05 08:53:23 +00005299 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005300 Code* code = Code::cast(*p);
5301 it.rinfo()->set_target_address(code->instruction_start());
5302 } else {
5303 it.rinfo()->apply(delta);
5304 }
5305 }
5306 CPU::FlushICache(instruction_start(), instruction_size());
5307}
5308
5309
5310// Locate the source position which is closest to the address in the code. This
5311// is using the source position information embedded in the relocation info.
5312// The position returned is relative to the beginning of the script where the
5313// source for this function is found.
5314int Code::SourcePosition(Address pc) {
5315 int distance = kMaxInt;
5316 int position = RelocInfo::kNoPosition; // Initially no position found.
5317 // Run through all the relocation info to find the best matching source
5318 // position. All the code needs to be considered as the sequence of the
5319 // instructions in the code does not necessarily follow the same order as the
5320 // source.
5321 RelocIterator it(this, RelocInfo::kPositionMask);
5322 while (!it.done()) {
5323 // Only look at positions after the current pc.
5324 if (it.rinfo()->pc() < pc) {
5325 // Get position and distance.
Steve Blockd0582a62009-12-15 09:54:21 +00005326
5327 int dist = static_cast<int>(pc - it.rinfo()->pc());
5328 int pos = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005329 // If this position is closer than the current candidate or if it has the
5330 // same distance as the current candidate and the position is higher then
5331 // this position is the new candidate.
5332 if ((dist < distance) ||
5333 (dist == distance && pos > position)) {
5334 position = pos;
5335 distance = dist;
5336 }
5337 }
5338 it.next();
5339 }
5340 return position;
5341}
5342
5343
5344// Same as Code::SourcePosition above except it only looks for statement
5345// positions.
5346int Code::SourceStatementPosition(Address pc) {
5347 // First find the position as close as possible using all position
5348 // information.
5349 int position = SourcePosition(pc);
5350 // Now find the closest statement position before the position.
5351 int statement_position = 0;
5352 RelocIterator it(this, RelocInfo::kPositionMask);
5353 while (!it.done()) {
5354 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
Steve Blockd0582a62009-12-15 09:54:21 +00005355 int p = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005356 if (statement_position < p && p <= position) {
5357 statement_position = p;
5358 }
5359 }
5360 it.next();
5361 }
5362 return statement_position;
5363}
5364
5365
5366#ifdef ENABLE_DISASSEMBLER
5367// Identify kind of code.
5368const char* Code::Kind2String(Kind kind) {
5369 switch (kind) {
5370 case FUNCTION: return "FUNCTION";
5371 case STUB: return "STUB";
5372 case BUILTIN: return "BUILTIN";
5373 case LOAD_IC: return "LOAD_IC";
5374 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
5375 case STORE_IC: return "STORE_IC";
5376 case KEYED_STORE_IC: return "KEYED_STORE_IC";
5377 case CALL_IC: return "CALL_IC";
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005378 case KEYED_CALL_IC: return "KEYED_CALL_IC";
Steve Block6ded16b2010-05-10 14:33:55 +01005379 case BINARY_OP_IC: return "BINARY_OP_IC";
Steve Blocka7e24c12009-10-30 11:49:00 +00005380 }
5381 UNREACHABLE();
5382 return NULL;
5383}
5384
5385
5386const char* Code::ICState2String(InlineCacheState state) {
5387 switch (state) {
5388 case UNINITIALIZED: return "UNINITIALIZED";
5389 case PREMONOMORPHIC: return "PREMONOMORPHIC";
5390 case MONOMORPHIC: return "MONOMORPHIC";
5391 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
5392 case MEGAMORPHIC: return "MEGAMORPHIC";
5393 case DEBUG_BREAK: return "DEBUG_BREAK";
5394 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
5395 }
5396 UNREACHABLE();
5397 return NULL;
5398}
5399
5400
5401const char* Code::PropertyType2String(PropertyType type) {
5402 switch (type) {
5403 case NORMAL: return "NORMAL";
5404 case FIELD: return "FIELD";
5405 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
5406 case CALLBACKS: return "CALLBACKS";
5407 case INTERCEPTOR: return "INTERCEPTOR";
5408 case MAP_TRANSITION: return "MAP_TRANSITION";
5409 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
5410 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
5411 }
5412 UNREACHABLE();
5413 return NULL;
5414}
5415
5416void Code::Disassemble(const char* name) {
5417 PrintF("kind = %s\n", Kind2String(kind()));
5418 if (is_inline_cache_stub()) {
5419 PrintF("ic_state = %s\n", ICState2String(ic_state()));
5420 PrintF("ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
5421 if (ic_state() == MONOMORPHIC) {
5422 PrintF("type = %s\n", PropertyType2String(type()));
5423 }
5424 }
5425 if ((name != NULL) && (name[0] != '\0')) {
5426 PrintF("name = %s\n", name);
5427 }
5428
5429 PrintF("Instructions (size = %d)\n", instruction_size());
5430 Disassembler::Decode(NULL, this);
5431 PrintF("\n");
5432
5433 PrintF("RelocInfo (size = %d)\n", relocation_size());
5434 for (RelocIterator it(this); !it.done(); it.next())
5435 it.rinfo()->Print();
5436 PrintF("\n");
5437}
5438#endif // ENABLE_DISASSEMBLER
5439
5440
Steve Block8defd9f2010-07-08 12:39:36 +01005441Object* JSObject::SetFastElementsCapacityAndLength(int capacity, int length) {
Steve Block3ce2e202009-11-05 08:53:23 +00005442 // We should never end in here with a pixel or external array.
5443 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Block8defd9f2010-07-08 12:39:36 +01005444
5445 Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
5446 if (obj->IsFailure()) return obj;
5447 FixedArray* elems = FixedArray::cast(obj);
5448
5449 obj = map()->GetFastElementsMap();
5450 if (obj->IsFailure()) return obj;
5451 Map* new_map = Map::cast(obj);
5452
Leon Clarke4515c472010-02-03 11:58:03 +00005453 AssertNoAllocation no_gc;
5454 WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00005455 switch (GetElementsKind()) {
5456 case FAST_ELEMENTS: {
5457 FixedArray* old_elements = FixedArray::cast(elements());
5458 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
5459 // Fill out the new array with this content and array holes.
5460 for (uint32_t i = 0; i < old_length; i++) {
5461 elems->set(i, old_elements->get(i), mode);
5462 }
5463 break;
5464 }
5465 case DICTIONARY_ELEMENTS: {
5466 NumberDictionary* dictionary = NumberDictionary::cast(elements());
5467 for (int i = 0; i < dictionary->Capacity(); i++) {
5468 Object* key = dictionary->KeyAt(i);
5469 if (key->IsNumber()) {
5470 uint32_t entry = static_cast<uint32_t>(key->Number());
5471 elems->set(entry, dictionary->ValueAt(i), mode);
5472 }
5473 }
5474 break;
5475 }
5476 default:
5477 UNREACHABLE();
5478 break;
5479 }
Steve Block8defd9f2010-07-08 12:39:36 +01005480
5481 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00005482 set_elements(elems);
Steve Block8defd9f2010-07-08 12:39:36 +01005483
5484 if (IsJSArray()) {
5485 JSArray::cast(this)->set_length(Smi::FromInt(length));
5486 }
5487
5488 return this;
Steve Blocka7e24c12009-10-30 11:49:00 +00005489}
5490
5491
5492Object* JSObject::SetSlowElements(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00005493 // We should never end in here with a pixel or external array.
5494 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00005495
5496 uint32_t new_length = static_cast<uint32_t>(len->Number());
5497
5498 switch (GetElementsKind()) {
5499 case FAST_ELEMENTS: {
5500 // Make sure we never try to shrink dense arrays into sparse arrays.
5501 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
5502 new_length);
5503 Object* obj = NormalizeElements();
5504 if (obj->IsFailure()) return obj;
5505
5506 // Update length for JSArrays.
5507 if (IsJSArray()) JSArray::cast(this)->set_length(len);
5508 break;
5509 }
5510 case DICTIONARY_ELEMENTS: {
5511 if (IsJSArray()) {
5512 uint32_t old_length =
Steve Block6ded16b2010-05-10 14:33:55 +01005513 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
Steve Blocka7e24c12009-10-30 11:49:00 +00005514 element_dictionary()->RemoveNumberEntries(new_length, old_length),
5515 JSArray::cast(this)->set_length(len);
5516 }
5517 break;
5518 }
5519 default:
5520 UNREACHABLE();
5521 break;
5522 }
5523 return this;
5524}
5525
5526
5527Object* JSArray::Initialize(int capacity) {
5528 ASSERT(capacity >= 0);
Leon Clarke4515c472010-02-03 11:58:03 +00005529 set_length(Smi::FromInt(0));
Steve Blocka7e24c12009-10-30 11:49:00 +00005530 FixedArray* new_elements;
5531 if (capacity == 0) {
5532 new_elements = Heap::empty_fixed_array();
5533 } else {
5534 Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
5535 if (obj->IsFailure()) return obj;
5536 new_elements = FixedArray::cast(obj);
5537 }
5538 set_elements(new_elements);
5539 return this;
5540}
5541
5542
5543void JSArray::Expand(int required_size) {
5544 Handle<JSArray> self(this);
5545 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
5546 int old_size = old_backing->length();
Steve Blockd0582a62009-12-15 09:54:21 +00005547 int new_size = required_size > old_size ? required_size : old_size;
Steve Blocka7e24c12009-10-30 11:49:00 +00005548 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
5549 // Can't use this any more now because we may have had a GC!
5550 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
5551 self->SetContent(*new_backing);
5552}
5553
5554
5555// Computes the new capacity when expanding the elements of a JSObject.
5556static int NewElementsCapacity(int old_capacity) {
5557 // (old_capacity + 50%) + 16
5558 return old_capacity + (old_capacity >> 1) + 16;
5559}
5560
5561
5562static Object* ArrayLengthRangeError() {
5563 HandleScope scope;
5564 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
5565 HandleVector<Object>(NULL, 0)));
5566}
5567
5568
5569Object* JSObject::SetElementsLength(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00005570 // We should never end in here with a pixel or external array.
Steve Block6ded16b2010-05-10 14:33:55 +01005571 ASSERT(AllowsSetElementsLength());
Steve Blocka7e24c12009-10-30 11:49:00 +00005572
5573 Object* smi_length = len->ToSmi();
5574 if (smi_length->IsSmi()) {
Steve Block8defd9f2010-07-08 12:39:36 +01005575 const int value = Smi::cast(smi_length)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00005576 if (value < 0) return ArrayLengthRangeError();
5577 switch (GetElementsKind()) {
5578 case FAST_ELEMENTS: {
5579 int old_capacity = FixedArray::cast(elements())->length();
5580 if (value <= old_capacity) {
5581 if (IsJSArray()) {
Iain Merrick75681382010-08-19 15:07:18 +01005582 Object* obj = EnsureWritableFastElements();
5583 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00005584 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
5585 // NOTE: We may be able to optimize this by removing the
5586 // last part of the elements backing storage array and
5587 // setting the capacity to the new size.
5588 for (int i = value; i < old_length; i++) {
5589 FixedArray::cast(elements())->set_the_hole(i);
5590 }
Leon Clarke4515c472010-02-03 11:58:03 +00005591 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00005592 }
5593 return this;
5594 }
5595 int min = NewElementsCapacity(old_capacity);
5596 int new_capacity = value > min ? value : min;
5597 if (new_capacity <= kMaxFastElementsLength ||
5598 !ShouldConvertToSlowElements(new_capacity)) {
Steve Block8defd9f2010-07-08 12:39:36 +01005599 Object* obj = SetFastElementsCapacityAndLength(new_capacity, value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005600 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00005601 return this;
5602 }
5603 break;
5604 }
5605 case DICTIONARY_ELEMENTS: {
5606 if (IsJSArray()) {
5607 if (value == 0) {
5608 // If the length of a slow array is reset to zero, we clear
5609 // the array and flush backing storage. This has the added
5610 // benefit that the array returns to fast mode.
Steve Block8defd9f2010-07-08 12:39:36 +01005611 Object* obj = ResetElements();
5612 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00005613 } else {
5614 // Remove deleted elements.
5615 uint32_t old_length =
5616 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
5617 element_dictionary()->RemoveNumberEntries(value, old_length);
5618 }
Leon Clarke4515c472010-02-03 11:58:03 +00005619 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00005620 }
5621 return this;
5622 }
5623 default:
5624 UNREACHABLE();
5625 break;
5626 }
5627 }
5628
5629 // General slow case.
5630 if (len->IsNumber()) {
5631 uint32_t length;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005632 if (len->ToArrayIndex(&length)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005633 return SetSlowElements(len);
5634 } else {
5635 return ArrayLengthRangeError();
5636 }
5637 }
5638
5639 // len is not a number so make the array size one and
5640 // set only element to len.
5641 Object* obj = Heap::AllocateFixedArray(1);
5642 if (obj->IsFailure()) return obj;
5643 FixedArray::cast(obj)->set(0, len);
Leon Clarke4515c472010-02-03 11:58:03 +00005644 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
Steve Blocka7e24c12009-10-30 11:49:00 +00005645 set_elements(FixedArray::cast(obj));
5646 return this;
5647}
5648
5649
Andrei Popescu402d9372010-02-26 13:31:12 +00005650Object* JSObject::SetPrototype(Object* value,
5651 bool skip_hidden_prototypes) {
5652 // Silently ignore the change if value is not a JSObject or null.
5653 // SpiderMonkey behaves this way.
5654 if (!value->IsJSObject() && !value->IsNull()) return value;
5655
5656 // Before we can set the prototype we need to be sure
5657 // prototype cycles are prevented.
5658 // It is sufficient to validate that the receiver is not in the new prototype
5659 // chain.
5660 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
5661 if (JSObject::cast(pt) == this) {
5662 // Cycle detected.
5663 HandleScope scope;
5664 return Top::Throw(*Factory::NewError("cyclic_proto",
5665 HandleVector<Object>(NULL, 0)));
5666 }
5667 }
5668
5669 JSObject* real_receiver = this;
5670
5671 if (skip_hidden_prototypes) {
5672 // Find the first object in the chain whose prototype object is not
5673 // hidden and set the new prototype on that object.
5674 Object* current_proto = real_receiver->GetPrototype();
5675 while (current_proto->IsJSObject() &&
5676 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
5677 real_receiver = JSObject::cast(current_proto);
5678 current_proto = current_proto->GetPrototype();
5679 }
5680 }
5681
5682 // Set the new prototype of the object.
5683 Object* new_map = real_receiver->map()->CopyDropTransitions();
5684 if (new_map->IsFailure()) return new_map;
5685 Map::cast(new_map)->set_prototype(value);
5686 real_receiver->set_map(Map::cast(new_map));
5687
Kristian Monsen25f61362010-05-21 11:50:48 +01005688 Heap::ClearInstanceofCache();
5689
Andrei Popescu402d9372010-02-26 13:31:12 +00005690 return value;
5691}
5692
5693
Steve Blocka7e24c12009-10-30 11:49:00 +00005694bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
5695 switch (GetElementsKind()) {
5696 case FAST_ELEMENTS: {
5697 uint32_t length = IsJSArray() ?
5698 static_cast<uint32_t>
5699 (Smi::cast(JSArray::cast(this)->length())->value()) :
5700 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5701 if ((index < length) &&
5702 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
5703 return true;
5704 }
5705 break;
5706 }
5707 case PIXEL_ELEMENTS: {
5708 // TODO(iposva): Add testcase.
5709 PixelArray* pixels = PixelArray::cast(elements());
5710 if (index < static_cast<uint32_t>(pixels->length())) {
5711 return true;
5712 }
5713 break;
5714 }
Steve Block3ce2e202009-11-05 08:53:23 +00005715 case EXTERNAL_BYTE_ELEMENTS:
5716 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5717 case EXTERNAL_SHORT_ELEMENTS:
5718 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5719 case EXTERNAL_INT_ELEMENTS:
5720 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5721 case EXTERNAL_FLOAT_ELEMENTS: {
5722 // TODO(kbr): Add testcase.
5723 ExternalArray* array = ExternalArray::cast(elements());
5724 if (index < static_cast<uint32_t>(array->length())) {
5725 return true;
5726 }
5727 break;
5728 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005729 case DICTIONARY_ELEMENTS: {
5730 if (element_dictionary()->FindEntry(index)
5731 != NumberDictionary::kNotFound) {
5732 return true;
5733 }
5734 break;
5735 }
5736 default:
5737 UNREACHABLE();
5738 break;
5739 }
5740
5741 // Handle [] on String objects.
5742 if (this->IsStringObjectWithCharacterAt(index)) return true;
5743
5744 Object* pt = GetPrototype();
5745 if (pt == Heap::null_value()) return false;
5746 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5747}
5748
5749
5750bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
5751 // Make sure that the top context does not change when doing
5752 // callbacks or interceptor calls.
5753 AssertNoContextChange ncc;
5754 HandleScope scope;
5755 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5756 Handle<JSObject> receiver_handle(receiver);
5757 Handle<JSObject> holder_handle(this);
5758 CustomArguments args(interceptor->data(), receiver, this);
5759 v8::AccessorInfo info(args.end());
5760 if (!interceptor->query()->IsUndefined()) {
5761 v8::IndexedPropertyQuery query =
5762 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
5763 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
Iain Merrick75681382010-08-19 15:07:18 +01005764 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00005765 {
5766 // Leaving JavaScript.
5767 VMState state(EXTERNAL);
5768 result = query(index, info);
5769 }
Iain Merrick75681382010-08-19 15:07:18 +01005770 if (!result.IsEmpty()) {
5771 ASSERT(result->IsInt32());
5772 return true; // absence of property is signaled by empty handle.
5773 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005774 } else if (!interceptor->getter()->IsUndefined()) {
5775 v8::IndexedPropertyGetter getter =
5776 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
5777 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
5778 v8::Handle<v8::Value> result;
5779 {
5780 // Leaving JavaScript.
5781 VMState state(EXTERNAL);
5782 result = getter(index, info);
5783 }
5784 if (!result.IsEmpty()) return true;
5785 }
5786 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
5787}
5788
5789
5790bool JSObject::HasLocalElement(uint32_t index) {
5791 // Check access rights if needed.
5792 if (IsAccessCheckNeeded() &&
5793 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5794 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5795 return false;
5796 }
5797
5798 // Check for lookup interceptor
5799 if (HasIndexedInterceptor()) {
5800 return HasElementWithInterceptor(this, index);
5801 }
5802
5803 // Handle [] on String objects.
5804 if (this->IsStringObjectWithCharacterAt(index)) return true;
5805
5806 switch (GetElementsKind()) {
5807 case FAST_ELEMENTS: {
5808 uint32_t length = IsJSArray() ?
5809 static_cast<uint32_t>
5810 (Smi::cast(JSArray::cast(this)->length())->value()) :
5811 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5812 return (index < length) &&
5813 !FixedArray::cast(elements())->get(index)->IsTheHole();
5814 }
5815 case PIXEL_ELEMENTS: {
5816 PixelArray* pixels = PixelArray::cast(elements());
5817 return (index < static_cast<uint32_t>(pixels->length()));
5818 }
Steve Block3ce2e202009-11-05 08:53:23 +00005819 case EXTERNAL_BYTE_ELEMENTS:
5820 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5821 case EXTERNAL_SHORT_ELEMENTS:
5822 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5823 case EXTERNAL_INT_ELEMENTS:
5824 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5825 case EXTERNAL_FLOAT_ELEMENTS: {
5826 ExternalArray* array = ExternalArray::cast(elements());
5827 return (index < static_cast<uint32_t>(array->length()));
5828 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005829 case DICTIONARY_ELEMENTS: {
5830 return element_dictionary()->FindEntry(index)
5831 != NumberDictionary::kNotFound;
5832 }
5833 default:
5834 UNREACHABLE();
5835 break;
5836 }
5837 UNREACHABLE();
5838 return Heap::null_value();
5839}
5840
5841
5842bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
5843 // Check access rights if needed.
5844 if (IsAccessCheckNeeded() &&
5845 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5846 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5847 return false;
5848 }
5849
5850 // Check for lookup interceptor
5851 if (HasIndexedInterceptor()) {
5852 return HasElementWithInterceptor(receiver, index);
5853 }
5854
5855 switch (GetElementsKind()) {
5856 case FAST_ELEMENTS: {
5857 uint32_t length = IsJSArray() ?
5858 static_cast<uint32_t>
5859 (Smi::cast(JSArray::cast(this)->length())->value()) :
5860 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5861 if ((index < length) &&
5862 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
5863 break;
5864 }
5865 case PIXEL_ELEMENTS: {
5866 PixelArray* pixels = PixelArray::cast(elements());
5867 if (index < static_cast<uint32_t>(pixels->length())) {
5868 return true;
5869 }
5870 break;
5871 }
Steve Block3ce2e202009-11-05 08:53:23 +00005872 case EXTERNAL_BYTE_ELEMENTS:
5873 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5874 case EXTERNAL_SHORT_ELEMENTS:
5875 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5876 case EXTERNAL_INT_ELEMENTS:
5877 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
5878 case EXTERNAL_FLOAT_ELEMENTS: {
5879 ExternalArray* array = ExternalArray::cast(elements());
5880 if (index < static_cast<uint32_t>(array->length())) {
5881 return true;
5882 }
5883 break;
5884 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005885 case DICTIONARY_ELEMENTS: {
5886 if (element_dictionary()->FindEntry(index)
5887 != NumberDictionary::kNotFound) {
5888 return true;
5889 }
5890 break;
5891 }
5892 default:
5893 UNREACHABLE();
5894 break;
5895 }
5896
5897 // Handle [] on String objects.
5898 if (this->IsStringObjectWithCharacterAt(index)) return true;
5899
5900 Object* pt = GetPrototype();
5901 if (pt == Heap::null_value()) return false;
5902 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5903}
5904
5905
5906Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) {
5907 // Make sure that the top context does not change when doing
5908 // callbacks or interceptor calls.
5909 AssertNoContextChange ncc;
5910 HandleScope scope;
5911 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5912 Handle<JSObject> this_handle(this);
5913 Handle<Object> value_handle(value);
5914 if (!interceptor->setter()->IsUndefined()) {
5915 v8::IndexedPropertySetter setter =
5916 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
5917 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
5918 CustomArguments args(interceptor->data(), this, this);
5919 v8::AccessorInfo info(args.end());
5920 v8::Handle<v8::Value> result;
5921 {
5922 // Leaving JavaScript.
5923 VMState state(EXTERNAL);
5924 result = setter(index, v8::Utils::ToLocal(value_handle), info);
5925 }
5926 RETURN_IF_SCHEDULED_EXCEPTION();
5927 if (!result.IsEmpty()) return *value_handle;
5928 }
5929 Object* raw_result =
5930 this_handle->SetElementWithoutInterceptor(index, *value_handle);
5931 RETURN_IF_SCHEDULED_EXCEPTION();
5932 return raw_result;
5933}
5934
5935
Leon Clarkef7060e22010-06-03 12:02:55 +01005936Object* JSObject::GetElementWithCallback(Object* receiver,
5937 Object* structure,
5938 uint32_t index,
5939 Object* holder) {
5940 ASSERT(!structure->IsProxy());
5941
5942 // api style callbacks.
5943 if (structure->IsAccessorInfo()) {
5944 AccessorInfo* data = AccessorInfo::cast(structure);
5945 Object* fun_obj = data->getter();
5946 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
5947 HandleScope scope;
5948 Handle<JSObject> self(JSObject::cast(receiver));
5949 Handle<JSObject> holder_handle(JSObject::cast(holder));
5950 Handle<Object> number = Factory::NewNumberFromUint(index);
5951 Handle<String> key(Factory::NumberToString(number));
5952 LOG(ApiNamedPropertyAccess("load", *self, *key));
5953 CustomArguments args(data->data(), *self, *holder_handle);
5954 v8::AccessorInfo info(args.end());
5955 v8::Handle<v8::Value> result;
5956 {
5957 // Leaving JavaScript.
5958 VMState state(EXTERNAL);
5959 result = call_fun(v8::Utils::ToLocal(key), info);
5960 }
5961 RETURN_IF_SCHEDULED_EXCEPTION();
5962 if (result.IsEmpty()) return Heap::undefined_value();
5963 return *v8::Utils::OpenHandle(*result);
5964 }
5965
5966 // __defineGetter__ callback
5967 if (structure->IsFixedArray()) {
5968 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
5969 if (getter->IsJSFunction()) {
5970 return Object::GetPropertyWithDefinedGetter(receiver,
5971 JSFunction::cast(getter));
5972 }
5973 // Getter is not a function.
5974 return Heap::undefined_value();
5975 }
5976
5977 UNREACHABLE();
5978 return NULL;
5979}
5980
5981
5982Object* JSObject::SetElementWithCallback(Object* structure,
5983 uint32_t index,
5984 Object* value,
5985 JSObject* holder) {
5986 HandleScope scope;
5987
5988 // We should never get here to initialize a const with the hole
5989 // value since a const declaration would conflict with the setter.
5990 ASSERT(!value->IsTheHole());
5991 Handle<Object> value_handle(value);
5992
5993 // To accommodate both the old and the new api we switch on the
5994 // data structure used to store the callbacks. Eventually proxy
5995 // callbacks should be phased out.
5996 ASSERT(!structure->IsProxy());
5997
5998 if (structure->IsAccessorInfo()) {
5999 // api style callbacks
6000 AccessorInfo* data = AccessorInfo::cast(structure);
6001 Object* call_obj = data->setter();
6002 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
6003 if (call_fun == NULL) return value;
6004 Handle<Object> number = Factory::NewNumberFromUint(index);
6005 Handle<String> key(Factory::NumberToString(number));
6006 LOG(ApiNamedPropertyAccess("store", this, *key));
6007 CustomArguments args(data->data(), this, JSObject::cast(holder));
6008 v8::AccessorInfo info(args.end());
6009 {
6010 // Leaving JavaScript.
6011 VMState state(EXTERNAL);
6012 call_fun(v8::Utils::ToLocal(key),
6013 v8::Utils::ToLocal(value_handle),
6014 info);
6015 }
6016 RETURN_IF_SCHEDULED_EXCEPTION();
6017 return *value_handle;
6018 }
6019
6020 if (structure->IsFixedArray()) {
6021 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
6022 if (setter->IsJSFunction()) {
6023 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
6024 } else {
6025 Handle<Object> holder_handle(holder);
6026 Handle<Object> key(Factory::NewNumberFromUint(index));
6027 Handle<Object> args[2] = { key, holder_handle };
6028 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
6029 HandleVector(args, 2)));
6030 }
6031 }
6032
6033 UNREACHABLE();
6034 return NULL;
6035}
6036
6037
Steve Blocka7e24c12009-10-30 11:49:00 +00006038// Adding n elements in fast case is O(n*n).
6039// Note: revisit design to have dual undefined values to capture absent
6040// elements.
6041Object* JSObject::SetFastElement(uint32_t index, Object* value) {
6042 ASSERT(HasFastElements());
6043
Iain Merrick75681382010-08-19 15:07:18 +01006044 Object* elms_obj = EnsureWritableFastElements();
6045 if (elms_obj->IsFailure()) return elms_obj;
6046 FixedArray* elms = FixedArray::cast(elms_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00006047 uint32_t elms_length = static_cast<uint32_t>(elms->length());
6048
6049 if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006050 if (SetElementWithCallbackSetterInPrototypes(index, value)) {
6051 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006052 }
6053 }
6054
6055 // Check whether there is extra space in fixed array..
6056 if (index < elms_length) {
6057 elms->set(index, value);
6058 if (IsJSArray()) {
6059 // Update the length of the array if needed.
6060 uint32_t array_length = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006061 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006062 if (index >= array_length) {
Leon Clarke4515c472010-02-03 11:58:03 +00006063 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006064 }
6065 }
6066 return value;
6067 }
6068
6069 // Allow gap in fast case.
6070 if ((index - elms_length) < kMaxGap) {
6071 // Try allocating extra space.
6072 int new_capacity = NewElementsCapacity(index+1);
6073 if (new_capacity <= kMaxFastElementsLength ||
6074 !ShouldConvertToSlowElements(new_capacity)) {
6075 ASSERT(static_cast<uint32_t>(new_capacity) > index);
Steve Block8defd9f2010-07-08 12:39:36 +01006076 Object* obj = SetFastElementsCapacityAndLength(new_capacity, index + 1);
Steve Blocka7e24c12009-10-30 11:49:00 +00006077 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00006078 FixedArray::cast(elements())->set(index, value);
6079 return value;
6080 }
6081 }
6082
6083 // Otherwise default to slow case.
6084 Object* obj = NormalizeElements();
6085 if (obj->IsFailure()) return obj;
6086 ASSERT(HasDictionaryElements());
6087 return SetElement(index, value);
6088}
6089
Iain Merrick75681382010-08-19 15:07:18 +01006090
Steve Blocka7e24c12009-10-30 11:49:00 +00006091Object* JSObject::SetElement(uint32_t index, Object* value) {
6092 // Check access rights if needed.
6093 if (IsAccessCheckNeeded() &&
6094 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
Iain Merrick75681382010-08-19 15:07:18 +01006095 HandleScope scope;
6096 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00006097 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01006098 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00006099 }
6100
6101 if (IsJSGlobalProxy()) {
6102 Object* proto = GetPrototype();
6103 if (proto->IsNull()) return value;
6104 ASSERT(proto->IsJSGlobalObject());
6105 return JSObject::cast(proto)->SetElement(index, value);
6106 }
6107
6108 // Check for lookup interceptor
6109 if (HasIndexedInterceptor()) {
6110 return SetElementWithInterceptor(index, value);
6111 }
6112
6113 return SetElementWithoutInterceptor(index, value);
6114}
6115
6116
6117Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
6118 switch (GetElementsKind()) {
6119 case FAST_ELEMENTS:
6120 // Fast case.
6121 return SetFastElement(index, value);
6122 case PIXEL_ELEMENTS: {
6123 PixelArray* pixels = PixelArray::cast(elements());
6124 return pixels->SetValue(index, value);
6125 }
Steve Block3ce2e202009-11-05 08:53:23 +00006126 case EXTERNAL_BYTE_ELEMENTS: {
6127 ExternalByteArray* array = ExternalByteArray::cast(elements());
6128 return array->SetValue(index, value);
6129 }
6130 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6131 ExternalUnsignedByteArray* array =
6132 ExternalUnsignedByteArray::cast(elements());
6133 return array->SetValue(index, value);
6134 }
6135 case EXTERNAL_SHORT_ELEMENTS: {
6136 ExternalShortArray* array = ExternalShortArray::cast(elements());
6137 return array->SetValue(index, value);
6138 }
6139 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6140 ExternalUnsignedShortArray* array =
6141 ExternalUnsignedShortArray::cast(elements());
6142 return array->SetValue(index, value);
6143 }
6144 case EXTERNAL_INT_ELEMENTS: {
6145 ExternalIntArray* array = ExternalIntArray::cast(elements());
6146 return array->SetValue(index, value);
6147 }
6148 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6149 ExternalUnsignedIntArray* array =
6150 ExternalUnsignedIntArray::cast(elements());
6151 return array->SetValue(index, value);
6152 }
6153 case EXTERNAL_FLOAT_ELEMENTS: {
6154 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
6155 return array->SetValue(index, value);
6156 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006157 case DICTIONARY_ELEMENTS: {
6158 // Insert element in the dictionary.
6159 FixedArray* elms = FixedArray::cast(elements());
6160 NumberDictionary* dictionary = NumberDictionary::cast(elms);
6161
6162 int entry = dictionary->FindEntry(index);
6163 if (entry != NumberDictionary::kNotFound) {
6164 Object* element = dictionary->ValueAt(entry);
6165 PropertyDetails details = dictionary->DetailsAt(entry);
6166 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006167 return SetElementWithCallback(element, index, value, this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006168 } else {
6169 dictionary->UpdateMaxNumberKey(index);
6170 dictionary->ValueAtPut(entry, value);
6171 }
6172 } else {
6173 // Index not already used. Look for an accessor in the prototype chain.
6174 if (!IsJSArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006175 if (SetElementWithCallbackSetterInPrototypes(index, value)) {
6176 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006177 }
6178 }
Steve Block8defd9f2010-07-08 12:39:36 +01006179 // When we set the is_extensible flag to false we always force
6180 // the element into dictionary mode (and force them to stay there).
6181 if (!map()->is_extensible()) {
6182 Handle<Object> number(Heap::NumberFromUint32(index));
6183 Handle<String> index_string(Factory::NumberToString(number));
6184 Handle<Object> args[1] = { index_string };
6185 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
6186 HandleVector(args, 1)));
6187 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006188 Object* result = dictionary->AtNumberPut(index, value);
6189 if (result->IsFailure()) return result;
6190 if (elms != FixedArray::cast(result)) {
6191 set_elements(FixedArray::cast(result));
6192 }
6193 }
6194
6195 // Update the array length if this JSObject is an array.
6196 if (IsJSArray()) {
6197 JSArray* array = JSArray::cast(this);
6198 Object* return_value = array->JSArrayUpdateLengthFromIndex(index,
6199 value);
6200 if (return_value->IsFailure()) return return_value;
6201 }
6202
6203 // Attempt to put this object back in fast case.
6204 if (ShouldConvertToFastElements()) {
6205 uint32_t new_length = 0;
6206 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006207 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006208 } else {
6209 new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
6210 }
Steve Block8defd9f2010-07-08 12:39:36 +01006211 Object* obj = SetFastElementsCapacityAndLength(new_length, new_length);
Steve Blocka7e24c12009-10-30 11:49:00 +00006212 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00006213#ifdef DEBUG
6214 if (FLAG_trace_normalization) {
6215 PrintF("Object elements are fast case again:\n");
6216 Print();
6217 }
6218#endif
6219 }
6220
6221 return value;
6222 }
6223 default:
6224 UNREACHABLE();
6225 break;
6226 }
6227 // All possible cases have been handled above. Add a return to avoid the
6228 // complaints from the compiler.
6229 UNREACHABLE();
6230 return Heap::null_value();
6231}
6232
6233
6234Object* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index, Object* value) {
6235 uint32_t old_len = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006236 CHECK(length()->ToArrayIndex(&old_len));
Steve Blocka7e24c12009-10-30 11:49:00 +00006237 // Check to see if we need to update the length. For now, we make
6238 // sure that the length stays within 32-bits (unsigned).
6239 if (index >= old_len && index != 0xffffffff) {
6240 Object* len =
6241 Heap::NumberFromDouble(static_cast<double>(index) + 1);
6242 if (len->IsFailure()) return len;
6243 set_length(len);
6244 }
6245 return value;
6246}
6247
6248
6249Object* JSObject::GetElementPostInterceptor(JSObject* receiver,
6250 uint32_t index) {
6251 // Get element works for both JSObject and JSArray since
6252 // JSArray::length cannot change.
6253 switch (GetElementsKind()) {
6254 case FAST_ELEMENTS: {
6255 FixedArray* elms = FixedArray::cast(elements());
6256 if (index < static_cast<uint32_t>(elms->length())) {
6257 Object* value = elms->get(index);
6258 if (!value->IsTheHole()) return value;
6259 }
6260 break;
6261 }
6262 case PIXEL_ELEMENTS: {
6263 // TODO(iposva): Add testcase and implement.
6264 UNIMPLEMENTED();
6265 break;
6266 }
Steve Block3ce2e202009-11-05 08:53:23 +00006267 case EXTERNAL_BYTE_ELEMENTS:
6268 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6269 case EXTERNAL_SHORT_ELEMENTS:
6270 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6271 case EXTERNAL_INT_ELEMENTS:
6272 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6273 case EXTERNAL_FLOAT_ELEMENTS: {
6274 // TODO(kbr): Add testcase and implement.
6275 UNIMPLEMENTED();
6276 break;
6277 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006278 case DICTIONARY_ELEMENTS: {
6279 NumberDictionary* dictionary = element_dictionary();
6280 int entry = dictionary->FindEntry(index);
6281 if (entry != NumberDictionary::kNotFound) {
6282 Object* element = dictionary->ValueAt(entry);
6283 PropertyDetails details = dictionary->DetailsAt(entry);
6284 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006285 return GetElementWithCallback(receiver,
6286 element,
6287 index,
6288 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006289 }
6290 return element;
6291 }
6292 break;
6293 }
6294 default:
6295 UNREACHABLE();
6296 break;
6297 }
6298
6299 // Continue searching via the prototype chain.
6300 Object* pt = GetPrototype();
6301 if (pt == Heap::null_value()) return Heap::undefined_value();
6302 return pt->GetElementWithReceiver(receiver, index);
6303}
6304
6305
6306Object* JSObject::GetElementWithInterceptor(JSObject* receiver,
6307 uint32_t index) {
6308 // Make sure that the top context does not change when doing
6309 // callbacks or interceptor calls.
6310 AssertNoContextChange ncc;
6311 HandleScope scope;
6312 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6313 Handle<JSObject> this_handle(receiver);
6314 Handle<JSObject> holder_handle(this);
6315
6316 if (!interceptor->getter()->IsUndefined()) {
6317 v8::IndexedPropertyGetter getter =
6318 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
6319 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
6320 CustomArguments args(interceptor->data(), receiver, this);
6321 v8::AccessorInfo info(args.end());
6322 v8::Handle<v8::Value> result;
6323 {
6324 // Leaving JavaScript.
6325 VMState state(EXTERNAL);
6326 result = getter(index, info);
6327 }
6328 RETURN_IF_SCHEDULED_EXCEPTION();
6329 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6330 }
6331
6332 Object* raw_result =
6333 holder_handle->GetElementPostInterceptor(*this_handle, index);
6334 RETURN_IF_SCHEDULED_EXCEPTION();
6335 return raw_result;
6336}
6337
6338
6339Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) {
6340 // Check access rights if needed.
6341 if (IsAccessCheckNeeded() &&
6342 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
6343 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
6344 return Heap::undefined_value();
6345 }
6346
6347 if (HasIndexedInterceptor()) {
6348 return GetElementWithInterceptor(receiver, index);
6349 }
6350
6351 // Get element works for both JSObject and JSArray since
6352 // JSArray::length cannot change.
6353 switch (GetElementsKind()) {
6354 case FAST_ELEMENTS: {
6355 FixedArray* elms = FixedArray::cast(elements());
6356 if (index < static_cast<uint32_t>(elms->length())) {
6357 Object* value = elms->get(index);
6358 if (!value->IsTheHole()) return value;
6359 }
6360 break;
6361 }
6362 case PIXEL_ELEMENTS: {
6363 PixelArray* pixels = PixelArray::cast(elements());
6364 if (index < static_cast<uint32_t>(pixels->length())) {
6365 uint8_t value = pixels->get(index);
6366 return Smi::FromInt(value);
6367 }
6368 break;
6369 }
Steve Block3ce2e202009-11-05 08:53:23 +00006370 case EXTERNAL_BYTE_ELEMENTS: {
6371 ExternalByteArray* array = ExternalByteArray::cast(elements());
6372 if (index < static_cast<uint32_t>(array->length())) {
6373 int8_t value = array->get(index);
6374 return Smi::FromInt(value);
6375 }
6376 break;
6377 }
6378 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6379 ExternalUnsignedByteArray* array =
6380 ExternalUnsignedByteArray::cast(elements());
6381 if (index < static_cast<uint32_t>(array->length())) {
6382 uint8_t value = array->get(index);
6383 return Smi::FromInt(value);
6384 }
6385 break;
6386 }
6387 case EXTERNAL_SHORT_ELEMENTS: {
6388 ExternalShortArray* array = ExternalShortArray::cast(elements());
6389 if (index < static_cast<uint32_t>(array->length())) {
6390 int16_t value = array->get(index);
6391 return Smi::FromInt(value);
6392 }
6393 break;
6394 }
6395 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6396 ExternalUnsignedShortArray* array =
6397 ExternalUnsignedShortArray::cast(elements());
6398 if (index < static_cast<uint32_t>(array->length())) {
6399 uint16_t value = array->get(index);
6400 return Smi::FromInt(value);
6401 }
6402 break;
6403 }
6404 case EXTERNAL_INT_ELEMENTS: {
6405 ExternalIntArray* array = ExternalIntArray::cast(elements());
6406 if (index < static_cast<uint32_t>(array->length())) {
6407 int32_t value = array->get(index);
6408 return Heap::NumberFromInt32(value);
6409 }
6410 break;
6411 }
6412 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6413 ExternalUnsignedIntArray* array =
6414 ExternalUnsignedIntArray::cast(elements());
6415 if (index < static_cast<uint32_t>(array->length())) {
6416 uint32_t value = array->get(index);
6417 return Heap::NumberFromUint32(value);
6418 }
6419 break;
6420 }
6421 case EXTERNAL_FLOAT_ELEMENTS: {
6422 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
6423 if (index < static_cast<uint32_t>(array->length())) {
6424 float value = array->get(index);
6425 return Heap::AllocateHeapNumber(value);
6426 }
6427 break;
6428 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006429 case DICTIONARY_ELEMENTS: {
6430 NumberDictionary* dictionary = element_dictionary();
6431 int entry = dictionary->FindEntry(index);
6432 if (entry != NumberDictionary::kNotFound) {
6433 Object* element = dictionary->ValueAt(entry);
6434 PropertyDetails details = dictionary->DetailsAt(entry);
6435 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006436 return GetElementWithCallback(receiver,
6437 element,
6438 index,
6439 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006440 }
6441 return element;
6442 }
6443 break;
6444 }
6445 }
6446
6447 Object* pt = GetPrototype();
6448 if (pt == Heap::null_value()) return Heap::undefined_value();
6449 return pt->GetElementWithReceiver(receiver, index);
6450}
6451
6452
6453bool JSObject::HasDenseElements() {
6454 int capacity = 0;
6455 int number_of_elements = 0;
6456
6457 switch (GetElementsKind()) {
6458 case FAST_ELEMENTS: {
6459 FixedArray* elms = FixedArray::cast(elements());
6460 capacity = elms->length();
6461 for (int i = 0; i < capacity; i++) {
6462 if (!elms->get(i)->IsTheHole()) number_of_elements++;
6463 }
6464 break;
6465 }
Steve Block3ce2e202009-11-05 08:53:23 +00006466 case PIXEL_ELEMENTS:
6467 case EXTERNAL_BYTE_ELEMENTS:
6468 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6469 case EXTERNAL_SHORT_ELEMENTS:
6470 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6471 case EXTERNAL_INT_ELEMENTS:
6472 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6473 case EXTERNAL_FLOAT_ELEMENTS: {
Steve Blocka7e24c12009-10-30 11:49:00 +00006474 return true;
6475 }
6476 case DICTIONARY_ELEMENTS: {
6477 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6478 capacity = dictionary->Capacity();
6479 number_of_elements = dictionary->NumberOfElements();
6480 break;
6481 }
6482 default:
6483 UNREACHABLE();
6484 break;
6485 }
6486
6487 if (capacity == 0) return true;
6488 return (number_of_elements > (capacity / 2));
6489}
6490
6491
6492bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
6493 ASSERT(HasFastElements());
6494 // Keep the array in fast case if the current backing storage is
6495 // almost filled and if the new capacity is no more than twice the
6496 // old capacity.
6497 int elements_length = FixedArray::cast(elements())->length();
6498 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
6499}
6500
6501
6502bool JSObject::ShouldConvertToFastElements() {
6503 ASSERT(HasDictionaryElements());
6504 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6505 // If the elements are sparse, we should not go back to fast case.
6506 if (!HasDenseElements()) return false;
6507 // If an element has been added at a very high index in the elements
6508 // dictionary, we cannot go back to fast case.
6509 if (dictionary->requires_slow_elements()) return false;
6510 // An object requiring access checks is never allowed to have fast
6511 // elements. If it had fast elements we would skip security checks.
6512 if (IsAccessCheckNeeded()) return false;
6513 // If the dictionary backing storage takes up roughly half as much
6514 // space as a fast-case backing storage would the array should have
6515 // fast elements.
6516 uint32_t length = 0;
6517 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006518 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006519 } else {
6520 length = dictionary->max_number_key();
6521 }
6522 return static_cast<uint32_t>(dictionary->Capacity()) >=
6523 (length / (2 * NumberDictionary::kEntrySize));
6524}
6525
6526
6527// Certain compilers request function template instantiation when they
6528// see the definition of the other template functions in the
6529// class. This requires us to have the template functions put
6530// together, so even though this function belongs in objects-debug.cc,
6531// we keep it here instead to satisfy certain compilers.
6532#ifdef DEBUG
6533template<typename Shape, typename Key>
6534void Dictionary<Shape, Key>::Print() {
6535 int capacity = HashTable<Shape, Key>::Capacity();
6536 for (int i = 0; i < capacity; i++) {
6537 Object* k = HashTable<Shape, Key>::KeyAt(i);
6538 if (HashTable<Shape, Key>::IsKey(k)) {
6539 PrintF(" ");
6540 if (k->IsString()) {
6541 String::cast(k)->StringPrint();
6542 } else {
6543 k->ShortPrint();
6544 }
6545 PrintF(": ");
6546 ValueAt(i)->ShortPrint();
6547 PrintF("\n");
6548 }
6549 }
6550}
6551#endif
6552
6553
6554template<typename Shape, typename Key>
6555void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
6556 int pos = 0;
6557 int capacity = HashTable<Shape, Key>::Capacity();
Leon Clarke4515c472010-02-03 11:58:03 +00006558 AssertNoAllocation no_gc;
6559 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00006560 for (int i = 0; i < capacity; i++) {
6561 Object* k = Dictionary<Shape, Key>::KeyAt(i);
6562 if (Dictionary<Shape, Key>::IsKey(k)) {
6563 elements->set(pos++, ValueAt(i), mode);
6564 }
6565 }
6566 ASSERT(pos == elements->length());
6567}
6568
6569
6570InterceptorInfo* JSObject::GetNamedInterceptor() {
6571 ASSERT(map()->has_named_interceptor());
6572 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01006573 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00006574 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01006575 constructor->shared()->get_api_func_data()->named_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00006576 return InterceptorInfo::cast(result);
6577}
6578
6579
6580InterceptorInfo* JSObject::GetIndexedInterceptor() {
6581 ASSERT(map()->has_indexed_interceptor());
6582 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01006583 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00006584 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01006585 constructor->shared()->get_api_func_data()->indexed_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00006586 return InterceptorInfo::cast(result);
6587}
6588
6589
6590Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver,
6591 String* name,
6592 PropertyAttributes* attributes) {
6593 // Check local property in holder, ignore interceptor.
6594 LookupResult result;
6595 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00006596 if (result.IsProperty()) {
6597 return GetProperty(receiver, &result, name, attributes);
6598 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006599 // Continue searching via the prototype chain.
6600 Object* pt = GetPrototype();
6601 *attributes = ABSENT;
6602 if (pt == Heap::null_value()) return Heap::undefined_value();
6603 return pt->GetPropertyWithReceiver(receiver, name, attributes);
6604}
6605
6606
Steve Blockd0582a62009-12-15 09:54:21 +00006607Object* JSObject::GetLocalPropertyPostInterceptor(
6608 JSObject* receiver,
6609 String* name,
6610 PropertyAttributes* attributes) {
6611 // Check local property in holder, ignore interceptor.
6612 LookupResult result;
6613 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00006614 if (result.IsProperty()) {
6615 return GetProperty(receiver, &result, name, attributes);
6616 }
6617 return Heap::undefined_value();
Steve Blockd0582a62009-12-15 09:54:21 +00006618}
6619
6620
Steve Blocka7e24c12009-10-30 11:49:00 +00006621Object* JSObject::GetPropertyWithInterceptor(
6622 JSObject* receiver,
6623 String* name,
6624 PropertyAttributes* attributes) {
6625 InterceptorInfo* interceptor = GetNamedInterceptor();
6626 HandleScope scope;
6627 Handle<JSObject> receiver_handle(receiver);
6628 Handle<JSObject> holder_handle(this);
6629 Handle<String> name_handle(name);
6630
6631 if (!interceptor->getter()->IsUndefined()) {
6632 v8::NamedPropertyGetter getter =
6633 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
6634 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
6635 CustomArguments args(interceptor->data(), receiver, this);
6636 v8::AccessorInfo info(args.end());
6637 v8::Handle<v8::Value> result;
6638 {
6639 // Leaving JavaScript.
6640 VMState state(EXTERNAL);
6641 result = getter(v8::Utils::ToLocal(name_handle), info);
6642 }
6643 RETURN_IF_SCHEDULED_EXCEPTION();
6644 if (!result.IsEmpty()) {
6645 *attributes = NONE;
6646 return *v8::Utils::OpenHandle(*result);
6647 }
6648 }
6649
6650 Object* result = holder_handle->GetPropertyPostInterceptor(
6651 *receiver_handle,
6652 *name_handle,
6653 attributes);
6654 RETURN_IF_SCHEDULED_EXCEPTION();
6655 return result;
6656}
6657
6658
6659bool JSObject::HasRealNamedProperty(String* key) {
6660 // Check access rights if needed.
6661 if (IsAccessCheckNeeded() &&
6662 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
6663 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6664 return false;
6665 }
6666
6667 LookupResult result;
6668 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00006669 return result.IsProperty() && (result.type() != INTERCEPTOR);
Steve Blocka7e24c12009-10-30 11:49:00 +00006670}
6671
6672
6673bool JSObject::HasRealElementProperty(uint32_t index) {
6674 // Check access rights if needed.
6675 if (IsAccessCheckNeeded() &&
6676 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6677 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6678 return false;
6679 }
6680
6681 // Handle [] on String objects.
6682 if (this->IsStringObjectWithCharacterAt(index)) return true;
6683
6684 switch (GetElementsKind()) {
6685 case FAST_ELEMENTS: {
6686 uint32_t length = IsJSArray() ?
6687 static_cast<uint32_t>(
6688 Smi::cast(JSArray::cast(this)->length())->value()) :
6689 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6690 return (index < length) &&
6691 !FixedArray::cast(elements())->get(index)->IsTheHole();
6692 }
6693 case PIXEL_ELEMENTS: {
6694 PixelArray* pixels = PixelArray::cast(elements());
6695 return index < static_cast<uint32_t>(pixels->length());
6696 }
Steve Block3ce2e202009-11-05 08:53:23 +00006697 case EXTERNAL_BYTE_ELEMENTS:
6698 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6699 case EXTERNAL_SHORT_ELEMENTS:
6700 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6701 case EXTERNAL_INT_ELEMENTS:
6702 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6703 case EXTERNAL_FLOAT_ELEMENTS: {
6704 ExternalArray* array = ExternalArray::cast(elements());
6705 return index < static_cast<uint32_t>(array->length());
6706 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006707 case DICTIONARY_ELEMENTS: {
6708 return element_dictionary()->FindEntry(index)
6709 != NumberDictionary::kNotFound;
6710 }
6711 default:
6712 UNREACHABLE();
6713 break;
6714 }
6715 // All possibilities have been handled above already.
6716 UNREACHABLE();
6717 return Heap::null_value();
6718}
6719
6720
6721bool JSObject::HasRealNamedCallbackProperty(String* key) {
6722 // Check access rights if needed.
6723 if (IsAccessCheckNeeded() &&
6724 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
6725 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6726 return false;
6727 }
6728
6729 LookupResult result;
6730 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00006731 return result.IsProperty() && (result.type() == CALLBACKS);
Steve Blocka7e24c12009-10-30 11:49:00 +00006732}
6733
6734
6735int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
6736 if (HasFastProperties()) {
6737 DescriptorArray* descs = map()->instance_descriptors();
6738 int result = 0;
6739 for (int i = 0; i < descs->number_of_descriptors(); i++) {
6740 PropertyDetails details = descs->GetDetails(i);
6741 if (details.IsProperty() && (details.attributes() & filter) == 0) {
6742 result++;
6743 }
6744 }
6745 return result;
6746 } else {
6747 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
6748 }
6749}
6750
6751
6752int JSObject::NumberOfEnumProperties() {
6753 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
6754}
6755
6756
6757void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
6758 Object* temp = get(i);
6759 set(i, get(j));
6760 set(j, temp);
6761 if (this != numbers) {
6762 temp = numbers->get(i);
6763 numbers->set(i, numbers->get(j));
6764 numbers->set(j, temp);
6765 }
6766}
6767
6768
6769static void InsertionSortPairs(FixedArray* content,
6770 FixedArray* numbers,
6771 int len) {
6772 for (int i = 1; i < len; i++) {
6773 int j = i;
6774 while (j > 0 &&
6775 (NumberToUint32(numbers->get(j - 1)) >
6776 NumberToUint32(numbers->get(j)))) {
6777 content->SwapPairs(numbers, j - 1, j);
6778 j--;
6779 }
6780 }
6781}
6782
6783
6784void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
6785 // In-place heap sort.
6786 ASSERT(content->length() == numbers->length());
6787
6788 // Bottom-up max-heap construction.
6789 for (int i = 1; i < len; ++i) {
6790 int child_index = i;
6791 while (child_index > 0) {
6792 int parent_index = ((child_index + 1) >> 1) - 1;
6793 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
6794 uint32_t child_value = NumberToUint32(numbers->get(child_index));
6795 if (parent_value < child_value) {
6796 content->SwapPairs(numbers, parent_index, child_index);
6797 } else {
6798 break;
6799 }
6800 child_index = parent_index;
6801 }
6802 }
6803
6804 // Extract elements and create sorted array.
6805 for (int i = len - 1; i > 0; --i) {
6806 // Put max element at the back of the array.
6807 content->SwapPairs(numbers, 0, i);
6808 // Sift down the new top element.
6809 int parent_index = 0;
6810 while (true) {
6811 int child_index = ((parent_index + 1) << 1) - 1;
6812 if (child_index >= i) break;
6813 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
6814 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
6815 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
6816 if (child_index + 1 >= i || child1_value > child2_value) {
6817 if (parent_value > child1_value) break;
6818 content->SwapPairs(numbers, parent_index, child_index);
6819 parent_index = child_index;
6820 } else {
6821 if (parent_value > child2_value) break;
6822 content->SwapPairs(numbers, parent_index, child_index + 1);
6823 parent_index = child_index + 1;
6824 }
6825 }
6826 }
6827}
6828
6829
6830// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
6831void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
6832 ASSERT(this->length() == numbers->length());
6833 // For small arrays, simply use insertion sort.
6834 if (len <= 10) {
6835 InsertionSortPairs(this, numbers, len);
6836 return;
6837 }
6838 // Check the range of indices.
6839 uint32_t min_index = NumberToUint32(numbers->get(0));
6840 uint32_t max_index = min_index;
6841 uint32_t i;
6842 for (i = 1; i < len; i++) {
6843 if (NumberToUint32(numbers->get(i)) < min_index) {
6844 min_index = NumberToUint32(numbers->get(i));
6845 } else if (NumberToUint32(numbers->get(i)) > max_index) {
6846 max_index = NumberToUint32(numbers->get(i));
6847 }
6848 }
6849 if (max_index - min_index + 1 == len) {
6850 // Indices form a contiguous range, unless there are duplicates.
6851 // Do an in-place linear time sort assuming distinct numbers, but
6852 // avoid hanging in case they are not.
6853 for (i = 0; i < len; i++) {
6854 uint32_t p;
6855 uint32_t j = 0;
6856 // While the current element at i is not at its correct position p,
6857 // swap the elements at these two positions.
6858 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
6859 j++ < len) {
6860 SwapPairs(numbers, i, p);
6861 }
6862 }
6863 } else {
6864 HeapSortPairs(this, numbers, len);
6865 return;
6866 }
6867}
6868
6869
6870// Fill in the names of local properties into the supplied storage. The main
6871// purpose of this function is to provide reflection information for the object
6872// mirrors.
6873void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
6874 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
6875 if (HasFastProperties()) {
6876 DescriptorArray* descs = map()->instance_descriptors();
6877 for (int i = 0; i < descs->number_of_descriptors(); i++) {
6878 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
6879 }
6880 ASSERT(storage->length() >= index);
6881 } else {
6882 property_dictionary()->CopyKeysTo(storage);
6883 }
6884}
6885
6886
6887int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
6888 return GetLocalElementKeys(NULL, filter);
6889}
6890
6891
6892int JSObject::NumberOfEnumElements() {
Steve Blockd0582a62009-12-15 09:54:21 +00006893 // Fast case for objects with no elements.
6894 if (!IsJSValue() && HasFastElements()) {
6895 uint32_t length = IsJSArray() ?
6896 static_cast<uint32_t>(
6897 Smi::cast(JSArray::cast(this)->length())->value()) :
6898 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6899 if (length == 0) return 0;
6900 }
6901 // Compute the number of enumerable elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00006902 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
6903}
6904
6905
6906int JSObject::GetLocalElementKeys(FixedArray* storage,
6907 PropertyAttributes filter) {
6908 int counter = 0;
6909 switch (GetElementsKind()) {
6910 case FAST_ELEMENTS: {
6911 int length = IsJSArray() ?
6912 Smi::cast(JSArray::cast(this)->length())->value() :
6913 FixedArray::cast(elements())->length();
6914 for (int i = 0; i < length; i++) {
6915 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
6916 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00006917 storage->set(counter, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00006918 }
6919 counter++;
6920 }
6921 }
6922 ASSERT(!storage || storage->length() >= counter);
6923 break;
6924 }
6925 case PIXEL_ELEMENTS: {
6926 int length = PixelArray::cast(elements())->length();
6927 while (counter < length) {
6928 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00006929 storage->set(counter, Smi::FromInt(counter));
Steve Blocka7e24c12009-10-30 11:49:00 +00006930 }
6931 counter++;
6932 }
6933 ASSERT(!storage || storage->length() >= counter);
6934 break;
6935 }
Steve Block3ce2e202009-11-05 08:53:23 +00006936 case EXTERNAL_BYTE_ELEMENTS:
6937 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6938 case EXTERNAL_SHORT_ELEMENTS:
6939 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6940 case EXTERNAL_INT_ELEMENTS:
6941 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6942 case EXTERNAL_FLOAT_ELEMENTS: {
6943 int length = ExternalArray::cast(elements())->length();
6944 while (counter < length) {
6945 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00006946 storage->set(counter, Smi::FromInt(counter));
Steve Block3ce2e202009-11-05 08:53:23 +00006947 }
6948 counter++;
6949 }
6950 ASSERT(!storage || storage->length() >= counter);
6951 break;
6952 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006953 case DICTIONARY_ELEMENTS: {
6954 if (storage != NULL) {
6955 element_dictionary()->CopyKeysTo(storage, filter);
6956 }
6957 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
6958 break;
6959 }
6960 default:
6961 UNREACHABLE();
6962 break;
6963 }
6964
6965 if (this->IsJSValue()) {
6966 Object* val = JSValue::cast(this)->value();
6967 if (val->IsString()) {
6968 String* str = String::cast(val);
6969 if (storage) {
6970 for (int i = 0; i < str->length(); i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00006971 storage->set(counter + i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00006972 }
6973 }
6974 counter += str->length();
6975 }
6976 }
6977 ASSERT(!storage || storage->length() == counter);
6978 return counter;
6979}
6980
6981
6982int JSObject::GetEnumElementKeys(FixedArray* storage) {
6983 return GetLocalElementKeys(storage,
6984 static_cast<PropertyAttributes>(DONT_ENUM));
6985}
6986
6987
6988bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
6989 ASSERT(other->IsNumber());
6990 return key == static_cast<uint32_t>(other->Number());
6991}
6992
6993
6994uint32_t NumberDictionaryShape::Hash(uint32_t key) {
6995 return ComputeIntegerHash(key);
6996}
6997
6998
6999uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) {
7000 ASSERT(other->IsNumber());
7001 return ComputeIntegerHash(static_cast<uint32_t>(other->Number()));
7002}
7003
7004
7005Object* NumberDictionaryShape::AsObject(uint32_t key) {
7006 return Heap::NumberFromUint32(key);
7007}
7008
7009
7010bool StringDictionaryShape::IsMatch(String* key, Object* other) {
7011 // We know that all entries in a hash table had their hash keys created.
7012 // Use that knowledge to have fast failure.
7013 if (key->Hash() != String::cast(other)->Hash()) return false;
7014 return key->Equals(String::cast(other));
7015}
7016
7017
7018uint32_t StringDictionaryShape::Hash(String* key) {
7019 return key->Hash();
7020}
7021
7022
7023uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) {
7024 return String::cast(other)->Hash();
7025}
7026
7027
7028Object* StringDictionaryShape::AsObject(String* key) {
7029 return key;
7030}
7031
7032
7033// StringKey simply carries a string object as key.
7034class StringKey : public HashTableKey {
7035 public:
7036 explicit StringKey(String* string) :
7037 string_(string),
7038 hash_(HashForObject(string)) { }
7039
7040 bool IsMatch(Object* string) {
7041 // We know that all entries in a hash table had their hash keys created.
7042 // Use that knowledge to have fast failure.
7043 if (hash_ != HashForObject(string)) {
7044 return false;
7045 }
7046 return string_->Equals(String::cast(string));
7047 }
7048
7049 uint32_t Hash() { return hash_; }
7050
7051 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
7052
7053 Object* AsObject() { return string_; }
7054
7055 String* string_;
7056 uint32_t hash_;
7057};
7058
7059
7060// StringSharedKeys are used as keys in the eval cache.
7061class StringSharedKey : public HashTableKey {
7062 public:
7063 StringSharedKey(String* source, SharedFunctionInfo* shared)
7064 : source_(source), shared_(shared) { }
7065
7066 bool IsMatch(Object* other) {
7067 if (!other->IsFixedArray()) return false;
7068 FixedArray* pair = FixedArray::cast(other);
7069 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7070 if (shared != shared_) return false;
7071 String* source = String::cast(pair->get(1));
7072 return source->Equals(source_);
7073 }
7074
7075 static uint32_t StringSharedHashHelper(String* source,
7076 SharedFunctionInfo* shared) {
7077 uint32_t hash = source->Hash();
7078 if (shared->HasSourceCode()) {
7079 // Instead of using the SharedFunctionInfo pointer in the hash
7080 // code computation, we use a combination of the hash of the
7081 // script source code and the start and end positions. We do
7082 // this to ensure that the cache entries can survive garbage
7083 // collection.
7084 Script* script = Script::cast(shared->script());
7085 hash ^= String::cast(script->source())->Hash();
7086 hash += shared->start_position();
7087 }
7088 return hash;
7089 }
7090
7091 uint32_t Hash() {
7092 return StringSharedHashHelper(source_, shared_);
7093 }
7094
7095 uint32_t HashForObject(Object* obj) {
7096 FixedArray* pair = FixedArray::cast(obj);
7097 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7098 String* source = String::cast(pair->get(1));
7099 return StringSharedHashHelper(source, shared);
7100 }
7101
7102 Object* AsObject() {
7103 Object* obj = Heap::AllocateFixedArray(2);
7104 if (obj->IsFailure()) return obj;
7105 FixedArray* pair = FixedArray::cast(obj);
7106 pair->set(0, shared_);
7107 pair->set(1, source_);
7108 return pair;
7109 }
7110
7111 private:
7112 String* source_;
7113 SharedFunctionInfo* shared_;
7114};
7115
7116
7117// RegExpKey carries the source and flags of a regular expression as key.
7118class RegExpKey : public HashTableKey {
7119 public:
7120 RegExpKey(String* string, JSRegExp::Flags flags)
7121 : string_(string),
7122 flags_(Smi::FromInt(flags.value())) { }
7123
Steve Block3ce2e202009-11-05 08:53:23 +00007124 // Rather than storing the key in the hash table, a pointer to the
7125 // stored value is stored where the key should be. IsMatch then
7126 // compares the search key to the found object, rather than comparing
7127 // a key to a key.
Steve Blocka7e24c12009-10-30 11:49:00 +00007128 bool IsMatch(Object* obj) {
7129 FixedArray* val = FixedArray::cast(obj);
7130 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
7131 && (flags_ == val->get(JSRegExp::kFlagsIndex));
7132 }
7133
7134 uint32_t Hash() { return RegExpHash(string_, flags_); }
7135
7136 Object* AsObject() {
7137 // Plain hash maps, which is where regexp keys are used, don't
7138 // use this function.
7139 UNREACHABLE();
7140 return NULL;
7141 }
7142
7143 uint32_t HashForObject(Object* obj) {
7144 FixedArray* val = FixedArray::cast(obj);
7145 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
7146 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
7147 }
7148
7149 static uint32_t RegExpHash(String* string, Smi* flags) {
7150 return string->Hash() + flags->value();
7151 }
7152
7153 String* string_;
7154 Smi* flags_;
7155};
7156
7157// Utf8SymbolKey carries a vector of chars as key.
7158class Utf8SymbolKey : public HashTableKey {
7159 public:
7160 explicit Utf8SymbolKey(Vector<const char> string)
Steve Blockd0582a62009-12-15 09:54:21 +00007161 : string_(string), hash_field_(0) { }
Steve Blocka7e24c12009-10-30 11:49:00 +00007162
7163 bool IsMatch(Object* string) {
7164 return String::cast(string)->IsEqualTo(string_);
7165 }
7166
7167 uint32_t Hash() {
Steve Blockd0582a62009-12-15 09:54:21 +00007168 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00007169 unibrow::Utf8InputBuffer<> buffer(string_.start(),
7170 static_cast<unsigned>(string_.length()));
7171 chars_ = buffer.Length();
Steve Blockd0582a62009-12-15 09:54:21 +00007172 hash_field_ = String::ComputeHashField(&buffer, chars_);
7173 uint32_t result = hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00007174 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
7175 return result;
7176 }
7177
7178 uint32_t HashForObject(Object* other) {
7179 return String::cast(other)->Hash();
7180 }
7181
7182 Object* AsObject() {
Steve Blockd0582a62009-12-15 09:54:21 +00007183 if (hash_field_ == 0) Hash();
7184 return Heap::AllocateSymbol(string_, chars_, hash_field_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007185 }
7186
7187 Vector<const char> string_;
Steve Blockd0582a62009-12-15 09:54:21 +00007188 uint32_t hash_field_;
Steve Blocka7e24c12009-10-30 11:49:00 +00007189 int chars_; // Caches the number of characters when computing the hash code.
7190};
7191
7192
7193// SymbolKey carries a string/symbol object as key.
7194class SymbolKey : public HashTableKey {
7195 public:
7196 explicit SymbolKey(String* string) : string_(string) { }
7197
7198 bool IsMatch(Object* string) {
7199 return String::cast(string)->Equals(string_);
7200 }
7201
7202 uint32_t Hash() { return string_->Hash(); }
7203
7204 uint32_t HashForObject(Object* other) {
7205 return String::cast(other)->Hash();
7206 }
7207
7208 Object* AsObject() {
Leon Clarkef7060e22010-06-03 12:02:55 +01007209 // Attempt to flatten the string, so that symbols will most often
7210 // be flat strings.
7211 string_ = string_->TryFlattenGetString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007212 // Transform string to symbol if possible.
7213 Map* map = Heap::SymbolMapForString(string_);
7214 if (map != NULL) {
7215 string_->set_map(map);
7216 ASSERT(string_->IsSymbol());
7217 return string_;
7218 }
7219 // Otherwise allocate a new symbol.
7220 StringInputBuffer buffer(string_);
7221 return Heap::AllocateInternalSymbol(&buffer,
7222 string_->length(),
Steve Blockd0582a62009-12-15 09:54:21 +00007223 string_->hash_field());
Steve Blocka7e24c12009-10-30 11:49:00 +00007224 }
7225
7226 static uint32_t StringHash(Object* obj) {
7227 return String::cast(obj)->Hash();
7228 }
7229
7230 String* string_;
7231};
7232
7233
7234template<typename Shape, typename Key>
7235void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
7236 IteratePointers(v, 0, kElementsStartOffset);
7237}
7238
7239
7240template<typename Shape, typename Key>
7241void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
7242 IteratePointers(v,
7243 kElementsStartOffset,
7244 kHeaderSize + length() * kPointerSize);
7245}
7246
7247
7248template<typename Shape, typename Key>
Steve Block6ded16b2010-05-10 14:33:55 +01007249Object* HashTable<Shape, Key>::Allocate(int at_least_space_for,
7250 PretenureFlag pretenure) {
7251 const int kMinCapacity = 32;
7252 int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
7253 if (capacity < kMinCapacity) {
7254 capacity = kMinCapacity; // Guarantee min capacity.
Leon Clarkee46be812010-01-19 14:06:41 +00007255 } else if (capacity > HashTable::kMaxCapacity) {
7256 return Failure::OutOfMemoryException();
7257 }
7258
Steve Block6ded16b2010-05-10 14:33:55 +01007259 Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity), pretenure);
Steve Blocka7e24c12009-10-30 11:49:00 +00007260 if (!obj->IsFailure()) {
7261 HashTable::cast(obj)->SetNumberOfElements(0);
Leon Clarkee46be812010-01-19 14:06:41 +00007262 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007263 HashTable::cast(obj)->SetCapacity(capacity);
7264 }
7265 return obj;
7266}
7267
7268
Leon Clarkee46be812010-01-19 14:06:41 +00007269// Find entry for key otherwise return kNotFound.
Steve Blocka7e24c12009-10-30 11:49:00 +00007270template<typename Shape, typename Key>
7271int HashTable<Shape, Key>::FindEntry(Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007272 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007273 uint32_t entry = FirstProbe(Shape::Hash(key), capacity);
7274 uint32_t count = 1;
7275 // EnsureCapacity will guarantee the hash table is never full.
7276 while (true) {
7277 Object* element = KeyAt(entry);
7278 if (element->IsUndefined()) break; // Empty entry.
7279 if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
7280 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007281 }
7282 return kNotFound;
7283}
7284
7285
Ben Murdoch3bec4d22010-07-22 14:51:16 +01007286// Find entry for key otherwise return kNotFound.
7287int StringDictionary::FindEntry(String* key) {
7288 if (!key->IsSymbol()) {
7289 return HashTable<StringDictionaryShape, String*>::FindEntry(key);
7290 }
7291
7292 // Optimized for symbol key. Knowledge of the key type allows:
7293 // 1. Move the check if the key is a symbol out of the loop.
7294 // 2. Avoid comparing hash codes in symbol to symbol comparision.
7295 // 3. Detect a case when a dictionary key is not a symbol but the key is.
7296 // In case of positive result the dictionary key may be replaced by
7297 // the symbol with minimal performance penalty. It gives a chance to
7298 // perform further lookups in code stubs (and significant performance boost
7299 // a certain style of code).
7300
7301 // EnsureCapacity will guarantee the hash table is never full.
7302 uint32_t capacity = Capacity();
7303 uint32_t entry = FirstProbe(key->Hash(), capacity);
7304 uint32_t count = 1;
7305
7306 while (true) {
7307 int index = EntryToIndex(entry);
7308 Object* element = get(index);
7309 if (element->IsUndefined()) break; // Empty entry.
7310 if (key == element) return entry;
7311 if (!element->IsSymbol() &&
7312 !element->IsNull() &&
7313 String::cast(element)->Equals(key)) {
7314 // Replace a non-symbol key by the equivalent symbol for faster further
7315 // lookups.
7316 set(index, key);
7317 return entry;
7318 }
7319 ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
7320 entry = NextProbe(entry, count++, capacity);
7321 }
7322 return kNotFound;
7323}
7324
7325
Steve Blocka7e24c12009-10-30 11:49:00 +00007326template<typename Shape, typename Key>
7327Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
7328 int capacity = Capacity();
7329 int nof = NumberOfElements() + n;
Leon Clarkee46be812010-01-19 14:06:41 +00007330 int nod = NumberOfDeletedElements();
7331 // Return if:
7332 // 50% is still free after adding n elements and
7333 // at most 50% of the free elements are deleted elements.
Steve Block6ded16b2010-05-10 14:33:55 +01007334 if (nod <= (capacity - nof) >> 1) {
7335 int needed_free = nof >> 1;
7336 if (nof + needed_free <= capacity) return this;
7337 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007338
Steve Block6ded16b2010-05-10 14:33:55 +01007339 const int kMinCapacityForPretenure = 256;
7340 bool pretenure =
7341 (capacity > kMinCapacityForPretenure) && !Heap::InNewSpace(this);
7342 Object* obj = Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
Steve Blocka7e24c12009-10-30 11:49:00 +00007343 if (obj->IsFailure()) return obj;
Leon Clarke4515c472010-02-03 11:58:03 +00007344
7345 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00007346 HashTable* table = HashTable::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00007347 WriteBarrierMode mode = table->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007348
7349 // Copy prefix to new array.
7350 for (int i = kPrefixStartIndex;
7351 i < kPrefixStartIndex + Shape::kPrefixSize;
7352 i++) {
7353 table->set(i, get(i), mode);
7354 }
7355 // Rehash the elements.
7356 for (int i = 0; i < capacity; i++) {
7357 uint32_t from_index = EntryToIndex(i);
7358 Object* k = get(from_index);
7359 if (IsKey(k)) {
7360 uint32_t hash = Shape::HashForObject(key, k);
7361 uint32_t insertion_index =
7362 EntryToIndex(table->FindInsertionEntry(hash));
7363 for (int j = 0; j < Shape::kEntrySize; j++) {
7364 table->set(insertion_index + j, get(from_index + j), mode);
7365 }
7366 }
7367 }
7368 table->SetNumberOfElements(NumberOfElements());
Leon Clarkee46be812010-01-19 14:06:41 +00007369 table->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007370 return table;
7371}
7372
7373
7374template<typename Shape, typename Key>
7375uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
7376 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007377 uint32_t entry = FirstProbe(hash, capacity);
7378 uint32_t count = 1;
7379 // EnsureCapacity will guarantee the hash table is never full.
7380 while (true) {
7381 Object* element = KeyAt(entry);
7382 if (element->IsUndefined() || element->IsNull()) break;
7383 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007384 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007385 return entry;
7386}
7387
7388// Force instantiation of template instances class.
7389// Please note this list is compiler dependent.
7390
7391template class HashTable<SymbolTableShape, HashTableKey*>;
7392
7393template class HashTable<CompilationCacheShape, HashTableKey*>;
7394
7395template class HashTable<MapCacheShape, HashTableKey*>;
7396
7397template class Dictionary<StringDictionaryShape, String*>;
7398
7399template class Dictionary<NumberDictionaryShape, uint32_t>;
7400
7401template Object* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
7402 int);
7403
7404template Object* Dictionary<StringDictionaryShape, String*>::Allocate(
7405 int);
7406
7407template Object* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
7408 uint32_t, Object*);
7409
7410template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
7411 Object*);
7412
7413template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
7414 Object*);
7415
7416template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
7417 FixedArray*, PropertyAttributes);
7418
7419template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
7420 int, JSObject::DeleteMode);
7421
7422template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
7423 int, JSObject::DeleteMode);
7424
7425template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
7426 FixedArray*);
7427
7428template int
7429Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
7430 PropertyAttributes);
7431
7432template Object* Dictionary<StringDictionaryShape, String*>::Add(
7433 String*, Object*, PropertyDetails);
7434
7435template Object*
7436Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
7437
7438template int
7439Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
7440 PropertyAttributes);
7441
7442template Object* Dictionary<NumberDictionaryShape, uint32_t>::Add(
7443 uint32_t, Object*, PropertyDetails);
7444
7445template Object* Dictionary<NumberDictionaryShape, uint32_t>::EnsureCapacity(
7446 int, uint32_t);
7447
7448template Object* Dictionary<StringDictionaryShape, String*>::EnsureCapacity(
7449 int, String*);
7450
7451template Object* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
7452 uint32_t, Object*, PropertyDetails, uint32_t);
7453
7454template Object* Dictionary<StringDictionaryShape, String*>::AddEntry(
7455 String*, Object*, PropertyDetails, uint32_t);
7456
7457template
7458int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
7459
7460template
7461int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
7462
Leon Clarkee46be812010-01-19 14:06:41 +00007463template
7464int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
7465
7466
Steve Blocka7e24c12009-10-30 11:49:00 +00007467// Collates undefined and unexisting elements below limit from position
7468// zero of the elements. The object stays in Dictionary mode.
7469Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
7470 ASSERT(HasDictionaryElements());
7471 // Must stay in dictionary mode, either because of requires_slow_elements,
7472 // or because we are not going to sort (and therefore compact) all of the
7473 // elements.
7474 NumberDictionary* dict = element_dictionary();
7475 HeapNumber* result_double = NULL;
7476 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7477 // Allocate space for result before we start mutating the object.
7478 Object* new_double = Heap::AllocateHeapNumber(0.0);
7479 if (new_double->IsFailure()) return new_double;
7480 result_double = HeapNumber::cast(new_double);
7481 }
7482
Steve Block6ded16b2010-05-10 14:33:55 +01007483 Object* obj = NumberDictionary::Allocate(dict->NumberOfElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00007484 if (obj->IsFailure()) return obj;
7485 NumberDictionary* new_dict = NumberDictionary::cast(obj);
7486
7487 AssertNoAllocation no_alloc;
7488
7489 uint32_t pos = 0;
7490 uint32_t undefs = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01007491 int capacity = dict->Capacity();
Steve Blocka7e24c12009-10-30 11:49:00 +00007492 for (int i = 0; i < capacity; i++) {
7493 Object* k = dict->KeyAt(i);
7494 if (dict->IsKey(k)) {
7495 ASSERT(k->IsNumber());
7496 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
7497 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
7498 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
7499 Object* value = dict->ValueAt(i);
7500 PropertyDetails details = dict->DetailsAt(i);
7501 if (details.type() == CALLBACKS) {
7502 // Bail out and do the sorting of undefineds and array holes in JS.
7503 return Smi::FromInt(-1);
7504 }
7505 uint32_t key = NumberToUint32(k);
7506 if (key < limit) {
7507 if (value->IsUndefined()) {
7508 undefs++;
7509 } else {
7510 new_dict->AddNumberEntry(pos, value, details);
7511 pos++;
7512 }
7513 } else {
7514 new_dict->AddNumberEntry(key, value, details);
7515 }
7516 }
7517 }
7518
7519 uint32_t result = pos;
7520 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
7521 while (undefs > 0) {
7522 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details);
7523 pos++;
7524 undefs--;
7525 }
7526
7527 set_elements(new_dict);
7528
7529 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7530 return Smi::FromInt(static_cast<int>(result));
7531 }
7532
7533 ASSERT_NE(NULL, result_double);
7534 result_double->set_value(static_cast<double>(result));
7535 return result_double;
7536}
7537
7538
7539// Collects all defined (non-hole) and non-undefined (array) elements at
7540// the start of the elements array.
7541// If the object is in dictionary mode, it is converted to fast elements
7542// mode.
7543Object* JSObject::PrepareElementsForSort(uint32_t limit) {
Steve Block3ce2e202009-11-05 08:53:23 +00007544 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00007545
7546 if (HasDictionaryElements()) {
7547 // Convert to fast elements containing only the existing properties.
7548 // Ordering is irrelevant, since we are going to sort anyway.
7549 NumberDictionary* dict = element_dictionary();
7550 if (IsJSArray() || dict->requires_slow_elements() ||
7551 dict->max_number_key() >= limit) {
7552 return PrepareSlowElementsForSort(limit);
7553 }
7554 // Convert to fast elements.
7555
Steve Block8defd9f2010-07-08 12:39:36 +01007556 Object* obj = map()->GetFastElementsMap();
7557 if (obj->IsFailure()) return obj;
7558 Map* new_map = Map::cast(obj);
7559
Steve Blocka7e24c12009-10-30 11:49:00 +00007560 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
7561 Object* new_array =
7562 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
Steve Block8defd9f2010-07-08 12:39:36 +01007563 if (new_array->IsFailure()) return new_array;
Steve Blocka7e24c12009-10-30 11:49:00 +00007564 FixedArray* fast_elements = FixedArray::cast(new_array);
7565 dict->CopyValuesTo(fast_elements);
Steve Block8defd9f2010-07-08 12:39:36 +01007566
7567 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00007568 set_elements(fast_elements);
Iain Merrick75681382010-08-19 15:07:18 +01007569 } else {
7570 Object* obj = EnsureWritableFastElements();
7571 if (obj->IsFailure()) return obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00007572 }
7573 ASSERT(HasFastElements());
7574
7575 // Collect holes at the end, undefined before that and the rest at the
7576 // start, and return the number of non-hole, non-undefined values.
7577
7578 FixedArray* elements = FixedArray::cast(this->elements());
7579 uint32_t elements_length = static_cast<uint32_t>(elements->length());
7580 if (limit > elements_length) {
7581 limit = elements_length ;
7582 }
7583 if (limit == 0) {
7584 return Smi::FromInt(0);
7585 }
7586
7587 HeapNumber* result_double = NULL;
7588 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7589 // Pessimistically allocate space for return value before
7590 // we start mutating the array.
7591 Object* new_double = Heap::AllocateHeapNumber(0.0);
7592 if (new_double->IsFailure()) return new_double;
7593 result_double = HeapNumber::cast(new_double);
7594 }
7595
7596 AssertNoAllocation no_alloc;
7597
7598 // Split elements into defined, undefined and the_hole, in that order.
7599 // Only count locations for undefined and the hole, and fill them afterwards.
Leon Clarke4515c472010-02-03 11:58:03 +00007600 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007601 unsigned int undefs = limit;
7602 unsigned int holes = limit;
7603 // Assume most arrays contain no holes and undefined values, so minimize the
7604 // number of stores of non-undefined, non-the-hole values.
7605 for (unsigned int i = 0; i < undefs; i++) {
7606 Object* current = elements->get(i);
7607 if (current->IsTheHole()) {
7608 holes--;
7609 undefs--;
7610 } else if (current->IsUndefined()) {
7611 undefs--;
7612 } else {
7613 continue;
7614 }
7615 // Position i needs to be filled.
7616 while (undefs > i) {
7617 current = elements->get(undefs);
7618 if (current->IsTheHole()) {
7619 holes--;
7620 undefs--;
7621 } else if (current->IsUndefined()) {
7622 undefs--;
7623 } else {
7624 elements->set(i, current, write_barrier);
7625 break;
7626 }
7627 }
7628 }
7629 uint32_t result = undefs;
7630 while (undefs < holes) {
7631 elements->set_undefined(undefs);
7632 undefs++;
7633 }
7634 while (holes < limit) {
7635 elements->set_the_hole(holes);
7636 holes++;
7637 }
7638
7639 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
7640 return Smi::FromInt(static_cast<int>(result));
7641 }
7642 ASSERT_NE(NULL, result_double);
7643 result_double->set_value(static_cast<double>(result));
7644 return result_double;
7645}
7646
7647
7648Object* PixelArray::SetValue(uint32_t index, Object* value) {
7649 uint8_t clamped_value = 0;
7650 if (index < static_cast<uint32_t>(length())) {
7651 if (value->IsSmi()) {
7652 int int_value = Smi::cast(value)->value();
7653 if (int_value < 0) {
7654 clamped_value = 0;
7655 } else if (int_value > 255) {
7656 clamped_value = 255;
7657 } else {
7658 clamped_value = static_cast<uint8_t>(int_value);
7659 }
7660 } else if (value->IsHeapNumber()) {
7661 double double_value = HeapNumber::cast(value)->value();
7662 if (!(double_value > 0)) {
7663 // NaN and less than zero clamp to zero.
7664 clamped_value = 0;
7665 } else if (double_value > 255) {
7666 // Greater than 255 clamp to 255.
7667 clamped_value = 255;
7668 } else {
7669 // Other doubles are rounded to the nearest integer.
7670 clamped_value = static_cast<uint8_t>(double_value + 0.5);
7671 }
7672 } else {
7673 // Clamp undefined to zero (default). All other types have been
7674 // converted to a number type further up in the call chain.
7675 ASSERT(value->IsUndefined());
7676 }
7677 set(index, clamped_value);
7678 }
7679 return Smi::FromInt(clamped_value);
7680}
7681
7682
Steve Block3ce2e202009-11-05 08:53:23 +00007683template<typename ExternalArrayClass, typename ValueType>
7684static Object* ExternalArrayIntSetter(ExternalArrayClass* receiver,
7685 uint32_t index,
7686 Object* value) {
7687 ValueType cast_value = 0;
7688 if (index < static_cast<uint32_t>(receiver->length())) {
7689 if (value->IsSmi()) {
7690 int int_value = Smi::cast(value)->value();
7691 cast_value = static_cast<ValueType>(int_value);
7692 } else if (value->IsHeapNumber()) {
7693 double double_value = HeapNumber::cast(value)->value();
7694 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
7695 } else {
7696 // Clamp undefined to zero (default). All other types have been
7697 // converted to a number type further up in the call chain.
7698 ASSERT(value->IsUndefined());
7699 }
7700 receiver->set(index, cast_value);
7701 }
7702 return Heap::NumberFromInt32(cast_value);
7703}
7704
7705
7706Object* ExternalByteArray::SetValue(uint32_t index, Object* value) {
7707 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
7708 (this, index, value);
7709}
7710
7711
7712Object* ExternalUnsignedByteArray::SetValue(uint32_t index, Object* value) {
7713 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
7714 (this, index, value);
7715}
7716
7717
7718Object* ExternalShortArray::SetValue(uint32_t index, Object* value) {
7719 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
7720 (this, index, value);
7721}
7722
7723
7724Object* ExternalUnsignedShortArray::SetValue(uint32_t index, Object* value) {
7725 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
7726 (this, index, value);
7727}
7728
7729
7730Object* ExternalIntArray::SetValue(uint32_t index, Object* value) {
7731 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
7732 (this, index, value);
7733}
7734
7735
7736Object* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
7737 uint32_t cast_value = 0;
7738 if (index < static_cast<uint32_t>(length())) {
7739 if (value->IsSmi()) {
7740 int int_value = Smi::cast(value)->value();
7741 cast_value = static_cast<uint32_t>(int_value);
7742 } else if (value->IsHeapNumber()) {
7743 double double_value = HeapNumber::cast(value)->value();
7744 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
7745 } else {
7746 // Clamp undefined to zero (default). All other types have been
7747 // converted to a number type further up in the call chain.
7748 ASSERT(value->IsUndefined());
7749 }
7750 set(index, cast_value);
7751 }
7752 return Heap::NumberFromUint32(cast_value);
7753}
7754
7755
7756Object* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
7757 float cast_value = 0;
7758 if (index < static_cast<uint32_t>(length())) {
7759 if (value->IsSmi()) {
7760 int int_value = Smi::cast(value)->value();
7761 cast_value = static_cast<float>(int_value);
7762 } else if (value->IsHeapNumber()) {
7763 double double_value = HeapNumber::cast(value)->value();
7764 cast_value = static_cast<float>(double_value);
7765 } else {
7766 // Clamp undefined to zero (default). All other types have been
7767 // converted to a number type further up in the call chain.
7768 ASSERT(value->IsUndefined());
7769 }
7770 set(index, cast_value);
7771 }
7772 return Heap::AllocateHeapNumber(cast_value);
7773}
7774
7775
Steve Blocka7e24c12009-10-30 11:49:00 +00007776Object* GlobalObject::GetPropertyCell(LookupResult* result) {
7777 ASSERT(!HasFastProperties());
7778 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
7779 ASSERT(value->IsJSGlobalPropertyCell());
7780 return value;
7781}
7782
7783
7784Object* GlobalObject::EnsurePropertyCell(String* name) {
7785 ASSERT(!HasFastProperties());
7786 int entry = property_dictionary()->FindEntry(name);
7787 if (entry == StringDictionary::kNotFound) {
7788 Object* cell = Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
7789 if (cell->IsFailure()) return cell;
7790 PropertyDetails details(NONE, NORMAL);
7791 details = details.AsDeleted();
7792 Object* dictionary = property_dictionary()->Add(name, cell, details);
7793 if (dictionary->IsFailure()) return dictionary;
7794 set_properties(StringDictionary::cast(dictionary));
7795 return cell;
7796 } else {
7797 Object* value = property_dictionary()->ValueAt(entry);
7798 ASSERT(value->IsJSGlobalPropertyCell());
7799 return value;
7800 }
7801}
7802
7803
7804Object* SymbolTable::LookupString(String* string, Object** s) {
7805 SymbolKey key(string);
7806 return LookupKey(&key, s);
7807}
7808
7809
Steve Blockd0582a62009-12-15 09:54:21 +00007810// This class is used for looking up two character strings in the symbol table.
7811// If we don't have a hit we don't want to waste much time so we unroll the
7812// string hash calculation loop here for speed. Doesn't work if the two
7813// characters form a decimal integer, since such strings have a different hash
7814// algorithm.
7815class TwoCharHashTableKey : public HashTableKey {
7816 public:
7817 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
7818 : c1_(c1), c2_(c2) {
7819 // Char 1.
7820 uint32_t hash = c1 + (c1 << 10);
7821 hash ^= hash >> 6;
7822 // Char 2.
7823 hash += c2;
7824 hash += hash << 10;
7825 hash ^= hash >> 6;
7826 // GetHash.
7827 hash += hash << 3;
7828 hash ^= hash >> 11;
7829 hash += hash << 15;
7830 if (hash == 0) hash = 27;
7831#ifdef DEBUG
7832 StringHasher hasher(2);
7833 hasher.AddCharacter(c1);
7834 hasher.AddCharacter(c2);
7835 // If this assert fails then we failed to reproduce the two-character
7836 // version of the string hashing algorithm above. One reason could be
7837 // that we were passed two digits as characters, since the hash
7838 // algorithm is different in that case.
7839 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
7840#endif
7841 hash_ = hash;
7842 }
7843
7844 bool IsMatch(Object* o) {
7845 if (!o->IsString()) return false;
7846 String* other = String::cast(o);
7847 if (other->length() != 2) return false;
7848 if (other->Get(0) != c1_) return false;
7849 return other->Get(1) == c2_;
7850 }
7851
7852 uint32_t Hash() { return hash_; }
7853 uint32_t HashForObject(Object* key) {
7854 if (!key->IsString()) return 0;
7855 return String::cast(key)->Hash();
7856 }
7857
7858 Object* AsObject() {
7859 // The TwoCharHashTableKey is only used for looking in the symbol
7860 // table, not for adding to it.
7861 UNREACHABLE();
7862 return NULL;
7863 }
7864 private:
7865 uint32_t c1_;
7866 uint32_t c2_;
7867 uint32_t hash_;
7868};
7869
7870
Steve Blocka7e24c12009-10-30 11:49:00 +00007871bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
7872 SymbolKey key(string);
7873 int entry = FindEntry(&key);
7874 if (entry == kNotFound) {
7875 return false;
7876 } else {
7877 String* result = String::cast(KeyAt(entry));
7878 ASSERT(StringShape(result).IsSymbol());
7879 *symbol = result;
7880 return true;
7881 }
7882}
7883
7884
Steve Blockd0582a62009-12-15 09:54:21 +00007885bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
7886 uint32_t c2,
7887 String** symbol) {
7888 TwoCharHashTableKey key(c1, c2);
7889 int entry = FindEntry(&key);
7890 if (entry == kNotFound) {
7891 return false;
7892 } else {
7893 String* result = String::cast(KeyAt(entry));
7894 ASSERT(StringShape(result).IsSymbol());
7895 *symbol = result;
7896 return true;
7897 }
7898}
7899
7900
Steve Blocka7e24c12009-10-30 11:49:00 +00007901Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
7902 Utf8SymbolKey key(str);
7903 return LookupKey(&key, s);
7904}
7905
7906
7907Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
7908 int entry = FindEntry(key);
7909
7910 // Symbol already in table.
7911 if (entry != kNotFound) {
7912 *s = KeyAt(entry);
7913 return this;
7914 }
7915
7916 // Adding new symbol. Grow table if needed.
7917 Object* obj = EnsureCapacity(1, key);
7918 if (obj->IsFailure()) return obj;
7919
7920 // Create symbol object.
7921 Object* symbol = key->AsObject();
7922 if (symbol->IsFailure()) return symbol;
7923
7924 // If the symbol table grew as part of EnsureCapacity, obj is not
7925 // the current symbol table and therefore we cannot use
7926 // SymbolTable::cast here.
7927 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
7928
7929 // Add the new symbol and return it along with the symbol table.
7930 entry = table->FindInsertionEntry(key->Hash());
7931 table->set(EntryToIndex(entry), symbol);
7932 table->ElementAdded();
7933 *s = symbol;
7934 return table;
7935}
7936
7937
7938Object* CompilationCacheTable::Lookup(String* src) {
7939 StringKey key(src);
7940 int entry = FindEntry(&key);
7941 if (entry == kNotFound) return Heap::undefined_value();
7942 return get(EntryToIndex(entry) + 1);
7943}
7944
7945
7946Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
7947 StringSharedKey key(src, context->closure()->shared());
7948 int entry = FindEntry(&key);
7949 if (entry == kNotFound) return Heap::undefined_value();
7950 return get(EntryToIndex(entry) + 1);
7951}
7952
7953
7954Object* CompilationCacheTable::LookupRegExp(String* src,
7955 JSRegExp::Flags flags) {
7956 RegExpKey key(src, flags);
7957 int entry = FindEntry(&key);
7958 if (entry == kNotFound) return Heap::undefined_value();
7959 return get(EntryToIndex(entry) + 1);
7960}
7961
7962
7963Object* CompilationCacheTable::Put(String* src, Object* value) {
7964 StringKey key(src);
7965 Object* obj = EnsureCapacity(1, &key);
7966 if (obj->IsFailure()) return obj;
7967
7968 CompilationCacheTable* cache =
7969 reinterpret_cast<CompilationCacheTable*>(obj);
7970 int entry = cache->FindInsertionEntry(key.Hash());
7971 cache->set(EntryToIndex(entry), src);
7972 cache->set(EntryToIndex(entry) + 1, value);
7973 cache->ElementAdded();
7974 return cache;
7975}
7976
7977
7978Object* CompilationCacheTable::PutEval(String* src,
7979 Context* context,
7980 Object* value) {
7981 StringSharedKey key(src, context->closure()->shared());
7982 Object* obj = EnsureCapacity(1, &key);
7983 if (obj->IsFailure()) return obj;
7984
7985 CompilationCacheTable* cache =
7986 reinterpret_cast<CompilationCacheTable*>(obj);
7987 int entry = cache->FindInsertionEntry(key.Hash());
7988
7989 Object* k = key.AsObject();
7990 if (k->IsFailure()) return k;
7991
7992 cache->set(EntryToIndex(entry), k);
7993 cache->set(EntryToIndex(entry) + 1, value);
7994 cache->ElementAdded();
7995 return cache;
7996}
7997
7998
7999Object* CompilationCacheTable::PutRegExp(String* src,
8000 JSRegExp::Flags flags,
8001 FixedArray* value) {
8002 RegExpKey key(src, flags);
8003 Object* obj = EnsureCapacity(1, &key);
8004 if (obj->IsFailure()) return obj;
8005
8006 CompilationCacheTable* cache =
8007 reinterpret_cast<CompilationCacheTable*>(obj);
8008 int entry = cache->FindInsertionEntry(key.Hash());
Steve Block3ce2e202009-11-05 08:53:23 +00008009 // We store the value in the key slot, and compare the search key
8010 // to the stored value with a custon IsMatch function during lookups.
Steve Blocka7e24c12009-10-30 11:49:00 +00008011 cache->set(EntryToIndex(entry), value);
8012 cache->set(EntryToIndex(entry) + 1, value);
8013 cache->ElementAdded();
8014 return cache;
8015}
8016
8017
8018// SymbolsKey used for HashTable where key is array of symbols.
8019class SymbolsKey : public HashTableKey {
8020 public:
8021 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
8022
8023 bool IsMatch(Object* symbols) {
8024 FixedArray* o = FixedArray::cast(symbols);
8025 int len = symbols_->length();
8026 if (o->length() != len) return false;
8027 for (int i = 0; i < len; i++) {
8028 if (o->get(i) != symbols_->get(i)) return false;
8029 }
8030 return true;
8031 }
8032
8033 uint32_t Hash() { return HashForObject(symbols_); }
8034
8035 uint32_t HashForObject(Object* obj) {
8036 FixedArray* symbols = FixedArray::cast(obj);
8037 int len = symbols->length();
8038 uint32_t hash = 0;
8039 for (int i = 0; i < len; i++) {
8040 hash ^= String::cast(symbols->get(i))->Hash();
8041 }
8042 return hash;
8043 }
8044
8045 Object* AsObject() { return symbols_; }
8046
8047 private:
8048 FixedArray* symbols_;
8049};
8050
8051
8052Object* MapCache::Lookup(FixedArray* array) {
8053 SymbolsKey key(array);
8054 int entry = FindEntry(&key);
8055 if (entry == kNotFound) return Heap::undefined_value();
8056 return get(EntryToIndex(entry) + 1);
8057}
8058
8059
8060Object* MapCache::Put(FixedArray* array, Map* value) {
8061 SymbolsKey key(array);
8062 Object* obj = EnsureCapacity(1, &key);
8063 if (obj->IsFailure()) return obj;
8064
8065 MapCache* cache = reinterpret_cast<MapCache*>(obj);
8066 int entry = cache->FindInsertionEntry(key.Hash());
8067 cache->set(EntryToIndex(entry), array);
8068 cache->set(EntryToIndex(entry) + 1, value);
8069 cache->ElementAdded();
8070 return cache;
8071}
8072
8073
8074template<typename Shape, typename Key>
8075Object* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
8076 Object* obj = HashTable<Shape, Key>::Allocate(at_least_space_for);
8077 // Initialize the next enumeration index.
8078 if (!obj->IsFailure()) {
8079 Dictionary<Shape, Key>::cast(obj)->
8080 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
8081 }
8082 return obj;
8083}
8084
8085
8086template<typename Shape, typename Key>
8087Object* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
8088 int length = HashTable<Shape, Key>::NumberOfElements();
8089
8090 // Allocate and initialize iteration order array.
8091 Object* obj = Heap::AllocateFixedArray(length);
8092 if (obj->IsFailure()) return obj;
8093 FixedArray* iteration_order = FixedArray::cast(obj);
8094 for (int i = 0; i < length; i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00008095 iteration_order->set(i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00008096 }
8097
8098 // Allocate array with enumeration order.
8099 obj = Heap::AllocateFixedArray(length);
8100 if (obj->IsFailure()) return obj;
8101 FixedArray* enumeration_order = FixedArray::cast(obj);
8102
8103 // Fill the enumeration order array with property details.
8104 int capacity = HashTable<Shape, Key>::Capacity();
8105 int pos = 0;
8106 for (int i = 0; i < capacity; i++) {
8107 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
Leon Clarke4515c472010-02-03 11:58:03 +00008108 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008109 }
8110 }
8111
8112 // Sort the arrays wrt. enumeration order.
8113 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
8114
8115 // Overwrite the enumeration_order with the enumeration indices.
8116 for (int i = 0; i < length; i++) {
8117 int index = Smi::cast(iteration_order->get(i))->value();
8118 int enum_index = PropertyDetails::kInitialIndex + i;
Leon Clarke4515c472010-02-03 11:58:03 +00008119 enumeration_order->set(index, Smi::FromInt(enum_index));
Steve Blocka7e24c12009-10-30 11:49:00 +00008120 }
8121
8122 // Update the dictionary with new indices.
8123 capacity = HashTable<Shape, Key>::Capacity();
8124 pos = 0;
8125 for (int i = 0; i < capacity; i++) {
8126 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
8127 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
8128 PropertyDetails details = DetailsAt(i);
8129 PropertyDetails new_details =
8130 PropertyDetails(details.attributes(), details.type(), enum_index);
8131 DetailsAtPut(i, new_details);
8132 }
8133 }
8134
8135 // Set the next enumeration index.
8136 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
8137 return this;
8138}
8139
8140template<typename Shape, typename Key>
8141Object* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
8142 // Check whether there are enough enumeration indices to add n elements.
8143 if (Shape::kIsEnumerable &&
8144 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
8145 // If not, we generate new indices for the properties.
8146 Object* result = GenerateNewEnumerationIndices();
8147 if (result->IsFailure()) return result;
8148 }
8149 return HashTable<Shape, Key>::EnsureCapacity(n, key);
8150}
8151
8152
8153void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
8154 // Do nothing if the interval [from, to) is empty.
8155 if (from >= to) return;
8156
8157 int removed_entries = 0;
8158 Object* sentinel = Heap::null_value();
8159 int capacity = Capacity();
8160 for (int i = 0; i < capacity; i++) {
8161 Object* key = KeyAt(i);
8162 if (key->IsNumber()) {
8163 uint32_t number = static_cast<uint32_t>(key->Number());
8164 if (from <= number && number < to) {
8165 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
8166 removed_entries++;
8167 }
8168 }
8169 }
8170
8171 // Update the number of elements.
Leon Clarkee46be812010-01-19 14:06:41 +00008172 ElementsRemoved(removed_entries);
Steve Blocka7e24c12009-10-30 11:49:00 +00008173}
8174
8175
8176template<typename Shape, typename Key>
8177Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
8178 JSObject::DeleteMode mode) {
8179 PropertyDetails details = DetailsAt(entry);
8180 // Ignore attributes if forcing a deletion.
8181 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
8182 return Heap::false_value();
8183 }
8184 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
8185 HashTable<Shape, Key>::ElementRemoved();
8186 return Heap::true_value();
8187}
8188
8189
8190template<typename Shape, typename Key>
8191Object* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008192 int entry = this->FindEntry(key);
Steve Blocka7e24c12009-10-30 11:49:00 +00008193
8194 // If the entry is present set the value;
8195 if (entry != Dictionary<Shape, Key>::kNotFound) {
8196 ValueAtPut(entry, value);
8197 return this;
8198 }
8199
8200 // Check whether the dictionary should be extended.
8201 Object* obj = EnsureCapacity(1, key);
8202 if (obj->IsFailure()) return obj;
8203
8204 Object* k = Shape::AsObject(key);
8205 if (k->IsFailure()) return k;
8206 PropertyDetails details = PropertyDetails(NONE, NORMAL);
8207 return Dictionary<Shape, Key>::cast(obj)->
8208 AddEntry(key, value, details, Shape::Hash(key));
8209}
8210
8211
8212template<typename Shape, typename Key>
8213Object* Dictionary<Shape, Key>::Add(Key key,
8214 Object* value,
8215 PropertyDetails details) {
8216 // Valdate key is absent.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008217 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
Steve Blocka7e24c12009-10-30 11:49:00 +00008218 // Check whether the dictionary should be extended.
8219 Object* obj = EnsureCapacity(1, key);
8220 if (obj->IsFailure()) return obj;
8221 return Dictionary<Shape, Key>::cast(obj)->
8222 AddEntry(key, value, details, Shape::Hash(key));
8223}
8224
8225
8226// Add a key, value pair to the dictionary.
8227template<typename Shape, typename Key>
8228Object* Dictionary<Shape, Key>::AddEntry(Key key,
8229 Object* value,
8230 PropertyDetails details,
8231 uint32_t hash) {
8232 // Compute the key object.
8233 Object* k = Shape::AsObject(key);
8234 if (k->IsFailure()) return k;
8235
8236 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
8237 // Insert element at empty or deleted entry
8238 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
8239 // Assign an enumeration index to the property and update
8240 // SetNextEnumerationIndex.
8241 int index = NextEnumerationIndex();
8242 details = PropertyDetails(details.attributes(), details.type(), index);
8243 SetNextEnumerationIndex(index + 1);
8244 }
8245 SetEntry(entry, k, value, details);
8246 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
8247 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
8248 HashTable<Shape, Key>::ElementAdded();
8249 return this;
8250}
8251
8252
8253void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
8254 // If the dictionary requires slow elements an element has already
8255 // been added at a high index.
8256 if (requires_slow_elements()) return;
8257 // Check if this index is high enough that we should require slow
8258 // elements.
8259 if (key > kRequiresSlowElementsLimit) {
8260 set_requires_slow_elements();
8261 return;
8262 }
8263 // Update max key value.
8264 Object* max_index_object = get(kMaxNumberKeyIndex);
8265 if (!max_index_object->IsSmi() || max_number_key() < key) {
8266 FixedArray::set(kMaxNumberKeyIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00008267 Smi::FromInt(key << kRequiresSlowElementsTagSize));
Steve Blocka7e24c12009-10-30 11:49:00 +00008268 }
8269}
8270
8271
8272Object* NumberDictionary::AddNumberEntry(uint32_t key,
8273 Object* value,
8274 PropertyDetails details) {
8275 UpdateMaxNumberKey(key);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008276 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
Steve Blocka7e24c12009-10-30 11:49:00 +00008277 return Add(key, value, details);
8278}
8279
8280
8281Object* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
8282 UpdateMaxNumberKey(key);
8283 return AtPut(key, value);
8284}
8285
8286
8287Object* NumberDictionary::Set(uint32_t key,
8288 Object* value,
8289 PropertyDetails details) {
8290 int entry = FindEntry(key);
8291 if (entry == kNotFound) return AddNumberEntry(key, value, details);
8292 // Preserve enumeration index.
8293 details = PropertyDetails(details.attributes(),
8294 details.type(),
8295 DetailsAt(entry).index());
8296 SetEntry(entry, NumberDictionaryShape::AsObject(key), value, details);
8297 return this;
8298}
8299
8300
8301
8302template<typename Shape, typename Key>
8303int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
8304 PropertyAttributes filter) {
8305 int capacity = HashTable<Shape, Key>::Capacity();
8306 int result = 0;
8307 for (int i = 0; i < capacity; i++) {
8308 Object* k = HashTable<Shape, Key>::KeyAt(i);
8309 if (HashTable<Shape, Key>::IsKey(k)) {
8310 PropertyDetails details = DetailsAt(i);
8311 if (details.IsDeleted()) continue;
8312 PropertyAttributes attr = details.attributes();
8313 if ((attr & filter) == 0) result++;
8314 }
8315 }
8316 return result;
8317}
8318
8319
8320template<typename Shape, typename Key>
8321int Dictionary<Shape, Key>::NumberOfEnumElements() {
8322 return NumberOfElementsFilterAttributes(
8323 static_cast<PropertyAttributes>(DONT_ENUM));
8324}
8325
8326
8327template<typename Shape, typename Key>
8328void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
8329 PropertyAttributes filter) {
8330 ASSERT(storage->length() >= NumberOfEnumElements());
8331 int capacity = HashTable<Shape, Key>::Capacity();
8332 int index = 0;
8333 for (int i = 0; i < capacity; i++) {
8334 Object* k = HashTable<Shape, Key>::KeyAt(i);
8335 if (HashTable<Shape, Key>::IsKey(k)) {
8336 PropertyDetails details = DetailsAt(i);
8337 if (details.IsDeleted()) continue;
8338 PropertyAttributes attr = details.attributes();
8339 if ((attr & filter) == 0) storage->set(index++, k);
8340 }
8341 }
8342 storage->SortPairs(storage, index);
8343 ASSERT(storage->length() >= index);
8344}
8345
8346
8347void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
8348 FixedArray* sort_array) {
8349 ASSERT(storage->length() >= NumberOfEnumElements());
8350 int capacity = Capacity();
8351 int index = 0;
8352 for (int i = 0; i < capacity; i++) {
8353 Object* k = KeyAt(i);
8354 if (IsKey(k)) {
8355 PropertyDetails details = DetailsAt(i);
8356 if (details.IsDeleted() || details.IsDontEnum()) continue;
8357 storage->set(index, k);
Leon Clarke4515c472010-02-03 11:58:03 +00008358 sort_array->set(index, Smi::FromInt(details.index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008359 index++;
8360 }
8361 }
8362 storage->SortPairs(sort_array, sort_array->length());
8363 ASSERT(storage->length() >= index);
8364}
8365
8366
8367template<typename Shape, typename Key>
8368void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
8369 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
8370 static_cast<PropertyAttributes>(NONE)));
8371 int capacity = HashTable<Shape, Key>::Capacity();
8372 int index = 0;
8373 for (int i = 0; i < capacity; i++) {
8374 Object* k = HashTable<Shape, Key>::KeyAt(i);
8375 if (HashTable<Shape, Key>::IsKey(k)) {
8376 PropertyDetails details = DetailsAt(i);
8377 if (details.IsDeleted()) continue;
8378 storage->set(index++, k);
8379 }
8380 }
8381 ASSERT(storage->length() >= index);
8382}
8383
8384
8385// Backwards lookup (slow).
8386template<typename Shape, typename Key>
8387Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
8388 int capacity = HashTable<Shape, Key>::Capacity();
8389 for (int i = 0; i < capacity; i++) {
8390 Object* k = HashTable<Shape, Key>::KeyAt(i);
8391 if (Dictionary<Shape, Key>::IsKey(k)) {
8392 Object* e = ValueAt(i);
8393 if (e->IsJSGlobalPropertyCell()) {
8394 e = JSGlobalPropertyCell::cast(e)->value();
8395 }
8396 if (e == value) return k;
8397 }
8398 }
8399 return Heap::undefined_value();
8400}
8401
8402
8403Object* StringDictionary::TransformPropertiesToFastFor(
8404 JSObject* obj, int unused_property_fields) {
8405 // Make sure we preserve dictionary representation if there are too many
8406 // descriptors.
8407 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
8408
8409 // Figure out if it is necessary to generate new enumeration indices.
8410 int max_enumeration_index =
8411 NextEnumerationIndex() +
8412 (DescriptorArray::kMaxNumberOfDescriptors -
8413 NumberOfElements());
8414 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
8415 Object* result = GenerateNewEnumerationIndices();
8416 if (result->IsFailure()) return result;
8417 }
8418
8419 int instance_descriptor_length = 0;
8420 int number_of_fields = 0;
8421
8422 // Compute the length of the instance descriptor.
8423 int capacity = Capacity();
8424 for (int i = 0; i < capacity; i++) {
8425 Object* k = KeyAt(i);
8426 if (IsKey(k)) {
8427 Object* value = ValueAt(i);
8428 PropertyType type = DetailsAt(i).type();
8429 ASSERT(type != FIELD);
8430 instance_descriptor_length++;
Leon Clarkee46be812010-01-19 14:06:41 +00008431 if (type == NORMAL &&
8432 (!value->IsJSFunction() || Heap::InNewSpace(value))) {
8433 number_of_fields += 1;
8434 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008435 }
8436 }
8437
8438 // Allocate the instance descriptor.
8439 Object* descriptors_unchecked =
8440 DescriptorArray::Allocate(instance_descriptor_length);
8441 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
8442 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
8443
8444 int inobject_props = obj->map()->inobject_properties();
8445 int number_of_allocated_fields =
8446 number_of_fields + unused_property_fields - inobject_props;
8447
8448 // Allocate the fixed array for the fields.
8449 Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields);
8450 if (fields->IsFailure()) return fields;
8451
8452 // Fill in the instance descriptor and the fields.
8453 int next_descriptor = 0;
8454 int current_offset = 0;
8455 for (int i = 0; i < capacity; i++) {
8456 Object* k = KeyAt(i);
8457 if (IsKey(k)) {
8458 Object* value = ValueAt(i);
8459 // Ensure the key is a symbol before writing into the instance descriptor.
8460 Object* key = Heap::LookupSymbol(String::cast(k));
8461 if (key->IsFailure()) return key;
8462 PropertyDetails details = DetailsAt(i);
8463 PropertyType type = details.type();
8464
Leon Clarkee46be812010-01-19 14:06:41 +00008465 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008466 ConstantFunctionDescriptor d(String::cast(key),
8467 JSFunction::cast(value),
8468 details.attributes(),
8469 details.index());
8470 descriptors->Set(next_descriptor++, &d);
8471 } else if (type == NORMAL) {
8472 if (current_offset < inobject_props) {
8473 obj->InObjectPropertyAtPut(current_offset,
8474 value,
8475 UPDATE_WRITE_BARRIER);
8476 } else {
8477 int offset = current_offset - inobject_props;
8478 FixedArray::cast(fields)->set(offset, value);
8479 }
8480 FieldDescriptor d(String::cast(key),
8481 current_offset++,
8482 details.attributes(),
8483 details.index());
8484 descriptors->Set(next_descriptor++, &d);
8485 } else if (type == CALLBACKS) {
8486 CallbacksDescriptor d(String::cast(key),
8487 value,
8488 details.attributes(),
8489 details.index());
8490 descriptors->Set(next_descriptor++, &d);
8491 } else {
8492 UNREACHABLE();
8493 }
8494 }
8495 }
8496 ASSERT(current_offset == number_of_fields);
8497
8498 descriptors->Sort();
8499 // Allocate new map.
8500 Object* new_map = obj->map()->CopyDropDescriptors();
8501 if (new_map->IsFailure()) return new_map;
8502
8503 // Transform the object.
8504 obj->set_map(Map::cast(new_map));
8505 obj->map()->set_instance_descriptors(descriptors);
8506 obj->map()->set_unused_property_fields(unused_property_fields);
8507
8508 obj->set_properties(FixedArray::cast(fields));
8509 ASSERT(obj->IsJSObject());
8510
8511 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
8512 // Check that it really works.
8513 ASSERT(obj->HasFastProperties());
8514
8515 return obj;
8516}
8517
8518
8519#ifdef ENABLE_DEBUGGER_SUPPORT
8520// Check if there is a break point at this code position.
8521bool DebugInfo::HasBreakPoint(int code_position) {
8522 // Get the break point info object for this code position.
8523 Object* break_point_info = GetBreakPointInfo(code_position);
8524
8525 // If there is no break point info object or no break points in the break
8526 // point info object there is no break point at this code position.
8527 if (break_point_info->IsUndefined()) return false;
8528 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
8529}
8530
8531
8532// Get the break point info object for this code position.
8533Object* DebugInfo::GetBreakPointInfo(int code_position) {
8534 // Find the index of the break point info object for this code position.
8535 int index = GetBreakPointInfoIndex(code_position);
8536
8537 // Return the break point info object if any.
8538 if (index == kNoBreakPointInfo) return Heap::undefined_value();
8539 return BreakPointInfo::cast(break_points()->get(index));
8540}
8541
8542
8543// Clear a break point at the specified code position.
8544void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
8545 int code_position,
8546 Handle<Object> break_point_object) {
8547 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
8548 if (break_point_info->IsUndefined()) return;
8549 BreakPointInfo::ClearBreakPoint(
8550 Handle<BreakPointInfo>::cast(break_point_info),
8551 break_point_object);
8552}
8553
8554
8555void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
8556 int code_position,
8557 int source_position,
8558 int statement_position,
8559 Handle<Object> break_point_object) {
8560 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
8561 if (!break_point_info->IsUndefined()) {
8562 BreakPointInfo::SetBreakPoint(
8563 Handle<BreakPointInfo>::cast(break_point_info),
8564 break_point_object);
8565 return;
8566 }
8567
8568 // Adding a new break point for a code position which did not have any
8569 // break points before. Try to find a free slot.
8570 int index = kNoBreakPointInfo;
8571 for (int i = 0; i < debug_info->break_points()->length(); i++) {
8572 if (debug_info->break_points()->get(i)->IsUndefined()) {
8573 index = i;
8574 break;
8575 }
8576 }
8577 if (index == kNoBreakPointInfo) {
8578 // No free slot - extend break point info array.
8579 Handle<FixedArray> old_break_points =
8580 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
8581 debug_info->set_break_points(*Factory::NewFixedArray(
8582 old_break_points->length() +
8583 Debug::kEstimatedNofBreakPointsInFunction));
8584 Handle<FixedArray> new_break_points =
8585 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
8586 for (int i = 0; i < old_break_points->length(); i++) {
8587 new_break_points->set(i, old_break_points->get(i));
8588 }
8589 index = old_break_points->length();
8590 }
8591 ASSERT(index != kNoBreakPointInfo);
8592
8593 // Allocate new BreakPointInfo object and set the break point.
8594 Handle<BreakPointInfo> new_break_point_info =
8595 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
8596 new_break_point_info->set_code_position(Smi::FromInt(code_position));
8597 new_break_point_info->set_source_position(Smi::FromInt(source_position));
8598 new_break_point_info->
8599 set_statement_position(Smi::FromInt(statement_position));
8600 new_break_point_info->set_break_point_objects(Heap::undefined_value());
8601 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
8602 debug_info->break_points()->set(index, *new_break_point_info);
8603}
8604
8605
8606// Get the break point objects for a code position.
8607Object* DebugInfo::GetBreakPointObjects(int code_position) {
8608 Object* break_point_info = GetBreakPointInfo(code_position);
8609 if (break_point_info->IsUndefined()) {
8610 return Heap::undefined_value();
8611 }
8612 return BreakPointInfo::cast(break_point_info)->break_point_objects();
8613}
8614
8615
8616// Get the total number of break points.
8617int DebugInfo::GetBreakPointCount() {
8618 if (break_points()->IsUndefined()) return 0;
8619 int count = 0;
8620 for (int i = 0; i < break_points()->length(); i++) {
8621 if (!break_points()->get(i)->IsUndefined()) {
8622 BreakPointInfo* break_point_info =
8623 BreakPointInfo::cast(break_points()->get(i));
8624 count += break_point_info->GetBreakPointCount();
8625 }
8626 }
8627 return count;
8628}
8629
8630
8631Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
8632 Handle<Object> break_point_object) {
8633 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
8634 for (int i = 0; i < debug_info->break_points()->length(); i++) {
8635 if (!debug_info->break_points()->get(i)->IsUndefined()) {
8636 Handle<BreakPointInfo> break_point_info =
8637 Handle<BreakPointInfo>(BreakPointInfo::cast(
8638 debug_info->break_points()->get(i)));
8639 if (BreakPointInfo::HasBreakPointObject(break_point_info,
8640 break_point_object)) {
8641 return *break_point_info;
8642 }
8643 }
8644 }
8645 return Heap::undefined_value();
8646}
8647
8648
8649// Find the index of the break point info object for the specified code
8650// position.
8651int DebugInfo::GetBreakPointInfoIndex(int code_position) {
8652 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
8653 for (int i = 0; i < break_points()->length(); i++) {
8654 if (!break_points()->get(i)->IsUndefined()) {
8655 BreakPointInfo* break_point_info =
8656 BreakPointInfo::cast(break_points()->get(i));
8657 if (break_point_info->code_position()->value() == code_position) {
8658 return i;
8659 }
8660 }
8661 }
8662 return kNoBreakPointInfo;
8663}
8664
8665
8666// Remove the specified break point object.
8667void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
8668 Handle<Object> break_point_object) {
8669 // If there are no break points just ignore.
8670 if (break_point_info->break_point_objects()->IsUndefined()) return;
8671 // If there is a single break point clear it if it is the same.
8672 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8673 if (break_point_info->break_point_objects() == *break_point_object) {
8674 break_point_info->set_break_point_objects(Heap::undefined_value());
8675 }
8676 return;
8677 }
8678 // If there are multiple break points shrink the array
8679 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
8680 Handle<FixedArray> old_array =
8681 Handle<FixedArray>(
8682 FixedArray::cast(break_point_info->break_point_objects()));
8683 Handle<FixedArray> new_array =
8684 Factory::NewFixedArray(old_array->length() - 1);
8685 int found_count = 0;
8686 for (int i = 0; i < old_array->length(); i++) {
8687 if (old_array->get(i) == *break_point_object) {
8688 ASSERT(found_count == 0);
8689 found_count++;
8690 } else {
8691 new_array->set(i - found_count, old_array->get(i));
8692 }
8693 }
8694 // If the break point was found in the list change it.
8695 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
8696}
8697
8698
8699// Add the specified break point object.
8700void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
8701 Handle<Object> break_point_object) {
8702 // If there was no break point objects before just set it.
8703 if (break_point_info->break_point_objects()->IsUndefined()) {
8704 break_point_info->set_break_point_objects(*break_point_object);
8705 return;
8706 }
8707 // If the break point object is the same as before just ignore.
8708 if (break_point_info->break_point_objects() == *break_point_object) return;
8709 // If there was one break point object before replace with array.
8710 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8711 Handle<FixedArray> array = Factory::NewFixedArray(2);
8712 array->set(0, break_point_info->break_point_objects());
8713 array->set(1, *break_point_object);
8714 break_point_info->set_break_point_objects(*array);
8715 return;
8716 }
8717 // If there was more than one break point before extend array.
8718 Handle<FixedArray> old_array =
8719 Handle<FixedArray>(
8720 FixedArray::cast(break_point_info->break_point_objects()));
8721 Handle<FixedArray> new_array =
8722 Factory::NewFixedArray(old_array->length() + 1);
8723 for (int i = 0; i < old_array->length(); i++) {
8724 // If the break point was there before just ignore.
8725 if (old_array->get(i) == *break_point_object) return;
8726 new_array->set(i, old_array->get(i));
8727 }
8728 // Add the new break point.
8729 new_array->set(old_array->length(), *break_point_object);
8730 break_point_info->set_break_point_objects(*new_array);
8731}
8732
8733
8734bool BreakPointInfo::HasBreakPointObject(
8735 Handle<BreakPointInfo> break_point_info,
8736 Handle<Object> break_point_object) {
8737 // No break point.
8738 if (break_point_info->break_point_objects()->IsUndefined()) return false;
8739 // Single beak point.
8740 if (!break_point_info->break_point_objects()->IsFixedArray()) {
8741 return break_point_info->break_point_objects() == *break_point_object;
8742 }
8743 // Multiple break points.
8744 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
8745 for (int i = 0; i < array->length(); i++) {
8746 if (array->get(i) == *break_point_object) {
8747 return true;
8748 }
8749 }
8750 return false;
8751}
8752
8753
8754// Get the number of break points.
8755int BreakPointInfo::GetBreakPointCount() {
8756 // No break point.
8757 if (break_point_objects()->IsUndefined()) return 0;
8758 // Single beak point.
8759 if (!break_point_objects()->IsFixedArray()) return 1;
8760 // Multiple break points.
8761 return FixedArray::cast(break_point_objects())->length();
8762}
8763#endif
8764
8765
8766} } // namespace v8::internal