blob: dcfd926ffc97335ab08cdd9b371dc151c0b61d4d [file] [log] [blame]
Ben Murdochf87a2032010-10-22 12:50:53 +01001// Copyright 2010 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// 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"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080038#include "scanner-base.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000039#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
John Reck59135872010-11-02 12:39:01 -070057MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor,
58 Object* value) {
59 Object* result;
60 { MaybeObject* maybe_result = Heap::AllocateJSObject(constructor);
61 if (!maybe_result->ToObject(&result)) return maybe_result;
62 }
Steve Blocka7e24c12009-10-30 11:49:00 +000063 JSValue::cast(result)->set_value(value);
64 return result;
65}
66
67
John Reck59135872010-11-02 12:39:01 -070068MaybeObject* Object::ToObject(Context* global_context) {
Steve Blocka7e24c12009-10-30 11:49:00 +000069 if (IsNumber()) {
70 return CreateJSValue(global_context->number_function(), this);
71 } else if (IsBoolean()) {
72 return CreateJSValue(global_context->boolean_function(), this);
73 } else if (IsString()) {
74 return CreateJSValue(global_context->string_function(), this);
75 }
76 ASSERT(IsJSObject());
77 return this;
78}
79
80
John Reck59135872010-11-02 12:39:01 -070081MaybeObject* Object::ToObject() {
Steve Blocka7e24c12009-10-30 11:49:00 +000082 Context* global_context = Top::context()->global_context();
83 if (IsJSObject()) {
84 return this;
85 } else if (IsNumber()) {
86 return CreateJSValue(global_context->number_function(), this);
87 } else if (IsBoolean()) {
88 return CreateJSValue(global_context->boolean_function(), this);
89 } else if (IsString()) {
90 return CreateJSValue(global_context->string_function(), this);
91 }
92
93 // Throw a type error.
94 return Failure::InternalError();
95}
96
97
98Object* Object::ToBoolean() {
99 if (IsTrue()) return Heap::true_value();
100 if (IsFalse()) return Heap::false_value();
101 if (IsSmi()) {
102 return Heap::ToBoolean(Smi::cast(this)->value() != 0);
103 }
104 if (IsUndefined() || IsNull()) return Heap::false_value();
105 // Undetectable object is false
106 if (IsUndetectableObject()) {
107 return Heap::false_value();
108 }
109 if (IsString()) {
110 return Heap::ToBoolean(String::cast(this)->length() != 0);
111 }
112 if (IsHeapNumber()) {
113 return HeapNumber::cast(this)->HeapNumberToBoolean();
114 }
115 return Heap::true_value();
116}
117
118
119void Object::Lookup(String* name, LookupResult* result) {
120 if (IsJSObject()) return JSObject::cast(this)->Lookup(name, result);
121 Object* holder = NULL;
122 Context* global_context = Top::context()->global_context();
123 if (IsString()) {
124 holder = global_context->string_function()->instance_prototype();
125 } else if (IsNumber()) {
126 holder = global_context->number_function()->instance_prototype();
127 } else if (IsBoolean()) {
128 holder = global_context->boolean_function()->instance_prototype();
129 }
130 ASSERT(holder != NULL); // Cannot handle null or undefined.
131 JSObject::cast(holder)->Lookup(name, result);
132}
133
134
John Reck59135872010-11-02 12:39:01 -0700135MaybeObject* Object::GetPropertyWithReceiver(Object* receiver,
136 String* name,
137 PropertyAttributes* attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000138 LookupResult result;
139 Lookup(name, &result);
John Reck59135872010-11-02 12:39:01 -0700140 MaybeObject* value = GetProperty(receiver, &result, name, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +0000141 ASSERT(*attributes <= ABSENT);
142 return value;
143}
144
145
John Reck59135872010-11-02 12:39:01 -0700146MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
147 Object* structure,
148 String* name,
149 Object* holder) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000150 // To accommodate both the old and the new api we switch on the
151 // data structure used to store the callbacks. Eventually proxy
152 // callbacks should be phased out.
153 if (structure->IsProxy()) {
154 AccessorDescriptor* callback =
155 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
John Reck59135872010-11-02 12:39:01 -0700156 MaybeObject* value = (callback->getter)(receiver, callback->data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000157 RETURN_IF_SCHEDULED_EXCEPTION();
158 return value;
159 }
160
161 // api style callbacks.
162 if (structure->IsAccessorInfo()) {
163 AccessorInfo* data = AccessorInfo::cast(structure);
164 Object* fun_obj = data->getter();
165 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
166 HandleScope scope;
167 JSObject* self = JSObject::cast(receiver);
168 JSObject* holder_handle = JSObject::cast(holder);
169 Handle<String> key(name);
170 LOG(ApiNamedPropertyAccess("load", self, name));
171 CustomArguments args(data->data(), self, holder_handle);
172 v8::AccessorInfo info(args.end());
173 v8::Handle<v8::Value> result;
174 {
175 // Leaving JavaScript.
176 VMState state(EXTERNAL);
177 result = call_fun(v8::Utils::ToLocal(key), info);
178 }
179 RETURN_IF_SCHEDULED_EXCEPTION();
180 if (result.IsEmpty()) return Heap::undefined_value();
181 return *v8::Utils::OpenHandle(*result);
182 }
183
184 // __defineGetter__ callback
185 if (structure->IsFixedArray()) {
186 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
187 if (getter->IsJSFunction()) {
188 return Object::GetPropertyWithDefinedGetter(receiver,
189 JSFunction::cast(getter));
190 }
191 // Getter is not a function.
192 return Heap::undefined_value();
193 }
194
195 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +0100196 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000197}
198
199
John Reck59135872010-11-02 12:39:01 -0700200MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver,
201 JSFunction* getter) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000202 HandleScope scope;
203 Handle<JSFunction> fun(JSFunction::cast(getter));
204 Handle<Object> self(receiver);
205#ifdef ENABLE_DEBUGGER_SUPPORT
206 // Handle stepping into a getter if step into is active.
207 if (Debug::StepInActive()) {
208 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
209 }
210#endif
211 bool has_pending_exception;
212 Handle<Object> result =
213 Execution::Call(fun, self, 0, NULL, &has_pending_exception);
214 // Check for pending exception and return the result.
215 if (has_pending_exception) return Failure::Exception();
216 return *result;
217}
218
219
220// Only deal with CALLBACKS and INTERCEPTOR
John Reck59135872010-11-02 12:39:01 -0700221MaybeObject* JSObject::GetPropertyWithFailedAccessCheck(
Steve Blocka7e24c12009-10-30 11:49:00 +0000222 Object* receiver,
223 LookupResult* result,
224 String* name,
225 PropertyAttributes* attributes) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000226 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000227 switch (result->type()) {
228 case CALLBACKS: {
229 // Only allow API accessors.
230 Object* obj = result->GetCallbackObject();
231 if (obj->IsAccessorInfo()) {
232 AccessorInfo* info = AccessorInfo::cast(obj);
233 if (info->all_can_read()) {
234 *attributes = result->GetAttributes();
235 return GetPropertyWithCallback(receiver,
236 result->GetCallbackObject(),
237 name,
238 result->holder());
239 }
240 }
241 break;
242 }
243 case NORMAL:
244 case FIELD:
245 case CONSTANT_FUNCTION: {
246 // Search ALL_CAN_READ accessors in prototype chain.
247 LookupResult r;
248 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000249 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000250 return GetPropertyWithFailedAccessCheck(receiver,
251 &r,
252 name,
253 attributes);
254 }
255 break;
256 }
257 case INTERCEPTOR: {
258 // If the object has an interceptor, try real named properties.
259 // No access check in GetPropertyAttributeWithInterceptor.
260 LookupResult r;
261 result->holder()->LookupRealNamedProperty(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000262 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000263 return GetPropertyWithFailedAccessCheck(receiver,
264 &r,
265 name,
266 attributes);
267 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000268 break;
269 }
Andrei Popescu402d9372010-02-26 13:31:12 +0000270 default:
271 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +0000272 }
273 }
274
275 // No accessible property found.
276 *attributes = ABSENT;
277 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
278 return Heap::undefined_value();
279}
280
281
282PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
283 Object* receiver,
284 LookupResult* result,
285 String* name,
286 bool continue_search) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000287 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000288 switch (result->type()) {
289 case CALLBACKS: {
290 // Only allow API accessors.
291 Object* obj = result->GetCallbackObject();
292 if (obj->IsAccessorInfo()) {
293 AccessorInfo* info = AccessorInfo::cast(obj);
294 if (info->all_can_read()) {
295 return result->GetAttributes();
296 }
297 }
298 break;
299 }
300
301 case NORMAL:
302 case FIELD:
303 case CONSTANT_FUNCTION: {
304 if (!continue_search) break;
305 // Search ALL_CAN_READ accessors in prototype chain.
306 LookupResult r;
307 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000308 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000309 return GetPropertyAttributeWithFailedAccessCheck(receiver,
310 &r,
311 name,
312 continue_search);
313 }
314 break;
315 }
316
317 case INTERCEPTOR: {
318 // If the object has an interceptor, try real named properties.
319 // No access check in GetPropertyAttributeWithInterceptor.
320 LookupResult r;
321 if (continue_search) {
322 result->holder()->LookupRealNamedProperty(name, &r);
323 } else {
324 result->holder()->LocalLookupRealNamedProperty(name, &r);
325 }
Andrei Popescu402d9372010-02-26 13:31:12 +0000326 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000327 return GetPropertyAttributeWithFailedAccessCheck(receiver,
328 &r,
329 name,
330 continue_search);
331 }
332 break;
333 }
334
Andrei Popescu402d9372010-02-26 13:31:12 +0000335 default:
336 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +0000337 }
338 }
339
340 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
341 return ABSENT;
342}
343
344
Steve Blocka7e24c12009-10-30 11:49:00 +0000345Object* JSObject::GetNormalizedProperty(LookupResult* result) {
346 ASSERT(!HasFastProperties());
347 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
348 if (IsGlobalObject()) {
349 value = JSGlobalPropertyCell::cast(value)->value();
350 }
351 ASSERT(!value->IsJSGlobalPropertyCell());
352 return value;
353}
354
355
356Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) {
357 ASSERT(!HasFastProperties());
358 if (IsGlobalObject()) {
359 JSGlobalPropertyCell* cell =
360 JSGlobalPropertyCell::cast(
361 property_dictionary()->ValueAt(result->GetDictionaryEntry()));
362 cell->set_value(value);
363 } else {
364 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
365 }
366 return value;
367}
368
369
John Reck59135872010-11-02 12:39:01 -0700370MaybeObject* JSObject::SetNormalizedProperty(String* name,
371 Object* value,
372 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000373 ASSERT(!HasFastProperties());
374 int entry = property_dictionary()->FindEntry(name);
375 if (entry == StringDictionary::kNotFound) {
376 Object* store_value = value;
377 if (IsGlobalObject()) {
John Reck59135872010-11-02 12:39:01 -0700378 { MaybeObject* maybe_store_value =
379 Heap::AllocateJSGlobalPropertyCell(value);
380 if (!maybe_store_value->ToObject(&store_value)) {
381 return maybe_store_value;
382 }
383 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000384 }
John Reck59135872010-11-02 12:39:01 -0700385 Object* dict;
386 { MaybeObject* maybe_dict =
387 property_dictionary()->Add(name, store_value, details);
388 if (!maybe_dict->ToObject(&dict)) return maybe_dict;
389 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000390 set_properties(StringDictionary::cast(dict));
391 return value;
392 }
393 // Preserve enumeration index.
394 details = PropertyDetails(details.attributes(),
395 details.type(),
396 property_dictionary()->DetailsAt(entry).index());
397 if (IsGlobalObject()) {
398 JSGlobalPropertyCell* cell =
399 JSGlobalPropertyCell::cast(property_dictionary()->ValueAt(entry));
400 cell->set_value(value);
401 // Please note we have to update the property details.
402 property_dictionary()->DetailsAtPut(entry, details);
403 } else {
404 property_dictionary()->SetEntry(entry, name, value, details);
405 }
406 return value;
407}
408
409
John Reck59135872010-11-02 12:39:01 -0700410MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000411 ASSERT(!HasFastProperties());
412 StringDictionary* dictionary = property_dictionary();
413 int entry = dictionary->FindEntry(name);
414 if (entry != StringDictionary::kNotFound) {
415 // If we have a global object set the cell to the hole.
416 if (IsGlobalObject()) {
417 PropertyDetails details = dictionary->DetailsAt(entry);
418 if (details.IsDontDelete()) {
419 if (mode != FORCE_DELETION) return Heap::false_value();
420 // When forced to delete global properties, we have to make a
421 // map change to invalidate any ICs that think they can load
422 // from the DontDelete cell without checking if it contains
423 // the hole value.
John Reck59135872010-11-02 12:39:01 -0700424 Object* new_map;
425 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
426 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
427 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000428 set_map(Map::cast(new_map));
429 }
430 JSGlobalPropertyCell* cell =
431 JSGlobalPropertyCell::cast(dictionary->ValueAt(entry));
432 cell->set_value(Heap::the_hole_value());
433 dictionary->DetailsAtPut(entry, details.AsDeleted());
434 } else {
435 return dictionary->DeleteProperty(entry, mode);
436 }
437 }
438 return Heap::true_value();
439}
440
441
442bool JSObject::IsDirty() {
443 Object* cons_obj = map()->constructor();
444 if (!cons_obj->IsJSFunction())
445 return true;
446 JSFunction* fun = JSFunction::cast(cons_obj);
Steve Block6ded16b2010-05-10 14:33:55 +0100447 if (!fun->shared()->IsApiFunction())
Steve Blocka7e24c12009-10-30 11:49:00 +0000448 return true;
449 // If the object is fully fast case and has the same map it was
450 // created with then no changes can have been made to it.
451 return map() != fun->initial_map()
452 || !HasFastElements()
453 || !HasFastProperties();
454}
455
456
John Reck59135872010-11-02 12:39:01 -0700457MaybeObject* Object::GetProperty(Object* receiver,
458 LookupResult* result,
459 String* name,
460 PropertyAttributes* attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000461 // Make sure that the top context does not change when doing
462 // callbacks or interceptor calls.
463 AssertNoContextChange ncc;
464
465 // Traverse the prototype chain from the current object (this) to
466 // the holder and check for access rights. This avoid traversing the
467 // objects more than once in case of interceptors, because the
468 // holder will always be the interceptor holder and the search may
469 // only continue with a current object just after the interceptor
470 // holder in the prototype chain.
Andrei Popescu402d9372010-02-26 13:31:12 +0000471 Object* last = result->IsProperty() ? result->holder() : Heap::null_value();
Steve Blocka7e24c12009-10-30 11:49:00 +0000472 for (Object* current = this; true; current = current->GetPrototype()) {
473 if (current->IsAccessCheckNeeded()) {
474 // Check if we're allowed to read from the current object. Note
475 // that even though we may not actually end up loading the named
476 // property from the current object, we still check that we have
477 // access to it.
478 JSObject* checked = JSObject::cast(current);
479 if (!Top::MayNamedAccess(checked, name, v8::ACCESS_GET)) {
480 return checked->GetPropertyWithFailedAccessCheck(receiver,
481 result,
482 name,
483 attributes);
484 }
485 }
486 // Stop traversing the chain once we reach the last object in the
487 // chain; either the holder of the result or null in case of an
488 // absent property.
489 if (current == last) break;
490 }
491
492 if (!result->IsProperty()) {
493 *attributes = ABSENT;
494 return Heap::undefined_value();
495 }
496 *attributes = result->GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +0000497 Object* value;
498 JSObject* holder = result->holder();
499 switch (result->type()) {
500 case NORMAL:
501 value = holder->GetNormalizedProperty(result);
502 ASSERT(!value->IsTheHole() || result->IsReadOnly());
503 return value->IsTheHole() ? Heap::undefined_value() : value;
504 case FIELD:
505 value = holder->FastPropertyAt(result->GetFieldIndex());
506 ASSERT(!value->IsTheHole() || result->IsReadOnly());
507 return value->IsTheHole() ? Heap::undefined_value() : value;
508 case CONSTANT_FUNCTION:
509 return result->GetConstantFunction();
510 case CALLBACKS:
511 return GetPropertyWithCallback(receiver,
512 result->GetCallbackObject(),
513 name,
514 holder);
515 case INTERCEPTOR: {
516 JSObject* recvr = JSObject::cast(receiver);
517 return holder->GetPropertyWithInterceptor(recvr, name, attributes);
518 }
519 default:
520 UNREACHABLE();
521 return NULL;
522 }
523}
524
525
John Reck59135872010-11-02 12:39:01 -0700526MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000527 // Non-JS objects do not have integer indexed properties.
528 if (!IsJSObject()) return Heap::undefined_value();
529 return JSObject::cast(this)->GetElementWithReceiver(JSObject::cast(receiver),
530 index);
531}
532
533
534Object* Object::GetPrototype() {
535 // The object is either a number, a string, a boolean, or a real JS object.
536 if (IsJSObject()) return JSObject::cast(this)->map()->prototype();
537 Context* context = Top::context()->global_context();
538
539 if (IsNumber()) return context->number_function()->instance_prototype();
540 if (IsString()) return context->string_function()->instance_prototype();
541 if (IsBoolean()) {
542 return context->boolean_function()->instance_prototype();
543 } else {
544 return Heap::null_value();
545 }
546}
547
548
549void Object::ShortPrint() {
550 HeapStringAllocator allocator;
551 StringStream accumulator(&allocator);
552 ShortPrint(&accumulator);
553 accumulator.OutputToStdOut();
554}
555
556
557void Object::ShortPrint(StringStream* accumulator) {
558 if (IsSmi()) {
559 Smi::cast(this)->SmiPrint(accumulator);
560 } else if (IsFailure()) {
561 Failure::cast(this)->FailurePrint(accumulator);
562 } else {
563 HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
564 }
565}
566
567
568void Smi::SmiPrint() {
569 PrintF("%d", value());
570}
571
572
573void Smi::SmiPrint(StringStream* accumulator) {
574 accumulator->Add("%d", value());
575}
576
577
578void Failure::FailurePrint(StringStream* accumulator) {
Steve Block3ce2e202009-11-05 08:53:23 +0000579 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000580}
581
582
583void Failure::FailurePrint() {
Steve Block3ce2e202009-11-05 08:53:23 +0000584 PrintF("Failure(%p)", reinterpret_cast<void*>(value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000585}
586
587
Steve Blocka7e24c12009-10-30 11:49:00 +0000588// Should a word be prefixed by 'a' or 'an' in order to read naturally in
589// English? Returns false for non-ASCII or words that don't start with
590// a capital letter. The a/an rule follows pronunciation in English.
591// We don't use the BBC's overcorrect "an historic occasion" though if
592// you speak a dialect you may well say "an 'istoric occasion".
593static bool AnWord(String* str) {
594 if (str->length() == 0) return false; // A nothing.
595 int c0 = str->Get(0);
596 int c1 = str->length() > 1 ? str->Get(1) : 0;
597 if (c0 == 'U') {
598 if (c1 > 'Z') {
599 return true; // An Umpire, but a UTF8String, a U.
600 }
601 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
602 return true; // An Ape, an ABCBook.
603 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
604 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
605 c0 == 'S' || c0 == 'X')) {
606 return true; // An MP3File, an M.
607 }
608 return false;
609}
610
611
John Reck59135872010-11-02 12:39:01 -0700612MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000613#ifdef DEBUG
614 // Do not attempt to flatten in debug mode when allocation is not
615 // allowed. This is to avoid an assertion failure when allocating.
616 // Flattening strings is the only case where we always allow
617 // allocation because no GC is performed if the allocation fails.
618 if (!Heap::IsAllocationAllowed()) return this;
619#endif
620
621 switch (StringShape(this).representation_tag()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000622 case kConsStringTag: {
623 ConsString* cs = ConsString::cast(this);
624 if (cs->second()->length() == 0) {
Leon Clarkef7060e22010-06-03 12:02:55 +0100625 return cs->first();
Steve Blocka7e24c12009-10-30 11:49:00 +0000626 }
627 // There's little point in putting the flat string in new space if the
628 // cons string is in old space. It can never get GCed until there is
629 // an old space GC.
Steve Block6ded16b2010-05-10 14:33:55 +0100630 PretenureFlag tenure = Heap::InNewSpace(this) ? pretenure : TENURED;
Steve Blocka7e24c12009-10-30 11:49:00 +0000631 int len = length();
632 Object* object;
633 String* result;
634 if (IsAsciiRepresentation()) {
John Reck59135872010-11-02 12:39:01 -0700635 { MaybeObject* maybe_object = Heap::AllocateRawAsciiString(len, tenure);
636 if (!maybe_object->ToObject(&object)) return maybe_object;
637 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000638 result = String::cast(object);
639 String* first = cs->first();
640 int first_length = first->length();
641 char* dest = SeqAsciiString::cast(result)->GetChars();
642 WriteToFlat(first, dest, 0, first_length);
643 String* second = cs->second();
644 WriteToFlat(second,
645 dest + first_length,
646 0,
647 len - first_length);
648 } else {
John Reck59135872010-11-02 12:39:01 -0700649 { MaybeObject* maybe_object =
650 Heap::AllocateRawTwoByteString(len, tenure);
651 if (!maybe_object->ToObject(&object)) return maybe_object;
652 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000653 result = String::cast(object);
654 uc16* dest = SeqTwoByteString::cast(result)->GetChars();
655 String* first = cs->first();
656 int first_length = first->length();
657 WriteToFlat(first, dest, 0, first_length);
658 String* second = cs->second();
659 WriteToFlat(second,
660 dest + first_length,
661 0,
662 len - first_length);
663 }
664 cs->set_first(result);
665 cs->set_second(Heap::empty_string());
Leon Clarkef7060e22010-06-03 12:02:55 +0100666 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +0000667 }
668 default:
669 return this;
670 }
671}
672
673
674bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
Steve Block8defd9f2010-07-08 12:39:36 +0100675 // Externalizing twice leaks the external resource, so it's
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100676 // prohibited by the API.
677 ASSERT(!this->IsExternalString());
Steve Blocka7e24c12009-10-30 11:49:00 +0000678#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000679 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000680 // Assert that the resource and the string are equivalent.
681 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100682 ScopedVector<uc16> smart_chars(this->length());
683 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
684 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000685 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100686 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000687 }
688#endif // DEBUG
689
690 int size = this->Size(); // Byte size of the original string.
691 if (size < ExternalString::kSize) {
692 // The string is too small to fit an external String in its place. This can
693 // only happen for zero length strings.
694 return false;
695 }
696 ASSERT(size >= ExternalString::kSize);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100697 bool is_ascii = this->IsAsciiRepresentation();
Steve Blocka7e24c12009-10-30 11:49:00 +0000698 bool is_symbol = this->IsSymbol();
699 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000700 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000701
702 // Morph the object to an external string by adjusting the map and
703 // reinitializing the fields.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100704 this->set_map(is_ascii ?
705 Heap::external_string_with_ascii_data_map() :
706 Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000707 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
708 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000709 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000710 self->set_resource(resource);
711 // Additionally make the object into an external symbol if the original string
712 // was a symbol to start with.
713 if (is_symbol) {
714 self->Hash(); // Force regeneration of the hash value.
715 // Now morph this external string into a external symbol.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100716 this->set_map(is_ascii ?
717 Heap::external_symbol_with_ascii_data_map() :
718 Heap::external_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000719 }
720
721 // Fill the remainder of the string with dead wood.
722 int new_size = this->Size(); // Byte size of the external String object.
723 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
724 return true;
725}
726
727
728bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
729#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000730 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000731 // Assert that the resource and the string are equivalent.
732 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100733 ScopedVector<char> smart_chars(this->length());
734 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
735 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000736 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100737 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000738 }
739#endif // DEBUG
740
741 int size = this->Size(); // Byte size of the original string.
742 if (size < ExternalString::kSize) {
743 // The string is too small to fit an external String in its place. This can
744 // only happen for zero length strings.
745 return false;
746 }
747 ASSERT(size >= ExternalString::kSize);
748 bool is_symbol = this->IsSymbol();
749 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000750 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000751
752 // Morph the object to an external string by adjusting the map and
753 // reinitializing the fields.
Steve Blockd0582a62009-12-15 09:54:21 +0000754 this->set_map(Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000755 ExternalAsciiString* self = ExternalAsciiString::cast(this);
756 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000757 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000758 self->set_resource(resource);
759 // Additionally make the object into an external symbol if the original string
760 // was a symbol to start with.
761 if (is_symbol) {
762 self->Hash(); // Force regeneration of the hash value.
763 // Now morph this external string into a external symbol.
Steve Blockd0582a62009-12-15 09:54:21 +0000764 this->set_map(Heap::external_ascii_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000765 }
766
767 // Fill the remainder of the string with dead wood.
768 int new_size = this->Size(); // Byte size of the external String object.
769 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
770 return true;
771}
772
773
774void String::StringShortPrint(StringStream* accumulator) {
775 int len = length();
Steve Blockd0582a62009-12-15 09:54:21 +0000776 if (len > kMaxShortPrintLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000777 accumulator->Add("<Very long string[%u]>", len);
778 return;
779 }
780
781 if (!LooksValid()) {
782 accumulator->Add("<Invalid String>");
783 return;
784 }
785
786 StringInputBuffer buf(this);
787
788 bool truncated = false;
789 if (len > kMaxShortPrintLength) {
790 len = kMaxShortPrintLength;
791 truncated = true;
792 }
793 bool ascii = true;
794 for (int i = 0; i < len; i++) {
795 int c = buf.GetNext();
796
797 if (c < 32 || c >= 127) {
798 ascii = false;
799 }
800 }
801 buf.Reset(this);
802 if (ascii) {
803 accumulator->Add("<String[%u]: ", length());
804 for (int i = 0; i < len; i++) {
805 accumulator->Put(buf.GetNext());
806 }
807 accumulator->Put('>');
808 } else {
809 // Backslash indicates that the string contains control
810 // characters and that backslashes are therefore escaped.
811 accumulator->Add("<String[%u]\\: ", length());
812 for (int i = 0; i < len; i++) {
813 int c = buf.GetNext();
814 if (c == '\n') {
815 accumulator->Add("\\n");
816 } else if (c == '\r') {
817 accumulator->Add("\\r");
818 } else if (c == '\\') {
819 accumulator->Add("\\\\");
820 } else if (c < 32 || c > 126) {
821 accumulator->Add("\\x%02x", c);
822 } else {
823 accumulator->Put(c);
824 }
825 }
826 if (truncated) {
827 accumulator->Put('.');
828 accumulator->Put('.');
829 accumulator->Put('.');
830 }
831 accumulator->Put('>');
832 }
833 return;
834}
835
836
837void JSObject::JSObjectShortPrint(StringStream* accumulator) {
838 switch (map()->instance_type()) {
839 case JS_ARRAY_TYPE: {
840 double length = JSArray::cast(this)->length()->Number();
841 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
842 break;
843 }
844 case JS_REGEXP_TYPE: {
845 accumulator->Add("<JS RegExp>");
846 break;
847 }
848 case JS_FUNCTION_TYPE: {
849 Object* fun_name = JSFunction::cast(this)->shared()->name();
850 bool printed = false;
851 if (fun_name->IsString()) {
852 String* str = String::cast(fun_name);
853 if (str->length() > 0) {
854 accumulator->Add("<JS Function ");
855 accumulator->Put(str);
856 accumulator->Put('>');
857 printed = true;
858 }
859 }
860 if (!printed) {
861 accumulator->Add("<JS Function>");
862 }
863 break;
864 }
865 // All other JSObjects are rather similar to each other (JSObject,
866 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
867 default: {
868 Object* constructor = map()->constructor();
869 bool printed = false;
870 if (constructor->IsHeapObject() &&
871 !Heap::Contains(HeapObject::cast(constructor))) {
872 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
873 } else {
874 bool global_object = IsJSGlobalProxy();
875 if (constructor->IsJSFunction()) {
876 if (!Heap::Contains(JSFunction::cast(constructor)->shared())) {
877 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
878 } else {
879 Object* constructor_name =
880 JSFunction::cast(constructor)->shared()->name();
881 if (constructor_name->IsString()) {
882 String* str = String::cast(constructor_name);
883 if (str->length() > 0) {
884 bool vowel = AnWord(str);
885 accumulator->Add("<%sa%s ",
886 global_object ? "Global Object: " : "",
887 vowel ? "n" : "");
888 accumulator->Put(str);
889 accumulator->Put('>');
890 printed = true;
891 }
892 }
893 }
894 }
895 if (!printed) {
896 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
897 }
898 }
899 if (IsJSValue()) {
900 accumulator->Add(" value = ");
901 JSValue::cast(this)->value()->ShortPrint(accumulator);
902 }
903 accumulator->Put('>');
904 break;
905 }
906 }
907}
908
909
910void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
911 // if (!Heap::InNewSpace(this)) PrintF("*", this);
912 if (!Heap::Contains(this)) {
913 accumulator->Add("!!!INVALID POINTER!!!");
914 return;
915 }
916 if (!Heap::Contains(map())) {
917 accumulator->Add("!!!INVALID MAP!!!");
918 return;
919 }
920
921 accumulator->Add("%p ", this);
922
923 if (IsString()) {
924 String::cast(this)->StringShortPrint(accumulator);
925 return;
926 }
927 if (IsJSObject()) {
928 JSObject::cast(this)->JSObjectShortPrint(accumulator);
929 return;
930 }
931 switch (map()->instance_type()) {
932 case MAP_TYPE:
933 accumulator->Add("<Map>");
934 break;
935 case FIXED_ARRAY_TYPE:
936 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
937 break;
938 case BYTE_ARRAY_TYPE:
939 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
940 break;
941 case PIXEL_ARRAY_TYPE:
942 accumulator->Add("<PixelArray[%u]>", PixelArray::cast(this)->length());
943 break;
Steve Block3ce2e202009-11-05 08:53:23 +0000944 case EXTERNAL_BYTE_ARRAY_TYPE:
945 accumulator->Add("<ExternalByteArray[%u]>",
946 ExternalByteArray::cast(this)->length());
947 break;
948 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
949 accumulator->Add("<ExternalUnsignedByteArray[%u]>",
950 ExternalUnsignedByteArray::cast(this)->length());
951 break;
952 case EXTERNAL_SHORT_ARRAY_TYPE:
953 accumulator->Add("<ExternalShortArray[%u]>",
954 ExternalShortArray::cast(this)->length());
955 break;
956 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
957 accumulator->Add("<ExternalUnsignedShortArray[%u]>",
958 ExternalUnsignedShortArray::cast(this)->length());
959 break;
960 case EXTERNAL_INT_ARRAY_TYPE:
961 accumulator->Add("<ExternalIntArray[%u]>",
962 ExternalIntArray::cast(this)->length());
963 break;
964 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
965 accumulator->Add("<ExternalUnsignedIntArray[%u]>",
966 ExternalUnsignedIntArray::cast(this)->length());
967 break;
968 case EXTERNAL_FLOAT_ARRAY_TYPE:
969 accumulator->Add("<ExternalFloatArray[%u]>",
970 ExternalFloatArray::cast(this)->length());
971 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000972 case SHARED_FUNCTION_INFO_TYPE:
973 accumulator->Add("<SharedFunctionInfo>");
974 break;
975#define MAKE_STRUCT_CASE(NAME, Name, name) \
976 case NAME##_TYPE: \
977 accumulator->Put('<'); \
978 accumulator->Add(#Name); \
979 accumulator->Put('>'); \
980 break;
981 STRUCT_LIST(MAKE_STRUCT_CASE)
982#undef MAKE_STRUCT_CASE
983 case CODE_TYPE:
984 accumulator->Add("<Code>");
985 break;
986 case ODDBALL_TYPE: {
987 if (IsUndefined())
988 accumulator->Add("<undefined>");
989 else if (IsTheHole())
990 accumulator->Add("<the hole>");
991 else if (IsNull())
992 accumulator->Add("<null>");
993 else if (IsTrue())
994 accumulator->Add("<true>");
995 else if (IsFalse())
996 accumulator->Add("<false>");
997 else
998 accumulator->Add("<Odd Oddball>");
999 break;
1000 }
1001 case HEAP_NUMBER_TYPE:
1002 accumulator->Add("<Number: ");
1003 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
1004 accumulator->Put('>');
1005 break;
1006 case PROXY_TYPE:
1007 accumulator->Add("<Proxy>");
1008 break;
1009 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1010 accumulator->Add("Cell for ");
1011 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator);
1012 break;
1013 default:
1014 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
1015 break;
1016 }
1017}
1018
1019
Steve Blocka7e24c12009-10-30 11:49:00 +00001020void HeapObject::Iterate(ObjectVisitor* v) {
1021 // Handle header
1022 IteratePointer(v, kMapOffset);
1023 // Handle object body
1024 Map* m = map();
1025 IterateBody(m->instance_type(), SizeFromMap(m), v);
1026}
1027
1028
1029void HeapObject::IterateBody(InstanceType type, int object_size,
1030 ObjectVisitor* v) {
1031 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
1032 // During GC, the map pointer field is encoded.
1033 if (type < FIRST_NONSTRING_TYPE) {
1034 switch (type & kStringRepresentationMask) {
1035 case kSeqStringTag:
1036 break;
1037 case kConsStringTag:
Iain Merrick75681382010-08-19 15:07:18 +01001038 ConsString::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001039 break;
Steve Blockd0582a62009-12-15 09:54:21 +00001040 case kExternalStringTag:
1041 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1042 reinterpret_cast<ExternalAsciiString*>(this)->
1043 ExternalAsciiStringIterateBody(v);
1044 } else {
1045 reinterpret_cast<ExternalTwoByteString*>(this)->
1046 ExternalTwoByteStringIterateBody(v);
1047 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001048 break;
1049 }
1050 return;
1051 }
1052
1053 switch (type) {
1054 case FIXED_ARRAY_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001055 FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001056 break;
1057 case JS_OBJECT_TYPE:
1058 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
1059 case JS_VALUE_TYPE:
1060 case JS_ARRAY_TYPE:
1061 case JS_REGEXP_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001062 case JS_GLOBAL_PROXY_TYPE:
1063 case JS_GLOBAL_OBJECT_TYPE:
1064 case JS_BUILTINS_OBJECT_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001065 JSObject::BodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001066 break;
Steve Block791712a2010-08-27 10:21:07 +01001067 case JS_FUNCTION_TYPE:
1068 reinterpret_cast<JSFunction*>(this)
1069 ->JSFunctionIterateBody(object_size, v);
1070 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001071 case ODDBALL_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001072 Oddball::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001073 break;
1074 case PROXY_TYPE:
1075 reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
1076 break;
1077 case MAP_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001078 Map::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001079 break;
1080 case CODE_TYPE:
1081 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1082 break;
1083 case JS_GLOBAL_PROPERTY_CELL_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001084 JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001085 break;
1086 case HEAP_NUMBER_TYPE:
1087 case FILLER_TYPE:
1088 case BYTE_ARRAY_TYPE:
1089 case PIXEL_ARRAY_TYPE:
Steve Block3ce2e202009-11-05 08:53:23 +00001090 case EXTERNAL_BYTE_ARRAY_TYPE:
1091 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1092 case EXTERNAL_SHORT_ARRAY_TYPE:
1093 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1094 case EXTERNAL_INT_ARRAY_TYPE:
1095 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1096 case EXTERNAL_FLOAT_ARRAY_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001097 break;
Iain Merrick75681382010-08-19 15:07:18 +01001098 case SHARED_FUNCTION_INFO_TYPE:
1099 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001100 break;
Iain Merrick75681382010-08-19 15:07:18 +01001101
Steve Blocka7e24c12009-10-30 11:49:00 +00001102#define MAKE_STRUCT_CASE(NAME, Name, name) \
1103 case NAME##_TYPE:
1104 STRUCT_LIST(MAKE_STRUCT_CASE)
1105#undef MAKE_STRUCT_CASE
Iain Merrick75681382010-08-19 15:07:18 +01001106 StructBodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001107 break;
1108 default:
1109 PrintF("Unknown type: %d\n", type);
1110 UNREACHABLE();
1111 }
1112}
1113
1114
Steve Blocka7e24c12009-10-30 11:49:00 +00001115Object* HeapNumber::HeapNumberToBoolean() {
1116 // NaN, +0, and -0 should return the false object
Iain Merrick75681382010-08-19 15:07:18 +01001117#if __BYTE_ORDER == __LITTLE_ENDIAN
1118 union IeeeDoubleLittleEndianArchType u;
1119#elif __BYTE_ORDER == __BIG_ENDIAN
1120 union IeeeDoubleBigEndianArchType u;
1121#endif
1122 u.d = value();
1123 if (u.bits.exp == 2047) {
1124 // Detect NaN for IEEE double precision floating point.
1125 if ((u.bits.man_low | u.bits.man_high) != 0)
1126 return Heap::false_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001127 }
Iain Merrick75681382010-08-19 15:07:18 +01001128 if (u.bits.exp == 0) {
1129 // Detect +0, and -0 for IEEE double precision floating point.
1130 if ((u.bits.man_low | u.bits.man_high) == 0)
1131 return Heap::false_value();
1132 }
1133 return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001134}
1135
1136
1137void HeapNumber::HeapNumberPrint() {
1138 PrintF("%.16g", Number());
1139}
1140
1141
1142void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1143 // The Windows version of vsnprintf can allocate when printing a %g string
1144 // into a buffer that may not be big enough. We don't want random memory
1145 // allocation when producing post-crash stack traces, so we print into a
1146 // buffer that is plenty big enough for any floating point number, then
1147 // print that using vsnprintf (which may truncate but never allocate if
1148 // there is no more space in the buffer).
1149 EmbeddedVector<char, 100> buffer;
1150 OS::SNPrintF(buffer, "%.16g", Number());
1151 accumulator->Add("%s", buffer.start());
1152}
1153
1154
1155String* JSObject::class_name() {
1156 if (IsJSFunction()) {
1157 return Heap::function_class_symbol();
1158 }
1159 if (map()->constructor()->IsJSFunction()) {
1160 JSFunction* constructor = JSFunction::cast(map()->constructor());
1161 return String::cast(constructor->shared()->instance_class_name());
1162 }
1163 // If the constructor is not present, return "Object".
1164 return Heap::Object_symbol();
1165}
1166
1167
1168String* JSObject::constructor_name() {
Steve Blocka7e24c12009-10-30 11:49:00 +00001169 if (map()->constructor()->IsJSFunction()) {
1170 JSFunction* constructor = JSFunction::cast(map()->constructor());
1171 String* name = String::cast(constructor->shared()->name());
Ben Murdochf87a2032010-10-22 12:50:53 +01001172 if (name->length() > 0) return name;
1173 String* inferred_name = constructor->shared()->inferred_name();
1174 if (inferred_name->length() > 0) return inferred_name;
1175 Object* proto = GetPrototype();
1176 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
Steve Blocka7e24c12009-10-30 11:49:00 +00001177 }
1178 // If the constructor is not present, return "Object".
1179 return Heap::Object_symbol();
1180}
1181
1182
John Reck59135872010-11-02 12:39:01 -07001183MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
1184 String* name,
1185 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001186 int index = new_map->PropertyIndexFor(name);
1187 if (map()->unused_property_fields() == 0) {
1188 ASSERT(map()->unused_property_fields() == 0);
1189 int new_unused = new_map->unused_property_fields();
John Reck59135872010-11-02 12:39:01 -07001190 Object* values;
1191 { MaybeObject* maybe_values =
1192 properties()->CopySize(properties()->length() + new_unused + 1);
1193 if (!maybe_values->ToObject(&values)) return maybe_values;
1194 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001195 set_properties(FixedArray::cast(values));
1196 }
1197 set_map(new_map);
1198 return FastPropertyAtPut(index, value);
1199}
1200
1201
John Reck59135872010-11-02 12:39:01 -07001202MaybeObject* JSObject::AddFastProperty(String* name,
1203 Object* value,
1204 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001205 // Normalize the object if the name is an actual string (not the
1206 // hidden symbols) and is not a real identifier.
1207 StringInputBuffer buffer(name);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001208 if (!ScannerConstants::IsIdentifier(&buffer)
1209 && name != Heap::hidden_symbol()) {
John Reck59135872010-11-02 12:39:01 -07001210 Object* obj;
1211 { MaybeObject* maybe_obj =
1212 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1213 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1214 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001215 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);
John Reck59135872010-11-02 12:39:01 -07001224 Object* new_descriptors;
1225 { MaybeObject* maybe_new_descriptors =
1226 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1227 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1228 return maybe_new_descriptors;
1229 }
1230 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001231
1232 // Only allow map transition if the object's map is NOT equal to the
1233 // global object_function's map and there is not a transition for name.
1234 bool allow_map_transition =
1235 !old_descriptors->Contains(name) &&
1236 (Top::context()->global_context()->object_function()->map() != map());
1237
1238 ASSERT(index < map()->inobject_properties() ||
1239 (index - map()->inobject_properties()) < properties()->length() ||
1240 map()->unused_property_fields() == 0);
1241 // Allocate a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001242 Object* r;
1243 { MaybeObject* maybe_r = map()->CopyDropDescriptors();
1244 if (!maybe_r->ToObject(&r)) return maybe_r;
1245 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001246 Map* new_map = Map::cast(r);
1247 if (allow_map_transition) {
1248 // Allocate new instance descriptors for the old map with map transition.
1249 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
John Reck59135872010-11-02 12:39:01 -07001250 Object* r;
1251 { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1252 if (!maybe_r->ToObject(&r)) return maybe_r;
1253 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001254 old_descriptors = DescriptorArray::cast(r);
1255 }
1256
1257 if (map()->unused_property_fields() == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01001258 if (properties()->length() > MaxFastProperties()) {
John Reck59135872010-11-02 12:39:01 -07001259 Object* obj;
1260 { MaybeObject* maybe_obj =
1261 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1262 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1263 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001264 return AddSlowProperty(name, value, attributes);
1265 }
1266 // Make room for the new value
John Reck59135872010-11-02 12:39:01 -07001267 Object* values;
1268 { MaybeObject* maybe_values =
1269 properties()->CopySize(properties()->length() + kFieldsAdded);
1270 if (!maybe_values->ToObject(&values)) return maybe_values;
1271 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001272 set_properties(FixedArray::cast(values));
1273 new_map->set_unused_property_fields(kFieldsAdded - 1);
1274 } else {
1275 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
1276 }
1277 // We have now allocated all the necessary objects.
1278 // All the changes can be applied at once, so they are atomic.
1279 map()->set_instance_descriptors(old_descriptors);
1280 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1281 set_map(new_map);
1282 return FastPropertyAtPut(index, value);
1283}
1284
1285
John Reck59135872010-11-02 12:39:01 -07001286MaybeObject* JSObject::AddConstantFunctionProperty(
1287 String* name,
1288 JSFunction* function,
1289 PropertyAttributes attributes) {
Leon Clarkee46be812010-01-19 14:06:41 +00001290 ASSERT(!Heap::InNewSpace(function));
1291
Steve Blocka7e24c12009-10-30 11:49:00 +00001292 // Allocate new instance descriptors with (name, function) added
1293 ConstantFunctionDescriptor d(name, function, attributes);
John Reck59135872010-11-02 12:39:01 -07001294 Object* new_descriptors;
1295 { MaybeObject* maybe_new_descriptors =
1296 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1297 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1298 return maybe_new_descriptors;
1299 }
1300 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001301
1302 // Allocate a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001303 Object* new_map;
1304 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
1305 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
1306 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001307
1308 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1309 Map::cast(new_map)->set_instance_descriptors(descriptors);
1310 Map* old_map = map();
1311 set_map(Map::cast(new_map));
1312
1313 // If the old map is the global object map (from new Object()),
1314 // then transitions are not added to it, so we are done.
1315 if (old_map == Top::context()->global_context()->object_function()->map()) {
1316 return function;
1317 }
1318
1319 // Do not add CONSTANT_TRANSITIONS to global objects
1320 if (IsGlobalObject()) {
1321 return function;
1322 }
1323
1324 // Add a CONSTANT_TRANSITION descriptor to the old map,
1325 // so future assignments to this property on other objects
1326 // of the same type will create a normal field, not a constant function.
1327 // Don't do this for special properties, with non-trival attributes.
1328 if (attributes != NONE) {
1329 return function;
1330 }
Iain Merrick75681382010-08-19 15:07:18 +01001331 ConstTransitionDescriptor mark(name, Map::cast(new_map));
John Reck59135872010-11-02 12:39:01 -07001332 { MaybeObject* maybe_new_descriptors =
1333 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1334 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1335 // We have accomplished the main goal, so return success.
1336 return function;
1337 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001338 }
1339 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1340
1341 return function;
1342}
1343
1344
1345// Add property in slow mode
John Reck59135872010-11-02 12:39:01 -07001346MaybeObject* JSObject::AddSlowProperty(String* name,
1347 Object* value,
1348 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001349 ASSERT(!HasFastProperties());
1350 StringDictionary* dict = property_dictionary();
1351 Object* store_value = value;
1352 if (IsGlobalObject()) {
1353 // In case name is an orphaned property reuse the cell.
1354 int entry = dict->FindEntry(name);
1355 if (entry != StringDictionary::kNotFound) {
1356 store_value = dict->ValueAt(entry);
1357 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1358 // Assign an enumeration index to the property and update
1359 // SetNextEnumerationIndex.
1360 int index = dict->NextEnumerationIndex();
1361 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1362 dict->SetNextEnumerationIndex(index + 1);
1363 dict->SetEntry(entry, name, store_value, details);
1364 return value;
1365 }
John Reck59135872010-11-02 12:39:01 -07001366 { MaybeObject* maybe_store_value =
1367 Heap::AllocateJSGlobalPropertyCell(value);
1368 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
1369 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001370 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1371 }
1372 PropertyDetails details = PropertyDetails(attributes, NORMAL);
John Reck59135872010-11-02 12:39:01 -07001373 Object* result;
1374 { MaybeObject* maybe_result = dict->Add(name, store_value, details);
1375 if (!maybe_result->ToObject(&result)) return maybe_result;
1376 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001377 if (dict != result) set_properties(StringDictionary::cast(result));
1378 return value;
1379}
1380
1381
John Reck59135872010-11-02 12:39:01 -07001382MaybeObject* JSObject::AddProperty(String* name,
1383 Object* value,
1384 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001385 ASSERT(!IsJSGlobalProxy());
Steve Block8defd9f2010-07-08 12:39:36 +01001386 if (!map()->is_extensible()) {
1387 Handle<Object> args[1] = {Handle<String>(name)};
1388 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
1389 HandleVector(args, 1)));
1390 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001391 if (HasFastProperties()) {
1392 // Ensure the descriptor array does not get too big.
1393 if (map()->instance_descriptors()->number_of_descriptors() <
1394 DescriptorArray::kMaxNumberOfDescriptors) {
Leon Clarkee46be812010-01-19 14:06:41 +00001395 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001396 return AddConstantFunctionProperty(name,
1397 JSFunction::cast(value),
1398 attributes);
1399 } else {
1400 return AddFastProperty(name, value, attributes);
1401 }
1402 } else {
1403 // Normalize the object to prevent very large instance descriptors.
1404 // This eliminates unwanted N^2 allocation and lookup behavior.
John Reck59135872010-11-02 12:39:01 -07001405 Object* obj;
1406 { MaybeObject* maybe_obj =
1407 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1408 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1409 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001410 }
1411 }
1412 return AddSlowProperty(name, value, attributes);
1413}
1414
1415
John Reck59135872010-11-02 12:39:01 -07001416MaybeObject* JSObject::SetPropertyPostInterceptor(
1417 String* name,
1418 Object* value,
1419 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001420 // Check local property, ignore interceptor.
1421 LookupResult result;
1422 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001423 if (result.IsFound()) {
1424 // An existing property, a map transition or a null descriptor was
1425 // found. Use set property to handle all these cases.
1426 return SetProperty(&result, name, value, attributes);
1427 }
1428 // Add a new real property.
Steve Blocka7e24c12009-10-30 11:49:00 +00001429 return AddProperty(name, value, attributes);
1430}
1431
1432
John Reck59135872010-11-02 12:39:01 -07001433MaybeObject* JSObject::ReplaceSlowProperty(String* name,
1434 Object* value,
1435 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001436 StringDictionary* dictionary = property_dictionary();
1437 int old_index = dictionary->FindEntry(name);
1438 int new_enumeration_index = 0; // 0 means "Use the next available index."
1439 if (old_index != -1) {
1440 // All calls to ReplaceSlowProperty have had all transitions removed.
1441 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1442 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1443 }
1444
1445 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
1446 return SetNormalizedProperty(name, value, new_details);
1447}
1448
Steve Blockd0582a62009-12-15 09:54:21 +00001449
John Reck59135872010-11-02 12:39:01 -07001450MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
Steve Blocka7e24c12009-10-30 11:49:00 +00001451 String* name,
1452 Object* new_value,
1453 PropertyAttributes attributes) {
1454 Map* old_map = map();
John Reck59135872010-11-02 12:39:01 -07001455 Object* result;
1456 { MaybeObject* maybe_result =
1457 ConvertDescriptorToField(name, new_value, attributes);
1458 if (!maybe_result->ToObject(&result)) return maybe_result;
1459 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001460 // If we get to this point we have succeeded - do not return failure
1461 // after this point. Later stuff is optional.
1462 if (!HasFastProperties()) {
1463 return result;
1464 }
1465 // Do not add transitions to the map of "new Object()".
1466 if (map() == Top::context()->global_context()->object_function()->map()) {
1467 return result;
1468 }
1469
1470 MapTransitionDescriptor transition(name,
1471 map(),
1472 attributes);
John Reck59135872010-11-02 12:39:01 -07001473 Object* new_descriptors;
1474 { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
1475 CopyInsert(&transition, KEEP_TRANSITIONS);
1476 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1477 return result; // Yes, return _result_.
1478 }
1479 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001480 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1481 return result;
1482}
1483
1484
John Reck59135872010-11-02 12:39:01 -07001485MaybeObject* JSObject::ConvertDescriptorToField(String* name,
1486 Object* new_value,
1487 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001488 if (map()->unused_property_fields() == 0 &&
Steve Block8defd9f2010-07-08 12:39:36 +01001489 properties()->length() > MaxFastProperties()) {
John Reck59135872010-11-02 12:39:01 -07001490 Object* obj;
1491 { MaybeObject* maybe_obj =
1492 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1493 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1494 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001495 return ReplaceSlowProperty(name, new_value, attributes);
1496 }
1497
1498 int index = map()->NextFreePropertyIndex();
1499 FieldDescriptor new_field(name, index, attributes);
1500 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
John Reck59135872010-11-02 12:39:01 -07001501 Object* descriptors_unchecked;
1502 { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
1503 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1504 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
1505 return maybe_descriptors_unchecked;
1506 }
1507 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001508 DescriptorArray* new_descriptors =
1509 DescriptorArray::cast(descriptors_unchecked);
1510
1511 // Make a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001512 Object* new_map_unchecked;
1513 { MaybeObject* maybe_new_map_unchecked = map()->CopyDropDescriptors();
1514 if (!maybe_new_map_unchecked->ToObject(&new_map_unchecked)) {
1515 return maybe_new_map_unchecked;
1516 }
1517 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001518 Map* new_map = Map::cast(new_map_unchecked);
1519 new_map->set_instance_descriptors(new_descriptors);
1520
1521 // Make new properties array if necessary.
1522 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1523 int new_unused_property_fields = map()->unused_property_fields() - 1;
1524 if (map()->unused_property_fields() == 0) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001525 new_unused_property_fields = kFieldsAdded - 1;
John Reck59135872010-11-02 12:39:01 -07001526 Object* new_properties_object;
1527 { MaybeObject* maybe_new_properties_object =
1528 properties()->CopySize(properties()->length() + kFieldsAdded);
1529 if (!maybe_new_properties_object->ToObject(&new_properties_object)) {
1530 return maybe_new_properties_object;
1531 }
1532 }
1533 new_properties = FixedArray::cast(new_properties_object);
Steve Blocka7e24c12009-10-30 11:49:00 +00001534 }
1535
1536 // Update pointers to commit changes.
1537 // Object points to the new map.
1538 new_map->set_unused_property_fields(new_unused_property_fields);
1539 set_map(new_map);
1540 if (new_properties) {
1541 set_properties(FixedArray::cast(new_properties));
1542 }
1543 return FastPropertyAtPut(index, new_value);
1544}
1545
1546
1547
John Reck59135872010-11-02 12:39:01 -07001548MaybeObject* JSObject::SetPropertyWithInterceptor(
1549 String* name,
1550 Object* value,
1551 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001552 HandleScope scope;
1553 Handle<JSObject> this_handle(this);
1554 Handle<String> name_handle(name);
1555 Handle<Object> value_handle(value);
1556 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1557 if (!interceptor->setter()->IsUndefined()) {
1558 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name));
1559 CustomArguments args(interceptor->data(), this, this);
1560 v8::AccessorInfo info(args.end());
1561 v8::NamedPropertySetter setter =
1562 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1563 v8::Handle<v8::Value> result;
1564 {
1565 // Leaving JavaScript.
1566 VMState state(EXTERNAL);
1567 Handle<Object> value_unhole(value->IsTheHole() ?
1568 Heap::undefined_value() :
1569 value);
1570 result = setter(v8::Utils::ToLocal(name_handle),
1571 v8::Utils::ToLocal(value_unhole),
1572 info);
1573 }
1574 RETURN_IF_SCHEDULED_EXCEPTION();
1575 if (!result.IsEmpty()) return *value_handle;
1576 }
John Reck59135872010-11-02 12:39:01 -07001577 MaybeObject* raw_result =
1578 this_handle->SetPropertyPostInterceptor(*name_handle,
1579 *value_handle,
1580 attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00001581 RETURN_IF_SCHEDULED_EXCEPTION();
1582 return raw_result;
1583}
1584
1585
John Reck59135872010-11-02 12:39:01 -07001586MaybeObject* JSObject::SetProperty(String* name,
1587 Object* value,
1588 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001589 LookupResult result;
1590 LocalLookup(name, &result);
1591 return SetProperty(&result, name, value, attributes);
1592}
1593
1594
John Reck59135872010-11-02 12:39:01 -07001595MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
1596 String* name,
1597 Object* value,
1598 JSObject* holder) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001599 HandleScope scope;
1600
1601 // We should never get here to initialize a const with the hole
1602 // value since a const declaration would conflict with the setter.
1603 ASSERT(!value->IsTheHole());
1604 Handle<Object> value_handle(value);
1605
1606 // To accommodate both the old and the new api we switch on the
1607 // data structure used to store the callbacks. Eventually proxy
1608 // callbacks should be phased out.
1609 if (structure->IsProxy()) {
1610 AccessorDescriptor* callback =
1611 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
John Reck59135872010-11-02 12:39:01 -07001612 MaybeObject* obj = (callback->setter)(this, value, callback->data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001613 RETURN_IF_SCHEDULED_EXCEPTION();
1614 if (obj->IsFailure()) return obj;
1615 return *value_handle;
1616 }
1617
1618 if (structure->IsAccessorInfo()) {
1619 // api style callbacks
1620 AccessorInfo* data = AccessorInfo::cast(structure);
1621 Object* call_obj = data->setter();
1622 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1623 if (call_fun == NULL) return value;
1624 Handle<String> key(name);
1625 LOG(ApiNamedPropertyAccess("store", this, name));
1626 CustomArguments args(data->data(), this, JSObject::cast(holder));
1627 v8::AccessorInfo info(args.end());
1628 {
1629 // Leaving JavaScript.
1630 VMState state(EXTERNAL);
1631 call_fun(v8::Utils::ToLocal(key),
1632 v8::Utils::ToLocal(value_handle),
1633 info);
1634 }
1635 RETURN_IF_SCHEDULED_EXCEPTION();
1636 return *value_handle;
1637 }
1638
1639 if (structure->IsFixedArray()) {
1640 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1641 if (setter->IsJSFunction()) {
1642 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
1643 } else {
1644 Handle<String> key(name);
1645 Handle<Object> holder_handle(holder);
1646 Handle<Object> args[2] = { key, holder_handle };
1647 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
1648 HandleVector(args, 2)));
1649 }
1650 }
1651
1652 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +01001653 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001654}
1655
1656
John Reck59135872010-11-02 12:39:01 -07001657MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1658 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001659 Handle<Object> value_handle(value);
1660 Handle<JSFunction> fun(JSFunction::cast(setter));
1661 Handle<JSObject> self(this);
1662#ifdef ENABLE_DEBUGGER_SUPPORT
1663 // Handle stepping into a setter if step into is active.
1664 if (Debug::StepInActive()) {
1665 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
1666 }
1667#endif
1668 bool has_pending_exception;
1669 Object** argv[] = { value_handle.location() };
1670 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1671 // Check for pending exception and return the result.
1672 if (has_pending_exception) return Failure::Exception();
1673 return *value_handle;
1674}
1675
1676
1677void JSObject::LookupCallbackSetterInPrototypes(String* name,
1678 LookupResult* result) {
1679 for (Object* pt = GetPrototype();
1680 pt != Heap::null_value();
1681 pt = pt->GetPrototype()) {
1682 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001683 if (result->IsProperty()) {
1684 if (result->IsReadOnly()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001685 result->NotFound();
1686 return;
1687 }
1688 if (result->type() == CALLBACKS) {
1689 return;
1690 }
1691 }
1692 }
1693 result->NotFound();
1694}
1695
1696
Leon Clarkef7060e22010-06-03 12:02:55 +01001697bool JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
1698 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001699 for (Object* pt = GetPrototype();
1700 pt != Heap::null_value();
1701 pt = pt->GetPrototype()) {
1702 if (!JSObject::cast(pt)->HasDictionaryElements()) {
1703 continue;
1704 }
1705 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1706 int entry = dictionary->FindEntry(index);
1707 if (entry != NumberDictionary::kNotFound) {
1708 Object* element = dictionary->ValueAt(entry);
1709 PropertyDetails details = dictionary->DetailsAt(entry);
1710 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01001711 SetElementWithCallback(element, index, value, JSObject::cast(pt));
1712 return true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001713 }
1714 }
1715 }
Leon Clarkef7060e22010-06-03 12:02:55 +01001716 return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001717}
1718
1719
1720void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1721 DescriptorArray* descriptors = map()->instance_descriptors();
Iain Merrick75681382010-08-19 15:07:18 +01001722 int number = descriptors->SearchWithCache(name);
Steve Blocka7e24c12009-10-30 11:49:00 +00001723 if (number != DescriptorArray::kNotFound) {
1724 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1725 } else {
1726 result->NotFound();
1727 }
1728}
1729
1730
1731void JSObject::LocalLookupRealNamedProperty(String* name,
1732 LookupResult* result) {
1733 if (IsJSGlobalProxy()) {
1734 Object* proto = GetPrototype();
1735 if (proto->IsNull()) return result->NotFound();
1736 ASSERT(proto->IsJSGlobalObject());
1737 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
1738 }
1739
1740 if (HasFastProperties()) {
1741 LookupInDescriptor(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001742 if (result->IsFound()) {
1743 // A property, a map transition or a null descriptor was found.
1744 // We return all of these result types because
1745 // LocalLookupRealNamedProperty is used when setting properties
1746 // where map transitions and null descriptors are handled.
Steve Blocka7e24c12009-10-30 11:49:00 +00001747 ASSERT(result->holder() == this && result->type() != NORMAL);
1748 // Disallow caching for uninitialized constants. These can only
1749 // occur as fields.
1750 if (result->IsReadOnly() && result->type() == FIELD &&
1751 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
1752 result->DisallowCaching();
1753 }
1754 return;
1755 }
1756 } else {
1757 int entry = property_dictionary()->FindEntry(name);
1758 if (entry != StringDictionary::kNotFound) {
1759 Object* value = property_dictionary()->ValueAt(entry);
1760 if (IsGlobalObject()) {
1761 PropertyDetails d = property_dictionary()->DetailsAt(entry);
1762 if (d.IsDeleted()) {
1763 result->NotFound();
1764 return;
1765 }
1766 value = JSGlobalPropertyCell::cast(value)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001767 }
1768 // Make sure to disallow caching for uninitialized constants
1769 // found in the dictionary-mode objects.
1770 if (value->IsTheHole()) result->DisallowCaching();
1771 result->DictionaryResult(this, entry);
1772 return;
1773 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001774 }
1775 result->NotFound();
1776}
1777
1778
1779void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
1780 LocalLookupRealNamedProperty(name, result);
1781 if (result->IsProperty()) return;
1782
1783 LookupRealNamedPropertyInPrototypes(name, result);
1784}
1785
1786
1787void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
1788 LookupResult* result) {
1789 for (Object* pt = GetPrototype();
1790 pt != Heap::null_value();
1791 pt = JSObject::cast(pt)->GetPrototype()) {
1792 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001793 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001794 }
1795 result->NotFound();
1796}
1797
1798
1799// We only need to deal with CALLBACKS and INTERCEPTORS
John Reck59135872010-11-02 12:39:01 -07001800MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
1801 String* name,
1802 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001803 if (!result->IsProperty()) {
1804 LookupCallbackSetterInPrototypes(name, result);
1805 }
1806
1807 if (result->IsProperty()) {
1808 if (!result->IsReadOnly()) {
1809 switch (result->type()) {
1810 case CALLBACKS: {
1811 Object* obj = result->GetCallbackObject();
1812 if (obj->IsAccessorInfo()) {
1813 AccessorInfo* info = AccessorInfo::cast(obj);
1814 if (info->all_can_write()) {
1815 return SetPropertyWithCallback(result->GetCallbackObject(),
1816 name,
1817 value,
1818 result->holder());
1819 }
1820 }
1821 break;
1822 }
1823 case INTERCEPTOR: {
1824 // Try lookup real named properties. Note that only property can be
1825 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
1826 LookupResult r;
1827 LookupRealNamedProperty(name, &r);
1828 if (r.IsProperty()) {
1829 return SetPropertyWithFailedAccessCheck(&r, name, value);
1830 }
1831 break;
1832 }
1833 default: {
1834 break;
1835 }
1836 }
1837 }
1838 }
1839
Iain Merrick75681382010-08-19 15:07:18 +01001840 HandleScope scope;
1841 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001842 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01001843 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00001844}
1845
1846
John Reck59135872010-11-02 12:39:01 -07001847MaybeObject* JSObject::SetProperty(LookupResult* result,
1848 String* name,
1849 Object* value,
1850 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001851 // Make sure that the top context does not change when doing callbacks or
1852 // interceptor calls.
1853 AssertNoContextChange ncc;
1854
Steve Blockd0582a62009-12-15 09:54:21 +00001855 // Optimization for 2-byte strings often used as keys in a decompression
1856 // dictionary. We make these short keys into symbols to avoid constantly
1857 // reallocating them.
1858 if (!name->IsSymbol() && name->length() <= 2) {
John Reck59135872010-11-02 12:39:01 -07001859 Object* symbol_version;
1860 { MaybeObject* maybe_symbol_version = Heap::LookupSymbol(name);
1861 if (maybe_symbol_version->ToObject(&symbol_version)) {
1862 name = String::cast(symbol_version);
1863 }
1864 }
Steve Blockd0582a62009-12-15 09:54:21 +00001865 }
1866
Steve Blocka7e24c12009-10-30 11:49:00 +00001867 // Check access rights if needed.
1868 if (IsAccessCheckNeeded()
1869 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1870 return SetPropertyWithFailedAccessCheck(result, name, value);
1871 }
1872
1873 if (IsJSGlobalProxy()) {
1874 Object* proto = GetPrototype();
1875 if (proto->IsNull()) return value;
1876 ASSERT(proto->IsJSGlobalObject());
1877 return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
1878 }
1879
1880 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
1881 // We could not find a local property so let's check whether there is an
1882 // accessor that wants to handle the property.
1883 LookupResult accessor_result;
1884 LookupCallbackSetterInPrototypes(name, &accessor_result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001885 if (accessor_result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001886 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
1887 name,
1888 value,
1889 accessor_result.holder());
1890 }
1891 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001892 if (!result->IsFound()) {
1893 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001894 return AddProperty(name, value, attributes);
1895 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001896 if (result->IsReadOnly() && result->IsProperty()) return value;
1897 // This is a real property that is not read-only, or it is a
1898 // transition or null descriptor and there are no setters in the prototypes.
1899 switch (result->type()) {
1900 case NORMAL:
1901 return SetNormalizedProperty(result, value);
1902 case FIELD:
1903 return FastPropertyAtPut(result->GetFieldIndex(), value);
1904 case MAP_TRANSITION:
1905 if (attributes == result->GetAttributes()) {
1906 // Only use map transition if the attributes match.
1907 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1908 name,
1909 value);
1910 }
1911 return ConvertDescriptorToField(name, value, attributes);
1912 case CONSTANT_FUNCTION:
1913 // Only replace the function if necessary.
1914 if (value == result->GetConstantFunction()) return value;
1915 // Preserve the attributes of this existing property.
1916 attributes = result->GetAttributes();
1917 return ConvertDescriptorToField(name, value, attributes);
1918 case CALLBACKS:
1919 return SetPropertyWithCallback(result->GetCallbackObject(),
1920 name,
1921 value,
1922 result->holder());
1923 case INTERCEPTOR:
1924 return SetPropertyWithInterceptor(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01001925 case CONSTANT_TRANSITION: {
1926 // If the same constant function is being added we can simply
1927 // transition to the target map.
1928 Map* target_map = result->GetTransitionMap();
1929 DescriptorArray* target_descriptors = target_map->instance_descriptors();
1930 int number = target_descriptors->SearchWithCache(name);
1931 ASSERT(number != DescriptorArray::kNotFound);
1932 ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
1933 JSFunction* function =
1934 JSFunction::cast(target_descriptors->GetValue(number));
1935 ASSERT(!Heap::InNewSpace(function));
1936 if (value == function) {
1937 set_map(target_map);
1938 return value;
1939 }
1940 // Otherwise, replace with a MAP_TRANSITION to a new map with a
1941 // FIELD, even if the value is a constant function.
Steve Blocka7e24c12009-10-30 11:49:00 +00001942 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01001943 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001944 case NULL_DESCRIPTOR:
1945 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1946 default:
1947 UNREACHABLE();
1948 }
1949 UNREACHABLE();
1950 return value;
1951}
1952
1953
1954// Set a real local property, even if it is READ_ONLY. If the property is not
1955// present, add it with attributes NONE. This code is an exact clone of
1956// SetProperty, with the check for IsReadOnly and the check for a
1957// callback setter removed. The two lines looking up the LookupResult
1958// result are also added. If one of the functions is changed, the other
1959// should be.
John Reck59135872010-11-02 12:39:01 -07001960MaybeObject* JSObject::IgnoreAttributesAndSetLocalProperty(
Steve Blocka7e24c12009-10-30 11:49:00 +00001961 String* name,
1962 Object* value,
1963 PropertyAttributes attributes) {
1964 // Make sure that the top context does not change when doing callbacks or
1965 // interceptor calls.
1966 AssertNoContextChange ncc;
Andrei Popescu402d9372010-02-26 13:31:12 +00001967 LookupResult result;
1968 LocalLookup(name, &result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001969 // Check access rights if needed.
1970 if (IsAccessCheckNeeded()
Andrei Popescu402d9372010-02-26 13:31:12 +00001971 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1972 return SetPropertyWithFailedAccessCheck(&result, name, value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001973 }
1974
1975 if (IsJSGlobalProxy()) {
1976 Object* proto = GetPrototype();
1977 if (proto->IsNull()) return value;
1978 ASSERT(proto->IsJSGlobalObject());
1979 return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
1980 name,
1981 value,
1982 attributes);
1983 }
1984
1985 // Check for accessor in prototype chain removed here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00001986 if (!result.IsFound()) {
1987 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001988 return AddProperty(name, value, attributes);
1989 }
Steve Block6ded16b2010-05-10 14:33:55 +01001990
Andrei Popescu402d9372010-02-26 13:31:12 +00001991 PropertyDetails details = PropertyDetails(attributes, NORMAL);
1992
Steve Blocka7e24c12009-10-30 11:49:00 +00001993 // Check of IsReadOnly removed from here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00001994 switch (result.type()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001995 case NORMAL:
Andrei Popescu402d9372010-02-26 13:31:12 +00001996 return SetNormalizedProperty(name, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00001997 case FIELD:
Andrei Popescu402d9372010-02-26 13:31:12 +00001998 return FastPropertyAtPut(result.GetFieldIndex(), value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001999 case MAP_TRANSITION:
Andrei Popescu402d9372010-02-26 13:31:12 +00002000 if (attributes == result.GetAttributes()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002001 // Only use map transition if the attributes match.
Andrei Popescu402d9372010-02-26 13:31:12 +00002002 return AddFastPropertyUsingMap(result.GetTransitionMap(),
Steve Blocka7e24c12009-10-30 11:49:00 +00002003 name,
2004 value);
2005 }
2006 return ConvertDescriptorToField(name, value, attributes);
2007 case CONSTANT_FUNCTION:
2008 // Only replace the function if necessary.
Andrei Popescu402d9372010-02-26 13:31:12 +00002009 if (value == result.GetConstantFunction()) return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00002010 // Preserve the attributes of this existing property.
Andrei Popescu402d9372010-02-26 13:31:12 +00002011 attributes = result.GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +00002012 return ConvertDescriptorToField(name, value, attributes);
2013 case CALLBACKS:
2014 case INTERCEPTOR:
2015 // Override callback in clone
2016 return ConvertDescriptorToField(name, value, attributes);
2017 case CONSTANT_TRANSITION:
2018 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
2019 // if the value is a function.
2020 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2021 case NULL_DESCRIPTOR:
2022 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2023 default:
2024 UNREACHABLE();
2025 }
2026 UNREACHABLE();
2027 return value;
2028}
2029
2030
2031PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
2032 JSObject* receiver,
2033 String* name,
2034 bool continue_search) {
2035 // Check local property, ignore interceptor.
2036 LookupResult result;
2037 LocalLookupRealNamedProperty(name, &result);
2038 if (result.IsProperty()) return result.GetAttributes();
2039
2040 if (continue_search) {
2041 // Continue searching via the prototype chain.
2042 Object* pt = GetPrototype();
2043 if (pt != Heap::null_value()) {
2044 return JSObject::cast(pt)->
2045 GetPropertyAttributeWithReceiver(receiver, name);
2046 }
2047 }
2048 return ABSENT;
2049}
2050
2051
2052PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
2053 JSObject* receiver,
2054 String* name,
2055 bool continue_search) {
2056 // Make sure that the top context does not change when doing
2057 // callbacks or interceptor calls.
2058 AssertNoContextChange ncc;
2059
2060 HandleScope scope;
2061 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2062 Handle<JSObject> receiver_handle(receiver);
2063 Handle<JSObject> holder_handle(this);
2064 Handle<String> name_handle(name);
2065 CustomArguments args(interceptor->data(), receiver, this);
2066 v8::AccessorInfo info(args.end());
2067 if (!interceptor->query()->IsUndefined()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002068 v8::NamedPropertyQuery query =
2069 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
Steve Blocka7e24c12009-10-30 11:49:00 +00002070 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002071 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00002072 {
2073 // Leaving JavaScript.
2074 VMState state(EXTERNAL);
2075 result = query(v8::Utils::ToLocal(name_handle), info);
2076 }
2077 if (!result.IsEmpty()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002078 ASSERT(result->IsInt32());
2079 return static_cast<PropertyAttributes>(result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002080 }
2081 } else if (!interceptor->getter()->IsUndefined()) {
2082 v8::NamedPropertyGetter getter =
2083 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
2084 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
2085 v8::Handle<v8::Value> result;
2086 {
2087 // Leaving JavaScript.
2088 VMState state(EXTERNAL);
2089 result = getter(v8::Utils::ToLocal(name_handle), info);
2090 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002091 if (!result.IsEmpty()) return DONT_ENUM;
Steve Blocka7e24c12009-10-30 11:49:00 +00002092 }
2093 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2094 *name_handle,
2095 continue_search);
2096}
2097
2098
2099PropertyAttributes JSObject::GetPropertyAttributeWithReceiver(
2100 JSObject* receiver,
2101 String* key) {
2102 uint32_t index = 0;
2103 if (key->AsArrayIndex(&index)) {
2104 if (HasElementWithReceiver(receiver, index)) return NONE;
2105 return ABSENT;
2106 }
2107 // Named property.
2108 LookupResult result;
2109 Lookup(key, &result);
2110 return GetPropertyAttribute(receiver, &result, key, true);
2111}
2112
2113
2114PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
2115 LookupResult* result,
2116 String* name,
2117 bool continue_search) {
2118 // Check access rights if needed.
2119 if (IsAccessCheckNeeded() &&
2120 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2121 return GetPropertyAttributeWithFailedAccessCheck(receiver,
2122 result,
2123 name,
2124 continue_search);
2125 }
Andrei Popescu402d9372010-02-26 13:31:12 +00002126 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002127 switch (result->type()) {
2128 case NORMAL: // fall through
2129 case FIELD:
2130 case CONSTANT_FUNCTION:
2131 case CALLBACKS:
2132 return result->GetAttributes();
2133 case INTERCEPTOR:
2134 return result->holder()->
2135 GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
Steve Blocka7e24c12009-10-30 11:49:00 +00002136 default:
2137 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +00002138 }
2139 }
2140 return ABSENT;
2141}
2142
2143
2144PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) {
2145 // Check whether the name is an array index.
2146 uint32_t index = 0;
2147 if (name->AsArrayIndex(&index)) {
2148 if (HasLocalElement(index)) return NONE;
2149 return ABSENT;
2150 }
2151 // Named property.
2152 LookupResult result;
2153 LocalLookup(name, &result);
2154 return GetPropertyAttribute(this, &result, name, false);
2155}
2156
2157
John Reck59135872010-11-02 12:39:01 -07002158MaybeObject* NormalizedMapCache::Get(JSObject* obj,
2159 PropertyNormalizationMode mode) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002160 Map* fast = obj->map();
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002161 int index = Hash(fast) % kEntries;
2162 Object* result = get(index);
2163 if (result->IsMap() && CheckHit(Map::cast(result), fast, mode)) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002164#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002165 if (FLAG_enable_slow_asserts) {
2166 // The cached map should match newly created normalized map bit-by-bit.
John Reck59135872010-11-02 12:39:01 -07002167 Object* fresh;
2168 { MaybeObject* maybe_fresh =
2169 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2170 if (maybe_fresh->ToObject(&fresh)) {
2171 ASSERT(memcmp(Map::cast(fresh)->address(),
2172 Map::cast(result)->address(),
2173 Map::kSize) == 0);
2174 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002175 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002176 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002177#endif
2178 return result;
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002179 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002180
John Reck59135872010-11-02 12:39:01 -07002181 { MaybeObject* maybe_result =
2182 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2183 if (!maybe_result->ToObject(&result)) return maybe_result;
2184 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002185 set(index, result);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002186 Counters::normalized_maps.Increment();
2187
2188 return result;
2189}
2190
2191
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002192void NormalizedMapCache::Clear() {
2193 int entries = length();
2194 for (int i = 0; i != entries; i++) {
2195 set_undefined(i);
2196 }
2197}
2198
2199
2200int NormalizedMapCache::Hash(Map* fast) {
2201 // For performance reasons we only hash the 3 most variable fields of a map:
2202 // constructor, prototype and bit_field2.
2203
2204 // Shift away the tag.
2205 int hash = (static_cast<uint32_t>(
2206 reinterpret_cast<uintptr_t>(fast->constructor())) >> 2);
2207
2208 // XOR-ing the prototype and constructor directly yields too many zero bits
2209 // when the two pointers are close (which is fairly common).
2210 // To avoid this we shift the prototype 4 bits relatively to the constructor.
2211 hash ^= (static_cast<uint32_t>(
2212 reinterpret_cast<uintptr_t>(fast->prototype())) << 2);
2213
2214 return hash ^ (hash >> 16) ^ fast->bit_field2();
2215}
2216
2217
2218bool NormalizedMapCache::CheckHit(Map* slow,
2219 Map* fast,
2220 PropertyNormalizationMode mode) {
2221#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002222 slow->SharedMapVerify();
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002223#endif
2224 return
2225 slow->constructor() == fast->constructor() &&
2226 slow->prototype() == fast->prototype() &&
2227 slow->inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ?
2228 0 :
2229 fast->inobject_properties()) &&
2230 slow->instance_type() == fast->instance_type() &&
2231 slow->bit_field() == fast->bit_field() &&
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002232 (slow->bit_field2() & ~(1<<Map::kIsShared)) == fast->bit_field2();
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002233}
2234
2235
John Reck59135872010-11-02 12:39:01 -07002236MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002237 if (map()->is_shared()) {
2238 // Fast case maps are never marked as shared.
2239 ASSERT(!HasFastProperties());
2240 // Replace the map with an identical copy that can be safely modified.
John Reck59135872010-11-02 12:39:01 -07002241 Object* obj;
2242 { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES,
2243 UNIQUE_NORMALIZED_MAP);
2244 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2245 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002246 Counters::normalized_maps.Increment();
2247
2248 set_map(Map::cast(obj));
2249 }
2250 return map()->UpdateCodeCache(name, code);
2251}
2252
2253
John Reck59135872010-11-02 12:39:01 -07002254MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2255 int expected_additional_properties) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002256 if (!HasFastProperties()) return this;
2257
2258 // The global object is always normalized.
2259 ASSERT(!IsGlobalObject());
2260
2261 // Allocate new content.
2262 int property_count = map()->NumberOfDescribedProperties();
2263 if (expected_additional_properties > 0) {
2264 property_count += expected_additional_properties;
2265 } else {
2266 property_count += 2; // Make space for two more properties.
2267 }
John Reck59135872010-11-02 12:39:01 -07002268 Object* obj;
2269 { MaybeObject* maybe_obj =
2270 StringDictionary::Allocate(property_count);
2271 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2272 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002273 StringDictionary* dictionary = StringDictionary::cast(obj);
2274
2275 DescriptorArray* descs = map()->instance_descriptors();
2276 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2277 PropertyDetails details = descs->GetDetails(i);
2278 switch (details.type()) {
2279 case CONSTANT_FUNCTION: {
2280 PropertyDetails d =
2281 PropertyDetails(details.attributes(), NORMAL, details.index());
2282 Object* value = descs->GetConstantFunction(i);
John Reck59135872010-11-02 12:39:01 -07002283 Object* result;
2284 { MaybeObject* maybe_result =
2285 dictionary->Add(descs->GetKey(i), value, d);
2286 if (!maybe_result->ToObject(&result)) return maybe_result;
2287 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002288 dictionary = StringDictionary::cast(result);
2289 break;
2290 }
2291 case FIELD: {
2292 PropertyDetails d =
2293 PropertyDetails(details.attributes(), NORMAL, details.index());
2294 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
John Reck59135872010-11-02 12:39:01 -07002295 Object* result;
2296 { MaybeObject* maybe_result =
2297 dictionary->Add(descs->GetKey(i), value, d);
2298 if (!maybe_result->ToObject(&result)) return maybe_result;
2299 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002300 dictionary = StringDictionary::cast(result);
2301 break;
2302 }
2303 case CALLBACKS: {
2304 PropertyDetails d =
2305 PropertyDetails(details.attributes(), CALLBACKS, details.index());
2306 Object* value = descs->GetCallbacksObject(i);
John Reck59135872010-11-02 12:39:01 -07002307 Object* result;
2308 { MaybeObject* maybe_result =
2309 dictionary->Add(descs->GetKey(i), value, d);
2310 if (!maybe_result->ToObject(&result)) return maybe_result;
2311 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002312 dictionary = StringDictionary::cast(result);
2313 break;
2314 }
2315 case MAP_TRANSITION:
2316 case CONSTANT_TRANSITION:
2317 case NULL_DESCRIPTOR:
2318 case INTERCEPTOR:
2319 break;
2320 default:
2321 UNREACHABLE();
2322 }
2323 }
2324
2325 // Copy the next enumeration index from instance descriptor.
2326 int index = map()->instance_descriptors()->NextEnumerationIndex();
2327 dictionary->SetNextEnumerationIndex(index);
2328
John Reck59135872010-11-02 12:39:01 -07002329 { MaybeObject* maybe_obj = Top::context()->global_context()->
2330 normalized_map_cache()->Get(this, mode);
2331 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2332 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002333 Map* new_map = Map::cast(obj);
2334
Steve Blocka7e24c12009-10-30 11:49:00 +00002335 // We have now successfully allocated all the necessary objects.
2336 // Changes can now be made with the guarantee that all of them take effect.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002337
2338 // Resize the object in the heap if necessary.
2339 int new_instance_size = new_map->instance_size();
2340 int instance_size_delta = map()->instance_size() - new_instance_size;
2341 ASSERT(instance_size_delta >= 0);
2342 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2343 instance_size_delta);
2344
Steve Blocka7e24c12009-10-30 11:49:00 +00002345 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002346
2347 set_properties(dictionary);
2348
2349 Counters::props_to_dictionary.Increment();
2350
2351#ifdef DEBUG
2352 if (FLAG_trace_normalization) {
2353 PrintF("Object properties have been normalized:\n");
2354 Print();
2355 }
2356#endif
2357 return this;
2358}
2359
2360
John Reck59135872010-11-02 12:39:01 -07002361MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002362 if (HasFastProperties()) return this;
2363 ASSERT(!IsGlobalObject());
2364 return property_dictionary()->
2365 TransformPropertiesToFastFor(this, unused_property_fields);
2366}
2367
2368
John Reck59135872010-11-02 12:39:01 -07002369MaybeObject* JSObject::NormalizeElements() {
Steve Block3ce2e202009-11-05 08:53:23 +00002370 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002371 if (HasDictionaryElements()) return this;
Steve Block8defd9f2010-07-08 12:39:36 +01002372 ASSERT(map()->has_fast_elements());
2373
John Reck59135872010-11-02 12:39:01 -07002374 Object* obj;
2375 { MaybeObject* maybe_obj = map()->GetSlowElementsMap();
2376 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2377 }
Steve Block8defd9f2010-07-08 12:39:36 +01002378 Map* new_map = Map::cast(obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00002379
2380 // Get number of entries.
2381 FixedArray* array = FixedArray::cast(elements());
2382
2383 // Compute the effective length.
2384 int length = IsJSArray() ?
2385 Smi::cast(JSArray::cast(this)->length())->value() :
2386 array->length();
John Reck59135872010-11-02 12:39:01 -07002387 { MaybeObject* maybe_obj = NumberDictionary::Allocate(length);
2388 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2389 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002390 NumberDictionary* dictionary = NumberDictionary::cast(obj);
2391 // Copy entries.
2392 for (int i = 0; i < length; i++) {
2393 Object* value = array->get(i);
2394 if (!value->IsTheHole()) {
2395 PropertyDetails details = PropertyDetails(NONE, NORMAL);
John Reck59135872010-11-02 12:39:01 -07002396 Object* result;
2397 { MaybeObject* maybe_result =
2398 dictionary->AddNumberEntry(i, array->get(i), details);
2399 if (!maybe_result->ToObject(&result)) return maybe_result;
2400 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002401 dictionary = NumberDictionary::cast(result);
2402 }
2403 }
Steve Block8defd9f2010-07-08 12:39:36 +01002404 // Switch to using the dictionary as the backing storage for
2405 // elements. Set the new map first to satify the elements type
2406 // assert in set_elements().
2407 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002408 set_elements(dictionary);
2409
2410 Counters::elements_to_dictionary.Increment();
2411
2412#ifdef DEBUG
2413 if (FLAG_trace_normalization) {
2414 PrintF("Object elements have been normalized:\n");
2415 Print();
2416 }
2417#endif
2418
2419 return this;
2420}
2421
2422
John Reck59135872010-11-02 12:39:01 -07002423MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
2424 DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002425 // Check local property, ignore interceptor.
2426 LookupResult result;
2427 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002428 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002429
2430 // Normalize object if needed.
John Reck59135872010-11-02 12:39:01 -07002431 Object* obj;
2432 { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2433 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2434 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002435
2436 return DeleteNormalizedProperty(name, mode);
2437}
2438
2439
John Reck59135872010-11-02 12:39:01 -07002440MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002441 HandleScope scope;
2442 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2443 Handle<String> name_handle(name);
2444 Handle<JSObject> this_handle(this);
2445 if (!interceptor->deleter()->IsUndefined()) {
2446 v8::NamedPropertyDeleter deleter =
2447 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2448 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2449 CustomArguments args(interceptor->data(), this, this);
2450 v8::AccessorInfo info(args.end());
2451 v8::Handle<v8::Boolean> result;
2452 {
2453 // Leaving JavaScript.
2454 VMState state(EXTERNAL);
2455 result = deleter(v8::Utils::ToLocal(name_handle), info);
2456 }
2457 RETURN_IF_SCHEDULED_EXCEPTION();
2458 if (!result.IsEmpty()) {
2459 ASSERT(result->IsBoolean());
2460 return *v8::Utils::OpenHandle(*result);
2461 }
2462 }
John Reck59135872010-11-02 12:39:01 -07002463 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00002464 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
2465 RETURN_IF_SCHEDULED_EXCEPTION();
2466 return raw_result;
2467}
2468
2469
John Reck59135872010-11-02 12:39:01 -07002470MaybeObject* JSObject::DeleteElementPostInterceptor(uint32_t index,
2471 DeleteMode mode) {
Steve Block3ce2e202009-11-05 08:53:23 +00002472 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002473 switch (GetElementsKind()) {
2474 case FAST_ELEMENTS: {
John Reck59135872010-11-02 12:39:01 -07002475 Object* obj;
2476 { MaybeObject* maybe_obj = EnsureWritableFastElements();
2477 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2478 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002479 uint32_t length = IsJSArray() ?
2480 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2481 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2482 if (index < length) {
2483 FixedArray::cast(elements())->set_the_hole(index);
2484 }
2485 break;
2486 }
2487 case DICTIONARY_ELEMENTS: {
2488 NumberDictionary* dictionary = element_dictionary();
2489 int entry = dictionary->FindEntry(index);
2490 if (entry != NumberDictionary::kNotFound) {
2491 return dictionary->DeleteProperty(entry, mode);
2492 }
2493 break;
2494 }
2495 default:
2496 UNREACHABLE();
2497 break;
2498 }
2499 return Heap::true_value();
2500}
2501
2502
John Reck59135872010-11-02 12:39:01 -07002503MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002504 // Make sure that the top context does not change when doing
2505 // callbacks or interceptor calls.
2506 AssertNoContextChange ncc;
2507 HandleScope scope;
2508 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2509 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2510 v8::IndexedPropertyDeleter deleter =
2511 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2512 Handle<JSObject> this_handle(this);
2513 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2514 CustomArguments args(interceptor->data(), this, this);
2515 v8::AccessorInfo info(args.end());
2516 v8::Handle<v8::Boolean> result;
2517 {
2518 // Leaving JavaScript.
2519 VMState state(EXTERNAL);
2520 result = deleter(index, info);
2521 }
2522 RETURN_IF_SCHEDULED_EXCEPTION();
2523 if (!result.IsEmpty()) {
2524 ASSERT(result->IsBoolean());
2525 return *v8::Utils::OpenHandle(*result);
2526 }
John Reck59135872010-11-02 12:39:01 -07002527 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00002528 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
2529 RETURN_IF_SCHEDULED_EXCEPTION();
2530 return raw_result;
2531}
2532
2533
John Reck59135872010-11-02 12:39:01 -07002534MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002535 // Check access rights if needed.
2536 if (IsAccessCheckNeeded() &&
2537 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
2538 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2539 return Heap::false_value();
2540 }
2541
2542 if (IsJSGlobalProxy()) {
2543 Object* proto = GetPrototype();
2544 if (proto->IsNull()) return Heap::false_value();
2545 ASSERT(proto->IsJSGlobalObject());
2546 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
2547 }
2548
2549 if (HasIndexedInterceptor()) {
2550 // Skip interceptor if forcing deletion.
2551 if (mode == FORCE_DELETION) {
2552 return DeleteElementPostInterceptor(index, mode);
2553 }
2554 return DeleteElementWithInterceptor(index);
2555 }
2556
2557 switch (GetElementsKind()) {
2558 case FAST_ELEMENTS: {
John Reck59135872010-11-02 12:39:01 -07002559 Object* obj;
2560 { MaybeObject* maybe_obj = EnsureWritableFastElements();
2561 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2562 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002563 uint32_t length = IsJSArray() ?
2564 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2565 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2566 if (index < length) {
2567 FixedArray::cast(elements())->set_the_hole(index);
2568 }
2569 break;
2570 }
Steve Block3ce2e202009-11-05 08:53:23 +00002571 case PIXEL_ELEMENTS:
2572 case EXTERNAL_BYTE_ELEMENTS:
2573 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2574 case EXTERNAL_SHORT_ELEMENTS:
2575 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2576 case EXTERNAL_INT_ELEMENTS:
2577 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2578 case EXTERNAL_FLOAT_ELEMENTS:
2579 // Pixel and external array elements cannot be deleted. Just
2580 // silently ignore here.
Steve Blocka7e24c12009-10-30 11:49:00 +00002581 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00002582 case DICTIONARY_ELEMENTS: {
2583 NumberDictionary* dictionary = element_dictionary();
2584 int entry = dictionary->FindEntry(index);
2585 if (entry != NumberDictionary::kNotFound) {
2586 return dictionary->DeleteProperty(entry, mode);
2587 }
2588 break;
2589 }
2590 default:
2591 UNREACHABLE();
2592 break;
2593 }
2594 return Heap::true_value();
2595}
2596
2597
John Reck59135872010-11-02 12:39:01 -07002598MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002599 // ECMA-262, 3rd, 8.6.2.5
2600 ASSERT(name->IsString());
2601
2602 // Check access rights if needed.
2603 if (IsAccessCheckNeeded() &&
2604 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2605 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2606 return Heap::false_value();
2607 }
2608
2609 if (IsJSGlobalProxy()) {
2610 Object* proto = GetPrototype();
2611 if (proto->IsNull()) return Heap::false_value();
2612 ASSERT(proto->IsJSGlobalObject());
2613 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
2614 }
2615
2616 uint32_t index = 0;
2617 if (name->AsArrayIndex(&index)) {
2618 return DeleteElement(index, mode);
2619 } else {
2620 LookupResult result;
2621 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002622 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002623 // Ignore attributes if forcing a deletion.
2624 if (result.IsDontDelete() && mode != FORCE_DELETION) {
2625 return Heap::false_value();
2626 }
2627 // Check for interceptor.
2628 if (result.type() == INTERCEPTOR) {
2629 // Skip interceptor if forcing a deletion.
2630 if (mode == FORCE_DELETION) {
2631 return DeletePropertyPostInterceptor(name, mode);
2632 }
2633 return DeletePropertyWithInterceptor(name);
2634 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002635 // Normalize object if needed.
John Reck59135872010-11-02 12:39:01 -07002636 Object* obj;
2637 { MaybeObject* maybe_obj =
2638 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2639 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2640 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002641 // Make sure the properties are normalized before removing the entry.
2642 return DeleteNormalizedProperty(name, mode);
2643 }
2644}
2645
2646
2647// Check whether this object references another object.
2648bool JSObject::ReferencesObject(Object* obj) {
2649 AssertNoAllocation no_alloc;
2650
2651 // Is the object the constructor for this object?
2652 if (map()->constructor() == obj) {
2653 return true;
2654 }
2655
2656 // Is the object the prototype for this object?
2657 if (map()->prototype() == obj) {
2658 return true;
2659 }
2660
2661 // Check if the object is among the named properties.
2662 Object* key = SlowReverseLookup(obj);
2663 if (key != Heap::undefined_value()) {
2664 return true;
2665 }
2666
2667 // Check if the object is among the indexed properties.
2668 switch (GetElementsKind()) {
2669 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002670 case EXTERNAL_BYTE_ELEMENTS:
2671 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2672 case EXTERNAL_SHORT_ELEMENTS:
2673 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2674 case EXTERNAL_INT_ELEMENTS:
2675 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2676 case EXTERNAL_FLOAT_ELEMENTS:
2677 // Raw pixels and external arrays do not reference other
2678 // objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00002679 break;
2680 case FAST_ELEMENTS: {
2681 int length = IsJSArray() ?
2682 Smi::cast(JSArray::cast(this)->length())->value() :
2683 FixedArray::cast(elements())->length();
2684 for (int i = 0; i < length; i++) {
2685 Object* element = FixedArray::cast(elements())->get(i);
2686 if (!element->IsTheHole() && element == obj) {
2687 return true;
2688 }
2689 }
2690 break;
2691 }
2692 case DICTIONARY_ELEMENTS: {
2693 key = element_dictionary()->SlowReverseLookup(obj);
2694 if (key != Heap::undefined_value()) {
2695 return true;
2696 }
2697 break;
2698 }
2699 default:
2700 UNREACHABLE();
2701 break;
2702 }
2703
Steve Block6ded16b2010-05-10 14:33:55 +01002704 // For functions check the context.
2705 if (IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002706 // Get the constructor function for arguments array.
2707 JSObject* arguments_boilerplate =
2708 Top::context()->global_context()->arguments_boilerplate();
2709 JSFunction* arguments_function =
2710 JSFunction::cast(arguments_boilerplate->map()->constructor());
2711
2712 // Get the context and don't check if it is the global context.
2713 JSFunction* f = JSFunction::cast(this);
2714 Context* context = f->context();
2715 if (context->IsGlobalContext()) {
2716 return false;
2717 }
2718
2719 // Check the non-special context slots.
2720 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2721 // Only check JS objects.
2722 if (context->get(i)->IsJSObject()) {
2723 JSObject* ctxobj = JSObject::cast(context->get(i));
2724 // If it is an arguments array check the content.
2725 if (ctxobj->map()->constructor() == arguments_function) {
2726 if (ctxobj->ReferencesObject(obj)) {
2727 return true;
2728 }
2729 } else if (ctxobj == obj) {
2730 return true;
2731 }
2732 }
2733 }
2734
2735 // Check the context extension if any.
2736 if (context->has_extension()) {
2737 return context->extension()->ReferencesObject(obj);
2738 }
2739 }
2740
2741 // No references to object.
2742 return false;
2743}
2744
2745
John Reck59135872010-11-02 12:39:01 -07002746MaybeObject* JSObject::PreventExtensions() {
Steve Block8defd9f2010-07-08 12:39:36 +01002747 // If there are fast elements we normalize.
2748 if (HasFastElements()) {
John Reck59135872010-11-02 12:39:01 -07002749 Object* ok;
2750 { MaybeObject* maybe_ok = NormalizeElements();
2751 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
2752 }
Steve Block8defd9f2010-07-08 12:39:36 +01002753 }
2754 // Make sure that we never go back to fast case.
2755 element_dictionary()->set_requires_slow_elements();
2756
2757 // Do a map transition, other objects with this map may still
2758 // be extensible.
John Reck59135872010-11-02 12:39:01 -07002759 Object* new_map;
2760 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
2761 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
2762 }
Steve Block8defd9f2010-07-08 12:39:36 +01002763 Map::cast(new_map)->set_is_extensible(false);
2764 set_map(Map::cast(new_map));
2765 ASSERT(!map()->is_extensible());
2766 return new_map;
2767}
2768
2769
Steve Blocka7e24c12009-10-30 11:49:00 +00002770// Tests for the fast common case for property enumeration:
Steve Blockd0582a62009-12-15 09:54:21 +00002771// - This object and all prototypes has an enum cache (which means that it has
2772// no interceptors and needs no access checks).
2773// - This object has no elements.
2774// - No prototype has enumerable properties/elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002775bool JSObject::IsSimpleEnum() {
Steve Blocka7e24c12009-10-30 11:49:00 +00002776 for (Object* o = this;
2777 o != Heap::null_value();
2778 o = JSObject::cast(o)->GetPrototype()) {
2779 JSObject* curr = JSObject::cast(o);
Steve Blocka7e24c12009-10-30 11:49:00 +00002780 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00002781 ASSERT(!curr->HasNamedInterceptor());
2782 ASSERT(!curr->HasIndexedInterceptor());
2783 ASSERT(!curr->IsAccessCheckNeeded());
Steve Blocka7e24c12009-10-30 11:49:00 +00002784 if (curr->NumberOfEnumElements() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002785 if (curr != this) {
2786 FixedArray* curr_fixed_array =
2787 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
Steve Blockd0582a62009-12-15 09:54:21 +00002788 if (curr_fixed_array->length() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002789 }
2790 }
2791 return true;
2792}
2793
2794
2795int Map::NumberOfDescribedProperties() {
2796 int result = 0;
2797 DescriptorArray* descs = instance_descriptors();
2798 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2799 if (descs->IsProperty(i)) result++;
2800 }
2801 return result;
2802}
2803
2804
2805int Map::PropertyIndexFor(String* name) {
2806 DescriptorArray* descs = instance_descriptors();
2807 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2808 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
2809 return descs->GetFieldIndex(i);
2810 }
2811 }
2812 return -1;
2813}
2814
2815
2816int Map::NextFreePropertyIndex() {
2817 int max_index = -1;
2818 DescriptorArray* descs = instance_descriptors();
2819 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2820 if (descs->GetType(i) == FIELD) {
2821 int current_index = descs->GetFieldIndex(i);
2822 if (current_index > max_index) max_index = current_index;
2823 }
2824 }
2825 return max_index + 1;
2826}
2827
2828
2829AccessorDescriptor* Map::FindAccessor(String* name) {
2830 DescriptorArray* descs = instance_descriptors();
2831 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2832 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
2833 return descs->GetCallbacks(i);
2834 }
2835 }
2836 return NULL;
2837}
2838
2839
2840void JSObject::LocalLookup(String* name, LookupResult* result) {
2841 ASSERT(name->IsString());
2842
2843 if (IsJSGlobalProxy()) {
2844 Object* proto = GetPrototype();
2845 if (proto->IsNull()) return result->NotFound();
2846 ASSERT(proto->IsJSGlobalObject());
2847 return JSObject::cast(proto)->LocalLookup(name, result);
2848 }
2849
2850 // Do not use inline caching if the object is a non-global object
2851 // that requires access checks.
2852 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
2853 result->DisallowCaching();
2854 }
2855
2856 // Check __proto__ before interceptor.
2857 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
2858 result->ConstantResult(this);
2859 return;
2860 }
2861
2862 // Check for lookup interceptor except when bootstrapping.
2863 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2864 result->InterceptorResult(this);
2865 return;
2866 }
2867
2868 LocalLookupRealNamedProperty(name, result);
2869}
2870
2871
2872void JSObject::Lookup(String* name, LookupResult* result) {
2873 // Ecma-262 3rd 8.6.2.4
2874 for (Object* current = this;
2875 current != Heap::null_value();
2876 current = JSObject::cast(current)->GetPrototype()) {
2877 JSObject::cast(current)->LocalLookup(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002878 if (result->IsProperty()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002879 }
2880 result->NotFound();
2881}
2882
2883
2884// Search object and it's prototype chain for callback properties.
2885void JSObject::LookupCallback(String* name, LookupResult* result) {
2886 for (Object* current = this;
2887 current != Heap::null_value();
2888 current = JSObject::cast(current)->GetPrototype()) {
2889 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002890 if (result->IsProperty() && result->type() == CALLBACKS) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002891 }
2892 result->NotFound();
2893}
2894
2895
John Reck59135872010-11-02 12:39:01 -07002896MaybeObject* JSObject::DefineGetterSetter(String* name,
2897 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002898 // Make sure that the top context does not change when doing callbacks or
2899 // interceptor calls.
2900 AssertNoContextChange ncc;
2901
Steve Blocka7e24c12009-10-30 11:49:00 +00002902 // Try to flatten before operating on the string.
Steve Block6ded16b2010-05-10 14:33:55 +01002903 name->TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00002904
Leon Clarkef7060e22010-06-03 12:02:55 +01002905 if (!CanSetCallback(name)) {
2906 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002907 }
2908
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002909 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00002910 bool is_element = name->AsArrayIndex(&index);
2911 if (is_element && IsJSArray()) return Heap::undefined_value();
2912
2913 if (is_element) {
2914 switch (GetElementsKind()) {
2915 case FAST_ELEMENTS:
2916 break;
2917 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002918 case EXTERNAL_BYTE_ELEMENTS:
2919 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2920 case EXTERNAL_SHORT_ELEMENTS:
2921 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2922 case EXTERNAL_INT_ELEMENTS:
2923 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2924 case EXTERNAL_FLOAT_ELEMENTS:
2925 // Ignore getters and setters on pixel and external array
2926 // elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002927 return Heap::undefined_value();
2928 case DICTIONARY_ELEMENTS: {
2929 // Lookup the index.
2930 NumberDictionary* dictionary = element_dictionary();
2931 int entry = dictionary->FindEntry(index);
2932 if (entry != NumberDictionary::kNotFound) {
2933 Object* result = dictionary->ValueAt(entry);
2934 PropertyDetails details = dictionary->DetailsAt(entry);
2935 if (details.IsReadOnly()) return Heap::undefined_value();
2936 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002937 if (result->IsFixedArray()) {
2938 return result;
2939 }
2940 // Otherwise allow to override it.
Steve Blocka7e24c12009-10-30 11:49:00 +00002941 }
2942 }
2943 break;
2944 }
2945 default:
2946 UNREACHABLE();
2947 break;
2948 }
2949 } else {
2950 // Lookup the name.
2951 LookupResult result;
2952 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002953 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002954 if (result.IsReadOnly()) return Heap::undefined_value();
2955 if (result.type() == CALLBACKS) {
2956 Object* obj = result.GetCallbackObject();
Leon Clarkef7060e22010-06-03 12:02:55 +01002957 // Need to preserve old getters/setters.
Leon Clarked91b9f72010-01-27 17:25:45 +00002958 if (obj->IsFixedArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002959 // Use set to update attributes.
2960 return SetPropertyCallback(name, obj, attributes);
Leon Clarked91b9f72010-01-27 17:25:45 +00002961 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002962 }
2963 }
2964 }
2965
2966 // Allocate the fixed array to hold getter and setter.
John Reck59135872010-11-02 12:39:01 -07002967 Object* structure;
2968 { MaybeObject* maybe_structure = Heap::AllocateFixedArray(2, TENURED);
2969 if (!maybe_structure->ToObject(&structure)) return maybe_structure;
2970 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002971
2972 if (is_element) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002973 return SetElementCallback(index, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002974 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01002975 return SetPropertyCallback(name, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002976 }
Leon Clarkef7060e22010-06-03 12:02:55 +01002977}
2978
2979
2980bool JSObject::CanSetCallback(String* name) {
2981 ASSERT(!IsAccessCheckNeeded()
2982 || Top::MayNamedAccess(this, name, v8::ACCESS_SET));
2983
2984 // Check if there is an API defined callback object which prohibits
2985 // callback overwriting in this object or it's prototype chain.
2986 // This mechanism is needed for instance in a browser setting, where
2987 // certain accessors such as window.location should not be allowed
2988 // to be overwritten because allowing overwriting could potentially
2989 // cause security problems.
2990 LookupResult callback_result;
2991 LookupCallback(name, &callback_result);
2992 if (callback_result.IsProperty()) {
2993 Object* obj = callback_result.GetCallbackObject();
2994 if (obj->IsAccessorInfo() &&
2995 AccessorInfo::cast(obj)->prohibits_overwriting()) {
2996 return false;
2997 }
2998 }
2999
3000 return true;
3001}
3002
3003
John Reck59135872010-11-02 12:39:01 -07003004MaybeObject* JSObject::SetElementCallback(uint32_t index,
3005 Object* structure,
3006 PropertyAttributes attributes) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003007 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3008
3009 // Normalize elements to make this operation simple.
John Reck59135872010-11-02 12:39:01 -07003010 Object* ok;
3011 { MaybeObject* maybe_ok = NormalizeElements();
3012 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3013 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003014
3015 // Update the dictionary with the new CALLBACKS property.
John Reck59135872010-11-02 12:39:01 -07003016 Object* dict;
3017 { MaybeObject* maybe_dict =
3018 element_dictionary()->Set(index, structure, details);
3019 if (!maybe_dict->ToObject(&dict)) return maybe_dict;
3020 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003021
3022 NumberDictionary* elements = NumberDictionary::cast(dict);
3023 elements->set_requires_slow_elements();
3024 // Set the potential new dictionary on the object.
3025 set_elements(elements);
Steve Blocka7e24c12009-10-30 11:49:00 +00003026
3027 return structure;
3028}
3029
3030
John Reck59135872010-11-02 12:39:01 -07003031MaybeObject* JSObject::SetPropertyCallback(String* name,
3032 Object* structure,
3033 PropertyAttributes attributes) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003034 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3035
3036 bool convert_back_to_fast = HasFastProperties() &&
3037 (map()->instance_descriptors()->number_of_descriptors()
3038 < DescriptorArray::kMaxNumberOfDescriptors);
3039
3040 // Normalize object to make this operation simple.
John Reck59135872010-11-02 12:39:01 -07003041 Object* ok;
3042 { MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3043 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3044 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003045
3046 // For the global object allocate a new map to invalidate the global inline
3047 // caches which have a global property cell reference directly in the code.
3048 if (IsGlobalObject()) {
John Reck59135872010-11-02 12:39:01 -07003049 Object* new_map;
3050 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
3051 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3052 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003053 set_map(Map::cast(new_map));
3054 }
3055
3056 // Update the dictionary with the new CALLBACKS property.
John Reck59135872010-11-02 12:39:01 -07003057 Object* result;
3058 { MaybeObject* maybe_result = SetNormalizedProperty(name, structure, details);
3059 if (!maybe_result->ToObject(&result)) return maybe_result;
3060 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003061
3062 if (convert_back_to_fast) {
John Reck59135872010-11-02 12:39:01 -07003063 { MaybeObject* maybe_ok = TransformToFastProperties(0);
3064 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3065 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003066 }
3067 return result;
3068}
3069
John Reck59135872010-11-02 12:39:01 -07003070MaybeObject* JSObject::DefineAccessor(String* name,
3071 bool is_getter,
3072 JSFunction* fun,
3073 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003074 // Check access rights if needed.
3075 if (IsAccessCheckNeeded() &&
Leon Clarkef7060e22010-06-03 12:02:55 +01003076 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
3077 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Steve Blocka7e24c12009-10-30 11:49:00 +00003078 return Heap::undefined_value();
3079 }
3080
3081 if (IsJSGlobalProxy()) {
3082 Object* proto = GetPrototype();
3083 if (proto->IsNull()) return this;
3084 ASSERT(proto->IsJSGlobalObject());
3085 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
3086 fun, attributes);
3087 }
3088
John Reck59135872010-11-02 12:39:01 -07003089 Object* array;
3090 { MaybeObject* maybe_array = DefineGetterSetter(name, attributes);
3091 if (!maybe_array->ToObject(&array)) return maybe_array;
3092 }
3093 if (array->IsUndefined()) return array;
Steve Blocka7e24c12009-10-30 11:49:00 +00003094 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
3095 return this;
3096}
3097
3098
John Reck59135872010-11-02 12:39:01 -07003099MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003100 String* name = String::cast(info->name());
3101 // Check access rights if needed.
3102 if (IsAccessCheckNeeded() &&
3103 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
3104 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
3105 return Heap::undefined_value();
3106 }
3107
3108 if (IsJSGlobalProxy()) {
3109 Object* proto = GetPrototype();
3110 if (proto->IsNull()) return this;
3111 ASSERT(proto->IsJSGlobalObject());
3112 return JSObject::cast(proto)->DefineAccessor(info);
3113 }
3114
3115 // Make sure that the top context does not change when doing callbacks or
3116 // interceptor calls.
3117 AssertNoContextChange ncc;
3118
3119 // Try to flatten before operating on the string.
3120 name->TryFlatten();
3121
3122 if (!CanSetCallback(name)) {
3123 return Heap::undefined_value();
3124 }
3125
3126 uint32_t index = 0;
3127 bool is_element = name->AsArrayIndex(&index);
3128
3129 if (is_element) {
3130 if (IsJSArray()) return Heap::undefined_value();
3131
3132 // Accessors overwrite previous callbacks (cf. with getters/setters).
3133 switch (GetElementsKind()) {
3134 case FAST_ELEMENTS:
3135 break;
3136 case PIXEL_ELEMENTS:
3137 case EXTERNAL_BYTE_ELEMENTS:
3138 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3139 case EXTERNAL_SHORT_ELEMENTS:
3140 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3141 case EXTERNAL_INT_ELEMENTS:
3142 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3143 case EXTERNAL_FLOAT_ELEMENTS:
3144 // Ignore getters and setters on pixel and external array
3145 // elements.
3146 return Heap::undefined_value();
3147 case DICTIONARY_ELEMENTS:
3148 break;
3149 default:
3150 UNREACHABLE();
3151 break;
3152 }
3153
John Reck59135872010-11-02 12:39:01 -07003154 Object* ok;
3155 { MaybeObject* maybe_ok =
3156 SetElementCallback(index, info, info->property_attributes());
3157 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3158 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003159 } else {
3160 // Lookup the name.
3161 LookupResult result;
3162 LocalLookup(name, &result);
3163 // ES5 forbids turning a property into an accessor if it's not
3164 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
3165 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
3166 return Heap::undefined_value();
3167 }
John Reck59135872010-11-02 12:39:01 -07003168 Object* ok;
3169 { MaybeObject* maybe_ok =
3170 SetPropertyCallback(name, info, info->property_attributes());
3171 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3172 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003173 }
3174
3175 return this;
3176}
3177
3178
Steve Blocka7e24c12009-10-30 11:49:00 +00003179Object* JSObject::LookupAccessor(String* name, bool is_getter) {
3180 // Make sure that the top context does not change when doing callbacks or
3181 // interceptor calls.
3182 AssertNoContextChange ncc;
3183
3184 // Check access rights if needed.
3185 if (IsAccessCheckNeeded() &&
3186 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
3187 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
3188 return Heap::undefined_value();
3189 }
3190
3191 // Make the lookup and include prototypes.
3192 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01003193 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00003194 if (name->AsArrayIndex(&index)) {
3195 for (Object* obj = this;
3196 obj != Heap::null_value();
3197 obj = JSObject::cast(obj)->GetPrototype()) {
3198 JSObject* js_object = JSObject::cast(obj);
3199 if (js_object->HasDictionaryElements()) {
3200 NumberDictionary* dictionary = js_object->element_dictionary();
3201 int entry = dictionary->FindEntry(index);
3202 if (entry != NumberDictionary::kNotFound) {
3203 Object* element = dictionary->ValueAt(entry);
3204 PropertyDetails details = dictionary->DetailsAt(entry);
3205 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003206 if (element->IsFixedArray()) {
3207 return FixedArray::cast(element)->get(accessor_index);
3208 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003209 }
3210 }
3211 }
3212 }
3213 } else {
3214 for (Object* obj = this;
3215 obj != Heap::null_value();
3216 obj = JSObject::cast(obj)->GetPrototype()) {
3217 LookupResult result;
3218 JSObject::cast(obj)->LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00003219 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003220 if (result.IsReadOnly()) return Heap::undefined_value();
3221 if (result.type() == CALLBACKS) {
3222 Object* obj = result.GetCallbackObject();
3223 if (obj->IsFixedArray()) {
3224 return FixedArray::cast(obj)->get(accessor_index);
3225 }
3226 }
3227 }
3228 }
3229 }
3230 return Heap::undefined_value();
3231}
3232
3233
3234Object* JSObject::SlowReverseLookup(Object* value) {
3235 if (HasFastProperties()) {
3236 DescriptorArray* descs = map()->instance_descriptors();
3237 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3238 if (descs->GetType(i) == FIELD) {
3239 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3240 return descs->GetKey(i);
3241 }
3242 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3243 if (descs->GetConstantFunction(i) == value) {
3244 return descs->GetKey(i);
3245 }
3246 }
3247 }
3248 return Heap::undefined_value();
3249 } else {
3250 return property_dictionary()->SlowReverseLookup(value);
3251 }
3252}
3253
3254
John Reck59135872010-11-02 12:39:01 -07003255MaybeObject* Map::CopyDropDescriptors() {
3256 Object* result;
3257 { MaybeObject* maybe_result =
3258 Heap::AllocateMap(instance_type(), instance_size());
3259 if (!maybe_result->ToObject(&result)) return maybe_result;
3260 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003261 Map::cast(result)->set_prototype(prototype());
3262 Map::cast(result)->set_constructor(constructor());
3263 // Don't copy descriptors, so map transitions always remain a forest.
3264 // If we retained the same descriptors we would have two maps
3265 // pointing to the same transition which is bad because the garbage
3266 // collector relies on being able to reverse pointers from transitions
3267 // to maps. If properties need to be retained use CopyDropTransitions.
3268 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
3269 // Please note instance_type and instance_size are set when allocated.
3270 Map::cast(result)->set_inobject_properties(inobject_properties());
3271 Map::cast(result)->set_unused_property_fields(unused_property_fields());
3272
3273 // If the map has pre-allocated properties always start out with a descriptor
3274 // array describing these properties.
3275 if (pre_allocated_property_fields() > 0) {
3276 ASSERT(constructor()->IsJSFunction());
3277 JSFunction* ctor = JSFunction::cast(constructor());
John Reck59135872010-11-02 12:39:01 -07003278 Object* descriptors;
3279 { MaybeObject* maybe_descriptors =
3280 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
3281 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
3282 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003283 Map::cast(result)->set_instance_descriptors(
3284 DescriptorArray::cast(descriptors));
3285 Map::cast(result)->set_pre_allocated_property_fields(
3286 pre_allocated_property_fields());
3287 }
3288 Map::cast(result)->set_bit_field(bit_field());
3289 Map::cast(result)->set_bit_field2(bit_field2());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003290 Map::cast(result)->set_is_shared(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00003291 Map::cast(result)->ClearCodeCache();
3292 return result;
3293}
3294
3295
John Reck59135872010-11-02 12:39:01 -07003296MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
3297 NormalizedMapSharingMode sharing) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003298 int new_instance_size = instance_size();
3299 if (mode == CLEAR_INOBJECT_PROPERTIES) {
3300 new_instance_size -= inobject_properties() * kPointerSize;
3301 }
3302
John Reck59135872010-11-02 12:39:01 -07003303 Object* result;
3304 { MaybeObject* maybe_result =
3305 Heap::AllocateMap(instance_type(), new_instance_size);
3306 if (!maybe_result->ToObject(&result)) return maybe_result;
3307 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003308
3309 if (mode != CLEAR_INOBJECT_PROPERTIES) {
3310 Map::cast(result)->set_inobject_properties(inobject_properties());
3311 }
3312
3313 Map::cast(result)->set_prototype(prototype());
3314 Map::cast(result)->set_constructor(constructor());
3315
3316 Map::cast(result)->set_bit_field(bit_field());
3317 Map::cast(result)->set_bit_field2(bit_field2());
3318
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003319 Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
3320
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003321#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003322 if (Map::cast(result)->is_shared()) {
3323 Map::cast(result)->SharedMapVerify();
3324 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003325#endif
3326
3327 return result;
3328}
3329
3330
John Reck59135872010-11-02 12:39:01 -07003331MaybeObject* Map::CopyDropTransitions() {
3332 Object* new_map;
3333 { MaybeObject* maybe_new_map = CopyDropDescriptors();
3334 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3335 }
3336 Object* descriptors;
3337 { MaybeObject* maybe_descriptors =
3338 instance_descriptors()->RemoveTransitions();
3339 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
3340 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003341 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
Steve Block8defd9f2010-07-08 12:39:36 +01003342 return new_map;
Steve Blocka7e24c12009-10-30 11:49:00 +00003343}
3344
3345
John Reck59135872010-11-02 12:39:01 -07003346MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003347 // Allocate the code cache if not present.
3348 if (code_cache()->IsFixedArray()) {
John Reck59135872010-11-02 12:39:01 -07003349 Object* result;
3350 { MaybeObject* maybe_result = Heap::AllocateCodeCache();
3351 if (!maybe_result->ToObject(&result)) return maybe_result;
3352 }
Steve Block6ded16b2010-05-10 14:33:55 +01003353 set_code_cache(result);
3354 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003355
Steve Block6ded16b2010-05-10 14:33:55 +01003356 // Update the code cache.
3357 return CodeCache::cast(code_cache())->Update(name, code);
3358}
3359
3360
3361Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3362 // Do a lookup if a code cache exists.
3363 if (!code_cache()->IsFixedArray()) {
3364 return CodeCache::cast(code_cache())->Lookup(name, flags);
3365 } else {
3366 return Heap::undefined_value();
3367 }
3368}
3369
3370
3371int Map::IndexInCodeCache(Object* name, Code* code) {
3372 // Get the internal index if a code cache exists.
3373 if (!code_cache()->IsFixedArray()) {
3374 return CodeCache::cast(code_cache())->GetIndex(name, code);
3375 }
3376 return -1;
3377}
3378
3379
3380void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
3381 // No GC is supposed to happen between a call to IndexInCodeCache and
3382 // RemoveFromCodeCache so the code cache must be there.
3383 ASSERT(!code_cache()->IsFixedArray());
3384 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
3385}
3386
3387
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003388void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
3389 Map* current = this;
3390 while (current != Heap::meta_map()) {
3391 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
3392 *RawField(current, Map::kInstanceDescriptorsOffset));
3393 if (d == Heap::empty_descriptor_array()) {
3394 Map* prev = current->map();
3395 current->set_map(Heap::meta_map());
3396 callback(current, data);
3397 current = prev;
3398 continue;
3399 }
3400
3401 FixedArray* contents = reinterpret_cast<FixedArray*>(
3402 d->get(DescriptorArray::kContentArrayIndex));
3403 Object** map_or_index_field = RawField(contents, HeapObject::kMapOffset);
3404 Object* map_or_index = *map_or_index_field;
3405 bool map_done = true;
3406 for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0;
3407 i < contents->length();
3408 i += 2) {
3409 PropertyDetails details(Smi::cast(contents->get(i + 1)));
3410 if (details.IsTransition()) {
3411 Map* next = reinterpret_cast<Map*>(contents->get(i));
3412 next->set_map(current);
3413 *map_or_index_field = Smi::FromInt(i + 2);
3414 current = next;
3415 map_done = false;
3416 break;
3417 }
3418 }
3419 if (!map_done) continue;
3420 *map_or_index_field = Heap::fixed_array_map();
3421 Map* prev = current->map();
3422 current->set_map(Heap::meta_map());
3423 callback(current, data);
3424 current = prev;
3425 }
3426}
3427
3428
John Reck59135872010-11-02 12:39:01 -07003429MaybeObject* CodeCache::Update(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003430 ASSERT(code->ic_state() == MONOMORPHIC);
3431
3432 // The number of monomorphic stubs for normal load/store/call IC's can grow to
3433 // a large number and therefore they need to go into a hash table. They are
3434 // used to load global properties from cells.
3435 if (code->type() == NORMAL) {
3436 // Make sure that a hash table is allocated for the normal load code cache.
3437 if (normal_type_cache()->IsUndefined()) {
John Reck59135872010-11-02 12:39:01 -07003438 Object* result;
3439 { MaybeObject* maybe_result =
3440 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
3441 if (!maybe_result->ToObject(&result)) return maybe_result;
3442 }
Steve Block6ded16b2010-05-10 14:33:55 +01003443 set_normal_type_cache(result);
3444 }
3445 return UpdateNormalTypeCache(name, code);
3446 } else {
3447 ASSERT(default_cache()->IsFixedArray());
3448 return UpdateDefaultCache(name, code);
3449 }
3450}
3451
3452
John Reck59135872010-11-02 12:39:01 -07003453MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003454 // When updating the default code cache we disregard the type encoded in the
Steve Blocka7e24c12009-10-30 11:49:00 +00003455 // flags. This allows call constant stubs to overwrite call field
3456 // stubs, etc.
3457 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
3458
3459 // First check whether we can update existing code cache without
3460 // extending it.
Steve Block6ded16b2010-05-10 14:33:55 +01003461 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003462 int length = cache->length();
3463 int deleted_index = -1;
Steve Block6ded16b2010-05-10 14:33:55 +01003464 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003465 Object* key = cache->get(i);
3466 if (key->IsNull()) {
3467 if (deleted_index < 0) deleted_index = i;
3468 continue;
3469 }
3470 if (key->IsUndefined()) {
3471 if (deleted_index >= 0) i = deleted_index;
Steve Block6ded16b2010-05-10 14:33:55 +01003472 cache->set(i + kCodeCacheEntryNameOffset, name);
3473 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003474 return this;
3475 }
3476 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003477 Code::Flags found =
3478 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
Steve Blocka7e24c12009-10-30 11:49:00 +00003479 if (Code::RemoveTypeFromFlags(found) == flags) {
Steve Block6ded16b2010-05-10 14:33:55 +01003480 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003481 return this;
3482 }
3483 }
3484 }
3485
3486 // Reached the end of the code cache. If there were deleted
3487 // elements, reuse the space for the first of them.
3488 if (deleted_index >= 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01003489 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
3490 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003491 return this;
3492 }
3493
Steve Block6ded16b2010-05-10 14:33:55 +01003494 // Extend the code cache with some new entries (at least one). Must be a
3495 // multiple of the entry size.
3496 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
3497 new_length = new_length - new_length % kCodeCacheEntrySize;
3498 ASSERT((new_length % kCodeCacheEntrySize) == 0);
John Reck59135872010-11-02 12:39:01 -07003499 Object* result;
3500 { MaybeObject* maybe_result = cache->CopySize(new_length);
3501 if (!maybe_result->ToObject(&result)) return maybe_result;
3502 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003503
3504 // Add the (name, code) pair to the new cache.
3505 cache = FixedArray::cast(result);
Steve Block6ded16b2010-05-10 14:33:55 +01003506 cache->set(length + kCodeCacheEntryNameOffset, name);
3507 cache->set(length + kCodeCacheEntryCodeOffset, code);
3508 set_default_cache(cache);
Steve Blocka7e24c12009-10-30 11:49:00 +00003509 return this;
3510}
3511
3512
John Reck59135872010-11-02 12:39:01 -07003513MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003514 // Adding a new entry can cause a new cache to be allocated.
3515 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
John Reck59135872010-11-02 12:39:01 -07003516 Object* new_cache;
3517 { MaybeObject* maybe_new_cache = cache->Put(name, code);
3518 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
3519 }
Steve Block6ded16b2010-05-10 14:33:55 +01003520 set_normal_type_cache(new_cache);
3521 return this;
3522}
3523
3524
3525Object* CodeCache::Lookup(String* name, Code::Flags flags) {
3526 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
3527 return LookupNormalTypeCache(name, flags);
3528 } else {
3529 return LookupDefaultCache(name, flags);
3530 }
3531}
3532
3533
3534Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
3535 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003536 int length = cache->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003537 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
3538 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
Steve Blocka7e24c12009-10-30 11:49:00 +00003539 // Skip deleted elements.
3540 if (key->IsNull()) continue;
3541 if (key->IsUndefined()) return key;
3542 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003543 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
3544 if (code->flags() == flags) {
3545 return code;
3546 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003547 }
3548 }
3549 return Heap::undefined_value();
3550}
3551
3552
Steve Block6ded16b2010-05-10 14:33:55 +01003553Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
3554 if (!normal_type_cache()->IsUndefined()) {
3555 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3556 return cache->Lookup(name, flags);
3557 } else {
3558 return Heap::undefined_value();
3559 }
3560}
3561
3562
3563int CodeCache::GetIndex(Object* name, Code* code) {
3564 if (code->type() == NORMAL) {
3565 if (normal_type_cache()->IsUndefined()) return -1;
3566 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3567 return cache->GetIndex(String::cast(name), code->flags());
3568 }
3569
3570 FixedArray* array = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003571 int len = array->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003572 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
3573 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003574 }
3575 return -1;
3576}
3577
3578
Steve Block6ded16b2010-05-10 14:33:55 +01003579void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
3580 if (code->type() == NORMAL) {
3581 ASSERT(!normal_type_cache()->IsUndefined());
3582 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3583 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
3584 cache->RemoveByIndex(index);
3585 } else {
3586 FixedArray* array = default_cache();
3587 ASSERT(array->length() >= index && array->get(index)->IsCode());
3588 // Use null instead of undefined for deleted elements to distinguish
3589 // deleted elements from unused elements. This distinction is used
3590 // when looking up in the cache and when updating the cache.
3591 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
3592 array->set_null(index - 1); // Name.
3593 array->set_null(index); // Code.
3594 }
3595}
3596
3597
3598// The key in the code cache hash table consists of the property name and the
3599// code object. The actual match is on the name and the code flags. If a key
3600// is created using the flags and not a code object it can only be used for
3601// lookup not to create a new entry.
3602class CodeCacheHashTableKey : public HashTableKey {
3603 public:
3604 CodeCacheHashTableKey(String* name, Code::Flags flags)
3605 : name_(name), flags_(flags), code_(NULL) { }
3606
3607 CodeCacheHashTableKey(String* name, Code* code)
3608 : name_(name),
3609 flags_(code->flags()),
3610 code_(code) { }
3611
3612
3613 bool IsMatch(Object* other) {
3614 if (!other->IsFixedArray()) return false;
3615 FixedArray* pair = FixedArray::cast(other);
3616 String* name = String::cast(pair->get(0));
3617 Code::Flags flags = Code::cast(pair->get(1))->flags();
3618 if (flags != flags_) {
3619 return false;
3620 }
3621 return name_->Equals(name);
3622 }
3623
3624 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
3625 return name->Hash() ^ flags;
3626 }
3627
3628 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
3629
3630 uint32_t HashForObject(Object* obj) {
3631 FixedArray* pair = FixedArray::cast(obj);
3632 String* name = String::cast(pair->get(0));
3633 Code* code = Code::cast(pair->get(1));
3634 return NameFlagsHashHelper(name, code->flags());
3635 }
3636
John Reck59135872010-11-02 12:39:01 -07003637 MUST_USE_RESULT MaybeObject* AsObject() {
Steve Block6ded16b2010-05-10 14:33:55 +01003638 ASSERT(code_ != NULL);
John Reck59135872010-11-02 12:39:01 -07003639 Object* obj;
3640 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(2);
3641 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3642 }
Steve Block6ded16b2010-05-10 14:33:55 +01003643 FixedArray* pair = FixedArray::cast(obj);
3644 pair->set(0, name_);
3645 pair->set(1, code_);
3646 return pair;
3647 }
3648
3649 private:
3650 String* name_;
3651 Code::Flags flags_;
3652 Code* code_;
3653};
3654
3655
3656Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
3657 CodeCacheHashTableKey key(name, flags);
3658 int entry = FindEntry(&key);
3659 if (entry == kNotFound) return Heap::undefined_value();
3660 return get(EntryToIndex(entry) + 1);
3661}
3662
3663
John Reck59135872010-11-02 12:39:01 -07003664MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003665 CodeCacheHashTableKey key(name, code);
John Reck59135872010-11-02 12:39:01 -07003666 Object* obj;
3667 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
3668 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3669 }
Steve Block6ded16b2010-05-10 14:33:55 +01003670
3671 // Don't use this, as the table might have grown.
3672 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
3673
3674 int entry = cache->FindInsertionEntry(key.Hash());
John Reck59135872010-11-02 12:39:01 -07003675 Object* k;
3676 { MaybeObject* maybe_k = key.AsObject();
3677 if (!maybe_k->ToObject(&k)) return maybe_k;
3678 }
Steve Block6ded16b2010-05-10 14:33:55 +01003679
3680 cache->set(EntryToIndex(entry), k);
3681 cache->set(EntryToIndex(entry) + 1, code);
3682 cache->ElementAdded();
3683 return cache;
3684}
3685
3686
3687int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
3688 CodeCacheHashTableKey key(name, flags);
3689 int entry = FindEntry(&key);
3690 return (entry == kNotFound) ? -1 : entry;
3691}
3692
3693
3694void CodeCacheHashTable::RemoveByIndex(int index) {
3695 ASSERT(index >= 0);
3696 set(EntryToIndex(index), Heap::null_value());
3697 set(EntryToIndex(index) + 1, Heap::null_value());
3698 ElementRemoved();
Steve Blocka7e24c12009-10-30 11:49:00 +00003699}
3700
3701
Steve Blocka7e24c12009-10-30 11:49:00 +00003702static bool HasKey(FixedArray* array, Object* key) {
3703 int len0 = array->length();
3704 for (int i = 0; i < len0; i++) {
3705 Object* element = array->get(i);
3706 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3707 if (element->IsString() &&
3708 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3709 return true;
3710 }
3711 }
3712 return false;
3713}
3714
3715
John Reck59135872010-11-02 12:39:01 -07003716MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
Steve Block3ce2e202009-11-05 08:53:23 +00003717 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00003718 switch (array->GetElementsKind()) {
3719 case JSObject::FAST_ELEMENTS:
3720 return UnionOfKeys(FixedArray::cast(array->elements()));
3721 case JSObject::DICTIONARY_ELEMENTS: {
3722 NumberDictionary* dict = array->element_dictionary();
3723 int size = dict->NumberOfElements();
3724
3725 // Allocate a temporary fixed array.
John Reck59135872010-11-02 12:39:01 -07003726 Object* object;
3727 { MaybeObject* maybe_object = Heap::AllocateFixedArray(size);
3728 if (!maybe_object->ToObject(&object)) return maybe_object;
3729 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003730 FixedArray* key_array = FixedArray::cast(object);
3731
3732 int capacity = dict->Capacity();
3733 int pos = 0;
3734 // Copy the elements from the JSArray to the temporary fixed array.
3735 for (int i = 0; i < capacity; i++) {
3736 if (dict->IsKey(dict->KeyAt(i))) {
3737 key_array->set(pos++, dict->ValueAt(i));
3738 }
3739 }
3740 // Compute the union of this and the temporary fixed array.
3741 return UnionOfKeys(key_array);
3742 }
3743 default:
3744 UNREACHABLE();
3745 }
3746 UNREACHABLE();
3747 return Heap::null_value(); // Failure case needs to "return" a value.
3748}
3749
3750
John Reck59135872010-11-02 12:39:01 -07003751MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003752 int len0 = length();
Ben Murdochf87a2032010-10-22 12:50:53 +01003753#ifdef DEBUG
3754 if (FLAG_enable_slow_asserts) {
3755 for (int i = 0; i < len0; i++) {
3756 ASSERT(get(i)->IsString() || get(i)->IsNumber());
3757 }
3758 }
3759#endif
Steve Blocka7e24c12009-10-30 11:49:00 +00003760 int len1 = other->length();
Ben Murdochf87a2032010-10-22 12:50:53 +01003761 // Optimize if 'other' is empty.
3762 // We cannot optimize if 'this' is empty, as other may have holes
3763 // or non keys.
Steve Blocka7e24c12009-10-30 11:49:00 +00003764 if (len1 == 0) return this;
3765
3766 // Compute how many elements are not in this.
3767 int extra = 0;
3768 for (int y = 0; y < len1; y++) {
3769 Object* value = other->get(y);
3770 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3771 }
3772
3773 if (extra == 0) return this;
3774
3775 // Allocate the result
John Reck59135872010-11-02 12:39:01 -07003776 Object* obj;
3777 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(len0 + extra);
3778 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3779 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003780 // Fill in the content
Leon Clarke4515c472010-02-03 11:58:03 +00003781 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003782 FixedArray* result = FixedArray::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00003783 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003784 for (int i = 0; i < len0; i++) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003785 Object* e = get(i);
3786 ASSERT(e->IsString() || e->IsNumber());
3787 result->set(i, e, mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00003788 }
3789 // Fill in the extra keys.
3790 int index = 0;
3791 for (int y = 0; y < len1; y++) {
3792 Object* value = other->get(y);
3793 if (!value->IsTheHole() && !HasKey(this, value)) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003794 Object* e = other->get(y);
3795 ASSERT(e->IsString() || e->IsNumber());
3796 result->set(len0 + index, e, mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00003797 index++;
3798 }
3799 }
3800 ASSERT(extra == index);
3801 return result;
3802}
3803
3804
John Reck59135872010-11-02 12:39:01 -07003805MaybeObject* FixedArray::CopySize(int new_length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003806 if (new_length == 0) return Heap::empty_fixed_array();
John Reck59135872010-11-02 12:39:01 -07003807 Object* obj;
3808 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(new_length);
3809 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3810 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003811 FixedArray* result = FixedArray::cast(obj);
3812 // Copy the content
Leon Clarke4515c472010-02-03 11:58:03 +00003813 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003814 int len = length();
3815 if (new_length < len) len = new_length;
3816 result->set_map(map());
Leon Clarke4515c472010-02-03 11:58:03 +00003817 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003818 for (int i = 0; i < len; i++) {
3819 result->set(i, get(i), mode);
3820 }
3821 return result;
3822}
3823
3824
3825void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
Leon Clarke4515c472010-02-03 11:58:03 +00003826 AssertNoAllocation no_gc;
3827 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003828 for (int index = 0; index < len; index++) {
3829 dest->set(dest_pos+index, get(pos+index), mode);
3830 }
3831}
3832
3833
3834#ifdef DEBUG
3835bool FixedArray::IsEqualTo(FixedArray* other) {
3836 if (length() != other->length()) return false;
3837 for (int i = 0 ; i < length(); ++i) {
3838 if (get(i) != other->get(i)) return false;
3839 }
3840 return true;
3841}
3842#endif
3843
3844
John Reck59135872010-11-02 12:39:01 -07003845MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003846 if (number_of_descriptors == 0) {
3847 return Heap::empty_descriptor_array();
3848 }
3849 // Allocate the array of keys.
John Reck59135872010-11-02 12:39:01 -07003850 Object* array;
3851 { MaybeObject* maybe_array =
3852 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
3853 if (!maybe_array->ToObject(&array)) return maybe_array;
3854 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003855 // Do not use DescriptorArray::cast on incomplete object.
3856 FixedArray* result = FixedArray::cast(array);
3857
3858 // Allocate the content array and set it in the descriptor array.
John Reck59135872010-11-02 12:39:01 -07003859 { MaybeObject* maybe_array =
3860 Heap::AllocateFixedArray(number_of_descriptors << 1);
3861 if (!maybe_array->ToObject(&array)) return maybe_array;
3862 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003863 result->set(kContentArrayIndex, array);
3864 result->set(kEnumerationIndexIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00003865 Smi::FromInt(PropertyDetails::kInitialIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +00003866 return result;
3867}
3868
3869
3870void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3871 FixedArray* new_cache) {
3872 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3873 if (HasEnumCache()) {
3874 FixedArray::cast(get(kEnumerationIndexIndex))->
3875 set(kEnumCacheBridgeCacheIndex, new_cache);
3876 } else {
3877 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3878 FixedArray::cast(bridge_storage)->
3879 set(kEnumCacheBridgeCacheIndex, new_cache);
3880 fast_set(FixedArray::cast(bridge_storage),
3881 kEnumCacheBridgeEnumIndex,
3882 get(kEnumerationIndexIndex));
3883 set(kEnumerationIndexIndex, bridge_storage);
3884 }
3885}
3886
3887
John Reck59135872010-11-02 12:39:01 -07003888MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
3889 TransitionFlag transition_flag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003890 // Transitions are only kept when inserting another transition.
3891 // This precondition is not required by this function's implementation, but
3892 // is currently required by the semantics of maps, so we check it.
3893 // Conversely, we filter after replacing, so replacing a transition and
3894 // removing all other transitions is not supported.
3895 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3896 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
3897 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
3898
3899 // Ensure the key is a symbol.
John Reck59135872010-11-02 12:39:01 -07003900 Object* result;
3901 { MaybeObject* maybe_result = descriptor->KeyToSymbol();
3902 if (!maybe_result->ToObject(&result)) return maybe_result;
3903 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003904
3905 int transitions = 0;
3906 int null_descriptors = 0;
3907 if (remove_transitions) {
3908 for (int i = 0; i < number_of_descriptors(); i++) {
3909 if (IsTransition(i)) transitions++;
3910 if (IsNullDescriptor(i)) null_descriptors++;
3911 }
3912 } else {
3913 for (int i = 0; i < number_of_descriptors(); i++) {
3914 if (IsNullDescriptor(i)) null_descriptors++;
3915 }
3916 }
3917 int new_size = number_of_descriptors() - transitions - null_descriptors;
3918
3919 // If key is in descriptor, we replace it in-place when filtering.
3920 // Count a null descriptor for key as inserted, not replaced.
3921 int index = Search(descriptor->GetKey());
3922 const bool inserting = (index == kNotFound);
3923 const bool replacing = !inserting;
3924 bool keep_enumeration_index = false;
3925 if (inserting) {
3926 ++new_size;
3927 }
3928 if (replacing) {
3929 // We are replacing an existing descriptor. We keep the enumeration
3930 // index of a visible property.
3931 PropertyType t = PropertyDetails(GetDetails(index)).type();
3932 if (t == CONSTANT_FUNCTION ||
3933 t == FIELD ||
3934 t == CALLBACKS ||
3935 t == INTERCEPTOR) {
3936 keep_enumeration_index = true;
3937 } else if (remove_transitions) {
3938 // Replaced descriptor has been counted as removed if it is
3939 // a transition that will be replaced. Adjust count in this case.
3940 ++new_size;
3941 }
3942 }
John Reck59135872010-11-02 12:39:01 -07003943 { MaybeObject* maybe_result = Allocate(new_size);
3944 if (!maybe_result->ToObject(&result)) return maybe_result;
3945 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003946 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3947 // Set the enumeration index in the descriptors and set the enumeration index
3948 // in the result.
3949 int enumeration_index = NextEnumerationIndex();
3950 if (!descriptor->GetDetails().IsTransition()) {
3951 if (keep_enumeration_index) {
3952 descriptor->SetEnumerationIndex(
3953 PropertyDetails(GetDetails(index)).index());
3954 } else {
3955 descriptor->SetEnumerationIndex(enumeration_index);
3956 ++enumeration_index;
3957 }
3958 }
3959 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3960
3961 // Copy the descriptors, filtering out transitions and null descriptors,
3962 // and inserting or replacing a descriptor.
3963 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
3964 int from_index = 0;
3965 int to_index = 0;
3966
3967 for (; from_index < number_of_descriptors(); from_index++) {
3968 String* key = GetKey(from_index);
3969 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
3970 break;
3971 }
3972 if (IsNullDescriptor(from_index)) continue;
3973 if (remove_transitions && IsTransition(from_index)) continue;
3974 new_descriptors->CopyFrom(to_index++, this, from_index);
3975 }
3976
3977 new_descriptors->Set(to_index++, descriptor);
3978 if (replacing) from_index++;
3979
3980 for (; from_index < number_of_descriptors(); from_index++) {
3981 if (IsNullDescriptor(from_index)) continue;
3982 if (remove_transitions && IsTransition(from_index)) continue;
3983 new_descriptors->CopyFrom(to_index++, this, from_index);
3984 }
3985
3986 ASSERT(to_index == new_descriptors->number_of_descriptors());
3987 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
3988
3989 return new_descriptors;
3990}
3991
3992
John Reck59135872010-11-02 12:39:01 -07003993MaybeObject* DescriptorArray::RemoveTransitions() {
Steve Blocka7e24c12009-10-30 11:49:00 +00003994 // Remove all transitions and null descriptors. Return a copy of the array
3995 // with all transitions removed, or a Failure object if the new array could
3996 // not be allocated.
3997
3998 // Compute the size of the map transition entries to be removed.
3999 int num_removed = 0;
4000 for (int i = 0; i < number_of_descriptors(); i++) {
4001 if (!IsProperty(i)) num_removed++;
4002 }
4003
4004 // Allocate the new descriptor array.
John Reck59135872010-11-02 12:39:01 -07004005 Object* result;
4006 { MaybeObject* maybe_result = Allocate(number_of_descriptors() - num_removed);
4007 if (!maybe_result->ToObject(&result)) return maybe_result;
4008 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004009 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
4010
4011 // Copy the content.
4012 int next_descriptor = 0;
4013 for (int i = 0; i < number_of_descriptors(); i++) {
4014 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
4015 }
4016 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
4017
4018 return new_descriptors;
4019}
4020
4021
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004022void DescriptorArray::SortUnchecked() {
Steve Blocka7e24c12009-10-30 11:49:00 +00004023 // In-place heap sort.
4024 int len = number_of_descriptors();
4025
4026 // Bottom-up max-heap construction.
Steve Block6ded16b2010-05-10 14:33:55 +01004027 // Index of the last node with children
4028 const int max_parent_index = (len / 2) - 1;
4029 for (int i = max_parent_index; i >= 0; --i) {
4030 int parent_index = i;
4031 const uint32_t parent_hash = GetKey(i)->Hash();
4032 while (parent_index <= max_parent_index) {
4033 int child_index = 2 * parent_index + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00004034 uint32_t child_hash = GetKey(child_index)->Hash();
Steve Block6ded16b2010-05-10 14:33:55 +01004035 if (child_index + 1 < len) {
4036 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4037 if (right_child_hash > child_hash) {
4038 child_index++;
4039 child_hash = right_child_hash;
4040 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004041 }
Steve Block6ded16b2010-05-10 14:33:55 +01004042 if (child_hash <= parent_hash) break;
4043 Swap(parent_index, child_index);
4044 // Now element at child_index could be < its children.
4045 parent_index = child_index; // parent_hash remains correct.
Steve Blocka7e24c12009-10-30 11:49:00 +00004046 }
4047 }
4048
4049 // Extract elements and create sorted array.
4050 for (int i = len - 1; i > 0; --i) {
4051 // Put max element at the back of the array.
4052 Swap(0, i);
4053 // Sift down the new top element.
4054 int parent_index = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01004055 const uint32_t parent_hash = GetKey(parent_index)->Hash();
4056 const int max_parent_index = (i / 2) - 1;
4057 while (parent_index <= max_parent_index) {
4058 int child_index = parent_index * 2 + 1;
4059 uint32_t child_hash = GetKey(child_index)->Hash();
4060 if (child_index + 1 < i) {
4061 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4062 if (right_child_hash > child_hash) {
4063 child_index++;
4064 child_hash = right_child_hash;
4065 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004066 }
Steve Block6ded16b2010-05-10 14:33:55 +01004067 if (child_hash <= parent_hash) break;
4068 Swap(parent_index, child_index);
4069 parent_index = child_index;
Steve Blocka7e24c12009-10-30 11:49:00 +00004070 }
4071 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004072}
Steve Blocka7e24c12009-10-30 11:49:00 +00004073
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004074
4075void DescriptorArray::Sort() {
4076 SortUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00004077 SLOW_ASSERT(IsSortedNoDuplicates());
4078}
4079
4080
4081int DescriptorArray::BinarySearch(String* name, int low, int high) {
4082 uint32_t hash = name->Hash();
4083
4084 while (low <= high) {
4085 int mid = (low + high) / 2;
4086 String* mid_name = GetKey(mid);
4087 uint32_t mid_hash = mid_name->Hash();
4088
4089 if (mid_hash > hash) {
4090 high = mid - 1;
4091 continue;
4092 }
4093 if (mid_hash < hash) {
4094 low = mid + 1;
4095 continue;
4096 }
4097 // Found an element with the same hash-code.
4098 ASSERT(hash == mid_hash);
4099 // There might be more, so we find the first one and
4100 // check them all to see if we have a match.
4101 if (name == mid_name && !is_null_descriptor(mid)) return mid;
4102 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
4103 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
4104 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
4105 }
4106 break;
4107 }
4108 return kNotFound;
4109}
4110
4111
4112int DescriptorArray::LinearSearch(String* name, int len) {
4113 uint32_t hash = name->Hash();
4114 for (int number = 0; number < len; number++) {
4115 String* entry = GetKey(number);
4116 if ((entry->Hash() == hash) &&
4117 name->Equals(entry) &&
4118 !is_null_descriptor(number)) {
4119 return number;
4120 }
4121 }
4122 return kNotFound;
4123}
4124
4125
4126#ifdef DEBUG
4127bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
4128 if (IsEmpty()) return other->IsEmpty();
4129 if (other->IsEmpty()) return false;
4130 if (length() != other->length()) return false;
4131 for (int i = 0; i < length(); ++i) {
4132 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
4133 }
4134 return GetContentArray()->IsEqualTo(other->GetContentArray());
4135}
4136#endif
4137
4138
4139static StaticResource<StringInputBuffer> string_input_buffer;
4140
4141
4142bool String::LooksValid() {
4143 if (!Heap::Contains(this)) return false;
4144 return true;
4145}
4146
4147
4148int String::Utf8Length() {
4149 if (IsAsciiRepresentation()) return length();
4150 // Attempt to flatten before accessing the string. It probably
4151 // doesn't make Utf8Length faster, but it is very likely that
4152 // the string will be accessed later (for example by WriteUtf8)
4153 // so it's still a good idea.
Steve Block6ded16b2010-05-10 14:33:55 +01004154 TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00004155 Access<StringInputBuffer> buffer(&string_input_buffer);
4156 buffer->Reset(0, this);
4157 int result = 0;
4158 while (buffer->has_more())
4159 result += unibrow::Utf8::Length(buffer->GetNext());
4160 return result;
4161}
4162
4163
4164Vector<const char> String::ToAsciiVector() {
4165 ASSERT(IsAsciiRepresentation());
4166 ASSERT(IsFlat());
4167
4168 int offset = 0;
4169 int length = this->length();
4170 StringRepresentationTag string_tag = StringShape(this).representation_tag();
4171 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00004172 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004173 ConsString* cons = ConsString::cast(string);
4174 ASSERT(cons->second()->length() == 0);
4175 string = cons->first();
4176 string_tag = StringShape(string).representation_tag();
4177 }
4178 if (string_tag == kSeqStringTag) {
4179 SeqAsciiString* seq = SeqAsciiString::cast(string);
4180 char* start = seq->GetChars();
4181 return Vector<const char>(start + offset, length);
4182 }
4183 ASSERT(string_tag == kExternalStringTag);
4184 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
4185 const char* start = ext->resource()->data();
4186 return Vector<const char>(start + offset, length);
4187}
4188
4189
4190Vector<const uc16> String::ToUC16Vector() {
4191 ASSERT(IsTwoByteRepresentation());
4192 ASSERT(IsFlat());
4193
4194 int offset = 0;
4195 int length = this->length();
4196 StringRepresentationTag string_tag = StringShape(this).representation_tag();
4197 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00004198 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004199 ConsString* cons = ConsString::cast(string);
4200 ASSERT(cons->second()->length() == 0);
4201 string = cons->first();
4202 string_tag = StringShape(string).representation_tag();
4203 }
4204 if (string_tag == kSeqStringTag) {
4205 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
4206 return Vector<const uc16>(seq->GetChars() + offset, length);
4207 }
4208 ASSERT(string_tag == kExternalStringTag);
4209 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
4210 const uc16* start =
4211 reinterpret_cast<const uc16*>(ext->resource()->data());
4212 return Vector<const uc16>(start + offset, length);
4213}
4214
4215
4216SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
4217 RobustnessFlag robust_flag,
4218 int offset,
4219 int length,
4220 int* length_return) {
4221 ASSERT(NativeAllocationChecker::allocation_allowed());
4222 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
4223 return SmartPointer<char>(NULL);
4224 }
4225
4226 // Negative length means the to the end of the string.
4227 if (length < 0) length = kMaxInt - offset;
4228
4229 // Compute the size of the UTF-8 string. Start at the specified offset.
4230 Access<StringInputBuffer> buffer(&string_input_buffer);
4231 buffer->Reset(offset, this);
4232 int character_position = offset;
4233 int utf8_bytes = 0;
4234 while (buffer->has_more()) {
4235 uint16_t character = buffer->GetNext();
4236 if (character_position < offset + length) {
4237 utf8_bytes += unibrow::Utf8::Length(character);
4238 }
4239 character_position++;
4240 }
4241
4242 if (length_return) {
4243 *length_return = utf8_bytes;
4244 }
4245
4246 char* result = NewArray<char>(utf8_bytes + 1);
4247
4248 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
4249 buffer->Rewind();
4250 buffer->Seek(offset);
4251 character_position = offset;
4252 int utf8_byte_position = 0;
4253 while (buffer->has_more()) {
4254 uint16_t character = buffer->GetNext();
4255 if (character_position < offset + length) {
4256 if (allow_nulls == DISALLOW_NULLS && character == 0) {
4257 character = ' ';
4258 }
4259 utf8_byte_position +=
4260 unibrow::Utf8::Encode(result + utf8_byte_position, character);
4261 }
4262 character_position++;
4263 }
4264 result[utf8_byte_position] = 0;
4265 return SmartPointer<char>(result);
4266}
4267
4268
4269SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
4270 RobustnessFlag robust_flag,
4271 int* length_return) {
4272 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
4273}
4274
4275
4276const uc16* String::GetTwoByteData() {
4277 return GetTwoByteData(0);
4278}
4279
4280
4281const uc16* String::GetTwoByteData(unsigned start) {
4282 ASSERT(!IsAsciiRepresentation());
4283 switch (StringShape(this).representation_tag()) {
4284 case kSeqStringTag:
4285 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
4286 case kExternalStringTag:
4287 return ExternalTwoByteString::cast(this)->
4288 ExternalTwoByteStringGetData(start);
Steve Blocka7e24c12009-10-30 11:49:00 +00004289 case kConsStringTag:
4290 UNREACHABLE();
4291 return NULL;
4292 }
4293 UNREACHABLE();
4294 return NULL;
4295}
4296
4297
4298SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
4299 ASSERT(NativeAllocationChecker::allocation_allowed());
4300
4301 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
4302 return SmartPointer<uc16>();
4303 }
4304
4305 Access<StringInputBuffer> buffer(&string_input_buffer);
4306 buffer->Reset(this);
4307
4308 uc16* result = NewArray<uc16>(length() + 1);
4309
4310 int i = 0;
4311 while (buffer->has_more()) {
4312 uint16_t character = buffer->GetNext();
4313 result[i++] = character;
4314 }
4315 result[i] = 0;
4316 return SmartPointer<uc16>(result);
4317}
4318
4319
4320const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
4321 return reinterpret_cast<uc16*>(
4322 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
4323}
4324
4325
4326void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4327 unsigned* offset_ptr,
4328 unsigned max_chars) {
4329 unsigned chars_read = 0;
4330 unsigned offset = *offset_ptr;
4331 while (chars_read < max_chars) {
4332 uint16_t c = *reinterpret_cast<uint16_t*>(
4333 reinterpret_cast<char*>(this) -
4334 kHeapObjectTag + kHeaderSize + offset * kShortSize);
4335 if (c <= kMaxAsciiCharCode) {
4336 // Fast case for ASCII characters. Cursor is an input output argument.
4337 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4338 rbb->util_buffer,
4339 rbb->capacity,
4340 rbb->cursor)) {
4341 break;
4342 }
4343 } else {
4344 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4345 rbb->util_buffer,
4346 rbb->capacity,
4347 rbb->cursor)) {
4348 break;
4349 }
4350 }
4351 offset++;
4352 chars_read++;
4353 }
4354 *offset_ptr = offset;
4355 rbb->remaining += chars_read;
4356}
4357
4358
4359const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
4360 unsigned* remaining,
4361 unsigned* offset_ptr,
4362 unsigned max_chars) {
4363 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
4364 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
4365 *remaining = max_chars;
4366 *offset_ptr += max_chars;
4367 return b;
4368}
4369
4370
4371// This will iterate unless the block of string data spans two 'halves' of
4372// a ConsString, in which case it will recurse. Since the block of string
4373// data to be read has a maximum size this limits the maximum recursion
4374// depth to something sane. Since C++ does not have tail call recursion
4375// elimination, the iteration must be explicit. Since this is not an
4376// -IntoBuffer method it can delegate to one of the efficient
4377// *AsciiStringReadBlock routines.
4378const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
4379 unsigned* offset_ptr,
4380 unsigned max_chars) {
4381 ConsString* current = this;
4382 unsigned offset = *offset_ptr;
4383 int offset_correction = 0;
4384
4385 while (true) {
4386 String* left = current->first();
4387 unsigned left_length = (unsigned)left->length();
4388 if (left_length > offset &&
4389 (max_chars <= left_length - offset ||
4390 (rbb->capacity <= left_length - offset &&
4391 (max_chars = left_length - offset, true)))) { // comma operator!
4392 // Left hand side only - iterate unless we have reached the bottom of
4393 // the cons tree. The assignment on the left of the comma operator is
4394 // in order to make use of the fact that the -IntoBuffer routines can
4395 // produce at most 'capacity' characters. This enables us to postpone
4396 // the point where we switch to the -IntoBuffer routines (below) in order
4397 // to maximize the chances of delegating a big chunk of work to the
4398 // efficient *AsciiStringReadBlock routines.
4399 if (StringShape(left).IsCons()) {
4400 current = ConsString::cast(left);
4401 continue;
4402 } else {
4403 const unibrow::byte* answer =
4404 String::ReadBlock(left, rbb, &offset, max_chars);
4405 *offset_ptr = offset + offset_correction;
4406 return answer;
4407 }
4408 } else if (left_length <= offset) {
4409 // Right hand side only - iterate unless we have reached the bottom of
4410 // the cons tree.
4411 String* right = current->second();
4412 offset -= left_length;
4413 offset_correction += left_length;
4414 if (StringShape(right).IsCons()) {
4415 current = ConsString::cast(right);
4416 continue;
4417 } else {
4418 const unibrow::byte* answer =
4419 String::ReadBlock(right, rbb, &offset, max_chars);
4420 *offset_ptr = offset + offset_correction;
4421 return answer;
4422 }
4423 } else {
4424 // The block to be read spans two sides of the ConsString, so we call the
4425 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
4426 // are able to assemble data from several part strings because they use
4427 // the util_buffer to store their data and never return direct pointers
4428 // to their storage. We don't try to read more than the buffer capacity
4429 // here or we can get too much recursion.
4430 ASSERT(rbb->remaining == 0);
4431 ASSERT(rbb->cursor == 0);
4432 current->ConsStringReadBlockIntoBuffer(
4433 rbb,
4434 &offset,
4435 max_chars > rbb->capacity ? rbb->capacity : max_chars);
4436 *offset_ptr = offset + offset_correction;
4437 return rbb->util_buffer;
4438 }
4439 }
4440}
4441
4442
Steve Blocka7e24c12009-10-30 11:49:00 +00004443uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
4444 ASSERT(index >= 0 && index < length());
4445 return resource()->data()[index];
4446}
4447
4448
4449const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
4450 unsigned* remaining,
4451 unsigned* offset_ptr,
4452 unsigned max_chars) {
4453 // Cast const char* to unibrow::byte* (signedness difference).
4454 const unibrow::byte* b =
4455 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
4456 *remaining = max_chars;
4457 *offset_ptr += max_chars;
4458 return b;
4459}
4460
4461
4462const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
4463 unsigned start) {
4464 return resource()->data() + start;
4465}
4466
4467
4468uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
4469 ASSERT(index >= 0 && index < length());
4470 return resource()->data()[index];
4471}
4472
4473
4474void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
4475 ReadBlockBuffer* rbb,
4476 unsigned* offset_ptr,
4477 unsigned max_chars) {
4478 unsigned chars_read = 0;
4479 unsigned offset = *offset_ptr;
4480 const uint16_t* data = resource()->data();
4481 while (chars_read < max_chars) {
4482 uint16_t c = data[offset];
4483 if (c <= kMaxAsciiCharCode) {
4484 // Fast case for ASCII characters. Cursor is an input output argument.
4485 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4486 rbb->util_buffer,
4487 rbb->capacity,
4488 rbb->cursor))
4489 break;
4490 } else {
4491 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4492 rbb->util_buffer,
4493 rbb->capacity,
4494 rbb->cursor))
4495 break;
4496 }
4497 offset++;
4498 chars_read++;
4499 }
4500 *offset_ptr = offset;
4501 rbb->remaining += chars_read;
4502}
4503
4504
4505void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4506 unsigned* offset_ptr,
4507 unsigned max_chars) {
4508 unsigned capacity = rbb->capacity - rbb->cursor;
4509 if (max_chars > capacity) max_chars = capacity;
4510 memcpy(rbb->util_buffer + rbb->cursor,
4511 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
4512 *offset_ptr * kCharSize,
4513 max_chars);
4514 rbb->remaining += max_chars;
4515 *offset_ptr += max_chars;
4516 rbb->cursor += max_chars;
4517}
4518
4519
4520void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
4521 ReadBlockBuffer* rbb,
4522 unsigned* offset_ptr,
4523 unsigned max_chars) {
4524 unsigned capacity = rbb->capacity - rbb->cursor;
4525 if (max_chars > capacity) max_chars = capacity;
4526 memcpy(rbb->util_buffer + rbb->cursor,
4527 resource()->data() + *offset_ptr,
4528 max_chars);
4529 rbb->remaining += max_chars;
4530 *offset_ptr += max_chars;
4531 rbb->cursor += max_chars;
4532}
4533
4534
4535// This method determines the type of string involved and then copies
4536// a whole chunk of characters into a buffer, or returns a pointer to a buffer
4537// where they can be found. The pointer is not necessarily valid across a GC
4538// (see AsciiStringReadBlock).
4539const unibrow::byte* String::ReadBlock(String* input,
4540 ReadBlockBuffer* rbb,
4541 unsigned* offset_ptr,
4542 unsigned max_chars) {
4543 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
4544 if (max_chars == 0) {
4545 rbb->remaining = 0;
4546 return NULL;
4547 }
4548 switch (StringShape(input).representation_tag()) {
4549 case kSeqStringTag:
4550 if (input->IsAsciiRepresentation()) {
4551 SeqAsciiString* str = SeqAsciiString::cast(input);
4552 return str->SeqAsciiStringReadBlock(&rbb->remaining,
4553 offset_ptr,
4554 max_chars);
4555 } else {
4556 SeqTwoByteString* str = SeqTwoByteString::cast(input);
4557 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4558 offset_ptr,
4559 max_chars);
4560 return rbb->util_buffer;
4561 }
4562 case kConsStringTag:
4563 return ConsString::cast(input)->ConsStringReadBlock(rbb,
4564 offset_ptr,
4565 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004566 case kExternalStringTag:
4567 if (input->IsAsciiRepresentation()) {
4568 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
4569 &rbb->remaining,
4570 offset_ptr,
4571 max_chars);
4572 } else {
4573 ExternalTwoByteString::cast(input)->
4574 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4575 offset_ptr,
4576 max_chars);
4577 return rbb->util_buffer;
4578 }
4579 default:
4580 break;
4581 }
4582
4583 UNREACHABLE();
4584 return 0;
4585}
4586
4587
4588Relocatable* Relocatable::top_ = NULL;
4589
4590
4591void Relocatable::PostGarbageCollectionProcessing() {
4592 Relocatable* current = top_;
4593 while (current != NULL) {
4594 current->PostGarbageCollection();
4595 current = current->prev_;
4596 }
4597}
4598
4599
4600// Reserve space for statics needing saving and restoring.
4601int Relocatable::ArchiveSpacePerThread() {
4602 return sizeof(top_);
4603}
4604
4605
4606// Archive statics that are thread local.
4607char* Relocatable::ArchiveState(char* to) {
4608 *reinterpret_cast<Relocatable**>(to) = top_;
4609 top_ = NULL;
4610 return to + ArchiveSpacePerThread();
4611}
4612
4613
4614// Restore statics that are thread local.
4615char* Relocatable::RestoreState(char* from) {
4616 top_ = *reinterpret_cast<Relocatable**>(from);
4617 return from + ArchiveSpacePerThread();
4618}
4619
4620
4621char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
4622 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
4623 Iterate(v, top);
4624 return thread_storage + ArchiveSpacePerThread();
4625}
4626
4627
4628void Relocatable::Iterate(ObjectVisitor* v) {
4629 Iterate(v, top_);
4630}
4631
4632
4633void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
4634 Relocatable* current = top;
4635 while (current != NULL) {
4636 current->IterateInstance(v);
4637 current = current->prev_;
4638 }
4639}
4640
4641
4642FlatStringReader::FlatStringReader(Handle<String> str)
4643 : str_(str.location()),
4644 length_(str->length()) {
4645 PostGarbageCollection();
4646}
4647
4648
4649FlatStringReader::FlatStringReader(Vector<const char> input)
4650 : str_(0),
4651 is_ascii_(true),
4652 length_(input.length()),
4653 start_(input.start()) { }
4654
4655
4656void FlatStringReader::PostGarbageCollection() {
4657 if (str_ == NULL) return;
4658 Handle<String> str(str_);
4659 ASSERT(str->IsFlat());
4660 is_ascii_ = str->IsAsciiRepresentation();
4661 if (is_ascii_) {
4662 start_ = str->ToAsciiVector().start();
4663 } else {
4664 start_ = str->ToUC16Vector().start();
4665 }
4666}
4667
4668
4669void StringInputBuffer::Seek(unsigned pos) {
4670 Reset(pos, input_);
4671}
4672
4673
4674void SafeStringInputBuffer::Seek(unsigned pos) {
4675 Reset(pos, input_);
4676}
4677
4678
4679// This method determines the type of string involved and then copies
4680// a whole chunk of characters into a buffer. It can be used with strings
4681// that have been glued together to form a ConsString and which must cooperate
4682// to fill up a buffer.
4683void String::ReadBlockIntoBuffer(String* input,
4684 ReadBlockBuffer* rbb,
4685 unsigned* offset_ptr,
4686 unsigned max_chars) {
4687 ASSERT(*offset_ptr <= (unsigned)input->length());
4688 if (max_chars == 0) return;
4689
4690 switch (StringShape(input).representation_tag()) {
4691 case kSeqStringTag:
4692 if (input->IsAsciiRepresentation()) {
4693 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4694 offset_ptr,
4695 max_chars);
4696 return;
4697 } else {
4698 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4699 offset_ptr,
4700 max_chars);
4701 return;
4702 }
4703 case kConsStringTag:
4704 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4705 offset_ptr,
4706 max_chars);
4707 return;
Steve Blocka7e24c12009-10-30 11:49:00 +00004708 case kExternalStringTag:
4709 if (input->IsAsciiRepresentation()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004710 ExternalAsciiString::cast(input)->
4711 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4712 } else {
4713 ExternalTwoByteString::cast(input)->
4714 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4715 offset_ptr,
4716 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004717 }
4718 return;
4719 default:
4720 break;
4721 }
4722
4723 UNREACHABLE();
4724 return;
4725}
4726
4727
4728const unibrow::byte* String::ReadBlock(String* input,
4729 unibrow::byte* util_buffer,
4730 unsigned capacity,
4731 unsigned* remaining,
4732 unsigned* offset_ptr) {
4733 ASSERT(*offset_ptr <= (unsigned)input->length());
4734 unsigned chars = input->length() - *offset_ptr;
4735 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4736 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4737 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4738 *remaining = rbb.remaining;
4739 return answer;
4740}
4741
4742
4743const unibrow::byte* String::ReadBlock(String** raw_input,
4744 unibrow::byte* util_buffer,
4745 unsigned capacity,
4746 unsigned* remaining,
4747 unsigned* offset_ptr) {
4748 Handle<String> input(raw_input);
4749 ASSERT(*offset_ptr <= (unsigned)input->length());
4750 unsigned chars = input->length() - *offset_ptr;
4751 if (chars > capacity) chars = capacity;
4752 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4753 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4754 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4755 *remaining = rbb.remaining;
4756 return rbb.util_buffer;
4757}
4758
4759
4760// This will iterate unless the block of string data spans two 'halves' of
4761// a ConsString, in which case it will recurse. Since the block of string
4762// data to be read has a maximum size this limits the maximum recursion
4763// depth to something sane. Since C++ does not have tail call recursion
4764// elimination, the iteration must be explicit.
4765void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4766 unsigned* offset_ptr,
4767 unsigned max_chars) {
4768 ConsString* current = this;
4769 unsigned offset = *offset_ptr;
4770 int offset_correction = 0;
4771
4772 while (true) {
4773 String* left = current->first();
4774 unsigned left_length = (unsigned)left->length();
4775 if (left_length > offset &&
4776 max_chars <= left_length - offset) {
4777 // Left hand side only - iterate unless we have reached the bottom of
4778 // the cons tree.
4779 if (StringShape(left).IsCons()) {
4780 current = ConsString::cast(left);
4781 continue;
4782 } else {
4783 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4784 *offset_ptr = offset + offset_correction;
4785 return;
4786 }
4787 } else if (left_length <= offset) {
4788 // Right hand side only - iterate unless we have reached the bottom of
4789 // the cons tree.
4790 offset -= left_length;
4791 offset_correction += left_length;
4792 String* right = current->second();
4793 if (StringShape(right).IsCons()) {
4794 current = ConsString::cast(right);
4795 continue;
4796 } else {
4797 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4798 *offset_ptr = offset + offset_correction;
4799 return;
4800 }
4801 } else {
4802 // The block to be read spans two sides of the ConsString, so we recurse.
4803 // First recurse on the left.
4804 max_chars -= left_length - offset;
4805 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4806 // We may have reached the max or there may not have been enough space
4807 // in the buffer for the characters in the left hand side.
4808 if (offset == left_length) {
4809 // Recurse on the right.
4810 String* right = String::cast(current->second());
4811 offset -= left_length;
4812 offset_correction += left_length;
4813 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4814 }
4815 *offset_ptr = offset + offset_correction;
4816 return;
4817 }
4818 }
4819}
4820
4821
Steve Blocka7e24c12009-10-30 11:49:00 +00004822uint16_t ConsString::ConsStringGet(int index) {
4823 ASSERT(index >= 0 && index < this->length());
4824
4825 // Check for a flattened cons string
4826 if (second()->length() == 0) {
4827 String* left = first();
4828 return left->Get(index);
4829 }
4830
4831 String* string = String::cast(this);
4832
4833 while (true) {
4834 if (StringShape(string).IsCons()) {
4835 ConsString* cons_string = ConsString::cast(string);
4836 String* left = cons_string->first();
4837 if (left->length() > index) {
4838 string = left;
4839 } else {
4840 index -= left->length();
4841 string = cons_string->second();
4842 }
4843 } else {
4844 return string->Get(index);
4845 }
4846 }
4847
4848 UNREACHABLE();
4849 return 0;
4850}
4851
4852
4853template <typename sinkchar>
4854void String::WriteToFlat(String* src,
4855 sinkchar* sink,
4856 int f,
4857 int t) {
4858 String* source = src;
4859 int from = f;
4860 int to = t;
4861 while (true) {
4862 ASSERT(0 <= from && from <= to && to <= source->length());
4863 switch (StringShape(source).full_representation_tag()) {
4864 case kAsciiStringTag | kExternalStringTag: {
4865 CopyChars(sink,
4866 ExternalAsciiString::cast(source)->resource()->data() + from,
4867 to - from);
4868 return;
4869 }
4870 case kTwoByteStringTag | kExternalStringTag: {
4871 const uc16* data =
4872 ExternalTwoByteString::cast(source)->resource()->data();
4873 CopyChars(sink,
4874 data + from,
4875 to - from);
4876 return;
4877 }
4878 case kAsciiStringTag | kSeqStringTag: {
4879 CopyChars(sink,
4880 SeqAsciiString::cast(source)->GetChars() + from,
4881 to - from);
4882 return;
4883 }
4884 case kTwoByteStringTag | kSeqStringTag: {
4885 CopyChars(sink,
4886 SeqTwoByteString::cast(source)->GetChars() + from,
4887 to - from);
4888 return;
4889 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004890 case kAsciiStringTag | kConsStringTag:
4891 case kTwoByteStringTag | kConsStringTag: {
4892 ConsString* cons_string = ConsString::cast(source);
4893 String* first = cons_string->first();
4894 int boundary = first->length();
4895 if (to - boundary >= boundary - from) {
4896 // Right hand side is longer. Recurse over left.
4897 if (from < boundary) {
4898 WriteToFlat(first, sink, from, boundary);
4899 sink += boundary - from;
4900 from = 0;
4901 } else {
4902 from -= boundary;
4903 }
4904 to -= boundary;
4905 source = cons_string->second();
4906 } else {
4907 // Left hand side is longer. Recurse over right.
4908 if (to > boundary) {
4909 String* second = cons_string->second();
4910 WriteToFlat(second,
4911 sink + boundary - from,
4912 0,
4913 to - boundary);
4914 to = boundary;
4915 }
4916 source = first;
4917 }
4918 break;
4919 }
4920 }
4921 }
4922}
4923
4924
Steve Blocka7e24c12009-10-30 11:49:00 +00004925template <typename IteratorA, typename IteratorB>
4926static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4927 // General slow case check. We know that the ia and ib iterators
4928 // have the same length.
4929 while (ia->has_more()) {
4930 uc32 ca = ia->GetNext();
4931 uc32 cb = ib->GetNext();
4932 if (ca != cb)
4933 return false;
4934 }
4935 return true;
4936}
4937
4938
4939// Compares the contents of two strings by reading and comparing
4940// int-sized blocks of characters.
4941template <typename Char>
4942static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4943 int length = a.length();
4944 ASSERT_EQ(length, b.length());
4945 const Char* pa = a.start();
4946 const Char* pb = b.start();
4947 int i = 0;
4948#ifndef V8_HOST_CAN_READ_UNALIGNED
4949 // If this architecture isn't comfortable reading unaligned ints
4950 // then we have to check that the strings are aligned before
4951 // comparing them blockwise.
4952 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4953 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4954 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
4955 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
4956#endif
4957 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
4958 int endpoint = length - kStepSize;
4959 // Compare blocks until we reach near the end of the string.
4960 for (; i <= endpoint; i += kStepSize) {
4961 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
4962 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
4963 if (wa != wb) {
4964 return false;
4965 }
4966 }
4967#ifndef V8_HOST_CAN_READ_UNALIGNED
4968 }
4969#endif
4970 // Compare the remaining characters that didn't fit into a block.
4971 for (; i < length; i++) {
4972 if (a[i] != b[i]) {
4973 return false;
4974 }
4975 }
4976 return true;
4977}
4978
4979
4980static StringInputBuffer string_compare_buffer_b;
4981
4982
4983template <typename IteratorA>
4984static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
4985 if (b->IsFlat()) {
4986 if (b->IsAsciiRepresentation()) {
4987 VectorIterator<char> ib(b->ToAsciiVector());
4988 return CompareStringContents(ia, &ib);
4989 } else {
4990 VectorIterator<uc16> ib(b->ToUC16Vector());
4991 return CompareStringContents(ia, &ib);
4992 }
4993 } else {
4994 string_compare_buffer_b.Reset(0, b);
4995 return CompareStringContents(ia, &string_compare_buffer_b);
4996 }
4997}
4998
4999
5000static StringInputBuffer string_compare_buffer_a;
5001
5002
5003bool String::SlowEquals(String* other) {
5004 // Fast check: negative check with lengths.
5005 int len = length();
5006 if (len != other->length()) return false;
5007 if (len == 0) return true;
5008
5009 // Fast check: if hash code is computed for both strings
5010 // a fast negative check can be performed.
5011 if (HasHashCode() && other->HasHashCode()) {
5012 if (Hash() != other->Hash()) return false;
5013 }
5014
Leon Clarkef7060e22010-06-03 12:02:55 +01005015 // We know the strings are both non-empty. Compare the first chars
5016 // before we try to flatten the strings.
5017 if (this->Get(0) != other->Get(0)) return false;
5018
5019 String* lhs = this->TryFlattenGetString();
5020 String* rhs = other->TryFlattenGetString();
5021
5022 if (StringShape(lhs).IsSequentialAscii() &&
5023 StringShape(rhs).IsSequentialAscii()) {
5024 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
5025 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
Steve Blocka7e24c12009-10-30 11:49:00 +00005026 return CompareRawStringContents(Vector<const char>(str1, len),
5027 Vector<const char>(str2, len));
5028 }
5029
Leon Clarkef7060e22010-06-03 12:02:55 +01005030 if (lhs->IsFlat()) {
Ben Murdochbb769b22010-08-11 14:56:33 +01005031 if (lhs->IsAsciiRepresentation()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01005032 Vector<const char> vec1 = lhs->ToAsciiVector();
5033 if (rhs->IsFlat()) {
5034 if (rhs->IsAsciiRepresentation()) {
5035 Vector<const char> vec2 = rhs->ToAsciiVector();
Steve Blocka7e24c12009-10-30 11:49:00 +00005036 return CompareRawStringContents(vec1, vec2);
5037 } else {
5038 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005039 VectorIterator<uc16> ib(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005040 return CompareStringContents(&buf1, &ib);
5041 }
5042 } else {
5043 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005044 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005045 return CompareStringContents(&buf1, &string_compare_buffer_b);
5046 }
5047 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005048 Vector<const uc16> vec1 = lhs->ToUC16Vector();
5049 if (rhs->IsFlat()) {
5050 if (rhs->IsAsciiRepresentation()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005051 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005052 VectorIterator<char> ib(rhs->ToAsciiVector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005053 return CompareStringContents(&buf1, &ib);
5054 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005055 Vector<const uc16> vec2(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005056 return CompareRawStringContents(vec1, vec2);
5057 }
5058 } else {
5059 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005060 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005061 return CompareStringContents(&buf1, &string_compare_buffer_b);
5062 }
5063 }
5064 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005065 string_compare_buffer_a.Reset(0, lhs);
5066 return CompareStringContentsPartial(&string_compare_buffer_a, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005067 }
5068}
5069
5070
5071bool String::MarkAsUndetectable() {
5072 if (StringShape(this).IsSymbol()) return false;
5073
5074 Map* map = this->map();
Steve Blockd0582a62009-12-15 09:54:21 +00005075 if (map == Heap::string_map()) {
5076 this->set_map(Heap::undetectable_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00005077 return true;
Steve Blockd0582a62009-12-15 09:54:21 +00005078 } else if (map == Heap::ascii_string_map()) {
5079 this->set_map(Heap::undetectable_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00005080 return true;
5081 }
5082 // Rest cannot be marked as undetectable
5083 return false;
5084}
5085
5086
5087bool String::IsEqualTo(Vector<const char> str) {
5088 int slen = length();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08005089 Access<ScannerConstants::Utf8Decoder>
5090 decoder(ScannerConstants::utf8_decoder());
Steve Blocka7e24c12009-10-30 11:49:00 +00005091 decoder->Reset(str.start(), str.length());
5092 int i;
5093 for (i = 0; i < slen && decoder->has_more(); i++) {
5094 uc32 r = decoder->GetNext();
5095 if (Get(i) != r) return false;
5096 }
5097 return i == slen && !decoder->has_more();
5098}
5099
5100
Steve Block6ded16b2010-05-10 14:33:55 +01005101template <typename schar>
5102static inline uint32_t HashSequentialString(const schar* chars, int length) {
5103 StringHasher hasher(length);
5104 if (!hasher.has_trivial_hash()) {
5105 int i;
5106 for (i = 0; hasher.is_array_index() && (i < length); i++) {
5107 hasher.AddCharacter(chars[i]);
5108 }
5109 for (; i < length; i++) {
5110 hasher.AddCharacterNoIndex(chars[i]);
5111 }
5112 }
5113 return hasher.GetHashField();
5114}
5115
5116
Steve Blocka7e24c12009-10-30 11:49:00 +00005117uint32_t String::ComputeAndSetHash() {
5118 // Should only be called if hash code has not yet been computed.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005119 ASSERT(!HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00005120
Steve Block6ded16b2010-05-10 14:33:55 +01005121 const int len = length();
5122
Steve Blocka7e24c12009-10-30 11:49:00 +00005123 // Compute the hash code.
Steve Block6ded16b2010-05-10 14:33:55 +01005124 uint32_t field = 0;
5125 if (StringShape(this).IsSequentialAscii()) {
5126 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
5127 } else if (StringShape(this).IsSequentialTwoByte()) {
5128 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
5129 } else {
5130 StringInputBuffer buffer(this);
5131 field = ComputeHashField(&buffer, len);
5132 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005133
5134 // Store the hash code in the object.
Steve Blockd0582a62009-12-15 09:54:21 +00005135 set_hash_field(field);
Steve Blocka7e24c12009-10-30 11:49:00 +00005136
5137 // Check the hash code is there.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005138 ASSERT(HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00005139 uint32_t result = field >> kHashShift;
5140 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
5141 return result;
5142}
5143
5144
5145bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
5146 uint32_t* index,
5147 int length) {
5148 if (length == 0 || length > kMaxArrayIndexSize) return false;
5149 uc32 ch = buffer->GetNext();
5150
5151 // If the string begins with a '0' character, it must only consist
5152 // of it to be a legal array index.
5153 if (ch == '0') {
5154 *index = 0;
5155 return length == 1;
5156 }
5157
5158 // Convert string to uint32 array index; character by character.
5159 int d = ch - '0';
5160 if (d < 0 || d > 9) return false;
5161 uint32_t result = d;
5162 while (buffer->has_more()) {
5163 d = buffer->GetNext() - '0';
5164 if (d < 0 || d > 9) return false;
5165 // Check that the new result is below the 32 bit limit.
5166 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
5167 result = (result * 10) + d;
5168 }
5169
5170 *index = result;
5171 return true;
5172}
5173
5174
5175bool String::SlowAsArrayIndex(uint32_t* index) {
5176 if (length() <= kMaxCachedArrayIndexLength) {
5177 Hash(); // force computation of hash code
Steve Blockd0582a62009-12-15 09:54:21 +00005178 uint32_t field = hash_field();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005179 if ((field & kIsNotArrayIndexMask) != 0) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00005180 // Isolate the array index form the full hash field.
5181 *index = (kArrayIndexHashMask & field) >> kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00005182 return true;
5183 } else {
5184 StringInputBuffer buffer(this);
5185 return ComputeArrayIndex(&buffer, index, length());
5186 }
5187}
5188
5189
Iain Merrick9ac36c92010-09-13 15:29:50 +01005190uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005191 // For array indexes mix the length into the hash as an array index could
5192 // be zero.
5193 ASSERT(length > 0);
5194 ASSERT(length <= String::kMaxArrayIndexSize);
5195 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
5196 (1 << String::kArrayIndexValueBits));
Iain Merrick9ac36c92010-09-13 15:29:50 +01005197
5198 value <<= String::kHashShift;
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005199 value |= length << String::kArrayIndexHashLengthShift;
Iain Merrick9ac36c92010-09-13 15:29:50 +01005200
5201 ASSERT((value & String::kIsNotArrayIndexMask) == 0);
5202 ASSERT((length > String::kMaxCachedArrayIndexLength) ||
5203 (value & String::kContainsCachedArrayIndexMask) == 0);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005204 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00005205}
5206
5207
5208uint32_t StringHasher::GetHashField() {
5209 ASSERT(is_valid());
Steve Blockd0582a62009-12-15 09:54:21 +00005210 if (length_ <= String::kMaxHashCalcLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005211 if (is_array_index()) {
Iain Merrick9ac36c92010-09-13 15:29:50 +01005212 return MakeArrayIndexHash(array_index(), length_);
Steve Blocka7e24c12009-10-30 11:49:00 +00005213 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005214 return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask;
Steve Blocka7e24c12009-10-30 11:49:00 +00005215 } else {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005216 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
Steve Blocka7e24c12009-10-30 11:49:00 +00005217 }
5218}
5219
5220
Steve Blockd0582a62009-12-15 09:54:21 +00005221uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
5222 int length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005223 StringHasher hasher(length);
5224
5225 // Very long strings have a trivial hash that doesn't inspect the
5226 // string contents.
5227 if (hasher.has_trivial_hash()) {
5228 return hasher.GetHashField();
5229 }
5230
5231 // Do the iterative array index computation as long as there is a
5232 // chance this is an array index.
5233 while (buffer->has_more() && hasher.is_array_index()) {
5234 hasher.AddCharacter(buffer->GetNext());
5235 }
5236
5237 // Process the remaining characters without updating the array
5238 // index.
5239 while (buffer->has_more()) {
5240 hasher.AddCharacterNoIndex(buffer->GetNext());
5241 }
5242
5243 return hasher.GetHashField();
5244}
5245
5246
John Reck59135872010-11-02 12:39:01 -07005247MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005248 if (start == 0 && end == length()) return this;
John Reck59135872010-11-02 12:39:01 -07005249 MaybeObject* result = Heap::AllocateSubString(this, start, end, pretenure);
Steve Blockd0582a62009-12-15 09:54:21 +00005250 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00005251}
5252
5253
5254void String::PrintOn(FILE* file) {
5255 int length = this->length();
5256 for (int i = 0; i < length; i++) {
5257 fprintf(file, "%c", Get(i));
5258 }
5259}
5260
5261
5262void Map::CreateBackPointers() {
5263 DescriptorArray* descriptors = instance_descriptors();
5264 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
Iain Merrick75681382010-08-19 15:07:18 +01005265 if (descriptors->GetType(i) == MAP_TRANSITION ||
5266 descriptors->GetType(i) == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005267 // Get target.
5268 Map* target = Map::cast(descriptors->GetValue(i));
5269#ifdef DEBUG
5270 // Verify target.
5271 Object* source_prototype = prototype();
5272 Object* target_prototype = target->prototype();
5273 ASSERT(source_prototype->IsJSObject() ||
5274 source_prototype->IsMap() ||
5275 source_prototype->IsNull());
5276 ASSERT(target_prototype->IsJSObject() ||
5277 target_prototype->IsNull());
5278 ASSERT(source_prototype->IsMap() ||
5279 source_prototype == target_prototype);
5280#endif
5281 // Point target back to source. set_prototype() will not let us set
5282 // the prototype to a map, as we do here.
5283 *RawField(target, kPrototypeOffset) = this;
5284 }
5285 }
5286}
5287
5288
5289void Map::ClearNonLiveTransitions(Object* real_prototype) {
5290 // Live DescriptorArray objects will be marked, so we must use
5291 // low-level accessors to get and modify their data.
5292 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
5293 *RawField(this, Map::kInstanceDescriptorsOffset));
5294 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
5295 Smi* NullDescriptorDetails =
5296 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
5297 FixedArray* contents = reinterpret_cast<FixedArray*>(
5298 d->get(DescriptorArray::kContentArrayIndex));
5299 ASSERT(contents->length() >= 2);
5300 for (int i = 0; i < contents->length(); i += 2) {
5301 // If the pair (value, details) is a map transition,
5302 // check if the target is live. If not, null the descriptor.
5303 // Also drop the back pointer for that map transition, so that this
5304 // map is not reached again by following a back pointer from a
5305 // non-live object.
5306 PropertyDetails details(Smi::cast(contents->get(i + 1)));
Iain Merrick75681382010-08-19 15:07:18 +01005307 if (details.type() == MAP_TRANSITION ||
5308 details.type() == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005309 Map* target = reinterpret_cast<Map*>(contents->get(i));
5310 ASSERT(target->IsHeapObject());
5311 if (!target->IsMarked()) {
5312 ASSERT(target->IsMap());
Iain Merrick75681382010-08-19 15:07:18 +01005313 contents->set_unchecked(i + 1, NullDescriptorDetails);
5314 contents->set_null_unchecked(i);
Steve Blocka7e24c12009-10-30 11:49:00 +00005315 ASSERT(target->prototype() == this ||
5316 target->prototype() == real_prototype);
5317 // Getter prototype() is read-only, set_prototype() has side effects.
5318 *RawField(target, Map::kPrototypeOffset) = real_prototype;
5319 }
5320 }
5321 }
5322}
5323
5324
Steve Block791712a2010-08-27 10:21:07 +01005325void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
5326 // Iterate over all fields in the body but take care in dealing with
5327 // the code entry.
5328 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
5329 v->VisitCodeEntry(this->address() + kCodeEntryOffset);
5330 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
5331}
5332
5333
Steve Blocka7e24c12009-10-30 11:49:00 +00005334Object* JSFunction::SetInstancePrototype(Object* value) {
5335 ASSERT(value->IsJSObject());
5336
5337 if (has_initial_map()) {
5338 initial_map()->set_prototype(value);
5339 } else {
5340 // Put the value in the initial map field until an initial map is
5341 // needed. At that point, a new initial map is created and the
5342 // prototype is put into the initial map where it belongs.
5343 set_prototype_or_initial_map(value);
5344 }
Kristian Monsen25f61362010-05-21 11:50:48 +01005345 Heap::ClearInstanceofCache();
Steve Blocka7e24c12009-10-30 11:49:00 +00005346 return value;
5347}
5348
5349
John Reck59135872010-11-02 12:39:01 -07005350MaybeObject* JSFunction::SetPrototype(Object* value) {
Steve Block6ded16b2010-05-10 14:33:55 +01005351 ASSERT(should_have_prototype());
Steve Blocka7e24c12009-10-30 11:49:00 +00005352 Object* construct_prototype = value;
5353
5354 // If the value is not a JSObject, store the value in the map's
5355 // constructor field so it can be accessed. Also, set the prototype
5356 // used for constructing objects to the original object prototype.
5357 // See ECMA-262 13.2.2.
5358 if (!value->IsJSObject()) {
5359 // Copy the map so this does not affect unrelated functions.
5360 // Remove map transitions because they point to maps with a
5361 // different prototype.
John Reck59135872010-11-02 12:39:01 -07005362 Object* new_map;
5363 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
5364 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
5365 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005366 set_map(Map::cast(new_map));
5367 map()->set_constructor(value);
5368 map()->set_non_instance_prototype(true);
5369 construct_prototype =
5370 Top::context()->global_context()->initial_object_prototype();
5371 } else {
5372 map()->set_non_instance_prototype(false);
5373 }
5374
5375 return SetInstancePrototype(construct_prototype);
5376}
5377
5378
Steve Block6ded16b2010-05-10 14:33:55 +01005379Object* JSFunction::RemovePrototype() {
5380 ASSERT(map() == context()->global_context()->function_map());
5381 set_map(context()->global_context()->function_without_prototype_map());
5382 set_prototype_or_initial_map(Heap::the_hole_value());
5383 return this;
5384}
5385
5386
Steve Blocka7e24c12009-10-30 11:49:00 +00005387Object* JSFunction::SetInstanceClassName(String* name) {
5388 shared()->set_instance_class_name(name);
5389 return this;
5390}
5391
5392
5393Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
5394 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
5395}
5396
5397
John Reck59135872010-11-02 12:39:01 -07005398MaybeObject* Oddball::Initialize(const char* to_string, Object* to_number) {
5399 Object* symbol;
5400 { MaybeObject* maybe_symbol = Heap::LookupAsciiSymbol(to_string);
5401 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
5402 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005403 set_to_string(String::cast(symbol));
5404 set_to_number(to_number);
5405 return this;
5406}
5407
5408
Ben Murdochf87a2032010-10-22 12:50:53 +01005409String* SharedFunctionInfo::DebugName() {
5410 Object* n = name();
5411 if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
5412 return String::cast(n);
5413}
5414
5415
Steve Blocka7e24c12009-10-30 11:49:00 +00005416bool SharedFunctionInfo::HasSourceCode() {
5417 return !script()->IsUndefined() &&
Iain Merrick75681382010-08-19 15:07:18 +01005418 !reinterpret_cast<Script*>(script())->source()->IsUndefined();
Steve Blocka7e24c12009-10-30 11:49:00 +00005419}
5420
5421
5422Object* SharedFunctionInfo::GetSourceCode() {
5423 HandleScope scope;
5424 if (script()->IsUndefined()) return Heap::undefined_value();
5425 Object* source = Script::cast(script())->source();
5426 if (source->IsUndefined()) return Heap::undefined_value();
5427 return *SubString(Handle<String>(String::cast(source)),
5428 start_position(), end_position());
5429}
5430
5431
5432int SharedFunctionInfo::CalculateInstanceSize() {
5433 int instance_size =
5434 JSObject::kHeaderSize +
5435 expected_nof_properties() * kPointerSize;
5436 if (instance_size > JSObject::kMaxInstanceSize) {
5437 instance_size = JSObject::kMaxInstanceSize;
5438 }
5439 return instance_size;
5440}
5441
5442
5443int SharedFunctionInfo::CalculateInObjectProperties() {
5444 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
5445}
5446
5447
Andrei Popescu402d9372010-02-26 13:31:12 +00005448bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
5449 // Check the basic conditions for generating inline constructor code.
5450 if (!FLAG_inline_new
5451 || !has_only_simple_this_property_assignments()
5452 || this_property_assignments_count() == 0) {
5453 return false;
5454 }
5455
5456 // If the prototype is null inline constructors cause no problems.
5457 if (!prototype->IsJSObject()) {
5458 ASSERT(prototype->IsNull());
5459 return true;
5460 }
5461
5462 // Traverse the proposed prototype chain looking for setters for properties of
5463 // the same names as are set by the inline constructor.
5464 for (Object* obj = prototype;
5465 obj != Heap::null_value();
5466 obj = obj->GetPrototype()) {
5467 JSObject* js_object = JSObject::cast(obj);
5468 for (int i = 0; i < this_property_assignments_count(); i++) {
5469 LookupResult result;
5470 String* name = GetThisPropertyAssignmentName(i);
5471 js_object->LocalLookupRealNamedProperty(name, &result);
5472 if (result.IsProperty() && result.type() == CALLBACKS) {
5473 return false;
5474 }
5475 }
5476 }
5477
5478 return true;
5479}
5480
5481
Kristian Monsen0d5e1162010-09-30 15:31:59 +01005482void SharedFunctionInfo::ForbidInlineConstructor() {
5483 set_compiler_hints(BooleanBit::set(compiler_hints(),
5484 kHasOnlySimpleThisPropertyAssignments,
5485 false));
5486}
5487
5488
Steve Blocka7e24c12009-10-30 11:49:00 +00005489void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +00005490 bool only_simple_this_property_assignments,
5491 FixedArray* assignments) {
5492 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005493 kHasOnlySimpleThisPropertyAssignments,
5494 only_simple_this_property_assignments));
5495 set_this_property_assignments(assignments);
5496 set_this_property_assignments_count(assignments->length() / 3);
5497}
5498
5499
5500void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
5501 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005502 kHasOnlySimpleThisPropertyAssignments,
5503 false));
5504 set_this_property_assignments(Heap::undefined_value());
5505 set_this_property_assignments_count(0);
5506}
5507
5508
5509String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
5510 Object* obj = this_property_assignments();
5511 ASSERT(obj->IsFixedArray());
5512 ASSERT(index < this_property_assignments_count());
5513 obj = FixedArray::cast(obj)->get(index * 3);
5514 ASSERT(obj->IsString());
5515 return String::cast(obj);
5516}
5517
5518
5519bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
5520 Object* obj = this_property_assignments();
5521 ASSERT(obj->IsFixedArray());
5522 ASSERT(index < this_property_assignments_count());
5523 obj = FixedArray::cast(obj)->get(index * 3 + 1);
5524 return Smi::cast(obj)->value() != -1;
5525}
5526
5527
5528int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
5529 ASSERT(IsThisPropertyAssignmentArgument(index));
5530 Object* obj =
5531 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
5532 return Smi::cast(obj)->value();
5533}
5534
5535
5536Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
5537 ASSERT(!IsThisPropertyAssignmentArgument(index));
5538 Object* obj =
5539 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
5540 return obj;
5541}
5542
5543
Steve Blocka7e24c12009-10-30 11:49:00 +00005544// Support function for printing the source code to a StringStream
5545// without any allocation in the heap.
5546void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
5547 int max_length) {
5548 // For some native functions there is no source.
5549 if (script()->IsUndefined() ||
5550 Script::cast(script())->source()->IsUndefined()) {
5551 accumulator->Add("<No Source>");
5552 return;
5553 }
5554
Steve Blockd0582a62009-12-15 09:54:21 +00005555 // Get the source for the script which this function came from.
Steve Blocka7e24c12009-10-30 11:49:00 +00005556 // Don't use String::cast because we don't want more assertion errors while
5557 // we are already creating a stack dump.
5558 String* script_source =
5559 reinterpret_cast<String*>(Script::cast(script())->source());
5560
5561 if (!script_source->LooksValid()) {
5562 accumulator->Add("<Invalid Source>");
5563 return;
5564 }
5565
5566 if (!is_toplevel()) {
5567 accumulator->Add("function ");
5568 Object* name = this->name();
5569 if (name->IsString() && String::cast(name)->length() > 0) {
5570 accumulator->PrintName(name);
5571 }
5572 }
5573
5574 int len = end_position() - start_position();
5575 if (len > max_length) {
5576 accumulator->Put(script_source,
5577 start_position(),
5578 start_position() + max_length);
5579 accumulator->Add("...\n");
5580 } else {
5581 accumulator->Put(script_source, start_position(), end_position());
5582 }
5583}
5584
5585
Kristian Monsen0d5e1162010-09-30 15:31:59 +01005586void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) {
5587 ASSERT(!IsInobjectSlackTrackingInProgress());
5588
5589 // Only initiate the tracking the first time.
5590 if (live_objects_may_exist()) return;
5591 set_live_objects_may_exist(true);
5592
5593 // No tracking during the snapshot construction phase.
5594 if (Serializer::enabled()) return;
5595
5596 if (map->unused_property_fields() == 0) return;
5597
5598 // Nonzero counter is a leftover from the previous attempt interrupted
5599 // by GC, keep it.
5600 if (construction_count() == 0) {
5601 set_construction_count(kGenerousAllocationCount);
5602 }
5603 set_initial_map(map);
5604 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubGeneric),
5605 construct_stub());
5606 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubCountdown));
5607}
5608
5609
5610// Called from GC, hence reinterpret_cast and unchecked accessors.
5611void SharedFunctionInfo::DetachInitialMap() {
5612 Map* map = reinterpret_cast<Map*>(initial_map());
5613
5614 // Make the map remember to restore the link if it survives the GC.
5615 map->set_bit_field2(
5616 map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo));
5617
5618 // Undo state changes made by StartInobjectTracking (except the
5619 // construction_count). This way if the initial map does not survive the GC
5620 // then StartInobjectTracking will be called again the next time the
5621 // constructor is called. The countdown will continue and (possibly after
5622 // several more GCs) CompleteInobjectSlackTracking will eventually be called.
5623 set_initial_map(Heap::raw_unchecked_undefined_value());
5624 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubCountdown),
5625 *RawField(this, kConstructStubOffset));
5626 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubGeneric));
5627 // It is safe to clear the flag: it will be set again if the map is live.
5628 set_live_objects_may_exist(false);
5629}
5630
5631
5632// Called from GC, hence reinterpret_cast and unchecked accessors.
5633void SharedFunctionInfo::AttachInitialMap(Map* map) {
5634 map->set_bit_field2(
5635 map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo));
5636
5637 // Resume inobject slack tracking.
5638 set_initial_map(map);
5639 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubGeneric),
5640 *RawField(this, kConstructStubOffset));
5641 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubCountdown));
5642 // The map survived the gc, so there may be objects referencing it.
5643 set_live_objects_may_exist(true);
5644}
5645
5646
5647static void GetMinInobjectSlack(Map* map, void* data) {
5648 int slack = map->unused_property_fields();
5649 if (*reinterpret_cast<int*>(data) > slack) {
5650 *reinterpret_cast<int*>(data) = slack;
5651 }
5652}
5653
5654
5655static void ShrinkInstanceSize(Map* map, void* data) {
5656 int slack = *reinterpret_cast<int*>(data);
5657 map->set_inobject_properties(map->inobject_properties() - slack);
5658 map->set_unused_property_fields(map->unused_property_fields() - slack);
5659 map->set_instance_size(map->instance_size() - slack * kPointerSize);
5660
5661 // Visitor id might depend on the instance size, recalculate it.
5662 map->set_visitor_id(StaticVisitorBase::GetVisitorId(map));
5663}
5664
5665
5666void SharedFunctionInfo::CompleteInobjectSlackTracking() {
5667 ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress());
5668 Map* map = Map::cast(initial_map());
5669
5670 set_initial_map(Heap::undefined_value());
5671 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubCountdown),
5672 construct_stub());
5673 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubGeneric));
5674
5675 int slack = map->unused_property_fields();
5676 map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
5677 if (slack != 0) {
5678 // Resize the initial map and all maps in its transition tree.
5679 map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
5680 // Give the correct expected_nof_properties to initial maps created later.
5681 ASSERT(expected_nof_properties() >= slack);
5682 set_expected_nof_properties(expected_nof_properties() - slack);
5683 }
5684}
5685
5686
Steve Blocka7e24c12009-10-30 11:49:00 +00005687void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
5688 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
5689 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
5690 Object* old_target = target;
5691 VisitPointer(&target);
5692 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5693}
5694
5695
Steve Block791712a2010-08-27 10:21:07 +01005696void ObjectVisitor::VisitCodeEntry(Address entry_address) {
5697 Object* code = Code::GetObjectFromEntryAddress(entry_address);
5698 Object* old_code = code;
5699 VisitPointer(&code);
5700 if (code != old_code) {
5701 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry();
5702 }
5703}
5704
5705
Steve Blocka7e24c12009-10-30 11:49:00 +00005706void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005707 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
5708 rinfo->IsPatchedReturnSequence()) ||
5709 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
5710 rinfo->IsPatchedDebugBreakSlotSequence()));
Steve Blocka7e24c12009-10-30 11:49:00 +00005711 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
5712 Object* old_target = target;
5713 VisitPointer(&target);
5714 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5715}
5716
5717
Steve Blockd0582a62009-12-15 09:54:21 +00005718void Code::Relocate(intptr_t delta) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005719 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
5720 it.rinfo()->apply(delta);
5721 }
5722 CPU::FlushICache(instruction_start(), instruction_size());
5723}
5724
5725
5726void Code::CopyFrom(const CodeDesc& desc) {
5727 // copy code
5728 memmove(instruction_start(), desc.buffer, desc.instr_size);
5729
Steve Blocka7e24c12009-10-30 11:49:00 +00005730 // copy reloc info
5731 memmove(relocation_start(),
5732 desc.buffer + desc.buffer_size - desc.reloc_size,
5733 desc.reloc_size);
5734
5735 // unbox handles and relocate
Steve Block3ce2e202009-11-05 08:53:23 +00005736 intptr_t delta = instruction_start() - desc.buffer;
Steve Blocka7e24c12009-10-30 11:49:00 +00005737 int mode_mask = RelocInfo::kCodeTargetMask |
5738 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
5739 RelocInfo::kApplyMask;
Steve Block3ce2e202009-11-05 08:53:23 +00005740 Assembler* origin = desc.origin; // Needed to find target_object on X64.
Steve Blocka7e24c12009-10-30 11:49:00 +00005741 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
5742 RelocInfo::Mode mode = it.rinfo()->rmode();
5743 if (mode == RelocInfo::EMBEDDED_OBJECT) {
Steve Block3ce2e202009-11-05 08:53:23 +00005744 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005745 it.rinfo()->set_target_object(*p);
5746 } else if (RelocInfo::IsCodeTarget(mode)) {
5747 // rewrite code handles in inline cache targets to direct
5748 // pointers to the first instruction in the code object
Steve Block3ce2e202009-11-05 08:53:23 +00005749 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005750 Code* code = Code::cast(*p);
5751 it.rinfo()->set_target_address(code->instruction_start());
5752 } else {
5753 it.rinfo()->apply(delta);
5754 }
5755 }
5756 CPU::FlushICache(instruction_start(), instruction_size());
5757}
5758
5759
5760// Locate the source position which is closest to the address in the code. This
5761// is using the source position information embedded in the relocation info.
5762// The position returned is relative to the beginning of the script where the
5763// source for this function is found.
5764int Code::SourcePosition(Address pc) {
5765 int distance = kMaxInt;
5766 int position = RelocInfo::kNoPosition; // Initially no position found.
5767 // Run through all the relocation info to find the best matching source
5768 // position. All the code needs to be considered as the sequence of the
5769 // instructions in the code does not necessarily follow the same order as the
5770 // source.
5771 RelocIterator it(this, RelocInfo::kPositionMask);
5772 while (!it.done()) {
5773 // Only look at positions after the current pc.
5774 if (it.rinfo()->pc() < pc) {
5775 // Get position and distance.
Steve Blockd0582a62009-12-15 09:54:21 +00005776
5777 int dist = static_cast<int>(pc - it.rinfo()->pc());
5778 int pos = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005779 // If this position is closer than the current candidate or if it has the
5780 // same distance as the current candidate and the position is higher then
5781 // this position is the new candidate.
5782 if ((dist < distance) ||
5783 (dist == distance && pos > position)) {
5784 position = pos;
5785 distance = dist;
5786 }
5787 }
5788 it.next();
5789 }
5790 return position;
5791}
5792
5793
5794// Same as Code::SourcePosition above except it only looks for statement
5795// positions.
5796int Code::SourceStatementPosition(Address pc) {
5797 // First find the position as close as possible using all position
5798 // information.
5799 int position = SourcePosition(pc);
5800 // Now find the closest statement position before the position.
5801 int statement_position = 0;
5802 RelocIterator it(this, RelocInfo::kPositionMask);
5803 while (!it.done()) {
5804 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
Steve Blockd0582a62009-12-15 09:54:21 +00005805 int p = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005806 if (statement_position < p && p <= position) {
5807 statement_position = p;
5808 }
5809 }
5810 it.next();
5811 }
5812 return statement_position;
5813}
5814
5815
5816#ifdef ENABLE_DISASSEMBLER
5817// Identify kind of code.
5818const char* Code::Kind2String(Kind kind) {
5819 switch (kind) {
5820 case FUNCTION: return "FUNCTION";
5821 case STUB: return "STUB";
5822 case BUILTIN: return "BUILTIN";
5823 case LOAD_IC: return "LOAD_IC";
5824 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
5825 case STORE_IC: return "STORE_IC";
5826 case KEYED_STORE_IC: return "KEYED_STORE_IC";
5827 case CALL_IC: return "CALL_IC";
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005828 case KEYED_CALL_IC: return "KEYED_CALL_IC";
Steve Block6ded16b2010-05-10 14:33:55 +01005829 case BINARY_OP_IC: return "BINARY_OP_IC";
Steve Blocka7e24c12009-10-30 11:49:00 +00005830 }
5831 UNREACHABLE();
5832 return NULL;
5833}
5834
5835
5836const char* Code::ICState2String(InlineCacheState state) {
5837 switch (state) {
5838 case UNINITIALIZED: return "UNINITIALIZED";
5839 case PREMONOMORPHIC: return "PREMONOMORPHIC";
5840 case MONOMORPHIC: return "MONOMORPHIC";
5841 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
5842 case MEGAMORPHIC: return "MEGAMORPHIC";
5843 case DEBUG_BREAK: return "DEBUG_BREAK";
5844 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
5845 }
5846 UNREACHABLE();
5847 return NULL;
5848}
5849
5850
5851const char* Code::PropertyType2String(PropertyType type) {
5852 switch (type) {
5853 case NORMAL: return "NORMAL";
5854 case FIELD: return "FIELD";
5855 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
5856 case CALLBACKS: return "CALLBACKS";
5857 case INTERCEPTOR: return "INTERCEPTOR";
5858 case MAP_TRANSITION: return "MAP_TRANSITION";
5859 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
5860 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
5861 }
5862 UNREACHABLE();
5863 return NULL;
5864}
5865
5866void Code::Disassemble(const char* name) {
5867 PrintF("kind = %s\n", Kind2String(kind()));
5868 if (is_inline_cache_stub()) {
5869 PrintF("ic_state = %s\n", ICState2String(ic_state()));
5870 PrintF("ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
5871 if (ic_state() == MONOMORPHIC) {
5872 PrintF("type = %s\n", PropertyType2String(type()));
5873 }
5874 }
5875 if ((name != NULL) && (name[0] != '\0')) {
5876 PrintF("name = %s\n", name);
5877 }
5878
5879 PrintF("Instructions (size = %d)\n", instruction_size());
5880 Disassembler::Decode(NULL, this);
5881 PrintF("\n");
5882
5883 PrintF("RelocInfo (size = %d)\n", relocation_size());
5884 for (RelocIterator it(this); !it.done(); it.next())
5885 it.rinfo()->Print();
5886 PrintF("\n");
5887}
5888#endif // ENABLE_DISASSEMBLER
5889
5890
John Reck59135872010-11-02 12:39:01 -07005891MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
5892 int length) {
Steve Block3ce2e202009-11-05 08:53:23 +00005893 // We should never end in here with a pixel or external array.
5894 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Block8defd9f2010-07-08 12:39:36 +01005895
John Reck59135872010-11-02 12:39:01 -07005896 Object* obj;
5897 { MaybeObject* maybe_obj = Heap::AllocateFixedArrayWithHoles(capacity);
5898 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
5899 }
Steve Block8defd9f2010-07-08 12:39:36 +01005900 FixedArray* elems = FixedArray::cast(obj);
5901
John Reck59135872010-11-02 12:39:01 -07005902 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
5903 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
5904 }
Steve Block8defd9f2010-07-08 12:39:36 +01005905 Map* new_map = Map::cast(obj);
5906
Leon Clarke4515c472010-02-03 11:58:03 +00005907 AssertNoAllocation no_gc;
5908 WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00005909 switch (GetElementsKind()) {
5910 case FAST_ELEMENTS: {
5911 FixedArray* old_elements = FixedArray::cast(elements());
5912 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
5913 // Fill out the new array with this content and array holes.
5914 for (uint32_t i = 0; i < old_length; i++) {
5915 elems->set(i, old_elements->get(i), mode);
5916 }
5917 break;
5918 }
5919 case DICTIONARY_ELEMENTS: {
5920 NumberDictionary* dictionary = NumberDictionary::cast(elements());
5921 for (int i = 0; i < dictionary->Capacity(); i++) {
5922 Object* key = dictionary->KeyAt(i);
5923 if (key->IsNumber()) {
5924 uint32_t entry = static_cast<uint32_t>(key->Number());
5925 elems->set(entry, dictionary->ValueAt(i), mode);
5926 }
5927 }
5928 break;
5929 }
5930 default:
5931 UNREACHABLE();
5932 break;
5933 }
Steve Block8defd9f2010-07-08 12:39:36 +01005934
5935 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00005936 set_elements(elems);
Steve Block8defd9f2010-07-08 12:39:36 +01005937
5938 if (IsJSArray()) {
5939 JSArray::cast(this)->set_length(Smi::FromInt(length));
5940 }
5941
5942 return this;
Steve Blocka7e24c12009-10-30 11:49:00 +00005943}
5944
5945
John Reck59135872010-11-02 12:39:01 -07005946MaybeObject* JSObject::SetSlowElements(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00005947 // We should never end in here with a pixel or external array.
5948 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00005949
5950 uint32_t new_length = static_cast<uint32_t>(len->Number());
5951
5952 switch (GetElementsKind()) {
5953 case FAST_ELEMENTS: {
5954 // Make sure we never try to shrink dense arrays into sparse arrays.
5955 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
5956 new_length);
John Reck59135872010-11-02 12:39:01 -07005957 Object* obj;
5958 { MaybeObject* maybe_obj = NormalizeElements();
5959 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
5960 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005961
5962 // Update length for JSArrays.
5963 if (IsJSArray()) JSArray::cast(this)->set_length(len);
5964 break;
5965 }
5966 case DICTIONARY_ELEMENTS: {
5967 if (IsJSArray()) {
5968 uint32_t old_length =
Steve Block6ded16b2010-05-10 14:33:55 +01005969 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
Steve Blocka7e24c12009-10-30 11:49:00 +00005970 element_dictionary()->RemoveNumberEntries(new_length, old_length),
5971 JSArray::cast(this)->set_length(len);
5972 }
5973 break;
5974 }
5975 default:
5976 UNREACHABLE();
5977 break;
5978 }
5979 return this;
5980}
5981
5982
John Reck59135872010-11-02 12:39:01 -07005983MaybeObject* JSArray::Initialize(int capacity) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005984 ASSERT(capacity >= 0);
Leon Clarke4515c472010-02-03 11:58:03 +00005985 set_length(Smi::FromInt(0));
Steve Blocka7e24c12009-10-30 11:49:00 +00005986 FixedArray* new_elements;
5987 if (capacity == 0) {
5988 new_elements = Heap::empty_fixed_array();
5989 } else {
John Reck59135872010-11-02 12:39:01 -07005990 Object* obj;
5991 { MaybeObject* maybe_obj = Heap::AllocateFixedArrayWithHoles(capacity);
5992 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
5993 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005994 new_elements = FixedArray::cast(obj);
5995 }
5996 set_elements(new_elements);
5997 return this;
5998}
5999
6000
6001void JSArray::Expand(int required_size) {
6002 Handle<JSArray> self(this);
6003 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
6004 int old_size = old_backing->length();
Steve Blockd0582a62009-12-15 09:54:21 +00006005 int new_size = required_size > old_size ? required_size : old_size;
Steve Blocka7e24c12009-10-30 11:49:00 +00006006 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
6007 // Can't use this any more now because we may have had a GC!
6008 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
6009 self->SetContent(*new_backing);
6010}
6011
6012
6013// Computes the new capacity when expanding the elements of a JSObject.
6014static int NewElementsCapacity(int old_capacity) {
6015 // (old_capacity + 50%) + 16
6016 return old_capacity + (old_capacity >> 1) + 16;
6017}
6018
6019
John Reck59135872010-11-02 12:39:01 -07006020static Failure* ArrayLengthRangeError() {
Steve Blocka7e24c12009-10-30 11:49:00 +00006021 HandleScope scope;
6022 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
6023 HandleVector<Object>(NULL, 0)));
6024}
6025
6026
John Reck59135872010-11-02 12:39:01 -07006027MaybeObject* JSObject::SetElementsLength(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00006028 // We should never end in here with a pixel or external array.
Steve Block6ded16b2010-05-10 14:33:55 +01006029 ASSERT(AllowsSetElementsLength());
Steve Blocka7e24c12009-10-30 11:49:00 +00006030
John Reck59135872010-11-02 12:39:01 -07006031 MaybeObject* maybe_smi_length = len->ToSmi();
6032 Object* smi_length = Smi::FromInt(0);
6033 if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) {
Steve Block8defd9f2010-07-08 12:39:36 +01006034 const int value = Smi::cast(smi_length)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00006035 if (value < 0) return ArrayLengthRangeError();
6036 switch (GetElementsKind()) {
6037 case FAST_ELEMENTS: {
6038 int old_capacity = FixedArray::cast(elements())->length();
6039 if (value <= old_capacity) {
6040 if (IsJSArray()) {
John Reck59135872010-11-02 12:39:01 -07006041 Object* obj;
6042 { MaybeObject* maybe_obj = EnsureWritableFastElements();
6043 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6044 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006045 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
6046 // NOTE: We may be able to optimize this by removing the
6047 // last part of the elements backing storage array and
6048 // setting the capacity to the new size.
6049 for (int i = value; i < old_length; i++) {
6050 FixedArray::cast(elements())->set_the_hole(i);
6051 }
Leon Clarke4515c472010-02-03 11:58:03 +00006052 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006053 }
6054 return this;
6055 }
6056 int min = NewElementsCapacity(old_capacity);
6057 int new_capacity = value > min ? value : min;
6058 if (new_capacity <= kMaxFastElementsLength ||
6059 !ShouldConvertToSlowElements(new_capacity)) {
John Reck59135872010-11-02 12:39:01 -07006060 Object* obj;
6061 { MaybeObject* maybe_obj =
6062 SetFastElementsCapacityAndLength(new_capacity, value);
6063 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6064 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006065 return this;
6066 }
6067 break;
6068 }
6069 case DICTIONARY_ELEMENTS: {
6070 if (IsJSArray()) {
6071 if (value == 0) {
6072 // If the length of a slow array is reset to zero, we clear
6073 // the array and flush backing storage. This has the added
6074 // benefit that the array returns to fast mode.
John Reck59135872010-11-02 12:39:01 -07006075 Object* obj;
6076 { MaybeObject* maybe_obj = ResetElements();
6077 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6078 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006079 } else {
6080 // Remove deleted elements.
6081 uint32_t old_length =
6082 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
6083 element_dictionary()->RemoveNumberEntries(value, old_length);
6084 }
Leon Clarke4515c472010-02-03 11:58:03 +00006085 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006086 }
6087 return this;
6088 }
6089 default:
6090 UNREACHABLE();
6091 break;
6092 }
6093 }
6094
6095 // General slow case.
6096 if (len->IsNumber()) {
6097 uint32_t length;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006098 if (len->ToArrayIndex(&length)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006099 return SetSlowElements(len);
6100 } else {
6101 return ArrayLengthRangeError();
6102 }
6103 }
6104
6105 // len is not a number so make the array size one and
6106 // set only element to len.
John Reck59135872010-11-02 12:39:01 -07006107 Object* obj;
6108 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(1);
6109 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6110 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006111 FixedArray::cast(obj)->set(0, len);
Leon Clarke4515c472010-02-03 11:58:03 +00006112 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006113 set_elements(FixedArray::cast(obj));
6114 return this;
6115}
6116
6117
John Reck59135872010-11-02 12:39:01 -07006118MaybeObject* JSObject::SetPrototype(Object* value,
6119 bool skip_hidden_prototypes) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006120 // Silently ignore the change if value is not a JSObject or null.
6121 // SpiderMonkey behaves this way.
6122 if (!value->IsJSObject() && !value->IsNull()) return value;
6123
6124 // Before we can set the prototype we need to be sure
6125 // prototype cycles are prevented.
6126 // It is sufficient to validate that the receiver is not in the new prototype
6127 // chain.
6128 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
6129 if (JSObject::cast(pt) == this) {
6130 // Cycle detected.
6131 HandleScope scope;
6132 return Top::Throw(*Factory::NewError("cyclic_proto",
6133 HandleVector<Object>(NULL, 0)));
6134 }
6135 }
6136
6137 JSObject* real_receiver = this;
6138
6139 if (skip_hidden_prototypes) {
6140 // Find the first object in the chain whose prototype object is not
6141 // hidden and set the new prototype on that object.
6142 Object* current_proto = real_receiver->GetPrototype();
6143 while (current_proto->IsJSObject() &&
6144 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
6145 real_receiver = JSObject::cast(current_proto);
6146 current_proto = current_proto->GetPrototype();
6147 }
6148 }
6149
6150 // Set the new prototype of the object.
John Reck59135872010-11-02 12:39:01 -07006151 Object* new_map;
6152 { MaybeObject* maybe_new_map = real_receiver->map()->CopyDropTransitions();
6153 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
6154 }
Andrei Popescu402d9372010-02-26 13:31:12 +00006155 Map::cast(new_map)->set_prototype(value);
6156 real_receiver->set_map(Map::cast(new_map));
6157
Kristian Monsen25f61362010-05-21 11:50:48 +01006158 Heap::ClearInstanceofCache();
6159
Andrei Popescu402d9372010-02-26 13:31:12 +00006160 return value;
6161}
6162
6163
Steve Blocka7e24c12009-10-30 11:49:00 +00006164bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
6165 switch (GetElementsKind()) {
6166 case FAST_ELEMENTS: {
6167 uint32_t length = IsJSArray() ?
6168 static_cast<uint32_t>
6169 (Smi::cast(JSArray::cast(this)->length())->value()) :
6170 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6171 if ((index < length) &&
6172 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
6173 return true;
6174 }
6175 break;
6176 }
6177 case PIXEL_ELEMENTS: {
6178 // TODO(iposva): Add testcase.
6179 PixelArray* pixels = PixelArray::cast(elements());
6180 if (index < static_cast<uint32_t>(pixels->length())) {
6181 return true;
6182 }
6183 break;
6184 }
Steve Block3ce2e202009-11-05 08:53:23 +00006185 case EXTERNAL_BYTE_ELEMENTS:
6186 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6187 case EXTERNAL_SHORT_ELEMENTS:
6188 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6189 case EXTERNAL_INT_ELEMENTS:
6190 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6191 case EXTERNAL_FLOAT_ELEMENTS: {
6192 // TODO(kbr): Add testcase.
6193 ExternalArray* array = ExternalArray::cast(elements());
6194 if (index < static_cast<uint32_t>(array->length())) {
6195 return true;
6196 }
6197 break;
6198 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006199 case DICTIONARY_ELEMENTS: {
6200 if (element_dictionary()->FindEntry(index)
6201 != NumberDictionary::kNotFound) {
6202 return true;
6203 }
6204 break;
6205 }
6206 default:
6207 UNREACHABLE();
6208 break;
6209 }
6210
6211 // Handle [] on String objects.
6212 if (this->IsStringObjectWithCharacterAt(index)) return true;
6213
6214 Object* pt = GetPrototype();
6215 if (pt == Heap::null_value()) return false;
6216 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
6217}
6218
6219
6220bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
6221 // Make sure that the top context does not change when doing
6222 // callbacks or interceptor calls.
6223 AssertNoContextChange ncc;
6224 HandleScope scope;
6225 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6226 Handle<JSObject> receiver_handle(receiver);
6227 Handle<JSObject> holder_handle(this);
6228 CustomArguments args(interceptor->data(), receiver, this);
6229 v8::AccessorInfo info(args.end());
6230 if (!interceptor->query()->IsUndefined()) {
6231 v8::IndexedPropertyQuery query =
6232 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
6233 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
Iain Merrick75681382010-08-19 15:07:18 +01006234 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00006235 {
6236 // Leaving JavaScript.
6237 VMState state(EXTERNAL);
6238 result = query(index, info);
6239 }
Iain Merrick75681382010-08-19 15:07:18 +01006240 if (!result.IsEmpty()) {
6241 ASSERT(result->IsInt32());
6242 return true; // absence of property is signaled by empty handle.
6243 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006244 } else if (!interceptor->getter()->IsUndefined()) {
6245 v8::IndexedPropertyGetter getter =
6246 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
6247 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
6248 v8::Handle<v8::Value> result;
6249 {
6250 // Leaving JavaScript.
6251 VMState state(EXTERNAL);
6252 result = getter(index, info);
6253 }
6254 if (!result.IsEmpty()) return true;
6255 }
6256 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
6257}
6258
6259
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006260JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006261 // Check access rights if needed.
6262 if (IsAccessCheckNeeded() &&
6263 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6264 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006265 return UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006266 }
6267
Steve Block103cc402011-02-16 13:27:44 +00006268 if (IsJSGlobalProxy()) {
6269 Object* proto = GetPrototype();
6270 if (proto->IsNull()) return UNDEFINED_ELEMENT;
6271 ASSERT(proto->IsJSGlobalObject());
6272 return JSObject::cast(proto)->HasLocalElement(index);
6273 }
6274
Steve Blocka7e24c12009-10-30 11:49:00 +00006275 // Check for lookup interceptor
6276 if (HasIndexedInterceptor()) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006277 return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
6278 : UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006279 }
6280
6281 // Handle [] on String objects.
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006282 if (this->IsStringObjectWithCharacterAt(index)) {
6283 return STRING_CHARACTER_ELEMENT;
6284 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006285
6286 switch (GetElementsKind()) {
6287 case FAST_ELEMENTS: {
6288 uint32_t length = IsJSArray() ?
6289 static_cast<uint32_t>
6290 (Smi::cast(JSArray::cast(this)->length())->value()) :
6291 static_cast<uint32_t>(FixedArray::cast(elements())->length());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006292 if ((index < length) &&
6293 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
6294 return FAST_ELEMENT;
6295 }
6296 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006297 }
6298 case PIXEL_ELEMENTS: {
6299 PixelArray* pixels = PixelArray::cast(elements());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006300 if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT;
6301 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006302 }
Steve Block3ce2e202009-11-05 08:53:23 +00006303 case EXTERNAL_BYTE_ELEMENTS:
6304 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6305 case EXTERNAL_SHORT_ELEMENTS:
6306 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6307 case EXTERNAL_INT_ELEMENTS:
6308 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6309 case EXTERNAL_FLOAT_ELEMENTS: {
6310 ExternalArray* array = ExternalArray::cast(elements());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006311 if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
6312 break;
Steve Block3ce2e202009-11-05 08:53:23 +00006313 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006314 case DICTIONARY_ELEMENTS: {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006315 if (element_dictionary()->FindEntry(index) !=
6316 NumberDictionary::kNotFound) {
6317 return DICTIONARY_ELEMENT;
6318 }
6319 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006320 }
6321 default:
6322 UNREACHABLE();
6323 break;
6324 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006325
6326 return UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006327}
6328
6329
6330bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
6331 // Check access rights if needed.
6332 if (IsAccessCheckNeeded() &&
6333 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6334 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6335 return false;
6336 }
6337
6338 // Check for lookup interceptor
6339 if (HasIndexedInterceptor()) {
6340 return HasElementWithInterceptor(receiver, index);
6341 }
6342
6343 switch (GetElementsKind()) {
6344 case FAST_ELEMENTS: {
6345 uint32_t length = IsJSArray() ?
6346 static_cast<uint32_t>
6347 (Smi::cast(JSArray::cast(this)->length())->value()) :
6348 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6349 if ((index < length) &&
6350 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
6351 break;
6352 }
6353 case PIXEL_ELEMENTS: {
6354 PixelArray* pixels = PixelArray::cast(elements());
6355 if (index < static_cast<uint32_t>(pixels->length())) {
6356 return true;
6357 }
6358 break;
6359 }
Steve Block3ce2e202009-11-05 08:53:23 +00006360 case EXTERNAL_BYTE_ELEMENTS:
6361 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6362 case EXTERNAL_SHORT_ELEMENTS:
6363 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6364 case EXTERNAL_INT_ELEMENTS:
6365 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6366 case EXTERNAL_FLOAT_ELEMENTS: {
6367 ExternalArray* array = ExternalArray::cast(elements());
6368 if (index < static_cast<uint32_t>(array->length())) {
6369 return true;
6370 }
6371 break;
6372 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006373 case DICTIONARY_ELEMENTS: {
6374 if (element_dictionary()->FindEntry(index)
6375 != NumberDictionary::kNotFound) {
6376 return true;
6377 }
6378 break;
6379 }
6380 default:
6381 UNREACHABLE();
6382 break;
6383 }
6384
6385 // Handle [] on String objects.
6386 if (this->IsStringObjectWithCharacterAt(index)) return true;
6387
6388 Object* pt = GetPrototype();
6389 if (pt == Heap::null_value()) return false;
6390 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
6391}
6392
6393
John Reck59135872010-11-02 12:39:01 -07006394MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
6395 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006396 // Make sure that the top context does not change when doing
6397 // callbacks or interceptor calls.
6398 AssertNoContextChange ncc;
6399 HandleScope scope;
6400 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6401 Handle<JSObject> this_handle(this);
6402 Handle<Object> value_handle(value);
6403 if (!interceptor->setter()->IsUndefined()) {
6404 v8::IndexedPropertySetter setter =
6405 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
6406 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
6407 CustomArguments args(interceptor->data(), this, this);
6408 v8::AccessorInfo info(args.end());
6409 v8::Handle<v8::Value> result;
6410 {
6411 // Leaving JavaScript.
6412 VMState state(EXTERNAL);
6413 result = setter(index, v8::Utils::ToLocal(value_handle), info);
6414 }
6415 RETURN_IF_SCHEDULED_EXCEPTION();
6416 if (!result.IsEmpty()) return *value_handle;
6417 }
John Reck59135872010-11-02 12:39:01 -07006418 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00006419 this_handle->SetElementWithoutInterceptor(index, *value_handle);
6420 RETURN_IF_SCHEDULED_EXCEPTION();
6421 return raw_result;
6422}
6423
6424
John Reck59135872010-11-02 12:39:01 -07006425MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
6426 Object* structure,
6427 uint32_t index,
6428 Object* holder) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006429 ASSERT(!structure->IsProxy());
6430
6431 // api style callbacks.
6432 if (structure->IsAccessorInfo()) {
6433 AccessorInfo* data = AccessorInfo::cast(structure);
6434 Object* fun_obj = data->getter();
6435 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
6436 HandleScope scope;
6437 Handle<JSObject> self(JSObject::cast(receiver));
6438 Handle<JSObject> holder_handle(JSObject::cast(holder));
6439 Handle<Object> number = Factory::NewNumberFromUint(index);
6440 Handle<String> key(Factory::NumberToString(number));
6441 LOG(ApiNamedPropertyAccess("load", *self, *key));
6442 CustomArguments args(data->data(), *self, *holder_handle);
6443 v8::AccessorInfo info(args.end());
6444 v8::Handle<v8::Value> result;
6445 {
6446 // Leaving JavaScript.
6447 VMState state(EXTERNAL);
6448 result = call_fun(v8::Utils::ToLocal(key), info);
6449 }
6450 RETURN_IF_SCHEDULED_EXCEPTION();
6451 if (result.IsEmpty()) return Heap::undefined_value();
6452 return *v8::Utils::OpenHandle(*result);
6453 }
6454
6455 // __defineGetter__ callback
6456 if (structure->IsFixedArray()) {
6457 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
6458 if (getter->IsJSFunction()) {
6459 return Object::GetPropertyWithDefinedGetter(receiver,
6460 JSFunction::cast(getter));
6461 }
6462 // Getter is not a function.
6463 return Heap::undefined_value();
6464 }
6465
6466 UNREACHABLE();
6467 return NULL;
6468}
6469
6470
John Reck59135872010-11-02 12:39:01 -07006471MaybeObject* JSObject::SetElementWithCallback(Object* structure,
6472 uint32_t index,
6473 Object* value,
6474 JSObject* holder) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006475 HandleScope scope;
6476
6477 // We should never get here to initialize a const with the hole
6478 // value since a const declaration would conflict with the setter.
6479 ASSERT(!value->IsTheHole());
6480 Handle<Object> value_handle(value);
6481
6482 // To accommodate both the old and the new api we switch on the
6483 // data structure used to store the callbacks. Eventually proxy
6484 // callbacks should be phased out.
6485 ASSERT(!structure->IsProxy());
6486
6487 if (structure->IsAccessorInfo()) {
6488 // api style callbacks
6489 AccessorInfo* data = AccessorInfo::cast(structure);
6490 Object* call_obj = data->setter();
6491 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
6492 if (call_fun == NULL) return value;
6493 Handle<Object> number = Factory::NewNumberFromUint(index);
6494 Handle<String> key(Factory::NumberToString(number));
6495 LOG(ApiNamedPropertyAccess("store", this, *key));
6496 CustomArguments args(data->data(), this, JSObject::cast(holder));
6497 v8::AccessorInfo info(args.end());
6498 {
6499 // Leaving JavaScript.
6500 VMState state(EXTERNAL);
6501 call_fun(v8::Utils::ToLocal(key),
6502 v8::Utils::ToLocal(value_handle),
6503 info);
6504 }
6505 RETURN_IF_SCHEDULED_EXCEPTION();
6506 return *value_handle;
6507 }
6508
6509 if (structure->IsFixedArray()) {
6510 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
6511 if (setter->IsJSFunction()) {
6512 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
6513 } else {
6514 Handle<Object> holder_handle(holder);
6515 Handle<Object> key(Factory::NewNumberFromUint(index));
6516 Handle<Object> args[2] = { key, holder_handle };
6517 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
6518 HandleVector(args, 2)));
6519 }
6520 }
6521
6522 UNREACHABLE();
6523 return NULL;
6524}
6525
6526
Steve Blocka7e24c12009-10-30 11:49:00 +00006527// Adding n elements in fast case is O(n*n).
6528// Note: revisit design to have dual undefined values to capture absent
6529// elements.
John Reck59135872010-11-02 12:39:01 -07006530MaybeObject* JSObject::SetFastElement(uint32_t index, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006531 ASSERT(HasFastElements());
6532
John Reck59135872010-11-02 12:39:01 -07006533 Object* elms_obj;
6534 { MaybeObject* maybe_elms_obj = EnsureWritableFastElements();
6535 if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
6536 }
Iain Merrick75681382010-08-19 15:07:18 +01006537 FixedArray* elms = FixedArray::cast(elms_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00006538 uint32_t elms_length = static_cast<uint32_t>(elms->length());
6539
6540 if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006541 if (SetElementWithCallbackSetterInPrototypes(index, value)) {
6542 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006543 }
6544 }
6545
6546 // Check whether there is extra space in fixed array..
6547 if (index < elms_length) {
6548 elms->set(index, value);
6549 if (IsJSArray()) {
6550 // Update the length of the array if needed.
6551 uint32_t array_length = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006552 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006553 if (index >= array_length) {
Leon Clarke4515c472010-02-03 11:58:03 +00006554 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006555 }
6556 }
6557 return value;
6558 }
6559
6560 // Allow gap in fast case.
6561 if ((index - elms_length) < kMaxGap) {
6562 // Try allocating extra space.
6563 int new_capacity = NewElementsCapacity(index+1);
6564 if (new_capacity <= kMaxFastElementsLength ||
6565 !ShouldConvertToSlowElements(new_capacity)) {
6566 ASSERT(static_cast<uint32_t>(new_capacity) > index);
John Reck59135872010-11-02 12:39:01 -07006567 Object* obj;
6568 { MaybeObject* maybe_obj =
6569 SetFastElementsCapacityAndLength(new_capacity, index + 1);
6570 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6571 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006572 FixedArray::cast(elements())->set(index, value);
6573 return value;
6574 }
6575 }
6576
6577 // Otherwise default to slow case.
John Reck59135872010-11-02 12:39:01 -07006578 Object* obj;
6579 { MaybeObject* maybe_obj = NormalizeElements();
6580 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6581 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006582 ASSERT(HasDictionaryElements());
6583 return SetElement(index, value);
6584}
6585
Iain Merrick75681382010-08-19 15:07:18 +01006586
John Reck59135872010-11-02 12:39:01 -07006587MaybeObject* JSObject::SetElement(uint32_t index, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006588 // Check access rights if needed.
6589 if (IsAccessCheckNeeded() &&
6590 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
Iain Merrick75681382010-08-19 15:07:18 +01006591 HandleScope scope;
6592 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00006593 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01006594 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00006595 }
6596
6597 if (IsJSGlobalProxy()) {
6598 Object* proto = GetPrototype();
6599 if (proto->IsNull()) return value;
6600 ASSERT(proto->IsJSGlobalObject());
6601 return JSObject::cast(proto)->SetElement(index, value);
6602 }
6603
6604 // Check for lookup interceptor
6605 if (HasIndexedInterceptor()) {
6606 return SetElementWithInterceptor(index, value);
6607 }
6608
6609 return SetElementWithoutInterceptor(index, value);
6610}
6611
6612
John Reck59135872010-11-02 12:39:01 -07006613MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
6614 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006615 switch (GetElementsKind()) {
6616 case FAST_ELEMENTS:
6617 // Fast case.
6618 return SetFastElement(index, value);
6619 case PIXEL_ELEMENTS: {
6620 PixelArray* pixels = PixelArray::cast(elements());
6621 return pixels->SetValue(index, value);
6622 }
Steve Block3ce2e202009-11-05 08:53:23 +00006623 case EXTERNAL_BYTE_ELEMENTS: {
6624 ExternalByteArray* array = ExternalByteArray::cast(elements());
6625 return array->SetValue(index, value);
6626 }
6627 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6628 ExternalUnsignedByteArray* array =
6629 ExternalUnsignedByteArray::cast(elements());
6630 return array->SetValue(index, value);
6631 }
6632 case EXTERNAL_SHORT_ELEMENTS: {
6633 ExternalShortArray* array = ExternalShortArray::cast(elements());
6634 return array->SetValue(index, value);
6635 }
6636 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6637 ExternalUnsignedShortArray* array =
6638 ExternalUnsignedShortArray::cast(elements());
6639 return array->SetValue(index, value);
6640 }
6641 case EXTERNAL_INT_ELEMENTS: {
6642 ExternalIntArray* array = ExternalIntArray::cast(elements());
6643 return array->SetValue(index, value);
6644 }
6645 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6646 ExternalUnsignedIntArray* array =
6647 ExternalUnsignedIntArray::cast(elements());
6648 return array->SetValue(index, value);
6649 }
6650 case EXTERNAL_FLOAT_ELEMENTS: {
6651 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
6652 return array->SetValue(index, value);
6653 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006654 case DICTIONARY_ELEMENTS: {
6655 // Insert element in the dictionary.
6656 FixedArray* elms = FixedArray::cast(elements());
6657 NumberDictionary* dictionary = NumberDictionary::cast(elms);
6658
6659 int entry = dictionary->FindEntry(index);
6660 if (entry != NumberDictionary::kNotFound) {
6661 Object* element = dictionary->ValueAt(entry);
6662 PropertyDetails details = dictionary->DetailsAt(entry);
6663 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006664 return SetElementWithCallback(element, index, value, this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006665 } else {
6666 dictionary->UpdateMaxNumberKey(index);
6667 dictionary->ValueAtPut(entry, value);
6668 }
6669 } else {
6670 // Index not already used. Look for an accessor in the prototype chain.
6671 if (!IsJSArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006672 if (SetElementWithCallbackSetterInPrototypes(index, value)) {
6673 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006674 }
6675 }
Steve Block8defd9f2010-07-08 12:39:36 +01006676 // When we set the is_extensible flag to false we always force
6677 // the element into dictionary mode (and force them to stay there).
6678 if (!map()->is_extensible()) {
Ben Murdochf87a2032010-10-22 12:50:53 +01006679 Handle<Object> number(Factory::NewNumberFromUint(index));
Steve Block8defd9f2010-07-08 12:39:36 +01006680 Handle<String> index_string(Factory::NumberToString(number));
6681 Handle<Object> args[1] = { index_string };
6682 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
6683 HandleVector(args, 1)));
6684 }
John Reck59135872010-11-02 12:39:01 -07006685 Object* result;
6686 { MaybeObject* maybe_result = dictionary->AtNumberPut(index, value);
6687 if (!maybe_result->ToObject(&result)) return maybe_result;
6688 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006689 if (elms != FixedArray::cast(result)) {
6690 set_elements(FixedArray::cast(result));
6691 }
6692 }
6693
6694 // Update the array length if this JSObject is an array.
6695 if (IsJSArray()) {
6696 JSArray* array = JSArray::cast(this);
John Reck59135872010-11-02 12:39:01 -07006697 Object* return_value;
6698 { MaybeObject* maybe_return_value =
6699 array->JSArrayUpdateLengthFromIndex(index, value);
6700 if (!maybe_return_value->ToObject(&return_value)) {
6701 return maybe_return_value;
6702 }
6703 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006704 }
6705
6706 // Attempt to put this object back in fast case.
6707 if (ShouldConvertToFastElements()) {
6708 uint32_t new_length = 0;
6709 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006710 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006711 } else {
6712 new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
6713 }
John Reck59135872010-11-02 12:39:01 -07006714 Object* obj;
6715 { MaybeObject* maybe_obj =
6716 SetFastElementsCapacityAndLength(new_length, new_length);
6717 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6718 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006719#ifdef DEBUG
6720 if (FLAG_trace_normalization) {
6721 PrintF("Object elements are fast case again:\n");
6722 Print();
6723 }
6724#endif
6725 }
6726
6727 return value;
6728 }
6729 default:
6730 UNREACHABLE();
6731 break;
6732 }
6733 // All possible cases have been handled above. Add a return to avoid the
6734 // complaints from the compiler.
6735 UNREACHABLE();
6736 return Heap::null_value();
6737}
6738
6739
John Reck59135872010-11-02 12:39:01 -07006740MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
6741 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006742 uint32_t old_len = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006743 CHECK(length()->ToArrayIndex(&old_len));
Steve Blocka7e24c12009-10-30 11:49:00 +00006744 // Check to see if we need to update the length. For now, we make
6745 // sure that the length stays within 32-bits (unsigned).
6746 if (index >= old_len && index != 0xffffffff) {
John Reck59135872010-11-02 12:39:01 -07006747 Object* len;
6748 { MaybeObject* maybe_len =
6749 Heap::NumberFromDouble(static_cast<double>(index) + 1);
6750 if (!maybe_len->ToObject(&len)) return maybe_len;
6751 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006752 set_length(len);
6753 }
6754 return value;
6755}
6756
6757
John Reck59135872010-11-02 12:39:01 -07006758MaybeObject* JSObject::GetElementPostInterceptor(JSObject* receiver,
6759 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006760 // Get element works for both JSObject and JSArray since
6761 // JSArray::length cannot change.
6762 switch (GetElementsKind()) {
6763 case FAST_ELEMENTS: {
6764 FixedArray* elms = FixedArray::cast(elements());
6765 if (index < static_cast<uint32_t>(elms->length())) {
6766 Object* value = elms->get(index);
6767 if (!value->IsTheHole()) return value;
6768 }
6769 break;
6770 }
6771 case PIXEL_ELEMENTS: {
6772 // TODO(iposva): Add testcase and implement.
6773 UNIMPLEMENTED();
6774 break;
6775 }
Steve Block3ce2e202009-11-05 08:53:23 +00006776 case EXTERNAL_BYTE_ELEMENTS:
6777 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6778 case EXTERNAL_SHORT_ELEMENTS:
6779 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6780 case EXTERNAL_INT_ELEMENTS:
6781 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6782 case EXTERNAL_FLOAT_ELEMENTS: {
6783 // TODO(kbr): Add testcase and implement.
6784 UNIMPLEMENTED();
6785 break;
6786 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006787 case DICTIONARY_ELEMENTS: {
6788 NumberDictionary* dictionary = element_dictionary();
6789 int entry = dictionary->FindEntry(index);
6790 if (entry != NumberDictionary::kNotFound) {
6791 Object* element = dictionary->ValueAt(entry);
6792 PropertyDetails details = dictionary->DetailsAt(entry);
6793 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006794 return GetElementWithCallback(receiver,
6795 element,
6796 index,
6797 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006798 }
6799 return element;
6800 }
6801 break;
6802 }
6803 default:
6804 UNREACHABLE();
6805 break;
6806 }
6807
6808 // Continue searching via the prototype chain.
6809 Object* pt = GetPrototype();
6810 if (pt == Heap::null_value()) return Heap::undefined_value();
6811 return pt->GetElementWithReceiver(receiver, index);
6812}
6813
6814
John Reck59135872010-11-02 12:39:01 -07006815MaybeObject* JSObject::GetElementWithInterceptor(JSObject* receiver,
6816 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006817 // Make sure that the top context does not change when doing
6818 // callbacks or interceptor calls.
6819 AssertNoContextChange ncc;
6820 HandleScope scope;
6821 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6822 Handle<JSObject> this_handle(receiver);
6823 Handle<JSObject> holder_handle(this);
6824
6825 if (!interceptor->getter()->IsUndefined()) {
6826 v8::IndexedPropertyGetter getter =
6827 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
6828 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
6829 CustomArguments args(interceptor->data(), receiver, this);
6830 v8::AccessorInfo info(args.end());
6831 v8::Handle<v8::Value> result;
6832 {
6833 // Leaving JavaScript.
6834 VMState state(EXTERNAL);
6835 result = getter(index, info);
6836 }
6837 RETURN_IF_SCHEDULED_EXCEPTION();
6838 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6839 }
6840
John Reck59135872010-11-02 12:39:01 -07006841 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00006842 holder_handle->GetElementPostInterceptor(*this_handle, index);
6843 RETURN_IF_SCHEDULED_EXCEPTION();
6844 return raw_result;
6845}
6846
6847
John Reck59135872010-11-02 12:39:01 -07006848MaybeObject* JSObject::GetElementWithReceiver(JSObject* receiver,
6849 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006850 // Check access rights if needed.
6851 if (IsAccessCheckNeeded() &&
6852 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
6853 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
6854 return Heap::undefined_value();
6855 }
6856
6857 if (HasIndexedInterceptor()) {
6858 return GetElementWithInterceptor(receiver, index);
6859 }
6860
6861 // Get element works for both JSObject and JSArray since
6862 // JSArray::length cannot change.
6863 switch (GetElementsKind()) {
6864 case FAST_ELEMENTS: {
6865 FixedArray* elms = FixedArray::cast(elements());
6866 if (index < static_cast<uint32_t>(elms->length())) {
6867 Object* value = elms->get(index);
6868 if (!value->IsTheHole()) return value;
6869 }
6870 break;
6871 }
6872 case PIXEL_ELEMENTS: {
6873 PixelArray* pixels = PixelArray::cast(elements());
6874 if (index < static_cast<uint32_t>(pixels->length())) {
6875 uint8_t value = pixels->get(index);
6876 return Smi::FromInt(value);
6877 }
6878 break;
6879 }
Steve Block3ce2e202009-11-05 08:53:23 +00006880 case EXTERNAL_BYTE_ELEMENTS: {
6881 ExternalByteArray* array = ExternalByteArray::cast(elements());
6882 if (index < static_cast<uint32_t>(array->length())) {
6883 int8_t value = array->get(index);
6884 return Smi::FromInt(value);
6885 }
6886 break;
6887 }
6888 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6889 ExternalUnsignedByteArray* array =
6890 ExternalUnsignedByteArray::cast(elements());
6891 if (index < static_cast<uint32_t>(array->length())) {
6892 uint8_t value = array->get(index);
6893 return Smi::FromInt(value);
6894 }
6895 break;
6896 }
6897 case EXTERNAL_SHORT_ELEMENTS: {
6898 ExternalShortArray* array = ExternalShortArray::cast(elements());
6899 if (index < static_cast<uint32_t>(array->length())) {
6900 int16_t value = array->get(index);
6901 return Smi::FromInt(value);
6902 }
6903 break;
6904 }
6905 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6906 ExternalUnsignedShortArray* array =
6907 ExternalUnsignedShortArray::cast(elements());
6908 if (index < static_cast<uint32_t>(array->length())) {
6909 uint16_t value = array->get(index);
6910 return Smi::FromInt(value);
6911 }
6912 break;
6913 }
6914 case EXTERNAL_INT_ELEMENTS: {
6915 ExternalIntArray* array = ExternalIntArray::cast(elements());
6916 if (index < static_cast<uint32_t>(array->length())) {
6917 int32_t value = array->get(index);
6918 return Heap::NumberFromInt32(value);
6919 }
6920 break;
6921 }
6922 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6923 ExternalUnsignedIntArray* array =
6924 ExternalUnsignedIntArray::cast(elements());
6925 if (index < static_cast<uint32_t>(array->length())) {
6926 uint32_t value = array->get(index);
6927 return Heap::NumberFromUint32(value);
6928 }
6929 break;
6930 }
6931 case EXTERNAL_FLOAT_ELEMENTS: {
6932 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
6933 if (index < static_cast<uint32_t>(array->length())) {
6934 float value = array->get(index);
6935 return Heap::AllocateHeapNumber(value);
6936 }
6937 break;
6938 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006939 case DICTIONARY_ELEMENTS: {
6940 NumberDictionary* dictionary = element_dictionary();
6941 int entry = dictionary->FindEntry(index);
6942 if (entry != NumberDictionary::kNotFound) {
6943 Object* element = dictionary->ValueAt(entry);
6944 PropertyDetails details = dictionary->DetailsAt(entry);
6945 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006946 return GetElementWithCallback(receiver,
6947 element,
6948 index,
6949 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006950 }
6951 return element;
6952 }
6953 break;
6954 }
6955 }
6956
6957 Object* pt = GetPrototype();
6958 if (pt == Heap::null_value()) return Heap::undefined_value();
6959 return pt->GetElementWithReceiver(receiver, index);
6960}
6961
6962
6963bool JSObject::HasDenseElements() {
6964 int capacity = 0;
6965 int number_of_elements = 0;
6966
6967 switch (GetElementsKind()) {
6968 case FAST_ELEMENTS: {
6969 FixedArray* elms = FixedArray::cast(elements());
6970 capacity = elms->length();
6971 for (int i = 0; i < capacity; i++) {
6972 if (!elms->get(i)->IsTheHole()) number_of_elements++;
6973 }
6974 break;
6975 }
Steve Block3ce2e202009-11-05 08:53:23 +00006976 case PIXEL_ELEMENTS:
6977 case EXTERNAL_BYTE_ELEMENTS:
6978 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6979 case EXTERNAL_SHORT_ELEMENTS:
6980 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6981 case EXTERNAL_INT_ELEMENTS:
6982 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6983 case EXTERNAL_FLOAT_ELEMENTS: {
Steve Blocka7e24c12009-10-30 11:49:00 +00006984 return true;
6985 }
6986 case DICTIONARY_ELEMENTS: {
6987 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6988 capacity = dictionary->Capacity();
6989 number_of_elements = dictionary->NumberOfElements();
6990 break;
6991 }
6992 default:
6993 UNREACHABLE();
6994 break;
6995 }
6996
6997 if (capacity == 0) return true;
6998 return (number_of_elements > (capacity / 2));
6999}
7000
7001
7002bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
7003 ASSERT(HasFastElements());
7004 // Keep the array in fast case if the current backing storage is
7005 // almost filled and if the new capacity is no more than twice the
7006 // old capacity.
7007 int elements_length = FixedArray::cast(elements())->length();
7008 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
7009}
7010
7011
7012bool JSObject::ShouldConvertToFastElements() {
7013 ASSERT(HasDictionaryElements());
7014 NumberDictionary* dictionary = NumberDictionary::cast(elements());
7015 // If the elements are sparse, we should not go back to fast case.
7016 if (!HasDenseElements()) return false;
7017 // If an element has been added at a very high index in the elements
7018 // dictionary, we cannot go back to fast case.
7019 if (dictionary->requires_slow_elements()) return false;
7020 // An object requiring access checks is never allowed to have fast
7021 // elements. If it had fast elements we would skip security checks.
7022 if (IsAccessCheckNeeded()) return false;
7023 // If the dictionary backing storage takes up roughly half as much
7024 // space as a fast-case backing storage would the array should have
7025 // fast elements.
7026 uint32_t length = 0;
7027 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007028 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
Steve Blocka7e24c12009-10-30 11:49:00 +00007029 } else {
7030 length = dictionary->max_number_key();
7031 }
7032 return static_cast<uint32_t>(dictionary->Capacity()) >=
7033 (length / (2 * NumberDictionary::kEntrySize));
7034}
7035
7036
7037// Certain compilers request function template instantiation when they
7038// see the definition of the other template functions in the
7039// class. This requires us to have the template functions put
7040// together, so even though this function belongs in objects-debug.cc,
7041// we keep it here instead to satisfy certain compilers.
7042#ifdef DEBUG
7043template<typename Shape, typename Key>
7044void Dictionary<Shape, Key>::Print() {
7045 int capacity = HashTable<Shape, Key>::Capacity();
7046 for (int i = 0; i < capacity; i++) {
7047 Object* k = HashTable<Shape, Key>::KeyAt(i);
7048 if (HashTable<Shape, Key>::IsKey(k)) {
7049 PrintF(" ");
7050 if (k->IsString()) {
7051 String::cast(k)->StringPrint();
7052 } else {
7053 k->ShortPrint();
7054 }
7055 PrintF(": ");
7056 ValueAt(i)->ShortPrint();
7057 PrintF("\n");
7058 }
7059 }
7060}
7061#endif
7062
7063
7064template<typename Shape, typename Key>
7065void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
7066 int pos = 0;
7067 int capacity = HashTable<Shape, Key>::Capacity();
Leon Clarke4515c472010-02-03 11:58:03 +00007068 AssertNoAllocation no_gc;
7069 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007070 for (int i = 0; i < capacity; i++) {
7071 Object* k = Dictionary<Shape, Key>::KeyAt(i);
7072 if (Dictionary<Shape, Key>::IsKey(k)) {
7073 elements->set(pos++, ValueAt(i), mode);
7074 }
7075 }
7076 ASSERT(pos == elements->length());
7077}
7078
7079
7080InterceptorInfo* JSObject::GetNamedInterceptor() {
7081 ASSERT(map()->has_named_interceptor());
7082 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01007083 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00007084 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01007085 constructor->shared()->get_api_func_data()->named_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00007086 return InterceptorInfo::cast(result);
7087}
7088
7089
7090InterceptorInfo* JSObject::GetIndexedInterceptor() {
7091 ASSERT(map()->has_indexed_interceptor());
7092 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01007093 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00007094 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01007095 constructor->shared()->get_api_func_data()->indexed_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00007096 return InterceptorInfo::cast(result);
7097}
7098
7099
John Reck59135872010-11-02 12:39:01 -07007100MaybeObject* JSObject::GetPropertyPostInterceptor(
7101 JSObject* receiver,
7102 String* name,
7103 PropertyAttributes* attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007104 // Check local property in holder, ignore interceptor.
7105 LookupResult result;
7106 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007107 if (result.IsProperty()) {
7108 return GetProperty(receiver, &result, name, attributes);
7109 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007110 // Continue searching via the prototype chain.
7111 Object* pt = GetPrototype();
7112 *attributes = ABSENT;
7113 if (pt == Heap::null_value()) return Heap::undefined_value();
7114 return pt->GetPropertyWithReceiver(receiver, name, attributes);
7115}
7116
7117
John Reck59135872010-11-02 12:39:01 -07007118MaybeObject* JSObject::GetLocalPropertyPostInterceptor(
Steve Blockd0582a62009-12-15 09:54:21 +00007119 JSObject* receiver,
7120 String* name,
7121 PropertyAttributes* attributes) {
7122 // Check local property in holder, ignore interceptor.
7123 LookupResult result;
7124 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007125 if (result.IsProperty()) {
7126 return GetProperty(receiver, &result, name, attributes);
7127 }
7128 return Heap::undefined_value();
Steve Blockd0582a62009-12-15 09:54:21 +00007129}
7130
7131
John Reck59135872010-11-02 12:39:01 -07007132MaybeObject* JSObject::GetPropertyWithInterceptor(
Steve Blocka7e24c12009-10-30 11:49:00 +00007133 JSObject* receiver,
7134 String* name,
7135 PropertyAttributes* attributes) {
7136 InterceptorInfo* interceptor = GetNamedInterceptor();
7137 HandleScope scope;
7138 Handle<JSObject> receiver_handle(receiver);
7139 Handle<JSObject> holder_handle(this);
7140 Handle<String> name_handle(name);
7141
7142 if (!interceptor->getter()->IsUndefined()) {
7143 v8::NamedPropertyGetter getter =
7144 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
7145 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
7146 CustomArguments args(interceptor->data(), receiver, this);
7147 v8::AccessorInfo info(args.end());
7148 v8::Handle<v8::Value> result;
7149 {
7150 // Leaving JavaScript.
7151 VMState state(EXTERNAL);
7152 result = getter(v8::Utils::ToLocal(name_handle), info);
7153 }
7154 RETURN_IF_SCHEDULED_EXCEPTION();
7155 if (!result.IsEmpty()) {
7156 *attributes = NONE;
7157 return *v8::Utils::OpenHandle(*result);
7158 }
7159 }
7160
John Reck59135872010-11-02 12:39:01 -07007161 MaybeObject* result = holder_handle->GetPropertyPostInterceptor(
Steve Blocka7e24c12009-10-30 11:49:00 +00007162 *receiver_handle,
7163 *name_handle,
7164 attributes);
7165 RETURN_IF_SCHEDULED_EXCEPTION();
7166 return result;
7167}
7168
7169
7170bool JSObject::HasRealNamedProperty(String* key) {
7171 // Check access rights if needed.
7172 if (IsAccessCheckNeeded() &&
7173 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
7174 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7175 return false;
7176 }
7177
7178 LookupResult result;
7179 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007180 return result.IsProperty() && (result.type() != INTERCEPTOR);
Steve Blocka7e24c12009-10-30 11:49:00 +00007181}
7182
7183
7184bool JSObject::HasRealElementProperty(uint32_t index) {
7185 // Check access rights if needed.
7186 if (IsAccessCheckNeeded() &&
7187 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
7188 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7189 return false;
7190 }
7191
7192 // Handle [] on String objects.
7193 if (this->IsStringObjectWithCharacterAt(index)) return true;
7194
7195 switch (GetElementsKind()) {
7196 case FAST_ELEMENTS: {
7197 uint32_t length = IsJSArray() ?
7198 static_cast<uint32_t>(
7199 Smi::cast(JSArray::cast(this)->length())->value()) :
7200 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7201 return (index < length) &&
7202 !FixedArray::cast(elements())->get(index)->IsTheHole();
7203 }
7204 case PIXEL_ELEMENTS: {
7205 PixelArray* pixels = PixelArray::cast(elements());
7206 return index < static_cast<uint32_t>(pixels->length());
7207 }
Steve Block3ce2e202009-11-05 08:53:23 +00007208 case EXTERNAL_BYTE_ELEMENTS:
7209 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7210 case EXTERNAL_SHORT_ELEMENTS:
7211 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7212 case EXTERNAL_INT_ELEMENTS:
7213 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7214 case EXTERNAL_FLOAT_ELEMENTS: {
7215 ExternalArray* array = ExternalArray::cast(elements());
7216 return index < static_cast<uint32_t>(array->length());
7217 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007218 case DICTIONARY_ELEMENTS: {
7219 return element_dictionary()->FindEntry(index)
7220 != NumberDictionary::kNotFound;
7221 }
7222 default:
7223 UNREACHABLE();
7224 break;
7225 }
7226 // All possibilities have been handled above already.
7227 UNREACHABLE();
7228 return Heap::null_value();
7229}
7230
7231
7232bool JSObject::HasRealNamedCallbackProperty(String* key) {
7233 // Check access rights if needed.
7234 if (IsAccessCheckNeeded() &&
7235 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
7236 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7237 return false;
7238 }
7239
7240 LookupResult result;
7241 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007242 return result.IsProperty() && (result.type() == CALLBACKS);
Steve Blocka7e24c12009-10-30 11:49:00 +00007243}
7244
7245
7246int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
7247 if (HasFastProperties()) {
7248 DescriptorArray* descs = map()->instance_descriptors();
7249 int result = 0;
7250 for (int i = 0; i < descs->number_of_descriptors(); i++) {
7251 PropertyDetails details = descs->GetDetails(i);
7252 if (details.IsProperty() && (details.attributes() & filter) == 0) {
7253 result++;
7254 }
7255 }
7256 return result;
7257 } else {
7258 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
7259 }
7260}
7261
7262
7263int JSObject::NumberOfEnumProperties() {
7264 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
7265}
7266
7267
7268void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
7269 Object* temp = get(i);
7270 set(i, get(j));
7271 set(j, temp);
7272 if (this != numbers) {
7273 temp = numbers->get(i);
7274 numbers->set(i, numbers->get(j));
7275 numbers->set(j, temp);
7276 }
7277}
7278
7279
7280static void InsertionSortPairs(FixedArray* content,
7281 FixedArray* numbers,
7282 int len) {
7283 for (int i = 1; i < len; i++) {
7284 int j = i;
7285 while (j > 0 &&
7286 (NumberToUint32(numbers->get(j - 1)) >
7287 NumberToUint32(numbers->get(j)))) {
7288 content->SwapPairs(numbers, j - 1, j);
7289 j--;
7290 }
7291 }
7292}
7293
7294
7295void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
7296 // In-place heap sort.
7297 ASSERT(content->length() == numbers->length());
7298
7299 // Bottom-up max-heap construction.
7300 for (int i = 1; i < len; ++i) {
7301 int child_index = i;
7302 while (child_index > 0) {
7303 int parent_index = ((child_index + 1) >> 1) - 1;
7304 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
7305 uint32_t child_value = NumberToUint32(numbers->get(child_index));
7306 if (parent_value < child_value) {
7307 content->SwapPairs(numbers, parent_index, child_index);
7308 } else {
7309 break;
7310 }
7311 child_index = parent_index;
7312 }
7313 }
7314
7315 // Extract elements and create sorted array.
7316 for (int i = len - 1; i > 0; --i) {
7317 // Put max element at the back of the array.
7318 content->SwapPairs(numbers, 0, i);
7319 // Sift down the new top element.
7320 int parent_index = 0;
7321 while (true) {
7322 int child_index = ((parent_index + 1) << 1) - 1;
7323 if (child_index >= i) break;
7324 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
7325 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
7326 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
7327 if (child_index + 1 >= i || child1_value > child2_value) {
7328 if (parent_value > child1_value) break;
7329 content->SwapPairs(numbers, parent_index, child_index);
7330 parent_index = child_index;
7331 } else {
7332 if (parent_value > child2_value) break;
7333 content->SwapPairs(numbers, parent_index, child_index + 1);
7334 parent_index = child_index + 1;
7335 }
7336 }
7337 }
7338}
7339
7340
7341// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
7342void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
7343 ASSERT(this->length() == numbers->length());
7344 // For small arrays, simply use insertion sort.
7345 if (len <= 10) {
7346 InsertionSortPairs(this, numbers, len);
7347 return;
7348 }
7349 // Check the range of indices.
7350 uint32_t min_index = NumberToUint32(numbers->get(0));
7351 uint32_t max_index = min_index;
7352 uint32_t i;
7353 for (i = 1; i < len; i++) {
7354 if (NumberToUint32(numbers->get(i)) < min_index) {
7355 min_index = NumberToUint32(numbers->get(i));
7356 } else if (NumberToUint32(numbers->get(i)) > max_index) {
7357 max_index = NumberToUint32(numbers->get(i));
7358 }
7359 }
7360 if (max_index - min_index + 1 == len) {
7361 // Indices form a contiguous range, unless there are duplicates.
7362 // Do an in-place linear time sort assuming distinct numbers, but
7363 // avoid hanging in case they are not.
7364 for (i = 0; i < len; i++) {
7365 uint32_t p;
7366 uint32_t j = 0;
7367 // While the current element at i is not at its correct position p,
7368 // swap the elements at these two positions.
7369 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
7370 j++ < len) {
7371 SwapPairs(numbers, i, p);
7372 }
7373 }
7374 } else {
7375 HeapSortPairs(this, numbers, len);
7376 return;
7377 }
7378}
7379
7380
7381// Fill in the names of local properties into the supplied storage. The main
7382// purpose of this function is to provide reflection information for the object
7383// mirrors.
7384void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
7385 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
7386 if (HasFastProperties()) {
7387 DescriptorArray* descs = map()->instance_descriptors();
7388 for (int i = 0; i < descs->number_of_descriptors(); i++) {
7389 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
7390 }
7391 ASSERT(storage->length() >= index);
7392 } else {
7393 property_dictionary()->CopyKeysTo(storage);
7394 }
7395}
7396
7397
7398int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
7399 return GetLocalElementKeys(NULL, filter);
7400}
7401
7402
7403int JSObject::NumberOfEnumElements() {
Steve Blockd0582a62009-12-15 09:54:21 +00007404 // Fast case for objects with no elements.
7405 if (!IsJSValue() && HasFastElements()) {
7406 uint32_t length = IsJSArray() ?
7407 static_cast<uint32_t>(
7408 Smi::cast(JSArray::cast(this)->length())->value()) :
7409 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7410 if (length == 0) return 0;
7411 }
7412 // Compute the number of enumerable elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00007413 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
7414}
7415
7416
7417int JSObject::GetLocalElementKeys(FixedArray* storage,
7418 PropertyAttributes filter) {
7419 int counter = 0;
7420 switch (GetElementsKind()) {
7421 case FAST_ELEMENTS: {
7422 int length = IsJSArray() ?
7423 Smi::cast(JSArray::cast(this)->length())->value() :
7424 FixedArray::cast(elements())->length();
7425 for (int i = 0; i < length; i++) {
7426 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
7427 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007428 storage->set(counter, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00007429 }
7430 counter++;
7431 }
7432 }
7433 ASSERT(!storage || storage->length() >= counter);
7434 break;
7435 }
7436 case PIXEL_ELEMENTS: {
7437 int length = PixelArray::cast(elements())->length();
7438 while (counter < length) {
7439 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007440 storage->set(counter, Smi::FromInt(counter));
Steve Blocka7e24c12009-10-30 11:49:00 +00007441 }
7442 counter++;
7443 }
7444 ASSERT(!storage || storage->length() >= counter);
7445 break;
7446 }
Steve Block3ce2e202009-11-05 08:53:23 +00007447 case EXTERNAL_BYTE_ELEMENTS:
7448 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7449 case EXTERNAL_SHORT_ELEMENTS:
7450 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7451 case EXTERNAL_INT_ELEMENTS:
7452 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7453 case EXTERNAL_FLOAT_ELEMENTS: {
7454 int length = ExternalArray::cast(elements())->length();
7455 while (counter < length) {
7456 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007457 storage->set(counter, Smi::FromInt(counter));
Steve Block3ce2e202009-11-05 08:53:23 +00007458 }
7459 counter++;
7460 }
7461 ASSERT(!storage || storage->length() >= counter);
7462 break;
7463 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007464 case DICTIONARY_ELEMENTS: {
7465 if (storage != NULL) {
7466 element_dictionary()->CopyKeysTo(storage, filter);
7467 }
7468 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
7469 break;
7470 }
7471 default:
7472 UNREACHABLE();
7473 break;
7474 }
7475
7476 if (this->IsJSValue()) {
7477 Object* val = JSValue::cast(this)->value();
7478 if (val->IsString()) {
7479 String* str = String::cast(val);
7480 if (storage) {
7481 for (int i = 0; i < str->length(); i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00007482 storage->set(counter + i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00007483 }
7484 }
7485 counter += str->length();
7486 }
7487 }
7488 ASSERT(!storage || storage->length() == counter);
7489 return counter;
7490}
7491
7492
7493int JSObject::GetEnumElementKeys(FixedArray* storage) {
7494 return GetLocalElementKeys(storage,
7495 static_cast<PropertyAttributes>(DONT_ENUM));
7496}
7497
7498
7499bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
7500 ASSERT(other->IsNumber());
7501 return key == static_cast<uint32_t>(other->Number());
7502}
7503
7504
7505uint32_t NumberDictionaryShape::Hash(uint32_t key) {
7506 return ComputeIntegerHash(key);
7507}
7508
7509
7510uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) {
7511 ASSERT(other->IsNumber());
7512 return ComputeIntegerHash(static_cast<uint32_t>(other->Number()));
7513}
7514
7515
John Reck59135872010-11-02 12:39:01 -07007516MaybeObject* NumberDictionaryShape::AsObject(uint32_t key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007517 return Heap::NumberFromUint32(key);
7518}
7519
7520
7521bool StringDictionaryShape::IsMatch(String* key, Object* other) {
7522 // We know that all entries in a hash table had their hash keys created.
7523 // Use that knowledge to have fast failure.
7524 if (key->Hash() != String::cast(other)->Hash()) return false;
7525 return key->Equals(String::cast(other));
7526}
7527
7528
7529uint32_t StringDictionaryShape::Hash(String* key) {
7530 return key->Hash();
7531}
7532
7533
7534uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) {
7535 return String::cast(other)->Hash();
7536}
7537
7538
John Reck59135872010-11-02 12:39:01 -07007539MaybeObject* StringDictionaryShape::AsObject(String* key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007540 return key;
7541}
7542
7543
7544// StringKey simply carries a string object as key.
7545class StringKey : public HashTableKey {
7546 public:
7547 explicit StringKey(String* string) :
7548 string_(string),
7549 hash_(HashForObject(string)) { }
7550
7551 bool IsMatch(Object* string) {
7552 // We know that all entries in a hash table had their hash keys created.
7553 // Use that knowledge to have fast failure.
7554 if (hash_ != HashForObject(string)) {
7555 return false;
7556 }
7557 return string_->Equals(String::cast(string));
7558 }
7559
7560 uint32_t Hash() { return hash_; }
7561
7562 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
7563
7564 Object* AsObject() { return string_; }
7565
7566 String* string_;
7567 uint32_t hash_;
7568};
7569
7570
7571// StringSharedKeys are used as keys in the eval cache.
7572class StringSharedKey : public HashTableKey {
7573 public:
7574 StringSharedKey(String* source, SharedFunctionInfo* shared)
7575 : source_(source), shared_(shared) { }
7576
7577 bool IsMatch(Object* other) {
7578 if (!other->IsFixedArray()) return false;
7579 FixedArray* pair = FixedArray::cast(other);
7580 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7581 if (shared != shared_) return false;
7582 String* source = String::cast(pair->get(1));
7583 return source->Equals(source_);
7584 }
7585
7586 static uint32_t StringSharedHashHelper(String* source,
7587 SharedFunctionInfo* shared) {
7588 uint32_t hash = source->Hash();
7589 if (shared->HasSourceCode()) {
7590 // Instead of using the SharedFunctionInfo pointer in the hash
7591 // code computation, we use a combination of the hash of the
7592 // script source code and the start and end positions. We do
7593 // this to ensure that the cache entries can survive garbage
7594 // collection.
7595 Script* script = Script::cast(shared->script());
7596 hash ^= String::cast(script->source())->Hash();
7597 hash += shared->start_position();
7598 }
7599 return hash;
7600 }
7601
7602 uint32_t Hash() {
7603 return StringSharedHashHelper(source_, shared_);
7604 }
7605
7606 uint32_t HashForObject(Object* obj) {
7607 FixedArray* pair = FixedArray::cast(obj);
7608 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7609 String* source = String::cast(pair->get(1));
7610 return StringSharedHashHelper(source, shared);
7611 }
7612
John Reck59135872010-11-02 12:39:01 -07007613 MUST_USE_RESULT MaybeObject* AsObject() {
7614 Object* obj;
7615 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(2);
7616 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7617 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007618 FixedArray* pair = FixedArray::cast(obj);
7619 pair->set(0, shared_);
7620 pair->set(1, source_);
7621 return pair;
7622 }
7623
7624 private:
7625 String* source_;
7626 SharedFunctionInfo* shared_;
7627};
7628
7629
7630// RegExpKey carries the source and flags of a regular expression as key.
7631class RegExpKey : public HashTableKey {
7632 public:
7633 RegExpKey(String* string, JSRegExp::Flags flags)
7634 : string_(string),
7635 flags_(Smi::FromInt(flags.value())) { }
7636
Steve Block3ce2e202009-11-05 08:53:23 +00007637 // Rather than storing the key in the hash table, a pointer to the
7638 // stored value is stored where the key should be. IsMatch then
7639 // compares the search key to the found object, rather than comparing
7640 // a key to a key.
Steve Blocka7e24c12009-10-30 11:49:00 +00007641 bool IsMatch(Object* obj) {
7642 FixedArray* val = FixedArray::cast(obj);
7643 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
7644 && (flags_ == val->get(JSRegExp::kFlagsIndex));
7645 }
7646
7647 uint32_t Hash() { return RegExpHash(string_, flags_); }
7648
7649 Object* AsObject() {
7650 // Plain hash maps, which is where regexp keys are used, don't
7651 // use this function.
7652 UNREACHABLE();
7653 return NULL;
7654 }
7655
7656 uint32_t HashForObject(Object* obj) {
7657 FixedArray* val = FixedArray::cast(obj);
7658 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
7659 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
7660 }
7661
7662 static uint32_t RegExpHash(String* string, Smi* flags) {
7663 return string->Hash() + flags->value();
7664 }
7665
7666 String* string_;
7667 Smi* flags_;
7668};
7669
7670// Utf8SymbolKey carries a vector of chars as key.
7671class Utf8SymbolKey : public HashTableKey {
7672 public:
7673 explicit Utf8SymbolKey(Vector<const char> string)
Steve Blockd0582a62009-12-15 09:54:21 +00007674 : string_(string), hash_field_(0) { }
Steve Blocka7e24c12009-10-30 11:49:00 +00007675
7676 bool IsMatch(Object* string) {
7677 return String::cast(string)->IsEqualTo(string_);
7678 }
7679
7680 uint32_t Hash() {
Steve Blockd0582a62009-12-15 09:54:21 +00007681 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00007682 unibrow::Utf8InputBuffer<> buffer(string_.start(),
7683 static_cast<unsigned>(string_.length()));
7684 chars_ = buffer.Length();
Steve Blockd0582a62009-12-15 09:54:21 +00007685 hash_field_ = String::ComputeHashField(&buffer, chars_);
7686 uint32_t result = hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00007687 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
7688 return result;
7689 }
7690
7691 uint32_t HashForObject(Object* other) {
7692 return String::cast(other)->Hash();
7693 }
7694
John Reck59135872010-11-02 12:39:01 -07007695 MaybeObject* AsObject() {
Steve Blockd0582a62009-12-15 09:54:21 +00007696 if (hash_field_ == 0) Hash();
7697 return Heap::AllocateSymbol(string_, chars_, hash_field_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007698 }
7699
7700 Vector<const char> string_;
Steve Blockd0582a62009-12-15 09:54:21 +00007701 uint32_t hash_field_;
Steve Blocka7e24c12009-10-30 11:49:00 +00007702 int chars_; // Caches the number of characters when computing the hash code.
7703};
7704
7705
7706// SymbolKey carries a string/symbol object as key.
7707class SymbolKey : public HashTableKey {
7708 public:
7709 explicit SymbolKey(String* string) : string_(string) { }
7710
7711 bool IsMatch(Object* string) {
7712 return String::cast(string)->Equals(string_);
7713 }
7714
7715 uint32_t Hash() { return string_->Hash(); }
7716
7717 uint32_t HashForObject(Object* other) {
7718 return String::cast(other)->Hash();
7719 }
7720
John Reck59135872010-11-02 12:39:01 -07007721 MaybeObject* AsObject() {
Leon Clarkef7060e22010-06-03 12:02:55 +01007722 // Attempt to flatten the string, so that symbols will most often
7723 // be flat strings.
7724 string_ = string_->TryFlattenGetString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007725 // Transform string to symbol if possible.
7726 Map* map = Heap::SymbolMapForString(string_);
7727 if (map != NULL) {
7728 string_->set_map(map);
7729 ASSERT(string_->IsSymbol());
7730 return string_;
7731 }
7732 // Otherwise allocate a new symbol.
7733 StringInputBuffer buffer(string_);
7734 return Heap::AllocateInternalSymbol(&buffer,
7735 string_->length(),
Steve Blockd0582a62009-12-15 09:54:21 +00007736 string_->hash_field());
Steve Blocka7e24c12009-10-30 11:49:00 +00007737 }
7738
7739 static uint32_t StringHash(Object* obj) {
7740 return String::cast(obj)->Hash();
7741 }
7742
7743 String* string_;
7744};
7745
7746
7747template<typename Shape, typename Key>
7748void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
7749 IteratePointers(v, 0, kElementsStartOffset);
7750}
7751
7752
7753template<typename Shape, typename Key>
7754void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
7755 IteratePointers(v,
7756 kElementsStartOffset,
7757 kHeaderSize + length() * kPointerSize);
7758}
7759
7760
7761template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07007762MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
7763 PretenureFlag pretenure) {
Steve Block6ded16b2010-05-10 14:33:55 +01007764 const int kMinCapacity = 32;
7765 int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
7766 if (capacity < kMinCapacity) {
7767 capacity = kMinCapacity; // Guarantee min capacity.
Leon Clarkee46be812010-01-19 14:06:41 +00007768 } else if (capacity > HashTable::kMaxCapacity) {
7769 return Failure::OutOfMemoryException();
7770 }
7771
John Reck59135872010-11-02 12:39:01 -07007772 Object* obj;
7773 { MaybeObject* maybe_obj =
7774 Heap::AllocateHashTable(EntryToIndex(capacity), pretenure);
7775 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00007776 }
John Reck59135872010-11-02 12:39:01 -07007777 HashTable::cast(obj)->SetNumberOfElements(0);
7778 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
7779 HashTable::cast(obj)->SetCapacity(capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007780 return obj;
7781}
7782
7783
Leon Clarkee46be812010-01-19 14:06:41 +00007784// Find entry for key otherwise return kNotFound.
Steve Blocka7e24c12009-10-30 11:49:00 +00007785template<typename Shape, typename Key>
7786int HashTable<Shape, Key>::FindEntry(Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007787 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007788 uint32_t entry = FirstProbe(Shape::Hash(key), capacity);
7789 uint32_t count = 1;
7790 // EnsureCapacity will guarantee the hash table is never full.
7791 while (true) {
7792 Object* element = KeyAt(entry);
7793 if (element->IsUndefined()) break; // Empty entry.
7794 if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
7795 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007796 }
7797 return kNotFound;
7798}
7799
7800
Ben Murdoch3bec4d22010-07-22 14:51:16 +01007801// Find entry for key otherwise return kNotFound.
7802int StringDictionary::FindEntry(String* key) {
7803 if (!key->IsSymbol()) {
7804 return HashTable<StringDictionaryShape, String*>::FindEntry(key);
7805 }
7806
7807 // Optimized for symbol key. Knowledge of the key type allows:
7808 // 1. Move the check if the key is a symbol out of the loop.
7809 // 2. Avoid comparing hash codes in symbol to symbol comparision.
7810 // 3. Detect a case when a dictionary key is not a symbol but the key is.
7811 // In case of positive result the dictionary key may be replaced by
7812 // the symbol with minimal performance penalty. It gives a chance to
7813 // perform further lookups in code stubs (and significant performance boost
7814 // a certain style of code).
7815
7816 // EnsureCapacity will guarantee the hash table is never full.
7817 uint32_t capacity = Capacity();
7818 uint32_t entry = FirstProbe(key->Hash(), capacity);
7819 uint32_t count = 1;
7820
7821 while (true) {
7822 int index = EntryToIndex(entry);
7823 Object* element = get(index);
7824 if (element->IsUndefined()) break; // Empty entry.
7825 if (key == element) return entry;
7826 if (!element->IsSymbol() &&
7827 !element->IsNull() &&
7828 String::cast(element)->Equals(key)) {
7829 // Replace a non-symbol key by the equivalent symbol for faster further
7830 // lookups.
7831 set(index, key);
7832 return entry;
7833 }
7834 ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
7835 entry = NextProbe(entry, count++, capacity);
7836 }
7837 return kNotFound;
7838}
7839
7840
Steve Blocka7e24c12009-10-30 11:49:00 +00007841template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07007842MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007843 int capacity = Capacity();
7844 int nof = NumberOfElements() + n;
Leon Clarkee46be812010-01-19 14:06:41 +00007845 int nod = NumberOfDeletedElements();
7846 // Return if:
7847 // 50% is still free after adding n elements and
7848 // at most 50% of the free elements are deleted elements.
Steve Block6ded16b2010-05-10 14:33:55 +01007849 if (nod <= (capacity - nof) >> 1) {
7850 int needed_free = nof >> 1;
7851 if (nof + needed_free <= capacity) return this;
7852 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007853
Steve Block6ded16b2010-05-10 14:33:55 +01007854 const int kMinCapacityForPretenure = 256;
7855 bool pretenure =
7856 (capacity > kMinCapacityForPretenure) && !Heap::InNewSpace(this);
John Reck59135872010-11-02 12:39:01 -07007857 Object* obj;
7858 { MaybeObject* maybe_obj =
7859 Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
7860 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7861 }
Leon Clarke4515c472010-02-03 11:58:03 +00007862
7863 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00007864 HashTable* table = HashTable::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00007865 WriteBarrierMode mode = table->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007866
7867 // Copy prefix to new array.
7868 for (int i = kPrefixStartIndex;
7869 i < kPrefixStartIndex + Shape::kPrefixSize;
7870 i++) {
7871 table->set(i, get(i), mode);
7872 }
7873 // Rehash the elements.
7874 for (int i = 0; i < capacity; i++) {
7875 uint32_t from_index = EntryToIndex(i);
7876 Object* k = get(from_index);
7877 if (IsKey(k)) {
7878 uint32_t hash = Shape::HashForObject(key, k);
7879 uint32_t insertion_index =
7880 EntryToIndex(table->FindInsertionEntry(hash));
7881 for (int j = 0; j < Shape::kEntrySize; j++) {
7882 table->set(insertion_index + j, get(from_index + j), mode);
7883 }
7884 }
7885 }
7886 table->SetNumberOfElements(NumberOfElements());
Leon Clarkee46be812010-01-19 14:06:41 +00007887 table->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007888 return table;
7889}
7890
7891
7892template<typename Shape, typename Key>
7893uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
7894 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007895 uint32_t entry = FirstProbe(hash, capacity);
7896 uint32_t count = 1;
7897 // EnsureCapacity will guarantee the hash table is never full.
7898 while (true) {
7899 Object* element = KeyAt(entry);
7900 if (element->IsUndefined() || element->IsNull()) break;
7901 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007902 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007903 return entry;
7904}
7905
7906// Force instantiation of template instances class.
7907// Please note this list is compiler dependent.
7908
7909template class HashTable<SymbolTableShape, HashTableKey*>;
7910
7911template class HashTable<CompilationCacheShape, HashTableKey*>;
7912
7913template class HashTable<MapCacheShape, HashTableKey*>;
7914
7915template class Dictionary<StringDictionaryShape, String*>;
7916
7917template class Dictionary<NumberDictionaryShape, uint32_t>;
7918
John Reck59135872010-11-02 12:39:01 -07007919template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
Steve Blocka7e24c12009-10-30 11:49:00 +00007920 int);
7921
John Reck59135872010-11-02 12:39:01 -07007922template MaybeObject* Dictionary<StringDictionaryShape, String*>::Allocate(
Steve Blocka7e24c12009-10-30 11:49:00 +00007923 int);
7924
John Reck59135872010-11-02 12:39:01 -07007925template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
Steve Blocka7e24c12009-10-30 11:49:00 +00007926 uint32_t, Object*);
7927
7928template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
7929 Object*);
7930
7931template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
7932 Object*);
7933
7934template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
7935 FixedArray*, PropertyAttributes);
7936
7937template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
7938 int, JSObject::DeleteMode);
7939
7940template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
7941 int, JSObject::DeleteMode);
7942
7943template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
7944 FixedArray*);
7945
7946template int
7947Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
7948 PropertyAttributes);
7949
John Reck59135872010-11-02 12:39:01 -07007950template MaybeObject* Dictionary<StringDictionaryShape, String*>::Add(
Steve Blocka7e24c12009-10-30 11:49:00 +00007951 String*, Object*, PropertyDetails);
7952
John Reck59135872010-11-02 12:39:01 -07007953template MaybeObject*
Steve Blocka7e24c12009-10-30 11:49:00 +00007954Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
7955
7956template int
7957Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
7958 PropertyAttributes);
7959
John Reck59135872010-11-02 12:39:01 -07007960template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Add(
Steve Blocka7e24c12009-10-30 11:49:00 +00007961 uint32_t, Object*, PropertyDetails);
7962
John Reck59135872010-11-02 12:39:01 -07007963template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::
7964 EnsureCapacity(int, uint32_t);
Steve Blocka7e24c12009-10-30 11:49:00 +00007965
John Reck59135872010-11-02 12:39:01 -07007966template MaybeObject* Dictionary<StringDictionaryShape, String*>::
7967 EnsureCapacity(int, String*);
Steve Blocka7e24c12009-10-30 11:49:00 +00007968
John Reck59135872010-11-02 12:39:01 -07007969template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
Steve Blocka7e24c12009-10-30 11:49:00 +00007970 uint32_t, Object*, PropertyDetails, uint32_t);
7971
John Reck59135872010-11-02 12:39:01 -07007972template MaybeObject* Dictionary<StringDictionaryShape, String*>::AddEntry(
Steve Blocka7e24c12009-10-30 11:49:00 +00007973 String*, Object*, PropertyDetails, uint32_t);
7974
7975template
7976int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
7977
7978template
7979int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
7980
Leon Clarkee46be812010-01-19 14:06:41 +00007981template
7982int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
7983
7984
Steve Blocka7e24c12009-10-30 11:49:00 +00007985// Collates undefined and unexisting elements below limit from position
7986// zero of the elements. The object stays in Dictionary mode.
John Reck59135872010-11-02 12:39:01 -07007987MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007988 ASSERT(HasDictionaryElements());
7989 // Must stay in dictionary mode, either because of requires_slow_elements,
7990 // or because we are not going to sort (and therefore compact) all of the
7991 // elements.
7992 NumberDictionary* dict = element_dictionary();
7993 HeapNumber* result_double = NULL;
7994 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7995 // Allocate space for result before we start mutating the object.
John Reck59135872010-11-02 12:39:01 -07007996 Object* new_double;
7997 { MaybeObject* maybe_new_double = Heap::AllocateHeapNumber(0.0);
7998 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
7999 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008000 result_double = HeapNumber::cast(new_double);
8001 }
8002
John Reck59135872010-11-02 12:39:01 -07008003 Object* obj;
8004 { MaybeObject* maybe_obj =
8005 NumberDictionary::Allocate(dict->NumberOfElements());
8006 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8007 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008008 NumberDictionary* new_dict = NumberDictionary::cast(obj);
8009
8010 AssertNoAllocation no_alloc;
8011
8012 uint32_t pos = 0;
8013 uint32_t undefs = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01008014 int capacity = dict->Capacity();
Steve Blocka7e24c12009-10-30 11:49:00 +00008015 for (int i = 0; i < capacity; i++) {
8016 Object* k = dict->KeyAt(i);
8017 if (dict->IsKey(k)) {
8018 ASSERT(k->IsNumber());
8019 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
8020 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
8021 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
8022 Object* value = dict->ValueAt(i);
8023 PropertyDetails details = dict->DetailsAt(i);
8024 if (details.type() == CALLBACKS) {
8025 // Bail out and do the sorting of undefineds and array holes in JS.
8026 return Smi::FromInt(-1);
8027 }
8028 uint32_t key = NumberToUint32(k);
John Reck59135872010-11-02 12:39:01 -07008029 // In the following we assert that adding the entry to the new dictionary
8030 // does not cause GC. This is the case because we made sure to allocate
8031 // the dictionary big enough above, so it need not grow.
Steve Blocka7e24c12009-10-30 11:49:00 +00008032 if (key < limit) {
8033 if (value->IsUndefined()) {
8034 undefs++;
8035 } else {
John Reck59135872010-11-02 12:39:01 -07008036 new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008037 pos++;
8038 }
8039 } else {
John Reck59135872010-11-02 12:39:01 -07008040 new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008041 }
8042 }
8043 }
8044
8045 uint32_t result = pos;
8046 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
8047 while (undefs > 0) {
John Reck59135872010-11-02 12:39:01 -07008048 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details)->
8049 ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008050 pos++;
8051 undefs--;
8052 }
8053
8054 set_elements(new_dict);
8055
8056 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
8057 return Smi::FromInt(static_cast<int>(result));
8058 }
8059
8060 ASSERT_NE(NULL, result_double);
8061 result_double->set_value(static_cast<double>(result));
8062 return result_double;
8063}
8064
8065
8066// Collects all defined (non-hole) and non-undefined (array) elements at
8067// the start of the elements array.
8068// If the object is in dictionary mode, it is converted to fast elements
8069// mode.
John Reck59135872010-11-02 12:39:01 -07008070MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
Steve Block3ce2e202009-11-05 08:53:23 +00008071 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00008072
8073 if (HasDictionaryElements()) {
8074 // Convert to fast elements containing only the existing properties.
8075 // Ordering is irrelevant, since we are going to sort anyway.
8076 NumberDictionary* dict = element_dictionary();
8077 if (IsJSArray() || dict->requires_slow_elements() ||
8078 dict->max_number_key() >= limit) {
8079 return PrepareSlowElementsForSort(limit);
8080 }
8081 // Convert to fast elements.
8082
John Reck59135872010-11-02 12:39:01 -07008083 Object* obj;
8084 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
8085 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8086 }
Steve Block8defd9f2010-07-08 12:39:36 +01008087 Map* new_map = Map::cast(obj);
8088
Steve Blocka7e24c12009-10-30 11:49:00 +00008089 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
John Reck59135872010-11-02 12:39:01 -07008090 Object* new_array;
8091 { MaybeObject* maybe_new_array =
8092 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
8093 if (!maybe_new_array->ToObject(&new_array)) return maybe_new_array;
8094 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008095 FixedArray* fast_elements = FixedArray::cast(new_array);
8096 dict->CopyValuesTo(fast_elements);
Steve Block8defd9f2010-07-08 12:39:36 +01008097
8098 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00008099 set_elements(fast_elements);
Iain Merrick75681382010-08-19 15:07:18 +01008100 } else {
John Reck59135872010-11-02 12:39:01 -07008101 Object* obj;
8102 { MaybeObject* maybe_obj = EnsureWritableFastElements();
8103 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8104 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008105 }
8106 ASSERT(HasFastElements());
8107
8108 // Collect holes at the end, undefined before that and the rest at the
8109 // start, and return the number of non-hole, non-undefined values.
8110
8111 FixedArray* elements = FixedArray::cast(this->elements());
8112 uint32_t elements_length = static_cast<uint32_t>(elements->length());
8113 if (limit > elements_length) {
8114 limit = elements_length ;
8115 }
8116 if (limit == 0) {
8117 return Smi::FromInt(0);
8118 }
8119
8120 HeapNumber* result_double = NULL;
8121 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
8122 // Pessimistically allocate space for return value before
8123 // we start mutating the array.
John Reck59135872010-11-02 12:39:01 -07008124 Object* new_double;
8125 { MaybeObject* maybe_new_double = Heap::AllocateHeapNumber(0.0);
8126 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
8127 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008128 result_double = HeapNumber::cast(new_double);
8129 }
8130
8131 AssertNoAllocation no_alloc;
8132
8133 // Split elements into defined, undefined and the_hole, in that order.
8134 // Only count locations for undefined and the hole, and fill them afterwards.
Leon Clarke4515c472010-02-03 11:58:03 +00008135 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
Steve Blocka7e24c12009-10-30 11:49:00 +00008136 unsigned int undefs = limit;
8137 unsigned int holes = limit;
8138 // Assume most arrays contain no holes and undefined values, so minimize the
8139 // number of stores of non-undefined, non-the-hole values.
8140 for (unsigned int i = 0; i < undefs; i++) {
8141 Object* current = elements->get(i);
8142 if (current->IsTheHole()) {
8143 holes--;
8144 undefs--;
8145 } else if (current->IsUndefined()) {
8146 undefs--;
8147 } else {
8148 continue;
8149 }
8150 // Position i needs to be filled.
8151 while (undefs > i) {
8152 current = elements->get(undefs);
8153 if (current->IsTheHole()) {
8154 holes--;
8155 undefs--;
8156 } else if (current->IsUndefined()) {
8157 undefs--;
8158 } else {
8159 elements->set(i, current, write_barrier);
8160 break;
8161 }
8162 }
8163 }
8164 uint32_t result = undefs;
8165 while (undefs < holes) {
8166 elements->set_undefined(undefs);
8167 undefs++;
8168 }
8169 while (holes < limit) {
8170 elements->set_the_hole(holes);
8171 holes++;
8172 }
8173
8174 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
8175 return Smi::FromInt(static_cast<int>(result));
8176 }
8177 ASSERT_NE(NULL, result_double);
8178 result_double->set_value(static_cast<double>(result));
8179 return result_double;
8180}
8181
8182
8183Object* PixelArray::SetValue(uint32_t index, Object* value) {
8184 uint8_t clamped_value = 0;
8185 if (index < static_cast<uint32_t>(length())) {
8186 if (value->IsSmi()) {
8187 int int_value = Smi::cast(value)->value();
8188 if (int_value < 0) {
8189 clamped_value = 0;
8190 } else if (int_value > 255) {
8191 clamped_value = 255;
8192 } else {
8193 clamped_value = static_cast<uint8_t>(int_value);
8194 }
8195 } else if (value->IsHeapNumber()) {
8196 double double_value = HeapNumber::cast(value)->value();
8197 if (!(double_value > 0)) {
8198 // NaN and less than zero clamp to zero.
8199 clamped_value = 0;
8200 } else if (double_value > 255) {
8201 // Greater than 255 clamp to 255.
8202 clamped_value = 255;
8203 } else {
8204 // Other doubles are rounded to the nearest integer.
8205 clamped_value = static_cast<uint8_t>(double_value + 0.5);
8206 }
8207 } else {
8208 // Clamp undefined to zero (default). All other types have been
8209 // converted to a number type further up in the call chain.
8210 ASSERT(value->IsUndefined());
8211 }
8212 set(index, clamped_value);
8213 }
8214 return Smi::FromInt(clamped_value);
8215}
8216
8217
Steve Block3ce2e202009-11-05 08:53:23 +00008218template<typename ExternalArrayClass, typename ValueType>
John Reck59135872010-11-02 12:39:01 -07008219static MaybeObject* ExternalArrayIntSetter(ExternalArrayClass* receiver,
8220 uint32_t index,
8221 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008222 ValueType cast_value = 0;
8223 if (index < static_cast<uint32_t>(receiver->length())) {
8224 if (value->IsSmi()) {
8225 int int_value = Smi::cast(value)->value();
8226 cast_value = static_cast<ValueType>(int_value);
8227 } else if (value->IsHeapNumber()) {
8228 double double_value = HeapNumber::cast(value)->value();
8229 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
8230 } else {
8231 // Clamp undefined to zero (default). All other types have been
8232 // converted to a number type further up in the call chain.
8233 ASSERT(value->IsUndefined());
8234 }
8235 receiver->set(index, cast_value);
8236 }
8237 return Heap::NumberFromInt32(cast_value);
8238}
8239
8240
John Reck59135872010-11-02 12:39:01 -07008241MaybeObject* ExternalByteArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008242 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
8243 (this, index, value);
8244}
8245
8246
John Reck59135872010-11-02 12:39:01 -07008247MaybeObject* ExternalUnsignedByteArray::SetValue(uint32_t index,
8248 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008249 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
8250 (this, index, value);
8251}
8252
8253
John Reck59135872010-11-02 12:39:01 -07008254MaybeObject* ExternalShortArray::SetValue(uint32_t index,
8255 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008256 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
8257 (this, index, value);
8258}
8259
8260
John Reck59135872010-11-02 12:39:01 -07008261MaybeObject* ExternalUnsignedShortArray::SetValue(uint32_t index,
8262 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008263 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
8264 (this, index, value);
8265}
8266
8267
John Reck59135872010-11-02 12:39:01 -07008268MaybeObject* ExternalIntArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008269 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
8270 (this, index, value);
8271}
8272
8273
John Reck59135872010-11-02 12:39:01 -07008274MaybeObject* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008275 uint32_t cast_value = 0;
8276 if (index < static_cast<uint32_t>(length())) {
8277 if (value->IsSmi()) {
8278 int int_value = Smi::cast(value)->value();
8279 cast_value = static_cast<uint32_t>(int_value);
8280 } else if (value->IsHeapNumber()) {
8281 double double_value = HeapNumber::cast(value)->value();
8282 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
8283 } else {
8284 // Clamp undefined to zero (default). All other types have been
8285 // converted to a number type further up in the call chain.
8286 ASSERT(value->IsUndefined());
8287 }
8288 set(index, cast_value);
8289 }
8290 return Heap::NumberFromUint32(cast_value);
8291}
8292
8293
John Reck59135872010-11-02 12:39:01 -07008294MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008295 float cast_value = 0;
8296 if (index < static_cast<uint32_t>(length())) {
8297 if (value->IsSmi()) {
8298 int int_value = Smi::cast(value)->value();
8299 cast_value = static_cast<float>(int_value);
8300 } else if (value->IsHeapNumber()) {
8301 double double_value = HeapNumber::cast(value)->value();
8302 cast_value = static_cast<float>(double_value);
8303 } else {
8304 // Clamp undefined to zero (default). All other types have been
8305 // converted to a number type further up in the call chain.
8306 ASSERT(value->IsUndefined());
8307 }
8308 set(index, cast_value);
8309 }
8310 return Heap::AllocateHeapNumber(cast_value);
8311}
8312
8313
Steve Blocka7e24c12009-10-30 11:49:00 +00008314Object* GlobalObject::GetPropertyCell(LookupResult* result) {
8315 ASSERT(!HasFastProperties());
8316 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
8317 ASSERT(value->IsJSGlobalPropertyCell());
8318 return value;
8319}
8320
8321
John Reck59135872010-11-02 12:39:01 -07008322MaybeObject* GlobalObject::EnsurePropertyCell(String* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008323 ASSERT(!HasFastProperties());
8324 int entry = property_dictionary()->FindEntry(name);
8325 if (entry == StringDictionary::kNotFound) {
John Reck59135872010-11-02 12:39:01 -07008326 Object* cell;
8327 { MaybeObject* maybe_cell =
8328 Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
8329 if (!maybe_cell->ToObject(&cell)) return maybe_cell;
8330 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008331 PropertyDetails details(NONE, NORMAL);
8332 details = details.AsDeleted();
John Reck59135872010-11-02 12:39:01 -07008333 Object* dictionary;
8334 { MaybeObject* maybe_dictionary =
8335 property_dictionary()->Add(name, cell, details);
8336 if (!maybe_dictionary->ToObject(&dictionary)) return maybe_dictionary;
8337 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008338 set_properties(StringDictionary::cast(dictionary));
8339 return cell;
8340 } else {
8341 Object* value = property_dictionary()->ValueAt(entry);
8342 ASSERT(value->IsJSGlobalPropertyCell());
8343 return value;
8344 }
8345}
8346
8347
John Reck59135872010-11-02 12:39:01 -07008348MaybeObject* SymbolTable::LookupString(String* string, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008349 SymbolKey key(string);
8350 return LookupKey(&key, s);
8351}
8352
8353
Steve Blockd0582a62009-12-15 09:54:21 +00008354// This class is used for looking up two character strings in the symbol table.
8355// If we don't have a hit we don't want to waste much time so we unroll the
8356// string hash calculation loop here for speed. Doesn't work if the two
8357// characters form a decimal integer, since such strings have a different hash
8358// algorithm.
8359class TwoCharHashTableKey : public HashTableKey {
8360 public:
8361 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
8362 : c1_(c1), c2_(c2) {
8363 // Char 1.
8364 uint32_t hash = c1 + (c1 << 10);
8365 hash ^= hash >> 6;
8366 // Char 2.
8367 hash += c2;
8368 hash += hash << 10;
8369 hash ^= hash >> 6;
8370 // GetHash.
8371 hash += hash << 3;
8372 hash ^= hash >> 11;
8373 hash += hash << 15;
8374 if (hash == 0) hash = 27;
8375#ifdef DEBUG
8376 StringHasher hasher(2);
8377 hasher.AddCharacter(c1);
8378 hasher.AddCharacter(c2);
8379 // If this assert fails then we failed to reproduce the two-character
8380 // version of the string hashing algorithm above. One reason could be
8381 // that we were passed two digits as characters, since the hash
8382 // algorithm is different in that case.
8383 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
8384#endif
8385 hash_ = hash;
8386 }
8387
8388 bool IsMatch(Object* o) {
8389 if (!o->IsString()) return false;
8390 String* other = String::cast(o);
8391 if (other->length() != 2) return false;
8392 if (other->Get(0) != c1_) return false;
8393 return other->Get(1) == c2_;
8394 }
8395
8396 uint32_t Hash() { return hash_; }
8397 uint32_t HashForObject(Object* key) {
8398 if (!key->IsString()) return 0;
8399 return String::cast(key)->Hash();
8400 }
8401
8402 Object* AsObject() {
8403 // The TwoCharHashTableKey is only used for looking in the symbol
8404 // table, not for adding to it.
8405 UNREACHABLE();
8406 return NULL;
8407 }
8408 private:
8409 uint32_t c1_;
8410 uint32_t c2_;
8411 uint32_t hash_;
8412};
8413
8414
Steve Blocka7e24c12009-10-30 11:49:00 +00008415bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
8416 SymbolKey key(string);
8417 int entry = FindEntry(&key);
8418 if (entry == kNotFound) {
8419 return false;
8420 } else {
8421 String* result = String::cast(KeyAt(entry));
8422 ASSERT(StringShape(result).IsSymbol());
8423 *symbol = result;
8424 return true;
8425 }
8426}
8427
8428
Steve Blockd0582a62009-12-15 09:54:21 +00008429bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
8430 uint32_t c2,
8431 String** symbol) {
8432 TwoCharHashTableKey key(c1, c2);
8433 int entry = FindEntry(&key);
8434 if (entry == kNotFound) {
8435 return false;
8436 } else {
8437 String* result = String::cast(KeyAt(entry));
8438 ASSERT(StringShape(result).IsSymbol());
8439 *symbol = result;
8440 return true;
8441 }
8442}
8443
8444
John Reck59135872010-11-02 12:39:01 -07008445MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008446 Utf8SymbolKey key(str);
8447 return LookupKey(&key, s);
8448}
8449
8450
John Reck59135872010-11-02 12:39:01 -07008451MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008452 int entry = FindEntry(key);
8453
8454 // Symbol already in table.
8455 if (entry != kNotFound) {
8456 *s = KeyAt(entry);
8457 return this;
8458 }
8459
8460 // Adding new symbol. Grow table if needed.
John Reck59135872010-11-02 12:39:01 -07008461 Object* obj;
8462 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
8463 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8464 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008465
8466 // Create symbol object.
John Reck59135872010-11-02 12:39:01 -07008467 Object* symbol;
8468 { MaybeObject* maybe_symbol = key->AsObject();
8469 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
8470 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008471
8472 // If the symbol table grew as part of EnsureCapacity, obj is not
8473 // the current symbol table and therefore we cannot use
8474 // SymbolTable::cast here.
8475 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
8476
8477 // Add the new symbol and return it along with the symbol table.
8478 entry = table->FindInsertionEntry(key->Hash());
8479 table->set(EntryToIndex(entry), symbol);
8480 table->ElementAdded();
8481 *s = symbol;
8482 return table;
8483}
8484
8485
8486Object* CompilationCacheTable::Lookup(String* src) {
8487 StringKey key(src);
8488 int entry = FindEntry(&key);
8489 if (entry == kNotFound) return Heap::undefined_value();
8490 return get(EntryToIndex(entry) + 1);
8491}
8492
8493
8494Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
8495 StringSharedKey key(src, context->closure()->shared());
8496 int entry = FindEntry(&key);
8497 if (entry == kNotFound) return Heap::undefined_value();
8498 return get(EntryToIndex(entry) + 1);
8499}
8500
8501
8502Object* CompilationCacheTable::LookupRegExp(String* src,
8503 JSRegExp::Flags flags) {
8504 RegExpKey key(src, flags);
8505 int entry = FindEntry(&key);
8506 if (entry == kNotFound) return Heap::undefined_value();
8507 return get(EntryToIndex(entry) + 1);
8508}
8509
8510
John Reck59135872010-11-02 12:39:01 -07008511MaybeObject* CompilationCacheTable::Put(String* src, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008512 StringKey key(src);
John Reck59135872010-11-02 12:39:01 -07008513 Object* obj;
8514 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
8515 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8516 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008517
8518 CompilationCacheTable* cache =
8519 reinterpret_cast<CompilationCacheTable*>(obj);
8520 int entry = cache->FindInsertionEntry(key.Hash());
8521 cache->set(EntryToIndex(entry), src);
8522 cache->set(EntryToIndex(entry) + 1, value);
8523 cache->ElementAdded();
8524 return cache;
8525}
8526
8527
John Reck59135872010-11-02 12:39:01 -07008528MaybeObject* CompilationCacheTable::PutEval(String* src,
8529 Context* context,
8530 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008531 StringSharedKey key(src, context->closure()->shared());
John Reck59135872010-11-02 12:39:01 -07008532 Object* obj;
8533 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
8534 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8535 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008536
8537 CompilationCacheTable* cache =
8538 reinterpret_cast<CompilationCacheTable*>(obj);
8539 int entry = cache->FindInsertionEntry(key.Hash());
8540
John Reck59135872010-11-02 12:39:01 -07008541 Object* k;
8542 { MaybeObject* maybe_k = key.AsObject();
8543 if (!maybe_k->ToObject(&k)) return maybe_k;
8544 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008545
8546 cache->set(EntryToIndex(entry), k);
8547 cache->set(EntryToIndex(entry) + 1, value);
8548 cache->ElementAdded();
8549 return cache;
8550}
8551
8552
John Reck59135872010-11-02 12:39:01 -07008553MaybeObject* CompilationCacheTable::PutRegExp(String* src,
8554 JSRegExp::Flags flags,
8555 FixedArray* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008556 RegExpKey key(src, flags);
John Reck59135872010-11-02 12:39:01 -07008557 Object* obj;
8558 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
8559 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8560 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008561
8562 CompilationCacheTable* cache =
8563 reinterpret_cast<CompilationCacheTable*>(obj);
8564 int entry = cache->FindInsertionEntry(key.Hash());
Steve Block3ce2e202009-11-05 08:53:23 +00008565 // We store the value in the key slot, and compare the search key
8566 // to the stored value with a custon IsMatch function during lookups.
Steve Blocka7e24c12009-10-30 11:49:00 +00008567 cache->set(EntryToIndex(entry), value);
8568 cache->set(EntryToIndex(entry) + 1, value);
8569 cache->ElementAdded();
8570 return cache;
8571}
8572
8573
8574// SymbolsKey used for HashTable where key is array of symbols.
8575class SymbolsKey : public HashTableKey {
8576 public:
8577 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
8578
8579 bool IsMatch(Object* symbols) {
8580 FixedArray* o = FixedArray::cast(symbols);
8581 int len = symbols_->length();
8582 if (o->length() != len) return false;
8583 for (int i = 0; i < len; i++) {
8584 if (o->get(i) != symbols_->get(i)) return false;
8585 }
8586 return true;
8587 }
8588
8589 uint32_t Hash() { return HashForObject(symbols_); }
8590
8591 uint32_t HashForObject(Object* obj) {
8592 FixedArray* symbols = FixedArray::cast(obj);
8593 int len = symbols->length();
8594 uint32_t hash = 0;
8595 for (int i = 0; i < len; i++) {
8596 hash ^= String::cast(symbols->get(i))->Hash();
8597 }
8598 return hash;
8599 }
8600
8601 Object* AsObject() { return symbols_; }
8602
8603 private:
8604 FixedArray* symbols_;
8605};
8606
8607
8608Object* MapCache::Lookup(FixedArray* array) {
8609 SymbolsKey key(array);
8610 int entry = FindEntry(&key);
8611 if (entry == kNotFound) return Heap::undefined_value();
8612 return get(EntryToIndex(entry) + 1);
8613}
8614
8615
John Reck59135872010-11-02 12:39:01 -07008616MaybeObject* MapCache::Put(FixedArray* array, Map* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008617 SymbolsKey key(array);
John Reck59135872010-11-02 12:39:01 -07008618 Object* obj;
8619 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
8620 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8621 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008622
8623 MapCache* cache = reinterpret_cast<MapCache*>(obj);
8624 int entry = cache->FindInsertionEntry(key.Hash());
8625 cache->set(EntryToIndex(entry), array);
8626 cache->set(EntryToIndex(entry) + 1, value);
8627 cache->ElementAdded();
8628 return cache;
8629}
8630
8631
8632template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008633MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
8634 Object* obj;
8635 { MaybeObject* maybe_obj =
8636 HashTable<Shape, Key>::Allocate(at_least_space_for);
8637 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00008638 }
John Reck59135872010-11-02 12:39:01 -07008639 // Initialize the next enumeration index.
8640 Dictionary<Shape, Key>::cast(obj)->
8641 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
Steve Blocka7e24c12009-10-30 11:49:00 +00008642 return obj;
8643}
8644
8645
8646template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008647MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
Steve Blocka7e24c12009-10-30 11:49:00 +00008648 int length = HashTable<Shape, Key>::NumberOfElements();
8649
8650 // Allocate and initialize iteration order array.
John Reck59135872010-11-02 12:39:01 -07008651 Object* obj;
8652 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(length);
8653 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8654 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008655 FixedArray* iteration_order = FixedArray::cast(obj);
8656 for (int i = 0; i < length; i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00008657 iteration_order->set(i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00008658 }
8659
8660 // Allocate array with enumeration order.
John Reck59135872010-11-02 12:39:01 -07008661 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(length);
8662 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8663 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008664 FixedArray* enumeration_order = FixedArray::cast(obj);
8665
8666 // Fill the enumeration order array with property details.
8667 int capacity = HashTable<Shape, Key>::Capacity();
8668 int pos = 0;
8669 for (int i = 0; i < capacity; i++) {
8670 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
Leon Clarke4515c472010-02-03 11:58:03 +00008671 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008672 }
8673 }
8674
8675 // Sort the arrays wrt. enumeration order.
8676 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
8677
8678 // Overwrite the enumeration_order with the enumeration indices.
8679 for (int i = 0; i < length; i++) {
8680 int index = Smi::cast(iteration_order->get(i))->value();
8681 int enum_index = PropertyDetails::kInitialIndex + i;
Leon Clarke4515c472010-02-03 11:58:03 +00008682 enumeration_order->set(index, Smi::FromInt(enum_index));
Steve Blocka7e24c12009-10-30 11:49:00 +00008683 }
8684
8685 // Update the dictionary with new indices.
8686 capacity = HashTable<Shape, Key>::Capacity();
8687 pos = 0;
8688 for (int i = 0; i < capacity; i++) {
8689 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
8690 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
8691 PropertyDetails details = DetailsAt(i);
8692 PropertyDetails new_details =
8693 PropertyDetails(details.attributes(), details.type(), enum_index);
8694 DetailsAtPut(i, new_details);
8695 }
8696 }
8697
8698 // Set the next enumeration index.
8699 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
8700 return this;
8701}
8702
8703template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008704MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008705 // Check whether there are enough enumeration indices to add n elements.
8706 if (Shape::kIsEnumerable &&
8707 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
8708 // If not, we generate new indices for the properties.
John Reck59135872010-11-02 12:39:01 -07008709 Object* result;
8710 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
8711 if (!maybe_result->ToObject(&result)) return maybe_result;
8712 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008713 }
8714 return HashTable<Shape, Key>::EnsureCapacity(n, key);
8715}
8716
8717
8718void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
8719 // Do nothing if the interval [from, to) is empty.
8720 if (from >= to) return;
8721
8722 int removed_entries = 0;
8723 Object* sentinel = Heap::null_value();
8724 int capacity = Capacity();
8725 for (int i = 0; i < capacity; i++) {
8726 Object* key = KeyAt(i);
8727 if (key->IsNumber()) {
8728 uint32_t number = static_cast<uint32_t>(key->Number());
8729 if (from <= number && number < to) {
8730 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
8731 removed_entries++;
8732 }
8733 }
8734 }
8735
8736 // Update the number of elements.
Leon Clarkee46be812010-01-19 14:06:41 +00008737 ElementsRemoved(removed_entries);
Steve Blocka7e24c12009-10-30 11:49:00 +00008738}
8739
8740
8741template<typename Shape, typename Key>
8742Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
8743 JSObject::DeleteMode mode) {
8744 PropertyDetails details = DetailsAt(entry);
8745 // Ignore attributes if forcing a deletion.
8746 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
8747 return Heap::false_value();
8748 }
8749 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
8750 HashTable<Shape, Key>::ElementRemoved();
8751 return Heap::true_value();
8752}
8753
8754
8755template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008756MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008757 int entry = this->FindEntry(key);
Steve Blocka7e24c12009-10-30 11:49:00 +00008758
8759 // If the entry is present set the value;
8760 if (entry != Dictionary<Shape, Key>::kNotFound) {
8761 ValueAtPut(entry, value);
8762 return this;
8763 }
8764
8765 // Check whether the dictionary should be extended.
John Reck59135872010-11-02 12:39:01 -07008766 Object* obj;
8767 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
8768 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8769 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008770
John Reck59135872010-11-02 12:39:01 -07008771 Object* k;
8772 { MaybeObject* maybe_k = Shape::AsObject(key);
8773 if (!maybe_k->ToObject(&k)) return maybe_k;
8774 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008775 PropertyDetails details = PropertyDetails(NONE, NORMAL);
8776 return Dictionary<Shape, Key>::cast(obj)->
8777 AddEntry(key, value, details, Shape::Hash(key));
8778}
8779
8780
8781template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008782MaybeObject* Dictionary<Shape, Key>::Add(Key key,
8783 Object* value,
8784 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008785 // Valdate key is absent.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008786 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
Steve Blocka7e24c12009-10-30 11:49:00 +00008787 // Check whether the dictionary should be extended.
John Reck59135872010-11-02 12:39:01 -07008788 Object* obj;
8789 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
8790 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8791 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008792 return Dictionary<Shape, Key>::cast(obj)->
8793 AddEntry(key, value, details, Shape::Hash(key));
8794}
8795
8796
8797// Add a key, value pair to the dictionary.
8798template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008799MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key,
8800 Object* value,
8801 PropertyDetails details,
8802 uint32_t hash) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008803 // Compute the key object.
John Reck59135872010-11-02 12:39:01 -07008804 Object* k;
8805 { MaybeObject* maybe_k = Shape::AsObject(key);
8806 if (!maybe_k->ToObject(&k)) return maybe_k;
8807 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008808
8809 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
8810 // Insert element at empty or deleted entry
8811 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
8812 // Assign an enumeration index to the property and update
8813 // SetNextEnumerationIndex.
8814 int index = NextEnumerationIndex();
8815 details = PropertyDetails(details.attributes(), details.type(), index);
8816 SetNextEnumerationIndex(index + 1);
8817 }
8818 SetEntry(entry, k, value, details);
8819 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
8820 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
8821 HashTable<Shape, Key>::ElementAdded();
8822 return this;
8823}
8824
8825
8826void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
8827 // If the dictionary requires slow elements an element has already
8828 // been added at a high index.
8829 if (requires_slow_elements()) return;
8830 // Check if this index is high enough that we should require slow
8831 // elements.
8832 if (key > kRequiresSlowElementsLimit) {
8833 set_requires_slow_elements();
8834 return;
8835 }
8836 // Update max key value.
8837 Object* max_index_object = get(kMaxNumberKeyIndex);
8838 if (!max_index_object->IsSmi() || max_number_key() < key) {
8839 FixedArray::set(kMaxNumberKeyIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00008840 Smi::FromInt(key << kRequiresSlowElementsTagSize));
Steve Blocka7e24c12009-10-30 11:49:00 +00008841 }
8842}
8843
8844
John Reck59135872010-11-02 12:39:01 -07008845MaybeObject* NumberDictionary::AddNumberEntry(uint32_t key,
8846 Object* value,
8847 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008848 UpdateMaxNumberKey(key);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008849 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
Steve Blocka7e24c12009-10-30 11:49:00 +00008850 return Add(key, value, details);
8851}
8852
8853
John Reck59135872010-11-02 12:39:01 -07008854MaybeObject* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008855 UpdateMaxNumberKey(key);
8856 return AtPut(key, value);
8857}
8858
8859
John Reck59135872010-11-02 12:39:01 -07008860MaybeObject* NumberDictionary::Set(uint32_t key,
8861 Object* value,
8862 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008863 int entry = FindEntry(key);
8864 if (entry == kNotFound) return AddNumberEntry(key, value, details);
8865 // Preserve enumeration index.
8866 details = PropertyDetails(details.attributes(),
8867 details.type(),
8868 DetailsAt(entry).index());
John Reck59135872010-11-02 12:39:01 -07008869 MaybeObject* maybe_object_key = NumberDictionaryShape::AsObject(key);
8870 Object* object_key;
8871 if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
Ben Murdochf87a2032010-10-22 12:50:53 +01008872 SetEntry(entry, object_key, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00008873 return this;
8874}
8875
8876
8877
8878template<typename Shape, typename Key>
8879int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
8880 PropertyAttributes filter) {
8881 int capacity = HashTable<Shape, Key>::Capacity();
8882 int result = 0;
8883 for (int i = 0; i < capacity; i++) {
8884 Object* k = HashTable<Shape, Key>::KeyAt(i);
8885 if (HashTable<Shape, Key>::IsKey(k)) {
8886 PropertyDetails details = DetailsAt(i);
8887 if (details.IsDeleted()) continue;
8888 PropertyAttributes attr = details.attributes();
8889 if ((attr & filter) == 0) result++;
8890 }
8891 }
8892 return result;
8893}
8894
8895
8896template<typename Shape, typename Key>
8897int Dictionary<Shape, Key>::NumberOfEnumElements() {
8898 return NumberOfElementsFilterAttributes(
8899 static_cast<PropertyAttributes>(DONT_ENUM));
8900}
8901
8902
8903template<typename Shape, typename Key>
8904void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
8905 PropertyAttributes filter) {
8906 ASSERT(storage->length() >= NumberOfEnumElements());
8907 int capacity = HashTable<Shape, Key>::Capacity();
8908 int index = 0;
8909 for (int i = 0; i < capacity; i++) {
8910 Object* k = HashTable<Shape, Key>::KeyAt(i);
8911 if (HashTable<Shape, Key>::IsKey(k)) {
8912 PropertyDetails details = DetailsAt(i);
8913 if (details.IsDeleted()) continue;
8914 PropertyAttributes attr = details.attributes();
8915 if ((attr & filter) == 0) storage->set(index++, k);
8916 }
8917 }
8918 storage->SortPairs(storage, index);
8919 ASSERT(storage->length() >= index);
8920}
8921
8922
8923void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
8924 FixedArray* sort_array) {
8925 ASSERT(storage->length() >= NumberOfEnumElements());
8926 int capacity = Capacity();
8927 int index = 0;
8928 for (int i = 0; i < capacity; i++) {
8929 Object* k = KeyAt(i);
8930 if (IsKey(k)) {
8931 PropertyDetails details = DetailsAt(i);
8932 if (details.IsDeleted() || details.IsDontEnum()) continue;
8933 storage->set(index, k);
Leon Clarke4515c472010-02-03 11:58:03 +00008934 sort_array->set(index, Smi::FromInt(details.index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008935 index++;
8936 }
8937 }
8938 storage->SortPairs(sort_array, sort_array->length());
8939 ASSERT(storage->length() >= index);
8940}
8941
8942
8943template<typename Shape, typename Key>
8944void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
8945 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
8946 static_cast<PropertyAttributes>(NONE)));
8947 int capacity = HashTable<Shape, Key>::Capacity();
8948 int index = 0;
8949 for (int i = 0; i < capacity; i++) {
8950 Object* k = HashTable<Shape, Key>::KeyAt(i);
8951 if (HashTable<Shape, Key>::IsKey(k)) {
8952 PropertyDetails details = DetailsAt(i);
8953 if (details.IsDeleted()) continue;
8954 storage->set(index++, k);
8955 }
8956 }
8957 ASSERT(storage->length() >= index);
8958}
8959
8960
8961// Backwards lookup (slow).
8962template<typename Shape, typename Key>
8963Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
8964 int capacity = HashTable<Shape, Key>::Capacity();
8965 for (int i = 0; i < capacity; i++) {
8966 Object* k = HashTable<Shape, Key>::KeyAt(i);
8967 if (Dictionary<Shape, Key>::IsKey(k)) {
8968 Object* e = ValueAt(i);
8969 if (e->IsJSGlobalPropertyCell()) {
8970 e = JSGlobalPropertyCell::cast(e)->value();
8971 }
8972 if (e == value) return k;
8973 }
8974 }
8975 return Heap::undefined_value();
8976}
8977
8978
John Reck59135872010-11-02 12:39:01 -07008979MaybeObject* StringDictionary::TransformPropertiesToFastFor(
Steve Blocka7e24c12009-10-30 11:49:00 +00008980 JSObject* obj, int unused_property_fields) {
8981 // Make sure we preserve dictionary representation if there are too many
8982 // descriptors.
8983 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
8984
8985 // Figure out if it is necessary to generate new enumeration indices.
8986 int max_enumeration_index =
8987 NextEnumerationIndex() +
8988 (DescriptorArray::kMaxNumberOfDescriptors -
8989 NumberOfElements());
8990 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
John Reck59135872010-11-02 12:39:01 -07008991 Object* result;
8992 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
8993 if (!maybe_result->ToObject(&result)) return maybe_result;
8994 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008995 }
8996
8997 int instance_descriptor_length = 0;
8998 int number_of_fields = 0;
8999
9000 // Compute the length of the instance descriptor.
9001 int capacity = Capacity();
9002 for (int i = 0; i < capacity; i++) {
9003 Object* k = KeyAt(i);
9004 if (IsKey(k)) {
9005 Object* value = ValueAt(i);
9006 PropertyType type = DetailsAt(i).type();
9007 ASSERT(type != FIELD);
9008 instance_descriptor_length++;
Leon Clarkee46be812010-01-19 14:06:41 +00009009 if (type == NORMAL &&
9010 (!value->IsJSFunction() || Heap::InNewSpace(value))) {
9011 number_of_fields += 1;
9012 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009013 }
9014 }
9015
9016 // Allocate the instance descriptor.
John Reck59135872010-11-02 12:39:01 -07009017 Object* descriptors_unchecked;
9018 { MaybeObject* maybe_descriptors_unchecked =
9019 DescriptorArray::Allocate(instance_descriptor_length);
9020 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
9021 return maybe_descriptors_unchecked;
9022 }
9023 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009024 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
9025
9026 int inobject_props = obj->map()->inobject_properties();
9027 int number_of_allocated_fields =
9028 number_of_fields + unused_property_fields - inobject_props;
Ben Murdochf87a2032010-10-22 12:50:53 +01009029 if (number_of_allocated_fields < 0) {
9030 // There is enough inobject space for all fields (including unused).
9031 number_of_allocated_fields = 0;
9032 unused_property_fields = inobject_props - number_of_fields;
9033 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009034
9035 // Allocate the fixed array for the fields.
John Reck59135872010-11-02 12:39:01 -07009036 Object* fields;
9037 { MaybeObject* maybe_fields =
9038 Heap::AllocateFixedArray(number_of_allocated_fields);
9039 if (!maybe_fields->ToObject(&fields)) return maybe_fields;
9040 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009041
9042 // Fill in the instance descriptor and the fields.
9043 int next_descriptor = 0;
9044 int current_offset = 0;
9045 for (int i = 0; i < capacity; i++) {
9046 Object* k = KeyAt(i);
9047 if (IsKey(k)) {
9048 Object* value = ValueAt(i);
9049 // Ensure the key is a symbol before writing into the instance descriptor.
John Reck59135872010-11-02 12:39:01 -07009050 Object* key;
9051 { MaybeObject* maybe_key = Heap::LookupSymbol(String::cast(k));
9052 if (!maybe_key->ToObject(&key)) return maybe_key;
9053 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009054 PropertyDetails details = DetailsAt(i);
9055 PropertyType type = details.type();
9056
Leon Clarkee46be812010-01-19 14:06:41 +00009057 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009058 ConstantFunctionDescriptor d(String::cast(key),
9059 JSFunction::cast(value),
9060 details.attributes(),
9061 details.index());
9062 descriptors->Set(next_descriptor++, &d);
9063 } else if (type == NORMAL) {
9064 if (current_offset < inobject_props) {
9065 obj->InObjectPropertyAtPut(current_offset,
9066 value,
9067 UPDATE_WRITE_BARRIER);
9068 } else {
9069 int offset = current_offset - inobject_props;
9070 FixedArray::cast(fields)->set(offset, value);
9071 }
9072 FieldDescriptor d(String::cast(key),
9073 current_offset++,
9074 details.attributes(),
9075 details.index());
9076 descriptors->Set(next_descriptor++, &d);
9077 } else if (type == CALLBACKS) {
9078 CallbacksDescriptor d(String::cast(key),
9079 value,
9080 details.attributes(),
9081 details.index());
9082 descriptors->Set(next_descriptor++, &d);
9083 } else {
9084 UNREACHABLE();
9085 }
9086 }
9087 }
9088 ASSERT(current_offset == number_of_fields);
9089
9090 descriptors->Sort();
9091 // Allocate new map.
John Reck59135872010-11-02 12:39:01 -07009092 Object* new_map;
9093 { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
9094 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
9095 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009096
9097 // Transform the object.
9098 obj->set_map(Map::cast(new_map));
9099 obj->map()->set_instance_descriptors(descriptors);
9100 obj->map()->set_unused_property_fields(unused_property_fields);
9101
9102 obj->set_properties(FixedArray::cast(fields));
9103 ASSERT(obj->IsJSObject());
9104
9105 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
9106 // Check that it really works.
9107 ASSERT(obj->HasFastProperties());
9108
9109 return obj;
9110}
9111
9112
9113#ifdef ENABLE_DEBUGGER_SUPPORT
9114// Check if there is a break point at this code position.
9115bool DebugInfo::HasBreakPoint(int code_position) {
9116 // Get the break point info object for this code position.
9117 Object* break_point_info = GetBreakPointInfo(code_position);
9118
9119 // If there is no break point info object or no break points in the break
9120 // point info object there is no break point at this code position.
9121 if (break_point_info->IsUndefined()) return false;
9122 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
9123}
9124
9125
9126// Get the break point info object for this code position.
9127Object* DebugInfo::GetBreakPointInfo(int code_position) {
9128 // Find the index of the break point info object for this code position.
9129 int index = GetBreakPointInfoIndex(code_position);
9130
9131 // Return the break point info object if any.
9132 if (index == kNoBreakPointInfo) return Heap::undefined_value();
9133 return BreakPointInfo::cast(break_points()->get(index));
9134}
9135
9136
9137// Clear a break point at the specified code position.
9138void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
9139 int code_position,
9140 Handle<Object> break_point_object) {
9141 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
9142 if (break_point_info->IsUndefined()) return;
9143 BreakPointInfo::ClearBreakPoint(
9144 Handle<BreakPointInfo>::cast(break_point_info),
9145 break_point_object);
9146}
9147
9148
9149void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
9150 int code_position,
9151 int source_position,
9152 int statement_position,
9153 Handle<Object> break_point_object) {
9154 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
9155 if (!break_point_info->IsUndefined()) {
9156 BreakPointInfo::SetBreakPoint(
9157 Handle<BreakPointInfo>::cast(break_point_info),
9158 break_point_object);
9159 return;
9160 }
9161
9162 // Adding a new break point for a code position which did not have any
9163 // break points before. Try to find a free slot.
9164 int index = kNoBreakPointInfo;
9165 for (int i = 0; i < debug_info->break_points()->length(); i++) {
9166 if (debug_info->break_points()->get(i)->IsUndefined()) {
9167 index = i;
9168 break;
9169 }
9170 }
9171 if (index == kNoBreakPointInfo) {
9172 // No free slot - extend break point info array.
9173 Handle<FixedArray> old_break_points =
9174 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
Steve Blocka7e24c12009-10-30 11:49:00 +00009175 Handle<FixedArray> new_break_points =
Kristian Monsen0d5e1162010-09-30 15:31:59 +01009176 Factory::NewFixedArray(old_break_points->length() +
9177 Debug::kEstimatedNofBreakPointsInFunction);
9178
9179 debug_info->set_break_points(*new_break_points);
Steve Blocka7e24c12009-10-30 11:49:00 +00009180 for (int i = 0; i < old_break_points->length(); i++) {
9181 new_break_points->set(i, old_break_points->get(i));
9182 }
9183 index = old_break_points->length();
9184 }
9185 ASSERT(index != kNoBreakPointInfo);
9186
9187 // Allocate new BreakPointInfo object and set the break point.
9188 Handle<BreakPointInfo> new_break_point_info =
9189 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
9190 new_break_point_info->set_code_position(Smi::FromInt(code_position));
9191 new_break_point_info->set_source_position(Smi::FromInt(source_position));
9192 new_break_point_info->
9193 set_statement_position(Smi::FromInt(statement_position));
9194 new_break_point_info->set_break_point_objects(Heap::undefined_value());
9195 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
9196 debug_info->break_points()->set(index, *new_break_point_info);
9197}
9198
9199
9200// Get the break point objects for a code position.
9201Object* DebugInfo::GetBreakPointObjects(int code_position) {
9202 Object* break_point_info = GetBreakPointInfo(code_position);
9203 if (break_point_info->IsUndefined()) {
9204 return Heap::undefined_value();
9205 }
9206 return BreakPointInfo::cast(break_point_info)->break_point_objects();
9207}
9208
9209
9210// Get the total number of break points.
9211int DebugInfo::GetBreakPointCount() {
9212 if (break_points()->IsUndefined()) return 0;
9213 int count = 0;
9214 for (int i = 0; i < break_points()->length(); i++) {
9215 if (!break_points()->get(i)->IsUndefined()) {
9216 BreakPointInfo* break_point_info =
9217 BreakPointInfo::cast(break_points()->get(i));
9218 count += break_point_info->GetBreakPointCount();
9219 }
9220 }
9221 return count;
9222}
9223
9224
9225Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
9226 Handle<Object> break_point_object) {
9227 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
9228 for (int i = 0; i < debug_info->break_points()->length(); i++) {
9229 if (!debug_info->break_points()->get(i)->IsUndefined()) {
9230 Handle<BreakPointInfo> break_point_info =
9231 Handle<BreakPointInfo>(BreakPointInfo::cast(
9232 debug_info->break_points()->get(i)));
9233 if (BreakPointInfo::HasBreakPointObject(break_point_info,
9234 break_point_object)) {
9235 return *break_point_info;
9236 }
9237 }
9238 }
9239 return Heap::undefined_value();
9240}
9241
9242
9243// Find the index of the break point info object for the specified code
9244// position.
9245int DebugInfo::GetBreakPointInfoIndex(int code_position) {
9246 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
9247 for (int i = 0; i < break_points()->length(); i++) {
9248 if (!break_points()->get(i)->IsUndefined()) {
9249 BreakPointInfo* break_point_info =
9250 BreakPointInfo::cast(break_points()->get(i));
9251 if (break_point_info->code_position()->value() == code_position) {
9252 return i;
9253 }
9254 }
9255 }
9256 return kNoBreakPointInfo;
9257}
9258
9259
9260// Remove the specified break point object.
9261void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
9262 Handle<Object> break_point_object) {
9263 // If there are no break points just ignore.
9264 if (break_point_info->break_point_objects()->IsUndefined()) return;
9265 // If there is a single break point clear it if it is the same.
9266 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9267 if (break_point_info->break_point_objects() == *break_point_object) {
9268 break_point_info->set_break_point_objects(Heap::undefined_value());
9269 }
9270 return;
9271 }
9272 // If there are multiple break points shrink the array
9273 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
9274 Handle<FixedArray> old_array =
9275 Handle<FixedArray>(
9276 FixedArray::cast(break_point_info->break_point_objects()));
9277 Handle<FixedArray> new_array =
9278 Factory::NewFixedArray(old_array->length() - 1);
9279 int found_count = 0;
9280 for (int i = 0; i < old_array->length(); i++) {
9281 if (old_array->get(i) == *break_point_object) {
9282 ASSERT(found_count == 0);
9283 found_count++;
9284 } else {
9285 new_array->set(i - found_count, old_array->get(i));
9286 }
9287 }
9288 // If the break point was found in the list change it.
9289 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
9290}
9291
9292
9293// Add the specified break point object.
9294void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
9295 Handle<Object> break_point_object) {
9296 // If there was no break point objects before just set it.
9297 if (break_point_info->break_point_objects()->IsUndefined()) {
9298 break_point_info->set_break_point_objects(*break_point_object);
9299 return;
9300 }
9301 // If the break point object is the same as before just ignore.
9302 if (break_point_info->break_point_objects() == *break_point_object) return;
9303 // If there was one break point object before replace with array.
9304 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9305 Handle<FixedArray> array = Factory::NewFixedArray(2);
9306 array->set(0, break_point_info->break_point_objects());
9307 array->set(1, *break_point_object);
9308 break_point_info->set_break_point_objects(*array);
9309 return;
9310 }
9311 // If there was more than one break point before extend array.
9312 Handle<FixedArray> old_array =
9313 Handle<FixedArray>(
9314 FixedArray::cast(break_point_info->break_point_objects()));
9315 Handle<FixedArray> new_array =
9316 Factory::NewFixedArray(old_array->length() + 1);
9317 for (int i = 0; i < old_array->length(); i++) {
9318 // If the break point was there before just ignore.
9319 if (old_array->get(i) == *break_point_object) return;
9320 new_array->set(i, old_array->get(i));
9321 }
9322 // Add the new break point.
9323 new_array->set(old_array->length(), *break_point_object);
9324 break_point_info->set_break_point_objects(*new_array);
9325}
9326
9327
9328bool BreakPointInfo::HasBreakPointObject(
9329 Handle<BreakPointInfo> break_point_info,
9330 Handle<Object> break_point_object) {
9331 // No break point.
9332 if (break_point_info->break_point_objects()->IsUndefined()) return false;
9333 // Single beak point.
9334 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9335 return break_point_info->break_point_objects() == *break_point_object;
9336 }
9337 // Multiple break points.
9338 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
9339 for (int i = 0; i < array->length(); i++) {
9340 if (array->get(i) == *break_point_object) {
9341 return true;
9342 }
9343 }
9344 return false;
9345}
9346
9347
9348// Get the number of break points.
9349int BreakPointInfo::GetBreakPointCount() {
9350 // No break point.
9351 if (break_point_objects()->IsUndefined()) return 0;
9352 // Single beak point.
9353 if (!break_point_objects()->IsFixedArray()) return 1;
9354 // Multiple break points.
9355 return FixedArray::cast(break_point_objects())->length();
9356}
9357#endif
9358
9359
9360} } // namespace v8::internal