blob: c1cb922de55bf831cad9deb8a5a83876d05b65ad [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"
38#include "scanner.h"
39#include "scopeinfo.h"
40#include "string-stream.h"
Steve Blockd0582a62009-12-15 09:54:21 +000041#include "utils.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000042
43#ifdef ENABLE_DISASSEMBLER
44#include "disassembler.h"
45#endif
46
47
48namespace v8 {
49namespace internal {
50
51// Getters and setters are stored in a fixed array property. These are
52// constants for their indices.
53const int kGetterIndex = 0;
54const int kSetterIndex = 1;
55
56
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() {
1169 if (IsJSFunction()) {
Steve Block6ded16b2010-05-10 14:33:55 +01001170 return Heap::closure_symbol();
Steve Blocka7e24c12009-10-30 11:49:00 +00001171 }
1172 if (map()->constructor()->IsJSFunction()) {
1173 JSFunction* constructor = JSFunction::cast(map()->constructor());
1174 String* name = String::cast(constructor->shared()->name());
Ben Murdochf87a2032010-10-22 12:50:53 +01001175 if (name->length() > 0) return name;
1176 String* inferred_name = constructor->shared()->inferred_name();
1177 if (inferred_name->length() > 0) return inferred_name;
1178 Object* proto = GetPrototype();
1179 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
Steve Blocka7e24c12009-10-30 11:49:00 +00001180 }
1181 // If the constructor is not present, return "Object".
1182 return Heap::Object_symbol();
1183}
1184
1185
John Reck59135872010-11-02 12:39:01 -07001186MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
1187 String* name,
1188 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001189 int index = new_map->PropertyIndexFor(name);
1190 if (map()->unused_property_fields() == 0) {
1191 ASSERT(map()->unused_property_fields() == 0);
1192 int new_unused = new_map->unused_property_fields();
John Reck59135872010-11-02 12:39:01 -07001193 Object* values;
1194 { MaybeObject* maybe_values =
1195 properties()->CopySize(properties()->length() + new_unused + 1);
1196 if (!maybe_values->ToObject(&values)) return maybe_values;
1197 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001198 set_properties(FixedArray::cast(values));
1199 }
1200 set_map(new_map);
1201 return FastPropertyAtPut(index, value);
1202}
1203
1204
John Reck59135872010-11-02 12:39:01 -07001205MaybeObject* JSObject::AddFastProperty(String* name,
1206 Object* value,
1207 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001208 // Normalize the object if the name is an actual string (not the
1209 // hidden symbols) and is not a real identifier.
1210 StringInputBuffer buffer(name);
1211 if (!Scanner::IsIdentifier(&buffer) && name != Heap::hidden_symbol()) {
John Reck59135872010-11-02 12:39:01 -07001212 Object* obj;
1213 { MaybeObject* maybe_obj =
1214 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1215 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1216 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001217 return AddSlowProperty(name, value, attributes);
1218 }
1219
1220 DescriptorArray* old_descriptors = map()->instance_descriptors();
1221 // Compute the new index for new field.
1222 int index = map()->NextFreePropertyIndex();
1223
1224 // Allocate new instance descriptors with (name, index) added
1225 FieldDescriptor new_field(name, index, attributes);
John Reck59135872010-11-02 12:39:01 -07001226 Object* new_descriptors;
1227 { MaybeObject* maybe_new_descriptors =
1228 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1229 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1230 return maybe_new_descriptors;
1231 }
1232 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001233
1234 // Only allow map transition if the object's map is NOT equal to the
1235 // global object_function's map and there is not a transition for name.
1236 bool allow_map_transition =
1237 !old_descriptors->Contains(name) &&
1238 (Top::context()->global_context()->object_function()->map() != map());
1239
1240 ASSERT(index < map()->inobject_properties() ||
1241 (index - map()->inobject_properties()) < properties()->length() ||
1242 map()->unused_property_fields() == 0);
1243 // Allocate a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001244 Object* r;
1245 { MaybeObject* maybe_r = map()->CopyDropDescriptors();
1246 if (!maybe_r->ToObject(&r)) return maybe_r;
1247 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001248 Map* new_map = Map::cast(r);
1249 if (allow_map_transition) {
1250 // Allocate new instance descriptors for the old map with map transition.
1251 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
John Reck59135872010-11-02 12:39:01 -07001252 Object* r;
1253 { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1254 if (!maybe_r->ToObject(&r)) return maybe_r;
1255 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001256 old_descriptors = DescriptorArray::cast(r);
1257 }
1258
1259 if (map()->unused_property_fields() == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01001260 if (properties()->length() > MaxFastProperties()) {
John Reck59135872010-11-02 12:39:01 -07001261 Object* obj;
1262 { MaybeObject* maybe_obj =
1263 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1264 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1265 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001266 return AddSlowProperty(name, value, attributes);
1267 }
1268 // Make room for the new value
John Reck59135872010-11-02 12:39:01 -07001269 Object* values;
1270 { MaybeObject* maybe_values =
1271 properties()->CopySize(properties()->length() + kFieldsAdded);
1272 if (!maybe_values->ToObject(&values)) return maybe_values;
1273 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001274 set_properties(FixedArray::cast(values));
1275 new_map->set_unused_property_fields(kFieldsAdded - 1);
1276 } else {
1277 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
1278 }
1279 // We have now allocated all the necessary objects.
1280 // All the changes can be applied at once, so they are atomic.
1281 map()->set_instance_descriptors(old_descriptors);
1282 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1283 set_map(new_map);
1284 return FastPropertyAtPut(index, value);
1285}
1286
1287
John Reck59135872010-11-02 12:39:01 -07001288MaybeObject* JSObject::AddConstantFunctionProperty(
1289 String* name,
1290 JSFunction* function,
1291 PropertyAttributes attributes) {
Leon Clarkee46be812010-01-19 14:06:41 +00001292 ASSERT(!Heap::InNewSpace(function));
1293
Steve Blocka7e24c12009-10-30 11:49:00 +00001294 // Allocate new instance descriptors with (name, function) added
1295 ConstantFunctionDescriptor d(name, function, attributes);
John Reck59135872010-11-02 12:39:01 -07001296 Object* new_descriptors;
1297 { MaybeObject* maybe_new_descriptors =
1298 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1299 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1300 return maybe_new_descriptors;
1301 }
1302 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001303
1304 // Allocate a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001305 Object* new_map;
1306 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
1307 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
1308 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001309
1310 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1311 Map::cast(new_map)->set_instance_descriptors(descriptors);
1312 Map* old_map = map();
1313 set_map(Map::cast(new_map));
1314
1315 // If the old map is the global object map (from new Object()),
1316 // then transitions are not added to it, so we are done.
1317 if (old_map == Top::context()->global_context()->object_function()->map()) {
1318 return function;
1319 }
1320
1321 // Do not add CONSTANT_TRANSITIONS to global objects
1322 if (IsGlobalObject()) {
1323 return function;
1324 }
1325
1326 // Add a CONSTANT_TRANSITION descriptor to the old map,
1327 // so future assignments to this property on other objects
1328 // of the same type will create a normal field, not a constant function.
1329 // Don't do this for special properties, with non-trival attributes.
1330 if (attributes != NONE) {
1331 return function;
1332 }
Iain Merrick75681382010-08-19 15:07:18 +01001333 ConstTransitionDescriptor mark(name, Map::cast(new_map));
John Reck59135872010-11-02 12:39:01 -07001334 { MaybeObject* maybe_new_descriptors =
1335 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1336 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1337 // We have accomplished the main goal, so return success.
1338 return function;
1339 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001340 }
1341 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1342
1343 return function;
1344}
1345
1346
1347// Add property in slow mode
John Reck59135872010-11-02 12:39:01 -07001348MaybeObject* JSObject::AddSlowProperty(String* name,
1349 Object* value,
1350 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001351 ASSERT(!HasFastProperties());
1352 StringDictionary* dict = property_dictionary();
1353 Object* store_value = value;
1354 if (IsGlobalObject()) {
1355 // In case name is an orphaned property reuse the cell.
1356 int entry = dict->FindEntry(name);
1357 if (entry != StringDictionary::kNotFound) {
1358 store_value = dict->ValueAt(entry);
1359 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1360 // Assign an enumeration index to the property and update
1361 // SetNextEnumerationIndex.
1362 int index = dict->NextEnumerationIndex();
1363 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1364 dict->SetNextEnumerationIndex(index + 1);
1365 dict->SetEntry(entry, name, store_value, details);
1366 return value;
1367 }
John Reck59135872010-11-02 12:39:01 -07001368 { MaybeObject* maybe_store_value =
1369 Heap::AllocateJSGlobalPropertyCell(value);
1370 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
1371 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001372 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1373 }
1374 PropertyDetails details = PropertyDetails(attributes, NORMAL);
John Reck59135872010-11-02 12:39:01 -07001375 Object* result;
1376 { MaybeObject* maybe_result = dict->Add(name, store_value, details);
1377 if (!maybe_result->ToObject(&result)) return maybe_result;
1378 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001379 if (dict != result) set_properties(StringDictionary::cast(result));
1380 return value;
1381}
1382
1383
John Reck59135872010-11-02 12:39:01 -07001384MaybeObject* JSObject::AddProperty(String* name,
1385 Object* value,
1386 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001387 ASSERT(!IsJSGlobalProxy());
Steve Block8defd9f2010-07-08 12:39:36 +01001388 if (!map()->is_extensible()) {
1389 Handle<Object> args[1] = {Handle<String>(name)};
1390 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
1391 HandleVector(args, 1)));
1392 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001393 if (HasFastProperties()) {
1394 // Ensure the descriptor array does not get too big.
1395 if (map()->instance_descriptors()->number_of_descriptors() <
1396 DescriptorArray::kMaxNumberOfDescriptors) {
Leon Clarkee46be812010-01-19 14:06:41 +00001397 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001398 return AddConstantFunctionProperty(name,
1399 JSFunction::cast(value),
1400 attributes);
1401 } else {
1402 return AddFastProperty(name, value, attributes);
1403 }
1404 } else {
1405 // Normalize the object to prevent very large instance descriptors.
1406 // This eliminates unwanted N^2 allocation and lookup behavior.
John Reck59135872010-11-02 12:39:01 -07001407 Object* obj;
1408 { MaybeObject* maybe_obj =
1409 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1410 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1411 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001412 }
1413 }
1414 return AddSlowProperty(name, value, attributes);
1415}
1416
1417
John Reck59135872010-11-02 12:39:01 -07001418MaybeObject* JSObject::SetPropertyPostInterceptor(
1419 String* name,
1420 Object* value,
1421 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001422 // Check local property, ignore interceptor.
1423 LookupResult result;
1424 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001425 if (result.IsFound()) {
1426 // An existing property, a map transition or a null descriptor was
1427 // found. Use set property to handle all these cases.
1428 return SetProperty(&result, name, value, attributes);
1429 }
1430 // Add a new real property.
Steve Blocka7e24c12009-10-30 11:49:00 +00001431 return AddProperty(name, value, attributes);
1432}
1433
1434
John Reck59135872010-11-02 12:39:01 -07001435MaybeObject* JSObject::ReplaceSlowProperty(String* name,
1436 Object* value,
1437 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001438 StringDictionary* dictionary = property_dictionary();
1439 int old_index = dictionary->FindEntry(name);
1440 int new_enumeration_index = 0; // 0 means "Use the next available index."
1441 if (old_index != -1) {
1442 // All calls to ReplaceSlowProperty have had all transitions removed.
1443 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1444 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1445 }
1446
1447 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
1448 return SetNormalizedProperty(name, value, new_details);
1449}
1450
Steve Blockd0582a62009-12-15 09:54:21 +00001451
John Reck59135872010-11-02 12:39:01 -07001452MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
Steve Blocka7e24c12009-10-30 11:49:00 +00001453 String* name,
1454 Object* new_value,
1455 PropertyAttributes attributes) {
1456 Map* old_map = map();
John Reck59135872010-11-02 12:39:01 -07001457 Object* result;
1458 { MaybeObject* maybe_result =
1459 ConvertDescriptorToField(name, new_value, attributes);
1460 if (!maybe_result->ToObject(&result)) return maybe_result;
1461 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001462 // If we get to this point we have succeeded - do not return failure
1463 // after this point. Later stuff is optional.
1464 if (!HasFastProperties()) {
1465 return result;
1466 }
1467 // Do not add transitions to the map of "new Object()".
1468 if (map() == Top::context()->global_context()->object_function()->map()) {
1469 return result;
1470 }
1471
1472 MapTransitionDescriptor transition(name,
1473 map(),
1474 attributes);
John Reck59135872010-11-02 12:39:01 -07001475 Object* new_descriptors;
1476 { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
1477 CopyInsert(&transition, KEEP_TRANSITIONS);
1478 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1479 return result; // Yes, return _result_.
1480 }
1481 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001482 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1483 return result;
1484}
1485
1486
John Reck59135872010-11-02 12:39:01 -07001487MaybeObject* JSObject::ConvertDescriptorToField(String* name,
1488 Object* new_value,
1489 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001490 if (map()->unused_property_fields() == 0 &&
Steve Block8defd9f2010-07-08 12:39:36 +01001491 properties()->length() > MaxFastProperties()) {
John Reck59135872010-11-02 12:39:01 -07001492 Object* obj;
1493 { MaybeObject* maybe_obj =
1494 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1495 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1496 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001497 return ReplaceSlowProperty(name, new_value, attributes);
1498 }
1499
1500 int index = map()->NextFreePropertyIndex();
1501 FieldDescriptor new_field(name, index, attributes);
1502 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
John Reck59135872010-11-02 12:39:01 -07001503 Object* descriptors_unchecked;
1504 { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
1505 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1506 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
1507 return maybe_descriptors_unchecked;
1508 }
1509 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001510 DescriptorArray* new_descriptors =
1511 DescriptorArray::cast(descriptors_unchecked);
1512
1513 // Make a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001514 Object* new_map_unchecked;
1515 { MaybeObject* maybe_new_map_unchecked = map()->CopyDropDescriptors();
1516 if (!maybe_new_map_unchecked->ToObject(&new_map_unchecked)) {
1517 return maybe_new_map_unchecked;
1518 }
1519 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001520 Map* new_map = Map::cast(new_map_unchecked);
1521 new_map->set_instance_descriptors(new_descriptors);
1522
1523 // Make new properties array if necessary.
1524 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1525 int new_unused_property_fields = map()->unused_property_fields() - 1;
1526 if (map()->unused_property_fields() == 0) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001527 new_unused_property_fields = kFieldsAdded - 1;
John Reck59135872010-11-02 12:39:01 -07001528 Object* new_properties_object;
1529 { MaybeObject* maybe_new_properties_object =
1530 properties()->CopySize(properties()->length() + kFieldsAdded);
1531 if (!maybe_new_properties_object->ToObject(&new_properties_object)) {
1532 return maybe_new_properties_object;
1533 }
1534 }
1535 new_properties = FixedArray::cast(new_properties_object);
Steve Blocka7e24c12009-10-30 11:49:00 +00001536 }
1537
1538 // Update pointers to commit changes.
1539 // Object points to the new map.
1540 new_map->set_unused_property_fields(new_unused_property_fields);
1541 set_map(new_map);
1542 if (new_properties) {
1543 set_properties(FixedArray::cast(new_properties));
1544 }
1545 return FastPropertyAtPut(index, new_value);
1546}
1547
1548
1549
John Reck59135872010-11-02 12:39:01 -07001550MaybeObject* JSObject::SetPropertyWithInterceptor(
1551 String* name,
1552 Object* value,
1553 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001554 HandleScope scope;
1555 Handle<JSObject> this_handle(this);
1556 Handle<String> name_handle(name);
1557 Handle<Object> value_handle(value);
1558 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1559 if (!interceptor->setter()->IsUndefined()) {
1560 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name));
1561 CustomArguments args(interceptor->data(), this, this);
1562 v8::AccessorInfo info(args.end());
1563 v8::NamedPropertySetter setter =
1564 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1565 v8::Handle<v8::Value> result;
1566 {
1567 // Leaving JavaScript.
1568 VMState state(EXTERNAL);
1569 Handle<Object> value_unhole(value->IsTheHole() ?
1570 Heap::undefined_value() :
1571 value);
1572 result = setter(v8::Utils::ToLocal(name_handle),
1573 v8::Utils::ToLocal(value_unhole),
1574 info);
1575 }
1576 RETURN_IF_SCHEDULED_EXCEPTION();
1577 if (!result.IsEmpty()) return *value_handle;
1578 }
John Reck59135872010-11-02 12:39:01 -07001579 MaybeObject* raw_result =
1580 this_handle->SetPropertyPostInterceptor(*name_handle,
1581 *value_handle,
1582 attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00001583 RETURN_IF_SCHEDULED_EXCEPTION();
1584 return raw_result;
1585}
1586
1587
John Reck59135872010-11-02 12:39:01 -07001588MaybeObject* JSObject::SetProperty(String* name,
1589 Object* value,
1590 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001591 LookupResult result;
1592 LocalLookup(name, &result);
1593 return SetProperty(&result, name, value, attributes);
1594}
1595
1596
John Reck59135872010-11-02 12:39:01 -07001597MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
1598 String* name,
1599 Object* value,
1600 JSObject* holder) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001601 HandleScope scope;
1602
1603 // We should never get here to initialize a const with the hole
1604 // value since a const declaration would conflict with the setter.
1605 ASSERT(!value->IsTheHole());
1606 Handle<Object> value_handle(value);
1607
1608 // To accommodate both the old and the new api we switch on the
1609 // data structure used to store the callbacks. Eventually proxy
1610 // callbacks should be phased out.
1611 if (structure->IsProxy()) {
1612 AccessorDescriptor* callback =
1613 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
John Reck59135872010-11-02 12:39:01 -07001614 MaybeObject* obj = (callback->setter)(this, value, callback->data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001615 RETURN_IF_SCHEDULED_EXCEPTION();
1616 if (obj->IsFailure()) return obj;
1617 return *value_handle;
1618 }
1619
1620 if (structure->IsAccessorInfo()) {
1621 // api style callbacks
1622 AccessorInfo* data = AccessorInfo::cast(structure);
1623 Object* call_obj = data->setter();
1624 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1625 if (call_fun == NULL) return value;
1626 Handle<String> key(name);
1627 LOG(ApiNamedPropertyAccess("store", this, name));
1628 CustomArguments args(data->data(), this, JSObject::cast(holder));
1629 v8::AccessorInfo info(args.end());
1630 {
1631 // Leaving JavaScript.
1632 VMState state(EXTERNAL);
1633 call_fun(v8::Utils::ToLocal(key),
1634 v8::Utils::ToLocal(value_handle),
1635 info);
1636 }
1637 RETURN_IF_SCHEDULED_EXCEPTION();
1638 return *value_handle;
1639 }
1640
1641 if (structure->IsFixedArray()) {
1642 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1643 if (setter->IsJSFunction()) {
1644 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
1645 } else {
1646 Handle<String> key(name);
1647 Handle<Object> holder_handle(holder);
1648 Handle<Object> args[2] = { key, holder_handle };
1649 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
1650 HandleVector(args, 2)));
1651 }
1652 }
1653
1654 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +01001655 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001656}
1657
1658
John Reck59135872010-11-02 12:39:01 -07001659MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1660 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001661 Handle<Object> value_handle(value);
1662 Handle<JSFunction> fun(JSFunction::cast(setter));
1663 Handle<JSObject> self(this);
1664#ifdef ENABLE_DEBUGGER_SUPPORT
1665 // Handle stepping into a setter if step into is active.
1666 if (Debug::StepInActive()) {
1667 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
1668 }
1669#endif
1670 bool has_pending_exception;
1671 Object** argv[] = { value_handle.location() };
1672 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1673 // Check for pending exception and return the result.
1674 if (has_pending_exception) return Failure::Exception();
1675 return *value_handle;
1676}
1677
1678
1679void JSObject::LookupCallbackSetterInPrototypes(String* name,
1680 LookupResult* result) {
1681 for (Object* pt = GetPrototype();
1682 pt != Heap::null_value();
1683 pt = pt->GetPrototype()) {
1684 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001685 if (result->IsProperty()) {
1686 if (result->IsReadOnly()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001687 result->NotFound();
1688 return;
1689 }
1690 if (result->type() == CALLBACKS) {
1691 return;
1692 }
1693 }
1694 }
1695 result->NotFound();
1696}
1697
1698
Leon Clarkef7060e22010-06-03 12:02:55 +01001699bool JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
1700 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001701 for (Object* pt = GetPrototype();
1702 pt != Heap::null_value();
1703 pt = pt->GetPrototype()) {
1704 if (!JSObject::cast(pt)->HasDictionaryElements()) {
1705 continue;
1706 }
1707 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1708 int entry = dictionary->FindEntry(index);
1709 if (entry != NumberDictionary::kNotFound) {
1710 Object* element = dictionary->ValueAt(entry);
1711 PropertyDetails details = dictionary->DetailsAt(entry);
1712 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01001713 SetElementWithCallback(element, index, value, JSObject::cast(pt));
1714 return true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001715 }
1716 }
1717 }
Leon Clarkef7060e22010-06-03 12:02:55 +01001718 return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001719}
1720
1721
1722void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1723 DescriptorArray* descriptors = map()->instance_descriptors();
Iain Merrick75681382010-08-19 15:07:18 +01001724 int number = descriptors->SearchWithCache(name);
Steve Blocka7e24c12009-10-30 11:49:00 +00001725 if (number != DescriptorArray::kNotFound) {
1726 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1727 } else {
1728 result->NotFound();
1729 }
1730}
1731
1732
1733void JSObject::LocalLookupRealNamedProperty(String* name,
1734 LookupResult* result) {
1735 if (IsJSGlobalProxy()) {
1736 Object* proto = GetPrototype();
1737 if (proto->IsNull()) return result->NotFound();
1738 ASSERT(proto->IsJSGlobalObject());
1739 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
1740 }
1741
1742 if (HasFastProperties()) {
1743 LookupInDescriptor(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001744 if (result->IsFound()) {
1745 // A property, a map transition or a null descriptor was found.
1746 // We return all of these result types because
1747 // LocalLookupRealNamedProperty is used when setting properties
1748 // where map transitions and null descriptors are handled.
Steve Blocka7e24c12009-10-30 11:49:00 +00001749 ASSERT(result->holder() == this && result->type() != NORMAL);
1750 // Disallow caching for uninitialized constants. These can only
1751 // occur as fields.
1752 if (result->IsReadOnly() && result->type() == FIELD &&
1753 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
1754 result->DisallowCaching();
1755 }
1756 return;
1757 }
1758 } else {
1759 int entry = property_dictionary()->FindEntry(name);
1760 if (entry != StringDictionary::kNotFound) {
1761 Object* value = property_dictionary()->ValueAt(entry);
1762 if (IsGlobalObject()) {
1763 PropertyDetails d = property_dictionary()->DetailsAt(entry);
1764 if (d.IsDeleted()) {
1765 result->NotFound();
1766 return;
1767 }
1768 value = JSGlobalPropertyCell::cast(value)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001769 }
1770 // Make sure to disallow caching for uninitialized constants
1771 // found in the dictionary-mode objects.
1772 if (value->IsTheHole()) result->DisallowCaching();
1773 result->DictionaryResult(this, entry);
1774 return;
1775 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001776 }
1777 result->NotFound();
1778}
1779
1780
1781void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
1782 LocalLookupRealNamedProperty(name, result);
1783 if (result->IsProperty()) return;
1784
1785 LookupRealNamedPropertyInPrototypes(name, result);
1786}
1787
1788
1789void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
1790 LookupResult* result) {
1791 for (Object* pt = GetPrototype();
1792 pt != Heap::null_value();
1793 pt = JSObject::cast(pt)->GetPrototype()) {
1794 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001795 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001796 }
1797 result->NotFound();
1798}
1799
1800
1801// We only need to deal with CALLBACKS and INTERCEPTORS
John Reck59135872010-11-02 12:39:01 -07001802MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
1803 String* name,
1804 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001805 if (!result->IsProperty()) {
1806 LookupCallbackSetterInPrototypes(name, result);
1807 }
1808
1809 if (result->IsProperty()) {
1810 if (!result->IsReadOnly()) {
1811 switch (result->type()) {
1812 case CALLBACKS: {
1813 Object* obj = result->GetCallbackObject();
1814 if (obj->IsAccessorInfo()) {
1815 AccessorInfo* info = AccessorInfo::cast(obj);
1816 if (info->all_can_write()) {
1817 return SetPropertyWithCallback(result->GetCallbackObject(),
1818 name,
1819 value,
1820 result->holder());
1821 }
1822 }
1823 break;
1824 }
1825 case INTERCEPTOR: {
1826 // Try lookup real named properties. Note that only property can be
1827 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
1828 LookupResult r;
1829 LookupRealNamedProperty(name, &r);
1830 if (r.IsProperty()) {
1831 return SetPropertyWithFailedAccessCheck(&r, name, value);
1832 }
1833 break;
1834 }
1835 default: {
1836 break;
1837 }
1838 }
1839 }
1840 }
1841
Iain Merrick75681382010-08-19 15:07:18 +01001842 HandleScope scope;
1843 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001844 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01001845 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00001846}
1847
1848
John Reck59135872010-11-02 12:39:01 -07001849MaybeObject* JSObject::SetProperty(LookupResult* result,
1850 String* name,
1851 Object* value,
1852 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001853 // Make sure that the top context does not change when doing callbacks or
1854 // interceptor calls.
1855 AssertNoContextChange ncc;
1856
Steve Blockd0582a62009-12-15 09:54:21 +00001857 // Optimization for 2-byte strings often used as keys in a decompression
1858 // dictionary. We make these short keys into symbols to avoid constantly
1859 // reallocating them.
1860 if (!name->IsSymbol() && name->length() <= 2) {
John Reck59135872010-11-02 12:39:01 -07001861 Object* symbol_version;
1862 { MaybeObject* maybe_symbol_version = Heap::LookupSymbol(name);
1863 if (maybe_symbol_version->ToObject(&symbol_version)) {
1864 name = String::cast(symbol_version);
1865 }
1866 }
Steve Blockd0582a62009-12-15 09:54:21 +00001867 }
1868
Steve Blocka7e24c12009-10-30 11:49:00 +00001869 // Check access rights if needed.
1870 if (IsAccessCheckNeeded()
1871 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1872 return SetPropertyWithFailedAccessCheck(result, name, value);
1873 }
1874
1875 if (IsJSGlobalProxy()) {
1876 Object* proto = GetPrototype();
1877 if (proto->IsNull()) return value;
1878 ASSERT(proto->IsJSGlobalObject());
1879 return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
1880 }
1881
1882 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
1883 // We could not find a local property so let's check whether there is an
1884 // accessor that wants to handle the property.
1885 LookupResult accessor_result;
1886 LookupCallbackSetterInPrototypes(name, &accessor_result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001887 if (accessor_result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001888 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
1889 name,
1890 value,
1891 accessor_result.holder());
1892 }
1893 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001894 if (!result->IsFound()) {
1895 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001896 return AddProperty(name, value, attributes);
1897 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001898 if (result->IsReadOnly() && result->IsProperty()) return value;
1899 // This is a real property that is not read-only, or it is a
1900 // transition or null descriptor and there are no setters in the prototypes.
1901 switch (result->type()) {
1902 case NORMAL:
1903 return SetNormalizedProperty(result, value);
1904 case FIELD:
1905 return FastPropertyAtPut(result->GetFieldIndex(), value);
1906 case MAP_TRANSITION:
1907 if (attributes == result->GetAttributes()) {
1908 // Only use map transition if the attributes match.
1909 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1910 name,
1911 value);
1912 }
1913 return ConvertDescriptorToField(name, value, attributes);
1914 case CONSTANT_FUNCTION:
1915 // Only replace the function if necessary.
1916 if (value == result->GetConstantFunction()) return value;
1917 // Preserve the attributes of this existing property.
1918 attributes = result->GetAttributes();
1919 return ConvertDescriptorToField(name, value, attributes);
1920 case CALLBACKS:
1921 return SetPropertyWithCallback(result->GetCallbackObject(),
1922 name,
1923 value,
1924 result->holder());
1925 case INTERCEPTOR:
1926 return SetPropertyWithInterceptor(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01001927 case CONSTANT_TRANSITION: {
1928 // If the same constant function is being added we can simply
1929 // transition to the target map.
1930 Map* target_map = result->GetTransitionMap();
1931 DescriptorArray* target_descriptors = target_map->instance_descriptors();
1932 int number = target_descriptors->SearchWithCache(name);
1933 ASSERT(number != DescriptorArray::kNotFound);
1934 ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
1935 JSFunction* function =
1936 JSFunction::cast(target_descriptors->GetValue(number));
1937 ASSERT(!Heap::InNewSpace(function));
1938 if (value == function) {
1939 set_map(target_map);
1940 return value;
1941 }
1942 // Otherwise, replace with a MAP_TRANSITION to a new map with a
1943 // FIELD, even if the value is a constant function.
Steve Blocka7e24c12009-10-30 11:49:00 +00001944 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01001945 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001946 case NULL_DESCRIPTOR:
1947 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1948 default:
1949 UNREACHABLE();
1950 }
1951 UNREACHABLE();
1952 return value;
1953}
1954
1955
1956// Set a real local property, even if it is READ_ONLY. If the property is not
1957// present, add it with attributes NONE. This code is an exact clone of
1958// SetProperty, with the check for IsReadOnly and the check for a
1959// callback setter removed. The two lines looking up the LookupResult
1960// result are also added. If one of the functions is changed, the other
1961// should be.
John Reck59135872010-11-02 12:39:01 -07001962MaybeObject* JSObject::IgnoreAttributesAndSetLocalProperty(
Steve Blocka7e24c12009-10-30 11:49:00 +00001963 String* name,
1964 Object* value,
1965 PropertyAttributes attributes) {
1966 // Make sure that the top context does not change when doing callbacks or
1967 // interceptor calls.
1968 AssertNoContextChange ncc;
Andrei Popescu402d9372010-02-26 13:31:12 +00001969 LookupResult result;
1970 LocalLookup(name, &result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001971 // Check access rights if needed.
1972 if (IsAccessCheckNeeded()
Andrei Popescu402d9372010-02-26 13:31:12 +00001973 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1974 return SetPropertyWithFailedAccessCheck(&result, name, value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001975 }
1976
1977 if (IsJSGlobalProxy()) {
1978 Object* proto = GetPrototype();
1979 if (proto->IsNull()) return value;
1980 ASSERT(proto->IsJSGlobalObject());
1981 return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
1982 name,
1983 value,
1984 attributes);
1985 }
1986
1987 // Check for accessor in prototype chain removed here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00001988 if (!result.IsFound()) {
1989 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001990 return AddProperty(name, value, attributes);
1991 }
Steve Block6ded16b2010-05-10 14:33:55 +01001992
Andrei Popescu402d9372010-02-26 13:31:12 +00001993 PropertyDetails details = PropertyDetails(attributes, NORMAL);
1994
Steve Blocka7e24c12009-10-30 11:49:00 +00001995 // Check of IsReadOnly removed from here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00001996 switch (result.type()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001997 case NORMAL:
Andrei Popescu402d9372010-02-26 13:31:12 +00001998 return SetNormalizedProperty(name, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00001999 case FIELD:
Andrei Popescu402d9372010-02-26 13:31:12 +00002000 return FastPropertyAtPut(result.GetFieldIndex(), value);
Steve Blocka7e24c12009-10-30 11:49:00 +00002001 case MAP_TRANSITION:
Andrei Popescu402d9372010-02-26 13:31:12 +00002002 if (attributes == result.GetAttributes()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002003 // Only use map transition if the attributes match.
Andrei Popescu402d9372010-02-26 13:31:12 +00002004 return AddFastPropertyUsingMap(result.GetTransitionMap(),
Steve Blocka7e24c12009-10-30 11:49:00 +00002005 name,
2006 value);
2007 }
2008 return ConvertDescriptorToField(name, value, attributes);
2009 case CONSTANT_FUNCTION:
2010 // Only replace the function if necessary.
Andrei Popescu402d9372010-02-26 13:31:12 +00002011 if (value == result.GetConstantFunction()) return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00002012 // Preserve the attributes of this existing property.
Andrei Popescu402d9372010-02-26 13:31:12 +00002013 attributes = result.GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +00002014 return ConvertDescriptorToField(name, value, attributes);
2015 case CALLBACKS:
2016 case INTERCEPTOR:
2017 // Override callback in clone
2018 return ConvertDescriptorToField(name, value, attributes);
2019 case CONSTANT_TRANSITION:
2020 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
2021 // if the value is a function.
2022 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2023 case NULL_DESCRIPTOR:
2024 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2025 default:
2026 UNREACHABLE();
2027 }
2028 UNREACHABLE();
2029 return value;
2030}
2031
2032
2033PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
2034 JSObject* receiver,
2035 String* name,
2036 bool continue_search) {
2037 // Check local property, ignore interceptor.
2038 LookupResult result;
2039 LocalLookupRealNamedProperty(name, &result);
2040 if (result.IsProperty()) return result.GetAttributes();
2041
2042 if (continue_search) {
2043 // Continue searching via the prototype chain.
2044 Object* pt = GetPrototype();
2045 if (pt != Heap::null_value()) {
2046 return JSObject::cast(pt)->
2047 GetPropertyAttributeWithReceiver(receiver, name);
2048 }
2049 }
2050 return ABSENT;
2051}
2052
2053
2054PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
2055 JSObject* receiver,
2056 String* name,
2057 bool continue_search) {
2058 // Make sure that the top context does not change when doing
2059 // callbacks or interceptor calls.
2060 AssertNoContextChange ncc;
2061
2062 HandleScope scope;
2063 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2064 Handle<JSObject> receiver_handle(receiver);
2065 Handle<JSObject> holder_handle(this);
2066 Handle<String> name_handle(name);
2067 CustomArguments args(interceptor->data(), receiver, this);
2068 v8::AccessorInfo info(args.end());
2069 if (!interceptor->query()->IsUndefined()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002070 v8::NamedPropertyQuery query =
2071 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
Steve Blocka7e24c12009-10-30 11:49:00 +00002072 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002073 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00002074 {
2075 // Leaving JavaScript.
2076 VMState state(EXTERNAL);
2077 result = query(v8::Utils::ToLocal(name_handle), info);
2078 }
2079 if (!result.IsEmpty()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002080 ASSERT(result->IsInt32());
2081 return static_cast<PropertyAttributes>(result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002082 }
2083 } else if (!interceptor->getter()->IsUndefined()) {
2084 v8::NamedPropertyGetter getter =
2085 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
2086 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
2087 v8::Handle<v8::Value> result;
2088 {
2089 // Leaving JavaScript.
2090 VMState state(EXTERNAL);
2091 result = getter(v8::Utils::ToLocal(name_handle), info);
2092 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002093 if (!result.IsEmpty()) return DONT_ENUM;
Steve Blocka7e24c12009-10-30 11:49:00 +00002094 }
2095 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2096 *name_handle,
2097 continue_search);
2098}
2099
2100
2101PropertyAttributes JSObject::GetPropertyAttributeWithReceiver(
2102 JSObject* receiver,
2103 String* key) {
2104 uint32_t index = 0;
2105 if (key->AsArrayIndex(&index)) {
2106 if (HasElementWithReceiver(receiver, index)) return NONE;
2107 return ABSENT;
2108 }
2109 // Named property.
2110 LookupResult result;
2111 Lookup(key, &result);
2112 return GetPropertyAttribute(receiver, &result, key, true);
2113}
2114
2115
2116PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
2117 LookupResult* result,
2118 String* name,
2119 bool continue_search) {
2120 // Check access rights if needed.
2121 if (IsAccessCheckNeeded() &&
2122 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2123 return GetPropertyAttributeWithFailedAccessCheck(receiver,
2124 result,
2125 name,
2126 continue_search);
2127 }
Andrei Popescu402d9372010-02-26 13:31:12 +00002128 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002129 switch (result->type()) {
2130 case NORMAL: // fall through
2131 case FIELD:
2132 case CONSTANT_FUNCTION:
2133 case CALLBACKS:
2134 return result->GetAttributes();
2135 case INTERCEPTOR:
2136 return result->holder()->
2137 GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
Steve Blocka7e24c12009-10-30 11:49:00 +00002138 default:
2139 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +00002140 }
2141 }
2142 return ABSENT;
2143}
2144
2145
2146PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) {
2147 // Check whether the name is an array index.
2148 uint32_t index = 0;
2149 if (name->AsArrayIndex(&index)) {
2150 if (HasLocalElement(index)) return NONE;
2151 return ABSENT;
2152 }
2153 // Named property.
2154 LookupResult result;
2155 LocalLookup(name, &result);
2156 return GetPropertyAttribute(this, &result, name, false);
2157}
2158
2159
John Reck59135872010-11-02 12:39:01 -07002160MaybeObject* NormalizedMapCache::Get(JSObject* obj,
2161 PropertyNormalizationMode mode) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002162 Map* fast = obj->map();
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002163 int index = Hash(fast) % kEntries;
2164 Object* result = get(index);
2165 if (result->IsMap() && CheckHit(Map::cast(result), fast, mode)) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002166#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002167 if (FLAG_enable_slow_asserts) {
2168 // The cached map should match newly created normalized map bit-by-bit.
John Reck59135872010-11-02 12:39:01 -07002169 Object* fresh;
2170 { MaybeObject* maybe_fresh =
2171 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2172 if (maybe_fresh->ToObject(&fresh)) {
2173 ASSERT(memcmp(Map::cast(fresh)->address(),
2174 Map::cast(result)->address(),
2175 Map::kSize) == 0);
2176 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002177 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002178 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002179#endif
2180 return result;
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002181 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002182
John Reck59135872010-11-02 12:39:01 -07002183 { MaybeObject* maybe_result =
2184 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2185 if (!maybe_result->ToObject(&result)) return maybe_result;
2186 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002187 set(index, result);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002188 Counters::normalized_maps.Increment();
2189
2190 return result;
2191}
2192
2193
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002194void NormalizedMapCache::Clear() {
2195 int entries = length();
2196 for (int i = 0; i != entries; i++) {
2197 set_undefined(i);
2198 }
2199}
2200
2201
2202int NormalizedMapCache::Hash(Map* fast) {
2203 // For performance reasons we only hash the 3 most variable fields of a map:
2204 // constructor, prototype and bit_field2.
2205
2206 // Shift away the tag.
2207 int hash = (static_cast<uint32_t>(
2208 reinterpret_cast<uintptr_t>(fast->constructor())) >> 2);
2209
2210 // XOR-ing the prototype and constructor directly yields too many zero bits
2211 // when the two pointers are close (which is fairly common).
2212 // To avoid this we shift the prototype 4 bits relatively to the constructor.
2213 hash ^= (static_cast<uint32_t>(
2214 reinterpret_cast<uintptr_t>(fast->prototype())) << 2);
2215
2216 return hash ^ (hash >> 16) ^ fast->bit_field2();
2217}
2218
2219
2220bool NormalizedMapCache::CheckHit(Map* slow,
2221 Map* fast,
2222 PropertyNormalizationMode mode) {
2223#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002224 slow->SharedMapVerify();
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002225#endif
2226 return
2227 slow->constructor() == fast->constructor() &&
2228 slow->prototype() == fast->prototype() &&
2229 slow->inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ?
2230 0 :
2231 fast->inobject_properties()) &&
2232 slow->instance_type() == fast->instance_type() &&
2233 slow->bit_field() == fast->bit_field() &&
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002234 (slow->bit_field2() & ~(1<<Map::kIsShared)) == fast->bit_field2();
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002235}
2236
2237
John Reck59135872010-11-02 12:39:01 -07002238MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002239 if (map()->is_shared()) {
2240 // Fast case maps are never marked as shared.
2241 ASSERT(!HasFastProperties());
2242 // Replace the map with an identical copy that can be safely modified.
John Reck59135872010-11-02 12:39:01 -07002243 Object* obj;
2244 { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES,
2245 UNIQUE_NORMALIZED_MAP);
2246 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2247 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002248 Counters::normalized_maps.Increment();
2249
2250 set_map(Map::cast(obj));
2251 }
2252 return map()->UpdateCodeCache(name, code);
2253}
2254
2255
John Reck59135872010-11-02 12:39:01 -07002256MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2257 int expected_additional_properties) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002258 if (!HasFastProperties()) return this;
2259
2260 // The global object is always normalized.
2261 ASSERT(!IsGlobalObject());
2262
2263 // Allocate new content.
2264 int property_count = map()->NumberOfDescribedProperties();
2265 if (expected_additional_properties > 0) {
2266 property_count += expected_additional_properties;
2267 } else {
2268 property_count += 2; // Make space for two more properties.
2269 }
John Reck59135872010-11-02 12:39:01 -07002270 Object* obj;
2271 { MaybeObject* maybe_obj =
2272 StringDictionary::Allocate(property_count);
2273 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2274 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002275 StringDictionary* dictionary = StringDictionary::cast(obj);
2276
2277 DescriptorArray* descs = map()->instance_descriptors();
2278 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2279 PropertyDetails details = descs->GetDetails(i);
2280 switch (details.type()) {
2281 case CONSTANT_FUNCTION: {
2282 PropertyDetails d =
2283 PropertyDetails(details.attributes(), NORMAL, details.index());
2284 Object* value = descs->GetConstantFunction(i);
John Reck59135872010-11-02 12:39:01 -07002285 Object* result;
2286 { MaybeObject* maybe_result =
2287 dictionary->Add(descs->GetKey(i), value, d);
2288 if (!maybe_result->ToObject(&result)) return maybe_result;
2289 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002290 dictionary = StringDictionary::cast(result);
2291 break;
2292 }
2293 case FIELD: {
2294 PropertyDetails d =
2295 PropertyDetails(details.attributes(), NORMAL, details.index());
2296 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
John Reck59135872010-11-02 12:39:01 -07002297 Object* result;
2298 { MaybeObject* maybe_result =
2299 dictionary->Add(descs->GetKey(i), value, d);
2300 if (!maybe_result->ToObject(&result)) return maybe_result;
2301 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002302 dictionary = StringDictionary::cast(result);
2303 break;
2304 }
2305 case CALLBACKS: {
2306 PropertyDetails d =
2307 PropertyDetails(details.attributes(), CALLBACKS, details.index());
2308 Object* value = descs->GetCallbacksObject(i);
John Reck59135872010-11-02 12:39:01 -07002309 Object* result;
2310 { MaybeObject* maybe_result =
2311 dictionary->Add(descs->GetKey(i), value, d);
2312 if (!maybe_result->ToObject(&result)) return maybe_result;
2313 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002314 dictionary = StringDictionary::cast(result);
2315 break;
2316 }
2317 case MAP_TRANSITION:
2318 case CONSTANT_TRANSITION:
2319 case NULL_DESCRIPTOR:
2320 case INTERCEPTOR:
2321 break;
2322 default:
2323 UNREACHABLE();
2324 }
2325 }
2326
2327 // Copy the next enumeration index from instance descriptor.
2328 int index = map()->instance_descriptors()->NextEnumerationIndex();
2329 dictionary->SetNextEnumerationIndex(index);
2330
John Reck59135872010-11-02 12:39:01 -07002331 { MaybeObject* maybe_obj = Top::context()->global_context()->
2332 normalized_map_cache()->Get(this, mode);
2333 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2334 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002335 Map* new_map = Map::cast(obj);
2336
Steve Blocka7e24c12009-10-30 11:49:00 +00002337 // We have now successfully allocated all the necessary objects.
2338 // Changes can now be made with the guarantee that all of them take effect.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002339
2340 // Resize the object in the heap if necessary.
2341 int new_instance_size = new_map->instance_size();
2342 int instance_size_delta = map()->instance_size() - new_instance_size;
2343 ASSERT(instance_size_delta >= 0);
2344 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2345 instance_size_delta);
2346
Steve Blocka7e24c12009-10-30 11:49:00 +00002347 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002348
2349 set_properties(dictionary);
2350
2351 Counters::props_to_dictionary.Increment();
2352
2353#ifdef DEBUG
2354 if (FLAG_trace_normalization) {
2355 PrintF("Object properties have been normalized:\n");
2356 Print();
2357 }
2358#endif
2359 return this;
2360}
2361
2362
John Reck59135872010-11-02 12:39:01 -07002363MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002364 if (HasFastProperties()) return this;
2365 ASSERT(!IsGlobalObject());
2366 return property_dictionary()->
2367 TransformPropertiesToFastFor(this, unused_property_fields);
2368}
2369
2370
John Reck59135872010-11-02 12:39:01 -07002371MaybeObject* JSObject::NormalizeElements() {
Steve Block3ce2e202009-11-05 08:53:23 +00002372 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002373 if (HasDictionaryElements()) return this;
Steve Block8defd9f2010-07-08 12:39:36 +01002374 ASSERT(map()->has_fast_elements());
2375
John Reck59135872010-11-02 12:39:01 -07002376 Object* obj;
2377 { MaybeObject* maybe_obj = map()->GetSlowElementsMap();
2378 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2379 }
Steve Block8defd9f2010-07-08 12:39:36 +01002380 Map* new_map = Map::cast(obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00002381
2382 // Get number of entries.
2383 FixedArray* array = FixedArray::cast(elements());
2384
2385 // Compute the effective length.
2386 int length = IsJSArray() ?
2387 Smi::cast(JSArray::cast(this)->length())->value() :
2388 array->length();
John Reck59135872010-11-02 12:39:01 -07002389 { MaybeObject* maybe_obj = NumberDictionary::Allocate(length);
2390 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2391 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002392 NumberDictionary* dictionary = NumberDictionary::cast(obj);
2393 // Copy entries.
2394 for (int i = 0; i < length; i++) {
2395 Object* value = array->get(i);
2396 if (!value->IsTheHole()) {
2397 PropertyDetails details = PropertyDetails(NONE, NORMAL);
John Reck59135872010-11-02 12:39:01 -07002398 Object* result;
2399 { MaybeObject* maybe_result =
2400 dictionary->AddNumberEntry(i, array->get(i), details);
2401 if (!maybe_result->ToObject(&result)) return maybe_result;
2402 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002403 dictionary = NumberDictionary::cast(result);
2404 }
2405 }
Steve Block8defd9f2010-07-08 12:39:36 +01002406 // Switch to using the dictionary as the backing storage for
2407 // elements. Set the new map first to satify the elements type
2408 // assert in set_elements().
2409 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002410 set_elements(dictionary);
2411
2412 Counters::elements_to_dictionary.Increment();
2413
2414#ifdef DEBUG
2415 if (FLAG_trace_normalization) {
2416 PrintF("Object elements have been normalized:\n");
2417 Print();
2418 }
2419#endif
2420
2421 return this;
2422}
2423
2424
John Reck59135872010-11-02 12:39:01 -07002425MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
2426 DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002427 // Check local property, ignore interceptor.
2428 LookupResult result;
2429 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002430 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002431
2432 // Normalize object if needed.
John Reck59135872010-11-02 12:39:01 -07002433 Object* obj;
2434 { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2435 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2436 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002437
2438 return DeleteNormalizedProperty(name, mode);
2439}
2440
2441
John Reck59135872010-11-02 12:39:01 -07002442MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002443 HandleScope scope;
2444 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2445 Handle<String> name_handle(name);
2446 Handle<JSObject> this_handle(this);
2447 if (!interceptor->deleter()->IsUndefined()) {
2448 v8::NamedPropertyDeleter deleter =
2449 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2450 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2451 CustomArguments args(interceptor->data(), this, this);
2452 v8::AccessorInfo info(args.end());
2453 v8::Handle<v8::Boolean> result;
2454 {
2455 // Leaving JavaScript.
2456 VMState state(EXTERNAL);
2457 result = deleter(v8::Utils::ToLocal(name_handle), info);
2458 }
2459 RETURN_IF_SCHEDULED_EXCEPTION();
2460 if (!result.IsEmpty()) {
2461 ASSERT(result->IsBoolean());
2462 return *v8::Utils::OpenHandle(*result);
2463 }
2464 }
John Reck59135872010-11-02 12:39:01 -07002465 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00002466 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
2467 RETURN_IF_SCHEDULED_EXCEPTION();
2468 return raw_result;
2469}
2470
2471
John Reck59135872010-11-02 12:39:01 -07002472MaybeObject* JSObject::DeleteElementPostInterceptor(uint32_t index,
2473 DeleteMode mode) {
Steve Block3ce2e202009-11-05 08:53:23 +00002474 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002475 switch (GetElementsKind()) {
2476 case FAST_ELEMENTS: {
John Reck59135872010-11-02 12:39:01 -07002477 Object* obj;
2478 { MaybeObject* maybe_obj = EnsureWritableFastElements();
2479 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2480 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002481 uint32_t length = IsJSArray() ?
2482 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2483 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2484 if (index < length) {
2485 FixedArray::cast(elements())->set_the_hole(index);
2486 }
2487 break;
2488 }
2489 case DICTIONARY_ELEMENTS: {
2490 NumberDictionary* dictionary = element_dictionary();
2491 int entry = dictionary->FindEntry(index);
2492 if (entry != NumberDictionary::kNotFound) {
2493 return dictionary->DeleteProperty(entry, mode);
2494 }
2495 break;
2496 }
2497 default:
2498 UNREACHABLE();
2499 break;
2500 }
2501 return Heap::true_value();
2502}
2503
2504
John Reck59135872010-11-02 12:39:01 -07002505MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002506 // Make sure that the top context does not change when doing
2507 // callbacks or interceptor calls.
2508 AssertNoContextChange ncc;
2509 HandleScope scope;
2510 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2511 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2512 v8::IndexedPropertyDeleter deleter =
2513 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2514 Handle<JSObject> this_handle(this);
2515 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2516 CustomArguments args(interceptor->data(), this, this);
2517 v8::AccessorInfo info(args.end());
2518 v8::Handle<v8::Boolean> result;
2519 {
2520 // Leaving JavaScript.
2521 VMState state(EXTERNAL);
2522 result = deleter(index, info);
2523 }
2524 RETURN_IF_SCHEDULED_EXCEPTION();
2525 if (!result.IsEmpty()) {
2526 ASSERT(result->IsBoolean());
2527 return *v8::Utils::OpenHandle(*result);
2528 }
John Reck59135872010-11-02 12:39:01 -07002529 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00002530 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
2531 RETURN_IF_SCHEDULED_EXCEPTION();
2532 return raw_result;
2533}
2534
2535
John Reck59135872010-11-02 12:39:01 -07002536MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002537 // Check access rights if needed.
2538 if (IsAccessCheckNeeded() &&
2539 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
2540 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2541 return Heap::false_value();
2542 }
2543
2544 if (IsJSGlobalProxy()) {
2545 Object* proto = GetPrototype();
2546 if (proto->IsNull()) return Heap::false_value();
2547 ASSERT(proto->IsJSGlobalObject());
2548 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
2549 }
2550
2551 if (HasIndexedInterceptor()) {
2552 // Skip interceptor if forcing deletion.
2553 if (mode == FORCE_DELETION) {
2554 return DeleteElementPostInterceptor(index, mode);
2555 }
2556 return DeleteElementWithInterceptor(index);
2557 }
2558
2559 switch (GetElementsKind()) {
2560 case FAST_ELEMENTS: {
John Reck59135872010-11-02 12:39:01 -07002561 Object* obj;
2562 { MaybeObject* maybe_obj = EnsureWritableFastElements();
2563 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2564 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002565 uint32_t length = IsJSArray() ?
2566 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2567 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2568 if (index < length) {
2569 FixedArray::cast(elements())->set_the_hole(index);
2570 }
2571 break;
2572 }
Steve Block3ce2e202009-11-05 08:53:23 +00002573 case PIXEL_ELEMENTS:
2574 case EXTERNAL_BYTE_ELEMENTS:
2575 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2576 case EXTERNAL_SHORT_ELEMENTS:
2577 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2578 case EXTERNAL_INT_ELEMENTS:
2579 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2580 case EXTERNAL_FLOAT_ELEMENTS:
2581 // Pixel and external array elements cannot be deleted. Just
2582 // silently ignore here.
Steve Blocka7e24c12009-10-30 11:49:00 +00002583 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00002584 case DICTIONARY_ELEMENTS: {
2585 NumberDictionary* dictionary = element_dictionary();
2586 int entry = dictionary->FindEntry(index);
2587 if (entry != NumberDictionary::kNotFound) {
2588 return dictionary->DeleteProperty(entry, mode);
2589 }
2590 break;
2591 }
2592 default:
2593 UNREACHABLE();
2594 break;
2595 }
2596 return Heap::true_value();
2597}
2598
2599
John Reck59135872010-11-02 12:39:01 -07002600MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002601 // ECMA-262, 3rd, 8.6.2.5
2602 ASSERT(name->IsString());
2603
2604 // Check access rights if needed.
2605 if (IsAccessCheckNeeded() &&
2606 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2607 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2608 return Heap::false_value();
2609 }
2610
2611 if (IsJSGlobalProxy()) {
2612 Object* proto = GetPrototype();
2613 if (proto->IsNull()) return Heap::false_value();
2614 ASSERT(proto->IsJSGlobalObject());
2615 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
2616 }
2617
2618 uint32_t index = 0;
2619 if (name->AsArrayIndex(&index)) {
2620 return DeleteElement(index, mode);
2621 } else {
2622 LookupResult result;
2623 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002624 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002625 // Ignore attributes if forcing a deletion.
2626 if (result.IsDontDelete() && mode != FORCE_DELETION) {
2627 return Heap::false_value();
2628 }
2629 // Check for interceptor.
2630 if (result.type() == INTERCEPTOR) {
2631 // Skip interceptor if forcing a deletion.
2632 if (mode == FORCE_DELETION) {
2633 return DeletePropertyPostInterceptor(name, mode);
2634 }
2635 return DeletePropertyWithInterceptor(name);
2636 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002637 // Normalize object if needed.
John Reck59135872010-11-02 12:39:01 -07002638 Object* obj;
2639 { MaybeObject* maybe_obj =
2640 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2641 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2642 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002643 // Make sure the properties are normalized before removing the entry.
2644 return DeleteNormalizedProperty(name, mode);
2645 }
2646}
2647
2648
2649// Check whether this object references another object.
2650bool JSObject::ReferencesObject(Object* obj) {
2651 AssertNoAllocation no_alloc;
2652
2653 // Is the object the constructor for this object?
2654 if (map()->constructor() == obj) {
2655 return true;
2656 }
2657
2658 // Is the object the prototype for this object?
2659 if (map()->prototype() == obj) {
2660 return true;
2661 }
2662
2663 // Check if the object is among the named properties.
2664 Object* key = SlowReverseLookup(obj);
2665 if (key != Heap::undefined_value()) {
2666 return true;
2667 }
2668
2669 // Check if the object is among the indexed properties.
2670 switch (GetElementsKind()) {
2671 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002672 case EXTERNAL_BYTE_ELEMENTS:
2673 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2674 case EXTERNAL_SHORT_ELEMENTS:
2675 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2676 case EXTERNAL_INT_ELEMENTS:
2677 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2678 case EXTERNAL_FLOAT_ELEMENTS:
2679 // Raw pixels and external arrays do not reference other
2680 // objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00002681 break;
2682 case FAST_ELEMENTS: {
2683 int length = IsJSArray() ?
2684 Smi::cast(JSArray::cast(this)->length())->value() :
2685 FixedArray::cast(elements())->length();
2686 for (int i = 0; i < length; i++) {
2687 Object* element = FixedArray::cast(elements())->get(i);
2688 if (!element->IsTheHole() && element == obj) {
2689 return true;
2690 }
2691 }
2692 break;
2693 }
2694 case DICTIONARY_ELEMENTS: {
2695 key = element_dictionary()->SlowReverseLookup(obj);
2696 if (key != Heap::undefined_value()) {
2697 return true;
2698 }
2699 break;
2700 }
2701 default:
2702 UNREACHABLE();
2703 break;
2704 }
2705
Steve Block6ded16b2010-05-10 14:33:55 +01002706 // For functions check the context.
2707 if (IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002708 // Get the constructor function for arguments array.
2709 JSObject* arguments_boilerplate =
2710 Top::context()->global_context()->arguments_boilerplate();
2711 JSFunction* arguments_function =
2712 JSFunction::cast(arguments_boilerplate->map()->constructor());
2713
2714 // Get the context and don't check if it is the global context.
2715 JSFunction* f = JSFunction::cast(this);
2716 Context* context = f->context();
2717 if (context->IsGlobalContext()) {
2718 return false;
2719 }
2720
2721 // Check the non-special context slots.
2722 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2723 // Only check JS objects.
2724 if (context->get(i)->IsJSObject()) {
2725 JSObject* ctxobj = JSObject::cast(context->get(i));
2726 // If it is an arguments array check the content.
2727 if (ctxobj->map()->constructor() == arguments_function) {
2728 if (ctxobj->ReferencesObject(obj)) {
2729 return true;
2730 }
2731 } else if (ctxobj == obj) {
2732 return true;
2733 }
2734 }
2735 }
2736
2737 // Check the context extension if any.
2738 if (context->has_extension()) {
2739 return context->extension()->ReferencesObject(obj);
2740 }
2741 }
2742
2743 // No references to object.
2744 return false;
2745}
2746
2747
John Reck59135872010-11-02 12:39:01 -07002748MaybeObject* JSObject::PreventExtensions() {
Steve Block8defd9f2010-07-08 12:39:36 +01002749 // If there are fast elements we normalize.
2750 if (HasFastElements()) {
John Reck59135872010-11-02 12:39:01 -07002751 Object* ok;
2752 { MaybeObject* maybe_ok = NormalizeElements();
2753 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
2754 }
Steve Block8defd9f2010-07-08 12:39:36 +01002755 }
2756 // Make sure that we never go back to fast case.
2757 element_dictionary()->set_requires_slow_elements();
2758
2759 // Do a map transition, other objects with this map may still
2760 // be extensible.
John Reck59135872010-11-02 12:39:01 -07002761 Object* new_map;
2762 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
2763 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
2764 }
Steve Block8defd9f2010-07-08 12:39:36 +01002765 Map::cast(new_map)->set_is_extensible(false);
2766 set_map(Map::cast(new_map));
2767 ASSERT(!map()->is_extensible());
2768 return new_map;
2769}
2770
2771
Steve Blocka7e24c12009-10-30 11:49:00 +00002772// Tests for the fast common case for property enumeration:
Steve Blockd0582a62009-12-15 09:54:21 +00002773// - This object and all prototypes has an enum cache (which means that it has
2774// no interceptors and needs no access checks).
2775// - This object has no elements.
2776// - No prototype has enumerable properties/elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002777bool JSObject::IsSimpleEnum() {
Steve Blocka7e24c12009-10-30 11:49:00 +00002778 for (Object* o = this;
2779 o != Heap::null_value();
2780 o = JSObject::cast(o)->GetPrototype()) {
2781 JSObject* curr = JSObject::cast(o);
Steve Blocka7e24c12009-10-30 11:49:00 +00002782 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00002783 ASSERT(!curr->HasNamedInterceptor());
2784 ASSERT(!curr->HasIndexedInterceptor());
2785 ASSERT(!curr->IsAccessCheckNeeded());
Steve Blocka7e24c12009-10-30 11:49:00 +00002786 if (curr->NumberOfEnumElements() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002787 if (curr != this) {
2788 FixedArray* curr_fixed_array =
2789 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
Steve Blockd0582a62009-12-15 09:54:21 +00002790 if (curr_fixed_array->length() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002791 }
2792 }
2793 return true;
2794}
2795
2796
2797int Map::NumberOfDescribedProperties() {
2798 int result = 0;
2799 DescriptorArray* descs = instance_descriptors();
2800 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2801 if (descs->IsProperty(i)) result++;
2802 }
2803 return result;
2804}
2805
2806
2807int Map::PropertyIndexFor(String* name) {
2808 DescriptorArray* descs = instance_descriptors();
2809 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2810 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
2811 return descs->GetFieldIndex(i);
2812 }
2813 }
2814 return -1;
2815}
2816
2817
2818int Map::NextFreePropertyIndex() {
2819 int max_index = -1;
2820 DescriptorArray* descs = instance_descriptors();
2821 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2822 if (descs->GetType(i) == FIELD) {
2823 int current_index = descs->GetFieldIndex(i);
2824 if (current_index > max_index) max_index = current_index;
2825 }
2826 }
2827 return max_index + 1;
2828}
2829
2830
2831AccessorDescriptor* Map::FindAccessor(String* name) {
2832 DescriptorArray* descs = instance_descriptors();
2833 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2834 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
2835 return descs->GetCallbacks(i);
2836 }
2837 }
2838 return NULL;
2839}
2840
2841
2842void JSObject::LocalLookup(String* name, LookupResult* result) {
2843 ASSERT(name->IsString());
2844
2845 if (IsJSGlobalProxy()) {
2846 Object* proto = GetPrototype();
2847 if (proto->IsNull()) return result->NotFound();
2848 ASSERT(proto->IsJSGlobalObject());
2849 return JSObject::cast(proto)->LocalLookup(name, result);
2850 }
2851
2852 // Do not use inline caching if the object is a non-global object
2853 // that requires access checks.
2854 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
2855 result->DisallowCaching();
2856 }
2857
2858 // Check __proto__ before interceptor.
2859 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
2860 result->ConstantResult(this);
2861 return;
2862 }
2863
2864 // Check for lookup interceptor except when bootstrapping.
2865 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2866 result->InterceptorResult(this);
2867 return;
2868 }
2869
2870 LocalLookupRealNamedProperty(name, result);
2871}
2872
2873
2874void JSObject::Lookup(String* name, LookupResult* result) {
2875 // Ecma-262 3rd 8.6.2.4
2876 for (Object* current = this;
2877 current != Heap::null_value();
2878 current = JSObject::cast(current)->GetPrototype()) {
2879 JSObject::cast(current)->LocalLookup(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002880 if (result->IsProperty()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002881 }
2882 result->NotFound();
2883}
2884
2885
2886// Search object and it's prototype chain for callback properties.
2887void JSObject::LookupCallback(String* name, LookupResult* result) {
2888 for (Object* current = this;
2889 current != Heap::null_value();
2890 current = JSObject::cast(current)->GetPrototype()) {
2891 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002892 if (result->IsProperty() && result->type() == CALLBACKS) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002893 }
2894 result->NotFound();
2895}
2896
2897
John Reck59135872010-11-02 12:39:01 -07002898MaybeObject* JSObject::DefineGetterSetter(String* name,
2899 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002900 // Make sure that the top context does not change when doing callbacks or
2901 // interceptor calls.
2902 AssertNoContextChange ncc;
2903
Steve Blocka7e24c12009-10-30 11:49:00 +00002904 // Try to flatten before operating on the string.
Steve Block6ded16b2010-05-10 14:33:55 +01002905 name->TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00002906
Leon Clarkef7060e22010-06-03 12:02:55 +01002907 if (!CanSetCallback(name)) {
2908 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002909 }
2910
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002911 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00002912 bool is_element = name->AsArrayIndex(&index);
2913 if (is_element && IsJSArray()) return Heap::undefined_value();
2914
2915 if (is_element) {
2916 switch (GetElementsKind()) {
2917 case FAST_ELEMENTS:
2918 break;
2919 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002920 case EXTERNAL_BYTE_ELEMENTS:
2921 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2922 case EXTERNAL_SHORT_ELEMENTS:
2923 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2924 case EXTERNAL_INT_ELEMENTS:
2925 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2926 case EXTERNAL_FLOAT_ELEMENTS:
2927 // Ignore getters and setters on pixel and external array
2928 // elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002929 return Heap::undefined_value();
2930 case DICTIONARY_ELEMENTS: {
2931 // Lookup the index.
2932 NumberDictionary* dictionary = element_dictionary();
2933 int entry = dictionary->FindEntry(index);
2934 if (entry != NumberDictionary::kNotFound) {
2935 Object* result = dictionary->ValueAt(entry);
2936 PropertyDetails details = dictionary->DetailsAt(entry);
2937 if (details.IsReadOnly()) return Heap::undefined_value();
2938 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002939 if (result->IsFixedArray()) {
2940 return result;
2941 }
2942 // Otherwise allow to override it.
Steve Blocka7e24c12009-10-30 11:49:00 +00002943 }
2944 }
2945 break;
2946 }
2947 default:
2948 UNREACHABLE();
2949 break;
2950 }
2951 } else {
2952 // Lookup the name.
2953 LookupResult result;
2954 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002955 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002956 if (result.IsReadOnly()) return Heap::undefined_value();
2957 if (result.type() == CALLBACKS) {
2958 Object* obj = result.GetCallbackObject();
Leon Clarkef7060e22010-06-03 12:02:55 +01002959 // Need to preserve old getters/setters.
Leon Clarked91b9f72010-01-27 17:25:45 +00002960 if (obj->IsFixedArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002961 // Use set to update attributes.
2962 return SetPropertyCallback(name, obj, attributes);
Leon Clarked91b9f72010-01-27 17:25:45 +00002963 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002964 }
2965 }
2966 }
2967
2968 // Allocate the fixed array to hold getter and setter.
John Reck59135872010-11-02 12:39:01 -07002969 Object* structure;
2970 { MaybeObject* maybe_structure = Heap::AllocateFixedArray(2, TENURED);
2971 if (!maybe_structure->ToObject(&structure)) return maybe_structure;
2972 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002973
2974 if (is_element) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002975 return SetElementCallback(index, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002976 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01002977 return SetPropertyCallback(name, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002978 }
Leon Clarkef7060e22010-06-03 12:02:55 +01002979}
2980
2981
2982bool JSObject::CanSetCallback(String* name) {
2983 ASSERT(!IsAccessCheckNeeded()
2984 || Top::MayNamedAccess(this, name, v8::ACCESS_SET));
2985
2986 // Check if there is an API defined callback object which prohibits
2987 // callback overwriting in this object or it's prototype chain.
2988 // This mechanism is needed for instance in a browser setting, where
2989 // certain accessors such as window.location should not be allowed
2990 // to be overwritten because allowing overwriting could potentially
2991 // cause security problems.
2992 LookupResult callback_result;
2993 LookupCallback(name, &callback_result);
2994 if (callback_result.IsProperty()) {
2995 Object* obj = callback_result.GetCallbackObject();
2996 if (obj->IsAccessorInfo() &&
2997 AccessorInfo::cast(obj)->prohibits_overwriting()) {
2998 return false;
2999 }
3000 }
3001
3002 return true;
3003}
3004
3005
John Reck59135872010-11-02 12:39:01 -07003006MaybeObject* JSObject::SetElementCallback(uint32_t index,
3007 Object* structure,
3008 PropertyAttributes attributes) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003009 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3010
3011 // Normalize elements to make this operation simple.
John Reck59135872010-11-02 12:39:01 -07003012 Object* ok;
3013 { MaybeObject* maybe_ok = NormalizeElements();
3014 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3015 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003016
3017 // Update the dictionary with the new CALLBACKS property.
John Reck59135872010-11-02 12:39:01 -07003018 Object* dict;
3019 { MaybeObject* maybe_dict =
3020 element_dictionary()->Set(index, structure, details);
3021 if (!maybe_dict->ToObject(&dict)) return maybe_dict;
3022 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003023
3024 NumberDictionary* elements = NumberDictionary::cast(dict);
3025 elements->set_requires_slow_elements();
3026 // Set the potential new dictionary on the object.
3027 set_elements(elements);
Steve Blocka7e24c12009-10-30 11:49:00 +00003028
3029 return structure;
3030}
3031
3032
John Reck59135872010-11-02 12:39:01 -07003033MaybeObject* JSObject::SetPropertyCallback(String* name,
3034 Object* structure,
3035 PropertyAttributes attributes) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003036 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3037
3038 bool convert_back_to_fast = HasFastProperties() &&
3039 (map()->instance_descriptors()->number_of_descriptors()
3040 < DescriptorArray::kMaxNumberOfDescriptors);
3041
3042 // Normalize object to make this operation simple.
John Reck59135872010-11-02 12:39:01 -07003043 Object* ok;
3044 { MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3045 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3046 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003047
3048 // For the global object allocate a new map to invalidate the global inline
3049 // caches which have a global property cell reference directly in the code.
3050 if (IsGlobalObject()) {
John Reck59135872010-11-02 12:39:01 -07003051 Object* new_map;
3052 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
3053 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3054 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003055 set_map(Map::cast(new_map));
3056 }
3057
3058 // Update the dictionary with the new CALLBACKS property.
John Reck59135872010-11-02 12:39:01 -07003059 Object* result;
3060 { MaybeObject* maybe_result = SetNormalizedProperty(name, structure, details);
3061 if (!maybe_result->ToObject(&result)) return maybe_result;
3062 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003063
3064 if (convert_back_to_fast) {
John Reck59135872010-11-02 12:39:01 -07003065 { MaybeObject* maybe_ok = TransformToFastProperties(0);
3066 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3067 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003068 }
3069 return result;
3070}
3071
John Reck59135872010-11-02 12:39:01 -07003072MaybeObject* JSObject::DefineAccessor(String* name,
3073 bool is_getter,
3074 JSFunction* fun,
3075 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003076 // Check access rights if needed.
3077 if (IsAccessCheckNeeded() &&
Leon Clarkef7060e22010-06-03 12:02:55 +01003078 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
3079 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Steve Blocka7e24c12009-10-30 11:49:00 +00003080 return Heap::undefined_value();
3081 }
3082
3083 if (IsJSGlobalProxy()) {
3084 Object* proto = GetPrototype();
3085 if (proto->IsNull()) return this;
3086 ASSERT(proto->IsJSGlobalObject());
3087 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
3088 fun, attributes);
3089 }
3090
John Reck59135872010-11-02 12:39:01 -07003091 Object* array;
3092 { MaybeObject* maybe_array = DefineGetterSetter(name, attributes);
3093 if (!maybe_array->ToObject(&array)) return maybe_array;
3094 }
3095 if (array->IsUndefined()) return array;
Steve Blocka7e24c12009-10-30 11:49:00 +00003096 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
3097 return this;
3098}
3099
3100
John Reck59135872010-11-02 12:39:01 -07003101MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003102 String* name = String::cast(info->name());
3103 // Check access rights if needed.
3104 if (IsAccessCheckNeeded() &&
3105 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
3106 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
3107 return Heap::undefined_value();
3108 }
3109
3110 if (IsJSGlobalProxy()) {
3111 Object* proto = GetPrototype();
3112 if (proto->IsNull()) return this;
3113 ASSERT(proto->IsJSGlobalObject());
3114 return JSObject::cast(proto)->DefineAccessor(info);
3115 }
3116
3117 // Make sure that the top context does not change when doing callbacks or
3118 // interceptor calls.
3119 AssertNoContextChange ncc;
3120
3121 // Try to flatten before operating on the string.
3122 name->TryFlatten();
3123
3124 if (!CanSetCallback(name)) {
3125 return Heap::undefined_value();
3126 }
3127
3128 uint32_t index = 0;
3129 bool is_element = name->AsArrayIndex(&index);
3130
3131 if (is_element) {
3132 if (IsJSArray()) return Heap::undefined_value();
3133
3134 // Accessors overwrite previous callbacks (cf. with getters/setters).
3135 switch (GetElementsKind()) {
3136 case FAST_ELEMENTS:
3137 break;
3138 case PIXEL_ELEMENTS:
3139 case EXTERNAL_BYTE_ELEMENTS:
3140 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3141 case EXTERNAL_SHORT_ELEMENTS:
3142 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3143 case EXTERNAL_INT_ELEMENTS:
3144 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3145 case EXTERNAL_FLOAT_ELEMENTS:
3146 // Ignore getters and setters on pixel and external array
3147 // elements.
3148 return Heap::undefined_value();
3149 case DICTIONARY_ELEMENTS:
3150 break;
3151 default:
3152 UNREACHABLE();
3153 break;
3154 }
3155
John Reck59135872010-11-02 12:39:01 -07003156 Object* ok;
3157 { MaybeObject* maybe_ok =
3158 SetElementCallback(index, info, info->property_attributes());
3159 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3160 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003161 } else {
3162 // Lookup the name.
3163 LookupResult result;
3164 LocalLookup(name, &result);
3165 // ES5 forbids turning a property into an accessor if it's not
3166 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
3167 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
3168 return Heap::undefined_value();
3169 }
John Reck59135872010-11-02 12:39:01 -07003170 Object* ok;
3171 { MaybeObject* maybe_ok =
3172 SetPropertyCallback(name, info, info->property_attributes());
3173 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3174 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003175 }
3176
3177 return this;
3178}
3179
3180
Steve Blocka7e24c12009-10-30 11:49:00 +00003181Object* JSObject::LookupAccessor(String* name, bool is_getter) {
3182 // Make sure that the top context does not change when doing callbacks or
3183 // interceptor calls.
3184 AssertNoContextChange ncc;
3185
3186 // Check access rights if needed.
3187 if (IsAccessCheckNeeded() &&
3188 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
3189 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
3190 return Heap::undefined_value();
3191 }
3192
3193 // Make the lookup and include prototypes.
3194 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01003195 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00003196 if (name->AsArrayIndex(&index)) {
3197 for (Object* obj = this;
3198 obj != Heap::null_value();
3199 obj = JSObject::cast(obj)->GetPrototype()) {
3200 JSObject* js_object = JSObject::cast(obj);
3201 if (js_object->HasDictionaryElements()) {
3202 NumberDictionary* dictionary = js_object->element_dictionary();
3203 int entry = dictionary->FindEntry(index);
3204 if (entry != NumberDictionary::kNotFound) {
3205 Object* element = dictionary->ValueAt(entry);
3206 PropertyDetails details = dictionary->DetailsAt(entry);
3207 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003208 if (element->IsFixedArray()) {
3209 return FixedArray::cast(element)->get(accessor_index);
3210 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003211 }
3212 }
3213 }
3214 }
3215 } else {
3216 for (Object* obj = this;
3217 obj != Heap::null_value();
3218 obj = JSObject::cast(obj)->GetPrototype()) {
3219 LookupResult result;
3220 JSObject::cast(obj)->LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00003221 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003222 if (result.IsReadOnly()) return Heap::undefined_value();
3223 if (result.type() == CALLBACKS) {
3224 Object* obj = result.GetCallbackObject();
3225 if (obj->IsFixedArray()) {
3226 return FixedArray::cast(obj)->get(accessor_index);
3227 }
3228 }
3229 }
3230 }
3231 }
3232 return Heap::undefined_value();
3233}
3234
3235
3236Object* JSObject::SlowReverseLookup(Object* value) {
3237 if (HasFastProperties()) {
3238 DescriptorArray* descs = map()->instance_descriptors();
3239 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3240 if (descs->GetType(i) == FIELD) {
3241 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3242 return descs->GetKey(i);
3243 }
3244 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3245 if (descs->GetConstantFunction(i) == value) {
3246 return descs->GetKey(i);
3247 }
3248 }
3249 }
3250 return Heap::undefined_value();
3251 } else {
3252 return property_dictionary()->SlowReverseLookup(value);
3253 }
3254}
3255
3256
John Reck59135872010-11-02 12:39:01 -07003257MaybeObject* Map::CopyDropDescriptors() {
3258 Object* result;
3259 { MaybeObject* maybe_result =
3260 Heap::AllocateMap(instance_type(), instance_size());
3261 if (!maybe_result->ToObject(&result)) return maybe_result;
3262 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003263 Map::cast(result)->set_prototype(prototype());
3264 Map::cast(result)->set_constructor(constructor());
3265 // Don't copy descriptors, so map transitions always remain a forest.
3266 // If we retained the same descriptors we would have two maps
3267 // pointing to the same transition which is bad because the garbage
3268 // collector relies on being able to reverse pointers from transitions
3269 // to maps. If properties need to be retained use CopyDropTransitions.
3270 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
3271 // Please note instance_type and instance_size are set when allocated.
3272 Map::cast(result)->set_inobject_properties(inobject_properties());
3273 Map::cast(result)->set_unused_property_fields(unused_property_fields());
3274
3275 // If the map has pre-allocated properties always start out with a descriptor
3276 // array describing these properties.
3277 if (pre_allocated_property_fields() > 0) {
3278 ASSERT(constructor()->IsJSFunction());
3279 JSFunction* ctor = JSFunction::cast(constructor());
John Reck59135872010-11-02 12:39:01 -07003280 Object* descriptors;
3281 { MaybeObject* maybe_descriptors =
3282 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
3283 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
3284 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003285 Map::cast(result)->set_instance_descriptors(
3286 DescriptorArray::cast(descriptors));
3287 Map::cast(result)->set_pre_allocated_property_fields(
3288 pre_allocated_property_fields());
3289 }
3290 Map::cast(result)->set_bit_field(bit_field());
3291 Map::cast(result)->set_bit_field2(bit_field2());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003292 Map::cast(result)->set_is_shared(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00003293 Map::cast(result)->ClearCodeCache();
3294 return result;
3295}
3296
3297
John Reck59135872010-11-02 12:39:01 -07003298MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
3299 NormalizedMapSharingMode sharing) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003300 int new_instance_size = instance_size();
3301 if (mode == CLEAR_INOBJECT_PROPERTIES) {
3302 new_instance_size -= inobject_properties() * kPointerSize;
3303 }
3304
John Reck59135872010-11-02 12:39:01 -07003305 Object* result;
3306 { MaybeObject* maybe_result =
3307 Heap::AllocateMap(instance_type(), new_instance_size);
3308 if (!maybe_result->ToObject(&result)) return maybe_result;
3309 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003310
3311 if (mode != CLEAR_INOBJECT_PROPERTIES) {
3312 Map::cast(result)->set_inobject_properties(inobject_properties());
3313 }
3314
3315 Map::cast(result)->set_prototype(prototype());
3316 Map::cast(result)->set_constructor(constructor());
3317
3318 Map::cast(result)->set_bit_field(bit_field());
3319 Map::cast(result)->set_bit_field2(bit_field2());
3320
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003321 Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
3322
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003323#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003324 if (Map::cast(result)->is_shared()) {
3325 Map::cast(result)->SharedMapVerify();
3326 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003327#endif
3328
3329 return result;
3330}
3331
3332
John Reck59135872010-11-02 12:39:01 -07003333MaybeObject* Map::CopyDropTransitions() {
3334 Object* new_map;
3335 { MaybeObject* maybe_new_map = CopyDropDescriptors();
3336 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3337 }
3338 Object* descriptors;
3339 { MaybeObject* maybe_descriptors =
3340 instance_descriptors()->RemoveTransitions();
3341 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
3342 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003343 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
Steve Block8defd9f2010-07-08 12:39:36 +01003344 return new_map;
Steve Blocka7e24c12009-10-30 11:49:00 +00003345}
3346
3347
John Reck59135872010-11-02 12:39:01 -07003348MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003349 // Allocate the code cache if not present.
3350 if (code_cache()->IsFixedArray()) {
John Reck59135872010-11-02 12:39:01 -07003351 Object* result;
3352 { MaybeObject* maybe_result = Heap::AllocateCodeCache();
3353 if (!maybe_result->ToObject(&result)) return maybe_result;
3354 }
Steve Block6ded16b2010-05-10 14:33:55 +01003355 set_code_cache(result);
3356 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003357
Steve Block6ded16b2010-05-10 14:33:55 +01003358 // Update the code cache.
3359 return CodeCache::cast(code_cache())->Update(name, code);
3360}
3361
3362
3363Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3364 // Do a lookup if a code cache exists.
3365 if (!code_cache()->IsFixedArray()) {
3366 return CodeCache::cast(code_cache())->Lookup(name, flags);
3367 } else {
3368 return Heap::undefined_value();
3369 }
3370}
3371
3372
3373int Map::IndexInCodeCache(Object* name, Code* code) {
3374 // Get the internal index if a code cache exists.
3375 if (!code_cache()->IsFixedArray()) {
3376 return CodeCache::cast(code_cache())->GetIndex(name, code);
3377 }
3378 return -1;
3379}
3380
3381
3382void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
3383 // No GC is supposed to happen between a call to IndexInCodeCache and
3384 // RemoveFromCodeCache so the code cache must be there.
3385 ASSERT(!code_cache()->IsFixedArray());
3386 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
3387}
3388
3389
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003390void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
3391 Map* current = this;
3392 while (current != Heap::meta_map()) {
3393 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
3394 *RawField(current, Map::kInstanceDescriptorsOffset));
3395 if (d == Heap::empty_descriptor_array()) {
3396 Map* prev = current->map();
3397 current->set_map(Heap::meta_map());
3398 callback(current, data);
3399 current = prev;
3400 continue;
3401 }
3402
3403 FixedArray* contents = reinterpret_cast<FixedArray*>(
3404 d->get(DescriptorArray::kContentArrayIndex));
3405 Object** map_or_index_field = RawField(contents, HeapObject::kMapOffset);
3406 Object* map_or_index = *map_or_index_field;
3407 bool map_done = true;
3408 for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0;
3409 i < contents->length();
3410 i += 2) {
3411 PropertyDetails details(Smi::cast(contents->get(i + 1)));
3412 if (details.IsTransition()) {
3413 Map* next = reinterpret_cast<Map*>(contents->get(i));
3414 next->set_map(current);
3415 *map_or_index_field = Smi::FromInt(i + 2);
3416 current = next;
3417 map_done = false;
3418 break;
3419 }
3420 }
3421 if (!map_done) continue;
3422 *map_or_index_field = Heap::fixed_array_map();
3423 Map* prev = current->map();
3424 current->set_map(Heap::meta_map());
3425 callback(current, data);
3426 current = prev;
3427 }
3428}
3429
3430
John Reck59135872010-11-02 12:39:01 -07003431MaybeObject* CodeCache::Update(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003432 ASSERT(code->ic_state() == MONOMORPHIC);
3433
3434 // The number of monomorphic stubs for normal load/store/call IC's can grow to
3435 // a large number and therefore they need to go into a hash table. They are
3436 // used to load global properties from cells.
3437 if (code->type() == NORMAL) {
3438 // Make sure that a hash table is allocated for the normal load code cache.
3439 if (normal_type_cache()->IsUndefined()) {
John Reck59135872010-11-02 12:39:01 -07003440 Object* result;
3441 { MaybeObject* maybe_result =
3442 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
3443 if (!maybe_result->ToObject(&result)) return maybe_result;
3444 }
Steve Block6ded16b2010-05-10 14:33:55 +01003445 set_normal_type_cache(result);
3446 }
3447 return UpdateNormalTypeCache(name, code);
3448 } else {
3449 ASSERT(default_cache()->IsFixedArray());
3450 return UpdateDefaultCache(name, code);
3451 }
3452}
3453
3454
John Reck59135872010-11-02 12:39:01 -07003455MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003456 // When updating the default code cache we disregard the type encoded in the
Steve Blocka7e24c12009-10-30 11:49:00 +00003457 // flags. This allows call constant stubs to overwrite call field
3458 // stubs, etc.
3459 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
3460
3461 // First check whether we can update existing code cache without
3462 // extending it.
Steve Block6ded16b2010-05-10 14:33:55 +01003463 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003464 int length = cache->length();
3465 int deleted_index = -1;
Steve Block6ded16b2010-05-10 14:33:55 +01003466 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003467 Object* key = cache->get(i);
3468 if (key->IsNull()) {
3469 if (deleted_index < 0) deleted_index = i;
3470 continue;
3471 }
3472 if (key->IsUndefined()) {
3473 if (deleted_index >= 0) i = deleted_index;
Steve Block6ded16b2010-05-10 14:33:55 +01003474 cache->set(i + kCodeCacheEntryNameOffset, name);
3475 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003476 return this;
3477 }
3478 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003479 Code::Flags found =
3480 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
Steve Blocka7e24c12009-10-30 11:49:00 +00003481 if (Code::RemoveTypeFromFlags(found) == flags) {
Steve Block6ded16b2010-05-10 14:33:55 +01003482 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003483 return this;
3484 }
3485 }
3486 }
3487
3488 // Reached the end of the code cache. If there were deleted
3489 // elements, reuse the space for the first of them.
3490 if (deleted_index >= 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01003491 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
3492 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003493 return this;
3494 }
3495
Steve Block6ded16b2010-05-10 14:33:55 +01003496 // Extend the code cache with some new entries (at least one). Must be a
3497 // multiple of the entry size.
3498 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
3499 new_length = new_length - new_length % kCodeCacheEntrySize;
3500 ASSERT((new_length % kCodeCacheEntrySize) == 0);
John Reck59135872010-11-02 12:39:01 -07003501 Object* result;
3502 { MaybeObject* maybe_result = cache->CopySize(new_length);
3503 if (!maybe_result->ToObject(&result)) return maybe_result;
3504 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003505
3506 // Add the (name, code) pair to the new cache.
3507 cache = FixedArray::cast(result);
Steve Block6ded16b2010-05-10 14:33:55 +01003508 cache->set(length + kCodeCacheEntryNameOffset, name);
3509 cache->set(length + kCodeCacheEntryCodeOffset, code);
3510 set_default_cache(cache);
Steve Blocka7e24c12009-10-30 11:49:00 +00003511 return this;
3512}
3513
3514
John Reck59135872010-11-02 12:39:01 -07003515MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003516 // Adding a new entry can cause a new cache to be allocated.
3517 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
John Reck59135872010-11-02 12:39:01 -07003518 Object* new_cache;
3519 { MaybeObject* maybe_new_cache = cache->Put(name, code);
3520 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
3521 }
Steve Block6ded16b2010-05-10 14:33:55 +01003522 set_normal_type_cache(new_cache);
3523 return this;
3524}
3525
3526
3527Object* CodeCache::Lookup(String* name, Code::Flags flags) {
3528 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
3529 return LookupNormalTypeCache(name, flags);
3530 } else {
3531 return LookupDefaultCache(name, flags);
3532 }
3533}
3534
3535
3536Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
3537 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003538 int length = cache->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003539 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
3540 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
Steve Blocka7e24c12009-10-30 11:49:00 +00003541 // Skip deleted elements.
3542 if (key->IsNull()) continue;
3543 if (key->IsUndefined()) return key;
3544 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003545 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
3546 if (code->flags() == flags) {
3547 return code;
3548 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003549 }
3550 }
3551 return Heap::undefined_value();
3552}
3553
3554
Steve Block6ded16b2010-05-10 14:33:55 +01003555Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
3556 if (!normal_type_cache()->IsUndefined()) {
3557 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3558 return cache->Lookup(name, flags);
3559 } else {
3560 return Heap::undefined_value();
3561 }
3562}
3563
3564
3565int CodeCache::GetIndex(Object* name, Code* code) {
3566 if (code->type() == NORMAL) {
3567 if (normal_type_cache()->IsUndefined()) return -1;
3568 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3569 return cache->GetIndex(String::cast(name), code->flags());
3570 }
3571
3572 FixedArray* array = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003573 int len = array->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003574 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
3575 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003576 }
3577 return -1;
3578}
3579
3580
Steve Block6ded16b2010-05-10 14:33:55 +01003581void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
3582 if (code->type() == NORMAL) {
3583 ASSERT(!normal_type_cache()->IsUndefined());
3584 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3585 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
3586 cache->RemoveByIndex(index);
3587 } else {
3588 FixedArray* array = default_cache();
3589 ASSERT(array->length() >= index && array->get(index)->IsCode());
3590 // Use null instead of undefined for deleted elements to distinguish
3591 // deleted elements from unused elements. This distinction is used
3592 // when looking up in the cache and when updating the cache.
3593 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
3594 array->set_null(index - 1); // Name.
3595 array->set_null(index); // Code.
3596 }
3597}
3598
3599
3600// The key in the code cache hash table consists of the property name and the
3601// code object. The actual match is on the name and the code flags. If a key
3602// is created using the flags and not a code object it can only be used for
3603// lookup not to create a new entry.
3604class CodeCacheHashTableKey : public HashTableKey {
3605 public:
3606 CodeCacheHashTableKey(String* name, Code::Flags flags)
3607 : name_(name), flags_(flags), code_(NULL) { }
3608
3609 CodeCacheHashTableKey(String* name, Code* code)
3610 : name_(name),
3611 flags_(code->flags()),
3612 code_(code) { }
3613
3614
3615 bool IsMatch(Object* other) {
3616 if (!other->IsFixedArray()) return false;
3617 FixedArray* pair = FixedArray::cast(other);
3618 String* name = String::cast(pair->get(0));
3619 Code::Flags flags = Code::cast(pair->get(1))->flags();
3620 if (flags != flags_) {
3621 return false;
3622 }
3623 return name_->Equals(name);
3624 }
3625
3626 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
3627 return name->Hash() ^ flags;
3628 }
3629
3630 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
3631
3632 uint32_t HashForObject(Object* obj) {
3633 FixedArray* pair = FixedArray::cast(obj);
3634 String* name = String::cast(pair->get(0));
3635 Code* code = Code::cast(pair->get(1));
3636 return NameFlagsHashHelper(name, code->flags());
3637 }
3638
John Reck59135872010-11-02 12:39:01 -07003639 MUST_USE_RESULT MaybeObject* AsObject() {
Steve Block6ded16b2010-05-10 14:33:55 +01003640 ASSERT(code_ != NULL);
John Reck59135872010-11-02 12:39:01 -07003641 Object* obj;
3642 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(2);
3643 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3644 }
Steve Block6ded16b2010-05-10 14:33:55 +01003645 FixedArray* pair = FixedArray::cast(obj);
3646 pair->set(0, name_);
3647 pair->set(1, code_);
3648 return pair;
3649 }
3650
3651 private:
3652 String* name_;
3653 Code::Flags flags_;
3654 Code* code_;
3655};
3656
3657
3658Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
3659 CodeCacheHashTableKey key(name, flags);
3660 int entry = FindEntry(&key);
3661 if (entry == kNotFound) return Heap::undefined_value();
3662 return get(EntryToIndex(entry) + 1);
3663}
3664
3665
John Reck59135872010-11-02 12:39:01 -07003666MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003667 CodeCacheHashTableKey key(name, code);
John Reck59135872010-11-02 12:39:01 -07003668 Object* obj;
3669 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
3670 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3671 }
Steve Block6ded16b2010-05-10 14:33:55 +01003672
3673 // Don't use this, as the table might have grown.
3674 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
3675
3676 int entry = cache->FindInsertionEntry(key.Hash());
John Reck59135872010-11-02 12:39:01 -07003677 Object* k;
3678 { MaybeObject* maybe_k = key.AsObject();
3679 if (!maybe_k->ToObject(&k)) return maybe_k;
3680 }
Steve Block6ded16b2010-05-10 14:33:55 +01003681
3682 cache->set(EntryToIndex(entry), k);
3683 cache->set(EntryToIndex(entry) + 1, code);
3684 cache->ElementAdded();
3685 return cache;
3686}
3687
3688
3689int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
3690 CodeCacheHashTableKey key(name, flags);
3691 int entry = FindEntry(&key);
3692 return (entry == kNotFound) ? -1 : entry;
3693}
3694
3695
3696void CodeCacheHashTable::RemoveByIndex(int index) {
3697 ASSERT(index >= 0);
3698 set(EntryToIndex(index), Heap::null_value());
3699 set(EntryToIndex(index) + 1, Heap::null_value());
3700 ElementRemoved();
Steve Blocka7e24c12009-10-30 11:49:00 +00003701}
3702
3703
Steve Blocka7e24c12009-10-30 11:49:00 +00003704static bool HasKey(FixedArray* array, Object* key) {
3705 int len0 = array->length();
3706 for (int i = 0; i < len0; i++) {
3707 Object* element = array->get(i);
3708 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3709 if (element->IsString() &&
3710 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3711 return true;
3712 }
3713 }
3714 return false;
3715}
3716
3717
John Reck59135872010-11-02 12:39:01 -07003718MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
Steve Block3ce2e202009-11-05 08:53:23 +00003719 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00003720 switch (array->GetElementsKind()) {
3721 case JSObject::FAST_ELEMENTS:
3722 return UnionOfKeys(FixedArray::cast(array->elements()));
3723 case JSObject::DICTIONARY_ELEMENTS: {
3724 NumberDictionary* dict = array->element_dictionary();
3725 int size = dict->NumberOfElements();
3726
3727 // Allocate a temporary fixed array.
John Reck59135872010-11-02 12:39:01 -07003728 Object* object;
3729 { MaybeObject* maybe_object = Heap::AllocateFixedArray(size);
3730 if (!maybe_object->ToObject(&object)) return maybe_object;
3731 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003732 FixedArray* key_array = FixedArray::cast(object);
3733
3734 int capacity = dict->Capacity();
3735 int pos = 0;
3736 // Copy the elements from the JSArray to the temporary fixed array.
3737 for (int i = 0; i < capacity; i++) {
3738 if (dict->IsKey(dict->KeyAt(i))) {
3739 key_array->set(pos++, dict->ValueAt(i));
3740 }
3741 }
3742 // Compute the union of this and the temporary fixed array.
3743 return UnionOfKeys(key_array);
3744 }
3745 default:
3746 UNREACHABLE();
3747 }
3748 UNREACHABLE();
3749 return Heap::null_value(); // Failure case needs to "return" a value.
3750}
3751
3752
John Reck59135872010-11-02 12:39:01 -07003753MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003754 int len0 = length();
Ben Murdochf87a2032010-10-22 12:50:53 +01003755#ifdef DEBUG
3756 if (FLAG_enable_slow_asserts) {
3757 for (int i = 0; i < len0; i++) {
3758 ASSERT(get(i)->IsString() || get(i)->IsNumber());
3759 }
3760 }
3761#endif
Steve Blocka7e24c12009-10-30 11:49:00 +00003762 int len1 = other->length();
Ben Murdochf87a2032010-10-22 12:50:53 +01003763 // Optimize if 'other' is empty.
3764 // We cannot optimize if 'this' is empty, as other may have holes
3765 // or non keys.
Steve Blocka7e24c12009-10-30 11:49:00 +00003766 if (len1 == 0) return this;
3767
3768 // Compute how many elements are not in this.
3769 int extra = 0;
3770 for (int y = 0; y < len1; y++) {
3771 Object* value = other->get(y);
3772 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3773 }
3774
3775 if (extra == 0) return this;
3776
3777 // Allocate the result
John Reck59135872010-11-02 12:39:01 -07003778 Object* obj;
3779 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(len0 + extra);
3780 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3781 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003782 // Fill in the content
Leon Clarke4515c472010-02-03 11:58:03 +00003783 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003784 FixedArray* result = FixedArray::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00003785 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003786 for (int i = 0; i < len0; i++) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003787 Object* e = get(i);
3788 ASSERT(e->IsString() || e->IsNumber());
3789 result->set(i, e, mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00003790 }
3791 // Fill in the extra keys.
3792 int index = 0;
3793 for (int y = 0; y < len1; y++) {
3794 Object* value = other->get(y);
3795 if (!value->IsTheHole() && !HasKey(this, value)) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003796 Object* e = other->get(y);
3797 ASSERT(e->IsString() || e->IsNumber());
3798 result->set(len0 + index, e, mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00003799 index++;
3800 }
3801 }
3802 ASSERT(extra == index);
3803 return result;
3804}
3805
3806
John Reck59135872010-11-02 12:39:01 -07003807MaybeObject* FixedArray::CopySize(int new_length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003808 if (new_length == 0) return Heap::empty_fixed_array();
John Reck59135872010-11-02 12:39:01 -07003809 Object* obj;
3810 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(new_length);
3811 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3812 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003813 FixedArray* result = FixedArray::cast(obj);
3814 // Copy the content
Leon Clarke4515c472010-02-03 11:58:03 +00003815 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003816 int len = length();
3817 if (new_length < len) len = new_length;
3818 result->set_map(map());
Leon Clarke4515c472010-02-03 11:58:03 +00003819 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003820 for (int i = 0; i < len; i++) {
3821 result->set(i, get(i), mode);
3822 }
3823 return result;
3824}
3825
3826
3827void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
Leon Clarke4515c472010-02-03 11:58:03 +00003828 AssertNoAllocation no_gc;
3829 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003830 for (int index = 0; index < len; index++) {
3831 dest->set(dest_pos+index, get(pos+index), mode);
3832 }
3833}
3834
3835
3836#ifdef DEBUG
3837bool FixedArray::IsEqualTo(FixedArray* other) {
3838 if (length() != other->length()) return false;
3839 for (int i = 0 ; i < length(); ++i) {
3840 if (get(i) != other->get(i)) return false;
3841 }
3842 return true;
3843}
3844#endif
3845
3846
John Reck59135872010-11-02 12:39:01 -07003847MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003848 if (number_of_descriptors == 0) {
3849 return Heap::empty_descriptor_array();
3850 }
3851 // Allocate the array of keys.
John Reck59135872010-11-02 12:39:01 -07003852 Object* array;
3853 { MaybeObject* maybe_array =
3854 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
3855 if (!maybe_array->ToObject(&array)) return maybe_array;
3856 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003857 // Do not use DescriptorArray::cast on incomplete object.
3858 FixedArray* result = FixedArray::cast(array);
3859
3860 // Allocate the content array and set it in the descriptor array.
John Reck59135872010-11-02 12:39:01 -07003861 { MaybeObject* maybe_array =
3862 Heap::AllocateFixedArray(number_of_descriptors << 1);
3863 if (!maybe_array->ToObject(&array)) return maybe_array;
3864 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003865 result->set(kContentArrayIndex, array);
3866 result->set(kEnumerationIndexIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00003867 Smi::FromInt(PropertyDetails::kInitialIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +00003868 return result;
3869}
3870
3871
3872void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3873 FixedArray* new_cache) {
3874 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3875 if (HasEnumCache()) {
3876 FixedArray::cast(get(kEnumerationIndexIndex))->
3877 set(kEnumCacheBridgeCacheIndex, new_cache);
3878 } else {
3879 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3880 FixedArray::cast(bridge_storage)->
3881 set(kEnumCacheBridgeCacheIndex, new_cache);
3882 fast_set(FixedArray::cast(bridge_storage),
3883 kEnumCacheBridgeEnumIndex,
3884 get(kEnumerationIndexIndex));
3885 set(kEnumerationIndexIndex, bridge_storage);
3886 }
3887}
3888
3889
John Reck59135872010-11-02 12:39:01 -07003890MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
3891 TransitionFlag transition_flag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003892 // Transitions are only kept when inserting another transition.
3893 // This precondition is not required by this function's implementation, but
3894 // is currently required by the semantics of maps, so we check it.
3895 // Conversely, we filter after replacing, so replacing a transition and
3896 // removing all other transitions is not supported.
3897 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3898 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
3899 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
3900
3901 // Ensure the key is a symbol.
John Reck59135872010-11-02 12:39:01 -07003902 Object* result;
3903 { MaybeObject* maybe_result = descriptor->KeyToSymbol();
3904 if (!maybe_result->ToObject(&result)) return maybe_result;
3905 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003906
3907 int transitions = 0;
3908 int null_descriptors = 0;
3909 if (remove_transitions) {
3910 for (int i = 0; i < number_of_descriptors(); i++) {
3911 if (IsTransition(i)) transitions++;
3912 if (IsNullDescriptor(i)) null_descriptors++;
3913 }
3914 } else {
3915 for (int i = 0; i < number_of_descriptors(); i++) {
3916 if (IsNullDescriptor(i)) null_descriptors++;
3917 }
3918 }
3919 int new_size = number_of_descriptors() - transitions - null_descriptors;
3920
3921 // If key is in descriptor, we replace it in-place when filtering.
3922 // Count a null descriptor for key as inserted, not replaced.
3923 int index = Search(descriptor->GetKey());
3924 const bool inserting = (index == kNotFound);
3925 const bool replacing = !inserting;
3926 bool keep_enumeration_index = false;
3927 if (inserting) {
3928 ++new_size;
3929 }
3930 if (replacing) {
3931 // We are replacing an existing descriptor. We keep the enumeration
3932 // index of a visible property.
3933 PropertyType t = PropertyDetails(GetDetails(index)).type();
3934 if (t == CONSTANT_FUNCTION ||
3935 t == FIELD ||
3936 t == CALLBACKS ||
3937 t == INTERCEPTOR) {
3938 keep_enumeration_index = true;
3939 } else if (remove_transitions) {
3940 // Replaced descriptor has been counted as removed if it is
3941 // a transition that will be replaced. Adjust count in this case.
3942 ++new_size;
3943 }
3944 }
John Reck59135872010-11-02 12:39:01 -07003945 { MaybeObject* maybe_result = Allocate(new_size);
3946 if (!maybe_result->ToObject(&result)) return maybe_result;
3947 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003948 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3949 // Set the enumeration index in the descriptors and set the enumeration index
3950 // in the result.
3951 int enumeration_index = NextEnumerationIndex();
3952 if (!descriptor->GetDetails().IsTransition()) {
3953 if (keep_enumeration_index) {
3954 descriptor->SetEnumerationIndex(
3955 PropertyDetails(GetDetails(index)).index());
3956 } else {
3957 descriptor->SetEnumerationIndex(enumeration_index);
3958 ++enumeration_index;
3959 }
3960 }
3961 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3962
3963 // Copy the descriptors, filtering out transitions and null descriptors,
3964 // and inserting or replacing a descriptor.
3965 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
3966 int from_index = 0;
3967 int to_index = 0;
3968
3969 for (; from_index < number_of_descriptors(); from_index++) {
3970 String* key = GetKey(from_index);
3971 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
3972 break;
3973 }
3974 if (IsNullDescriptor(from_index)) continue;
3975 if (remove_transitions && IsTransition(from_index)) continue;
3976 new_descriptors->CopyFrom(to_index++, this, from_index);
3977 }
3978
3979 new_descriptors->Set(to_index++, descriptor);
3980 if (replacing) from_index++;
3981
3982 for (; from_index < number_of_descriptors(); from_index++) {
3983 if (IsNullDescriptor(from_index)) continue;
3984 if (remove_transitions && IsTransition(from_index)) continue;
3985 new_descriptors->CopyFrom(to_index++, this, from_index);
3986 }
3987
3988 ASSERT(to_index == new_descriptors->number_of_descriptors());
3989 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
3990
3991 return new_descriptors;
3992}
3993
3994
John Reck59135872010-11-02 12:39:01 -07003995MaybeObject* DescriptorArray::RemoveTransitions() {
Steve Blocka7e24c12009-10-30 11:49:00 +00003996 // Remove all transitions and null descriptors. Return a copy of the array
3997 // with all transitions removed, or a Failure object if the new array could
3998 // not be allocated.
3999
4000 // Compute the size of the map transition entries to be removed.
4001 int num_removed = 0;
4002 for (int i = 0; i < number_of_descriptors(); i++) {
4003 if (!IsProperty(i)) num_removed++;
4004 }
4005
4006 // Allocate the new descriptor array.
John Reck59135872010-11-02 12:39:01 -07004007 Object* result;
4008 { MaybeObject* maybe_result = Allocate(number_of_descriptors() - num_removed);
4009 if (!maybe_result->ToObject(&result)) return maybe_result;
4010 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004011 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
4012
4013 // Copy the content.
4014 int next_descriptor = 0;
4015 for (int i = 0; i < number_of_descriptors(); i++) {
4016 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
4017 }
4018 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
4019
4020 return new_descriptors;
4021}
4022
4023
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004024void DescriptorArray::SortUnchecked() {
Steve Blocka7e24c12009-10-30 11:49:00 +00004025 // In-place heap sort.
4026 int len = number_of_descriptors();
4027
4028 // Bottom-up max-heap construction.
Steve Block6ded16b2010-05-10 14:33:55 +01004029 // Index of the last node with children
4030 const int max_parent_index = (len / 2) - 1;
4031 for (int i = max_parent_index; i >= 0; --i) {
4032 int parent_index = i;
4033 const uint32_t parent_hash = GetKey(i)->Hash();
4034 while (parent_index <= max_parent_index) {
4035 int child_index = 2 * parent_index + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00004036 uint32_t child_hash = GetKey(child_index)->Hash();
Steve Block6ded16b2010-05-10 14:33:55 +01004037 if (child_index + 1 < len) {
4038 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4039 if (right_child_hash > child_hash) {
4040 child_index++;
4041 child_hash = right_child_hash;
4042 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004043 }
Steve Block6ded16b2010-05-10 14:33:55 +01004044 if (child_hash <= parent_hash) break;
4045 Swap(parent_index, child_index);
4046 // Now element at child_index could be < its children.
4047 parent_index = child_index; // parent_hash remains correct.
Steve Blocka7e24c12009-10-30 11:49:00 +00004048 }
4049 }
4050
4051 // Extract elements and create sorted array.
4052 for (int i = len - 1; i > 0; --i) {
4053 // Put max element at the back of the array.
4054 Swap(0, i);
4055 // Sift down the new top element.
4056 int parent_index = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01004057 const uint32_t parent_hash = GetKey(parent_index)->Hash();
4058 const int max_parent_index = (i / 2) - 1;
4059 while (parent_index <= max_parent_index) {
4060 int child_index = parent_index * 2 + 1;
4061 uint32_t child_hash = GetKey(child_index)->Hash();
4062 if (child_index + 1 < i) {
4063 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4064 if (right_child_hash > child_hash) {
4065 child_index++;
4066 child_hash = right_child_hash;
4067 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004068 }
Steve Block6ded16b2010-05-10 14:33:55 +01004069 if (child_hash <= parent_hash) break;
4070 Swap(parent_index, child_index);
4071 parent_index = child_index;
Steve Blocka7e24c12009-10-30 11:49:00 +00004072 }
4073 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004074}
Steve Blocka7e24c12009-10-30 11:49:00 +00004075
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004076
4077void DescriptorArray::Sort() {
4078 SortUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00004079 SLOW_ASSERT(IsSortedNoDuplicates());
4080}
4081
4082
4083int DescriptorArray::BinarySearch(String* name, int low, int high) {
4084 uint32_t hash = name->Hash();
4085
4086 while (low <= high) {
4087 int mid = (low + high) / 2;
4088 String* mid_name = GetKey(mid);
4089 uint32_t mid_hash = mid_name->Hash();
4090
4091 if (mid_hash > hash) {
4092 high = mid - 1;
4093 continue;
4094 }
4095 if (mid_hash < hash) {
4096 low = mid + 1;
4097 continue;
4098 }
4099 // Found an element with the same hash-code.
4100 ASSERT(hash == mid_hash);
4101 // There might be more, so we find the first one and
4102 // check them all to see if we have a match.
4103 if (name == mid_name && !is_null_descriptor(mid)) return mid;
4104 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
4105 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
4106 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
4107 }
4108 break;
4109 }
4110 return kNotFound;
4111}
4112
4113
4114int DescriptorArray::LinearSearch(String* name, int len) {
4115 uint32_t hash = name->Hash();
4116 for (int number = 0; number < len; number++) {
4117 String* entry = GetKey(number);
4118 if ((entry->Hash() == hash) &&
4119 name->Equals(entry) &&
4120 !is_null_descriptor(number)) {
4121 return number;
4122 }
4123 }
4124 return kNotFound;
4125}
4126
4127
4128#ifdef DEBUG
4129bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
4130 if (IsEmpty()) return other->IsEmpty();
4131 if (other->IsEmpty()) return false;
4132 if (length() != other->length()) return false;
4133 for (int i = 0; i < length(); ++i) {
4134 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
4135 }
4136 return GetContentArray()->IsEqualTo(other->GetContentArray());
4137}
4138#endif
4139
4140
4141static StaticResource<StringInputBuffer> string_input_buffer;
4142
4143
4144bool String::LooksValid() {
4145 if (!Heap::Contains(this)) return false;
4146 return true;
4147}
4148
4149
4150int String::Utf8Length() {
4151 if (IsAsciiRepresentation()) return length();
4152 // Attempt to flatten before accessing the string. It probably
4153 // doesn't make Utf8Length faster, but it is very likely that
4154 // the string will be accessed later (for example by WriteUtf8)
4155 // so it's still a good idea.
Steve Block6ded16b2010-05-10 14:33:55 +01004156 TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00004157 Access<StringInputBuffer> buffer(&string_input_buffer);
4158 buffer->Reset(0, this);
4159 int result = 0;
4160 while (buffer->has_more())
4161 result += unibrow::Utf8::Length(buffer->GetNext());
4162 return result;
4163}
4164
4165
4166Vector<const char> String::ToAsciiVector() {
4167 ASSERT(IsAsciiRepresentation());
4168 ASSERT(IsFlat());
4169
4170 int offset = 0;
4171 int length = this->length();
4172 StringRepresentationTag string_tag = StringShape(this).representation_tag();
4173 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00004174 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004175 ConsString* cons = ConsString::cast(string);
4176 ASSERT(cons->second()->length() == 0);
4177 string = cons->first();
4178 string_tag = StringShape(string).representation_tag();
4179 }
4180 if (string_tag == kSeqStringTag) {
4181 SeqAsciiString* seq = SeqAsciiString::cast(string);
4182 char* start = seq->GetChars();
4183 return Vector<const char>(start + offset, length);
4184 }
4185 ASSERT(string_tag == kExternalStringTag);
4186 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
4187 const char* start = ext->resource()->data();
4188 return Vector<const char>(start + offset, length);
4189}
4190
4191
4192Vector<const uc16> String::ToUC16Vector() {
4193 ASSERT(IsTwoByteRepresentation());
4194 ASSERT(IsFlat());
4195
4196 int offset = 0;
4197 int length = this->length();
4198 StringRepresentationTag string_tag = StringShape(this).representation_tag();
4199 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00004200 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004201 ConsString* cons = ConsString::cast(string);
4202 ASSERT(cons->second()->length() == 0);
4203 string = cons->first();
4204 string_tag = StringShape(string).representation_tag();
4205 }
4206 if (string_tag == kSeqStringTag) {
4207 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
4208 return Vector<const uc16>(seq->GetChars() + offset, length);
4209 }
4210 ASSERT(string_tag == kExternalStringTag);
4211 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
4212 const uc16* start =
4213 reinterpret_cast<const uc16*>(ext->resource()->data());
4214 return Vector<const uc16>(start + offset, length);
4215}
4216
4217
4218SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
4219 RobustnessFlag robust_flag,
4220 int offset,
4221 int length,
4222 int* length_return) {
4223 ASSERT(NativeAllocationChecker::allocation_allowed());
4224 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
4225 return SmartPointer<char>(NULL);
4226 }
4227
4228 // Negative length means the to the end of the string.
4229 if (length < 0) length = kMaxInt - offset;
4230
4231 // Compute the size of the UTF-8 string. Start at the specified offset.
4232 Access<StringInputBuffer> buffer(&string_input_buffer);
4233 buffer->Reset(offset, this);
4234 int character_position = offset;
4235 int utf8_bytes = 0;
4236 while (buffer->has_more()) {
4237 uint16_t character = buffer->GetNext();
4238 if (character_position < offset + length) {
4239 utf8_bytes += unibrow::Utf8::Length(character);
4240 }
4241 character_position++;
4242 }
4243
4244 if (length_return) {
4245 *length_return = utf8_bytes;
4246 }
4247
4248 char* result = NewArray<char>(utf8_bytes + 1);
4249
4250 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
4251 buffer->Rewind();
4252 buffer->Seek(offset);
4253 character_position = offset;
4254 int utf8_byte_position = 0;
4255 while (buffer->has_more()) {
4256 uint16_t character = buffer->GetNext();
4257 if (character_position < offset + length) {
4258 if (allow_nulls == DISALLOW_NULLS && character == 0) {
4259 character = ' ';
4260 }
4261 utf8_byte_position +=
4262 unibrow::Utf8::Encode(result + utf8_byte_position, character);
4263 }
4264 character_position++;
4265 }
4266 result[utf8_byte_position] = 0;
4267 return SmartPointer<char>(result);
4268}
4269
4270
4271SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
4272 RobustnessFlag robust_flag,
4273 int* length_return) {
4274 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
4275}
4276
4277
4278const uc16* String::GetTwoByteData() {
4279 return GetTwoByteData(0);
4280}
4281
4282
4283const uc16* String::GetTwoByteData(unsigned start) {
4284 ASSERT(!IsAsciiRepresentation());
4285 switch (StringShape(this).representation_tag()) {
4286 case kSeqStringTag:
4287 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
4288 case kExternalStringTag:
4289 return ExternalTwoByteString::cast(this)->
4290 ExternalTwoByteStringGetData(start);
Steve Blocka7e24c12009-10-30 11:49:00 +00004291 case kConsStringTag:
4292 UNREACHABLE();
4293 return NULL;
4294 }
4295 UNREACHABLE();
4296 return NULL;
4297}
4298
4299
4300SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
4301 ASSERT(NativeAllocationChecker::allocation_allowed());
4302
4303 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
4304 return SmartPointer<uc16>();
4305 }
4306
4307 Access<StringInputBuffer> buffer(&string_input_buffer);
4308 buffer->Reset(this);
4309
4310 uc16* result = NewArray<uc16>(length() + 1);
4311
4312 int i = 0;
4313 while (buffer->has_more()) {
4314 uint16_t character = buffer->GetNext();
4315 result[i++] = character;
4316 }
4317 result[i] = 0;
4318 return SmartPointer<uc16>(result);
4319}
4320
4321
4322const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
4323 return reinterpret_cast<uc16*>(
4324 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
4325}
4326
4327
4328void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4329 unsigned* offset_ptr,
4330 unsigned max_chars) {
4331 unsigned chars_read = 0;
4332 unsigned offset = *offset_ptr;
4333 while (chars_read < max_chars) {
4334 uint16_t c = *reinterpret_cast<uint16_t*>(
4335 reinterpret_cast<char*>(this) -
4336 kHeapObjectTag + kHeaderSize + offset * kShortSize);
4337 if (c <= kMaxAsciiCharCode) {
4338 // Fast case for ASCII characters. Cursor is an input output argument.
4339 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4340 rbb->util_buffer,
4341 rbb->capacity,
4342 rbb->cursor)) {
4343 break;
4344 }
4345 } else {
4346 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4347 rbb->util_buffer,
4348 rbb->capacity,
4349 rbb->cursor)) {
4350 break;
4351 }
4352 }
4353 offset++;
4354 chars_read++;
4355 }
4356 *offset_ptr = offset;
4357 rbb->remaining += chars_read;
4358}
4359
4360
4361const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
4362 unsigned* remaining,
4363 unsigned* offset_ptr,
4364 unsigned max_chars) {
4365 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
4366 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
4367 *remaining = max_chars;
4368 *offset_ptr += max_chars;
4369 return b;
4370}
4371
4372
4373// This will iterate unless the block of string data spans two 'halves' of
4374// a ConsString, in which case it will recurse. Since the block of string
4375// data to be read has a maximum size this limits the maximum recursion
4376// depth to something sane. Since C++ does not have tail call recursion
4377// elimination, the iteration must be explicit. Since this is not an
4378// -IntoBuffer method it can delegate to one of the efficient
4379// *AsciiStringReadBlock routines.
4380const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
4381 unsigned* offset_ptr,
4382 unsigned max_chars) {
4383 ConsString* current = this;
4384 unsigned offset = *offset_ptr;
4385 int offset_correction = 0;
4386
4387 while (true) {
4388 String* left = current->first();
4389 unsigned left_length = (unsigned)left->length();
4390 if (left_length > offset &&
4391 (max_chars <= left_length - offset ||
4392 (rbb->capacity <= left_length - offset &&
4393 (max_chars = left_length - offset, true)))) { // comma operator!
4394 // Left hand side only - iterate unless we have reached the bottom of
4395 // the cons tree. The assignment on the left of the comma operator is
4396 // in order to make use of the fact that the -IntoBuffer routines can
4397 // produce at most 'capacity' characters. This enables us to postpone
4398 // the point where we switch to the -IntoBuffer routines (below) in order
4399 // to maximize the chances of delegating a big chunk of work to the
4400 // efficient *AsciiStringReadBlock routines.
4401 if (StringShape(left).IsCons()) {
4402 current = ConsString::cast(left);
4403 continue;
4404 } else {
4405 const unibrow::byte* answer =
4406 String::ReadBlock(left, rbb, &offset, max_chars);
4407 *offset_ptr = offset + offset_correction;
4408 return answer;
4409 }
4410 } else if (left_length <= offset) {
4411 // Right hand side only - iterate unless we have reached the bottom of
4412 // the cons tree.
4413 String* right = current->second();
4414 offset -= left_length;
4415 offset_correction += left_length;
4416 if (StringShape(right).IsCons()) {
4417 current = ConsString::cast(right);
4418 continue;
4419 } else {
4420 const unibrow::byte* answer =
4421 String::ReadBlock(right, rbb, &offset, max_chars);
4422 *offset_ptr = offset + offset_correction;
4423 return answer;
4424 }
4425 } else {
4426 // The block to be read spans two sides of the ConsString, so we call the
4427 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
4428 // are able to assemble data from several part strings because they use
4429 // the util_buffer to store their data and never return direct pointers
4430 // to their storage. We don't try to read more than the buffer capacity
4431 // here or we can get too much recursion.
4432 ASSERT(rbb->remaining == 0);
4433 ASSERT(rbb->cursor == 0);
4434 current->ConsStringReadBlockIntoBuffer(
4435 rbb,
4436 &offset,
4437 max_chars > rbb->capacity ? rbb->capacity : max_chars);
4438 *offset_ptr = offset + offset_correction;
4439 return rbb->util_buffer;
4440 }
4441 }
4442}
4443
4444
Steve Blocka7e24c12009-10-30 11:49:00 +00004445uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
4446 ASSERT(index >= 0 && index < length());
4447 return resource()->data()[index];
4448}
4449
4450
4451const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
4452 unsigned* remaining,
4453 unsigned* offset_ptr,
4454 unsigned max_chars) {
4455 // Cast const char* to unibrow::byte* (signedness difference).
4456 const unibrow::byte* b =
4457 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
4458 *remaining = max_chars;
4459 *offset_ptr += max_chars;
4460 return b;
4461}
4462
4463
4464const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
4465 unsigned start) {
4466 return resource()->data() + start;
4467}
4468
4469
4470uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
4471 ASSERT(index >= 0 && index < length());
4472 return resource()->data()[index];
4473}
4474
4475
4476void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
4477 ReadBlockBuffer* rbb,
4478 unsigned* offset_ptr,
4479 unsigned max_chars) {
4480 unsigned chars_read = 0;
4481 unsigned offset = *offset_ptr;
4482 const uint16_t* data = resource()->data();
4483 while (chars_read < max_chars) {
4484 uint16_t c = data[offset];
4485 if (c <= kMaxAsciiCharCode) {
4486 // Fast case for ASCII characters. Cursor is an input output argument.
4487 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4488 rbb->util_buffer,
4489 rbb->capacity,
4490 rbb->cursor))
4491 break;
4492 } else {
4493 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4494 rbb->util_buffer,
4495 rbb->capacity,
4496 rbb->cursor))
4497 break;
4498 }
4499 offset++;
4500 chars_read++;
4501 }
4502 *offset_ptr = offset;
4503 rbb->remaining += chars_read;
4504}
4505
4506
4507void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4508 unsigned* offset_ptr,
4509 unsigned max_chars) {
4510 unsigned capacity = rbb->capacity - rbb->cursor;
4511 if (max_chars > capacity) max_chars = capacity;
4512 memcpy(rbb->util_buffer + rbb->cursor,
4513 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
4514 *offset_ptr * kCharSize,
4515 max_chars);
4516 rbb->remaining += max_chars;
4517 *offset_ptr += max_chars;
4518 rbb->cursor += max_chars;
4519}
4520
4521
4522void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
4523 ReadBlockBuffer* rbb,
4524 unsigned* offset_ptr,
4525 unsigned max_chars) {
4526 unsigned capacity = rbb->capacity - rbb->cursor;
4527 if (max_chars > capacity) max_chars = capacity;
4528 memcpy(rbb->util_buffer + rbb->cursor,
4529 resource()->data() + *offset_ptr,
4530 max_chars);
4531 rbb->remaining += max_chars;
4532 *offset_ptr += max_chars;
4533 rbb->cursor += max_chars;
4534}
4535
4536
4537// This method determines the type of string involved and then copies
4538// a whole chunk of characters into a buffer, or returns a pointer to a buffer
4539// where they can be found. The pointer is not necessarily valid across a GC
4540// (see AsciiStringReadBlock).
4541const unibrow::byte* String::ReadBlock(String* input,
4542 ReadBlockBuffer* rbb,
4543 unsigned* offset_ptr,
4544 unsigned max_chars) {
4545 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
4546 if (max_chars == 0) {
4547 rbb->remaining = 0;
4548 return NULL;
4549 }
4550 switch (StringShape(input).representation_tag()) {
4551 case kSeqStringTag:
4552 if (input->IsAsciiRepresentation()) {
4553 SeqAsciiString* str = SeqAsciiString::cast(input);
4554 return str->SeqAsciiStringReadBlock(&rbb->remaining,
4555 offset_ptr,
4556 max_chars);
4557 } else {
4558 SeqTwoByteString* str = SeqTwoByteString::cast(input);
4559 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4560 offset_ptr,
4561 max_chars);
4562 return rbb->util_buffer;
4563 }
4564 case kConsStringTag:
4565 return ConsString::cast(input)->ConsStringReadBlock(rbb,
4566 offset_ptr,
4567 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004568 case kExternalStringTag:
4569 if (input->IsAsciiRepresentation()) {
4570 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
4571 &rbb->remaining,
4572 offset_ptr,
4573 max_chars);
4574 } else {
4575 ExternalTwoByteString::cast(input)->
4576 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4577 offset_ptr,
4578 max_chars);
4579 return rbb->util_buffer;
4580 }
4581 default:
4582 break;
4583 }
4584
4585 UNREACHABLE();
4586 return 0;
4587}
4588
4589
4590Relocatable* Relocatable::top_ = NULL;
4591
4592
4593void Relocatable::PostGarbageCollectionProcessing() {
4594 Relocatable* current = top_;
4595 while (current != NULL) {
4596 current->PostGarbageCollection();
4597 current = current->prev_;
4598 }
4599}
4600
4601
4602// Reserve space for statics needing saving and restoring.
4603int Relocatable::ArchiveSpacePerThread() {
4604 return sizeof(top_);
4605}
4606
4607
4608// Archive statics that are thread local.
4609char* Relocatable::ArchiveState(char* to) {
4610 *reinterpret_cast<Relocatable**>(to) = top_;
4611 top_ = NULL;
4612 return to + ArchiveSpacePerThread();
4613}
4614
4615
4616// Restore statics that are thread local.
4617char* Relocatable::RestoreState(char* from) {
4618 top_ = *reinterpret_cast<Relocatable**>(from);
4619 return from + ArchiveSpacePerThread();
4620}
4621
4622
4623char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
4624 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
4625 Iterate(v, top);
4626 return thread_storage + ArchiveSpacePerThread();
4627}
4628
4629
4630void Relocatable::Iterate(ObjectVisitor* v) {
4631 Iterate(v, top_);
4632}
4633
4634
4635void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
4636 Relocatable* current = top;
4637 while (current != NULL) {
4638 current->IterateInstance(v);
4639 current = current->prev_;
4640 }
4641}
4642
4643
4644FlatStringReader::FlatStringReader(Handle<String> str)
4645 : str_(str.location()),
4646 length_(str->length()) {
4647 PostGarbageCollection();
4648}
4649
4650
4651FlatStringReader::FlatStringReader(Vector<const char> input)
4652 : str_(0),
4653 is_ascii_(true),
4654 length_(input.length()),
4655 start_(input.start()) { }
4656
4657
4658void FlatStringReader::PostGarbageCollection() {
4659 if (str_ == NULL) return;
4660 Handle<String> str(str_);
4661 ASSERT(str->IsFlat());
4662 is_ascii_ = str->IsAsciiRepresentation();
4663 if (is_ascii_) {
4664 start_ = str->ToAsciiVector().start();
4665 } else {
4666 start_ = str->ToUC16Vector().start();
4667 }
4668}
4669
4670
4671void StringInputBuffer::Seek(unsigned pos) {
4672 Reset(pos, input_);
4673}
4674
4675
4676void SafeStringInputBuffer::Seek(unsigned pos) {
4677 Reset(pos, input_);
4678}
4679
4680
4681// This method determines the type of string involved and then copies
4682// a whole chunk of characters into a buffer. It can be used with strings
4683// that have been glued together to form a ConsString and which must cooperate
4684// to fill up a buffer.
4685void String::ReadBlockIntoBuffer(String* input,
4686 ReadBlockBuffer* rbb,
4687 unsigned* offset_ptr,
4688 unsigned max_chars) {
4689 ASSERT(*offset_ptr <= (unsigned)input->length());
4690 if (max_chars == 0) return;
4691
4692 switch (StringShape(input).representation_tag()) {
4693 case kSeqStringTag:
4694 if (input->IsAsciiRepresentation()) {
4695 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4696 offset_ptr,
4697 max_chars);
4698 return;
4699 } else {
4700 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4701 offset_ptr,
4702 max_chars);
4703 return;
4704 }
4705 case kConsStringTag:
4706 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4707 offset_ptr,
4708 max_chars);
4709 return;
Steve Blocka7e24c12009-10-30 11:49:00 +00004710 case kExternalStringTag:
4711 if (input->IsAsciiRepresentation()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004712 ExternalAsciiString::cast(input)->
4713 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4714 } else {
4715 ExternalTwoByteString::cast(input)->
4716 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4717 offset_ptr,
4718 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004719 }
4720 return;
4721 default:
4722 break;
4723 }
4724
4725 UNREACHABLE();
4726 return;
4727}
4728
4729
4730const unibrow::byte* String::ReadBlock(String* input,
4731 unibrow::byte* util_buffer,
4732 unsigned capacity,
4733 unsigned* remaining,
4734 unsigned* offset_ptr) {
4735 ASSERT(*offset_ptr <= (unsigned)input->length());
4736 unsigned chars = input->length() - *offset_ptr;
4737 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4738 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4739 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4740 *remaining = rbb.remaining;
4741 return answer;
4742}
4743
4744
4745const unibrow::byte* String::ReadBlock(String** raw_input,
4746 unibrow::byte* util_buffer,
4747 unsigned capacity,
4748 unsigned* remaining,
4749 unsigned* offset_ptr) {
4750 Handle<String> input(raw_input);
4751 ASSERT(*offset_ptr <= (unsigned)input->length());
4752 unsigned chars = input->length() - *offset_ptr;
4753 if (chars > capacity) chars = capacity;
4754 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4755 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4756 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4757 *remaining = rbb.remaining;
4758 return rbb.util_buffer;
4759}
4760
4761
4762// This will iterate unless the block of string data spans two 'halves' of
4763// a ConsString, in which case it will recurse. Since the block of string
4764// data to be read has a maximum size this limits the maximum recursion
4765// depth to something sane. Since C++ does not have tail call recursion
4766// elimination, the iteration must be explicit.
4767void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4768 unsigned* offset_ptr,
4769 unsigned max_chars) {
4770 ConsString* current = this;
4771 unsigned offset = *offset_ptr;
4772 int offset_correction = 0;
4773
4774 while (true) {
4775 String* left = current->first();
4776 unsigned left_length = (unsigned)left->length();
4777 if (left_length > offset &&
4778 max_chars <= left_length - offset) {
4779 // Left hand side only - iterate unless we have reached the bottom of
4780 // the cons tree.
4781 if (StringShape(left).IsCons()) {
4782 current = ConsString::cast(left);
4783 continue;
4784 } else {
4785 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4786 *offset_ptr = offset + offset_correction;
4787 return;
4788 }
4789 } else if (left_length <= offset) {
4790 // Right hand side only - iterate unless we have reached the bottom of
4791 // the cons tree.
4792 offset -= left_length;
4793 offset_correction += left_length;
4794 String* right = current->second();
4795 if (StringShape(right).IsCons()) {
4796 current = ConsString::cast(right);
4797 continue;
4798 } else {
4799 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4800 *offset_ptr = offset + offset_correction;
4801 return;
4802 }
4803 } else {
4804 // The block to be read spans two sides of the ConsString, so we recurse.
4805 // First recurse on the left.
4806 max_chars -= left_length - offset;
4807 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4808 // We may have reached the max or there may not have been enough space
4809 // in the buffer for the characters in the left hand side.
4810 if (offset == left_length) {
4811 // Recurse on the right.
4812 String* right = String::cast(current->second());
4813 offset -= left_length;
4814 offset_correction += left_length;
4815 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4816 }
4817 *offset_ptr = offset + offset_correction;
4818 return;
4819 }
4820 }
4821}
4822
4823
Steve Blocka7e24c12009-10-30 11:49:00 +00004824uint16_t ConsString::ConsStringGet(int index) {
4825 ASSERT(index >= 0 && index < this->length());
4826
4827 // Check for a flattened cons string
4828 if (second()->length() == 0) {
4829 String* left = first();
4830 return left->Get(index);
4831 }
4832
4833 String* string = String::cast(this);
4834
4835 while (true) {
4836 if (StringShape(string).IsCons()) {
4837 ConsString* cons_string = ConsString::cast(string);
4838 String* left = cons_string->first();
4839 if (left->length() > index) {
4840 string = left;
4841 } else {
4842 index -= left->length();
4843 string = cons_string->second();
4844 }
4845 } else {
4846 return string->Get(index);
4847 }
4848 }
4849
4850 UNREACHABLE();
4851 return 0;
4852}
4853
4854
4855template <typename sinkchar>
4856void String::WriteToFlat(String* src,
4857 sinkchar* sink,
4858 int f,
4859 int t) {
4860 String* source = src;
4861 int from = f;
4862 int to = t;
4863 while (true) {
4864 ASSERT(0 <= from && from <= to && to <= source->length());
4865 switch (StringShape(source).full_representation_tag()) {
4866 case kAsciiStringTag | kExternalStringTag: {
4867 CopyChars(sink,
4868 ExternalAsciiString::cast(source)->resource()->data() + from,
4869 to - from);
4870 return;
4871 }
4872 case kTwoByteStringTag | kExternalStringTag: {
4873 const uc16* data =
4874 ExternalTwoByteString::cast(source)->resource()->data();
4875 CopyChars(sink,
4876 data + from,
4877 to - from);
4878 return;
4879 }
4880 case kAsciiStringTag | kSeqStringTag: {
4881 CopyChars(sink,
4882 SeqAsciiString::cast(source)->GetChars() + from,
4883 to - from);
4884 return;
4885 }
4886 case kTwoByteStringTag | kSeqStringTag: {
4887 CopyChars(sink,
4888 SeqTwoByteString::cast(source)->GetChars() + from,
4889 to - from);
4890 return;
4891 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004892 case kAsciiStringTag | kConsStringTag:
4893 case kTwoByteStringTag | kConsStringTag: {
4894 ConsString* cons_string = ConsString::cast(source);
4895 String* first = cons_string->first();
4896 int boundary = first->length();
4897 if (to - boundary >= boundary - from) {
4898 // Right hand side is longer. Recurse over left.
4899 if (from < boundary) {
4900 WriteToFlat(first, sink, from, boundary);
4901 sink += boundary - from;
4902 from = 0;
4903 } else {
4904 from -= boundary;
4905 }
4906 to -= boundary;
4907 source = cons_string->second();
4908 } else {
4909 // Left hand side is longer. Recurse over right.
4910 if (to > boundary) {
4911 String* second = cons_string->second();
4912 WriteToFlat(second,
4913 sink + boundary - from,
4914 0,
4915 to - boundary);
4916 to = boundary;
4917 }
4918 source = first;
4919 }
4920 break;
4921 }
4922 }
4923 }
4924}
4925
4926
Steve Blocka7e24c12009-10-30 11:49:00 +00004927template <typename IteratorA, typename IteratorB>
4928static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4929 // General slow case check. We know that the ia and ib iterators
4930 // have the same length.
4931 while (ia->has_more()) {
4932 uc32 ca = ia->GetNext();
4933 uc32 cb = ib->GetNext();
4934 if (ca != cb)
4935 return false;
4936 }
4937 return true;
4938}
4939
4940
4941// Compares the contents of two strings by reading and comparing
4942// int-sized blocks of characters.
4943template <typename Char>
4944static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4945 int length = a.length();
4946 ASSERT_EQ(length, b.length());
4947 const Char* pa = a.start();
4948 const Char* pb = b.start();
4949 int i = 0;
4950#ifndef V8_HOST_CAN_READ_UNALIGNED
4951 // If this architecture isn't comfortable reading unaligned ints
4952 // then we have to check that the strings are aligned before
4953 // comparing them blockwise.
4954 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4955 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4956 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
4957 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
4958#endif
4959 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
4960 int endpoint = length - kStepSize;
4961 // Compare blocks until we reach near the end of the string.
4962 for (; i <= endpoint; i += kStepSize) {
4963 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
4964 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
4965 if (wa != wb) {
4966 return false;
4967 }
4968 }
4969#ifndef V8_HOST_CAN_READ_UNALIGNED
4970 }
4971#endif
4972 // Compare the remaining characters that didn't fit into a block.
4973 for (; i < length; i++) {
4974 if (a[i] != b[i]) {
4975 return false;
4976 }
4977 }
4978 return true;
4979}
4980
4981
4982static StringInputBuffer string_compare_buffer_b;
4983
4984
4985template <typename IteratorA>
4986static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
4987 if (b->IsFlat()) {
4988 if (b->IsAsciiRepresentation()) {
4989 VectorIterator<char> ib(b->ToAsciiVector());
4990 return CompareStringContents(ia, &ib);
4991 } else {
4992 VectorIterator<uc16> ib(b->ToUC16Vector());
4993 return CompareStringContents(ia, &ib);
4994 }
4995 } else {
4996 string_compare_buffer_b.Reset(0, b);
4997 return CompareStringContents(ia, &string_compare_buffer_b);
4998 }
4999}
5000
5001
5002static StringInputBuffer string_compare_buffer_a;
5003
5004
5005bool String::SlowEquals(String* other) {
5006 // Fast check: negative check with lengths.
5007 int len = length();
5008 if (len != other->length()) return false;
5009 if (len == 0) return true;
5010
5011 // Fast check: if hash code is computed for both strings
5012 // a fast negative check can be performed.
5013 if (HasHashCode() && other->HasHashCode()) {
5014 if (Hash() != other->Hash()) return false;
5015 }
5016
Leon Clarkef7060e22010-06-03 12:02:55 +01005017 // We know the strings are both non-empty. Compare the first chars
5018 // before we try to flatten the strings.
5019 if (this->Get(0) != other->Get(0)) return false;
5020
5021 String* lhs = this->TryFlattenGetString();
5022 String* rhs = other->TryFlattenGetString();
5023
5024 if (StringShape(lhs).IsSequentialAscii() &&
5025 StringShape(rhs).IsSequentialAscii()) {
5026 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
5027 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
Steve Blocka7e24c12009-10-30 11:49:00 +00005028 return CompareRawStringContents(Vector<const char>(str1, len),
5029 Vector<const char>(str2, len));
5030 }
5031
Leon Clarkef7060e22010-06-03 12:02:55 +01005032 if (lhs->IsFlat()) {
Ben Murdochbb769b22010-08-11 14:56:33 +01005033 if (lhs->IsAsciiRepresentation()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01005034 Vector<const char> vec1 = lhs->ToAsciiVector();
5035 if (rhs->IsFlat()) {
5036 if (rhs->IsAsciiRepresentation()) {
5037 Vector<const char> vec2 = rhs->ToAsciiVector();
Steve Blocka7e24c12009-10-30 11:49:00 +00005038 return CompareRawStringContents(vec1, vec2);
5039 } else {
5040 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005041 VectorIterator<uc16> ib(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005042 return CompareStringContents(&buf1, &ib);
5043 }
5044 } else {
5045 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005046 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005047 return CompareStringContents(&buf1, &string_compare_buffer_b);
5048 }
5049 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005050 Vector<const uc16> vec1 = lhs->ToUC16Vector();
5051 if (rhs->IsFlat()) {
5052 if (rhs->IsAsciiRepresentation()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005053 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005054 VectorIterator<char> ib(rhs->ToAsciiVector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005055 return CompareStringContents(&buf1, &ib);
5056 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005057 Vector<const uc16> vec2(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005058 return CompareRawStringContents(vec1, vec2);
5059 }
5060 } else {
5061 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005062 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005063 return CompareStringContents(&buf1, &string_compare_buffer_b);
5064 }
5065 }
5066 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005067 string_compare_buffer_a.Reset(0, lhs);
5068 return CompareStringContentsPartial(&string_compare_buffer_a, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005069 }
5070}
5071
5072
5073bool String::MarkAsUndetectable() {
5074 if (StringShape(this).IsSymbol()) return false;
5075
5076 Map* map = this->map();
Steve Blockd0582a62009-12-15 09:54:21 +00005077 if (map == Heap::string_map()) {
5078 this->set_map(Heap::undetectable_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00005079 return true;
Steve Blockd0582a62009-12-15 09:54:21 +00005080 } else if (map == Heap::ascii_string_map()) {
5081 this->set_map(Heap::undetectable_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00005082 return true;
5083 }
5084 // Rest cannot be marked as undetectable
5085 return false;
5086}
5087
5088
5089bool String::IsEqualTo(Vector<const char> str) {
5090 int slen = length();
5091 Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
5092 decoder->Reset(str.start(), str.length());
5093 int i;
5094 for (i = 0; i < slen && decoder->has_more(); i++) {
5095 uc32 r = decoder->GetNext();
5096 if (Get(i) != r) return false;
5097 }
5098 return i == slen && !decoder->has_more();
5099}
5100
5101
Steve Block6ded16b2010-05-10 14:33:55 +01005102template <typename schar>
5103static inline uint32_t HashSequentialString(const schar* chars, int length) {
5104 StringHasher hasher(length);
5105 if (!hasher.has_trivial_hash()) {
5106 int i;
5107 for (i = 0; hasher.is_array_index() && (i < length); i++) {
5108 hasher.AddCharacter(chars[i]);
5109 }
5110 for (; i < length; i++) {
5111 hasher.AddCharacterNoIndex(chars[i]);
5112 }
5113 }
5114 return hasher.GetHashField();
5115}
5116
5117
Steve Blocka7e24c12009-10-30 11:49:00 +00005118uint32_t String::ComputeAndSetHash() {
5119 // Should only be called if hash code has not yet been computed.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005120 ASSERT(!HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00005121
Steve Block6ded16b2010-05-10 14:33:55 +01005122 const int len = length();
5123
Steve Blocka7e24c12009-10-30 11:49:00 +00005124 // Compute the hash code.
Steve Block6ded16b2010-05-10 14:33:55 +01005125 uint32_t field = 0;
5126 if (StringShape(this).IsSequentialAscii()) {
5127 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
5128 } else if (StringShape(this).IsSequentialTwoByte()) {
5129 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
5130 } else {
5131 StringInputBuffer buffer(this);
5132 field = ComputeHashField(&buffer, len);
5133 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005134
5135 // Store the hash code in the object.
Steve Blockd0582a62009-12-15 09:54:21 +00005136 set_hash_field(field);
Steve Blocka7e24c12009-10-30 11:49:00 +00005137
5138 // Check the hash code is there.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005139 ASSERT(HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00005140 uint32_t result = field >> kHashShift;
5141 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
5142 return result;
5143}
5144
5145
5146bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
5147 uint32_t* index,
5148 int length) {
5149 if (length == 0 || length > kMaxArrayIndexSize) return false;
5150 uc32 ch = buffer->GetNext();
5151
5152 // If the string begins with a '0' character, it must only consist
5153 // of it to be a legal array index.
5154 if (ch == '0') {
5155 *index = 0;
5156 return length == 1;
5157 }
5158
5159 // Convert string to uint32 array index; character by character.
5160 int d = ch - '0';
5161 if (d < 0 || d > 9) return false;
5162 uint32_t result = d;
5163 while (buffer->has_more()) {
5164 d = buffer->GetNext() - '0';
5165 if (d < 0 || d > 9) return false;
5166 // Check that the new result is below the 32 bit limit.
5167 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
5168 result = (result * 10) + d;
5169 }
5170
5171 *index = result;
5172 return true;
5173}
5174
5175
5176bool String::SlowAsArrayIndex(uint32_t* index) {
5177 if (length() <= kMaxCachedArrayIndexLength) {
5178 Hash(); // force computation of hash code
Steve Blockd0582a62009-12-15 09:54:21 +00005179 uint32_t field = hash_field();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005180 if ((field & kIsNotArrayIndexMask) != 0) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00005181 // Isolate the array index form the full hash field.
5182 *index = (kArrayIndexHashMask & field) >> kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00005183 return true;
5184 } else {
5185 StringInputBuffer buffer(this);
5186 return ComputeArrayIndex(&buffer, index, length());
5187 }
5188}
5189
5190
Iain Merrick9ac36c92010-09-13 15:29:50 +01005191uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005192 // For array indexes mix the length into the hash as an array index could
5193 // be zero.
5194 ASSERT(length > 0);
5195 ASSERT(length <= String::kMaxArrayIndexSize);
5196 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
5197 (1 << String::kArrayIndexValueBits));
Iain Merrick9ac36c92010-09-13 15:29:50 +01005198
5199 value <<= String::kHashShift;
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005200 value |= length << String::kArrayIndexHashLengthShift;
Iain Merrick9ac36c92010-09-13 15:29:50 +01005201
5202 ASSERT((value & String::kIsNotArrayIndexMask) == 0);
5203 ASSERT((length > String::kMaxCachedArrayIndexLength) ||
5204 (value & String::kContainsCachedArrayIndexMask) == 0);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005205 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00005206}
5207
5208
5209uint32_t StringHasher::GetHashField() {
5210 ASSERT(is_valid());
Steve Blockd0582a62009-12-15 09:54:21 +00005211 if (length_ <= String::kMaxHashCalcLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005212 if (is_array_index()) {
Iain Merrick9ac36c92010-09-13 15:29:50 +01005213 return MakeArrayIndexHash(array_index(), length_);
Steve Blocka7e24c12009-10-30 11:49:00 +00005214 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005215 return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask;
Steve Blocka7e24c12009-10-30 11:49:00 +00005216 } else {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005217 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
Steve Blocka7e24c12009-10-30 11:49:00 +00005218 }
5219}
5220
5221
Steve Blockd0582a62009-12-15 09:54:21 +00005222uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
5223 int length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005224 StringHasher hasher(length);
5225
5226 // Very long strings have a trivial hash that doesn't inspect the
5227 // string contents.
5228 if (hasher.has_trivial_hash()) {
5229 return hasher.GetHashField();
5230 }
5231
5232 // Do the iterative array index computation as long as there is a
5233 // chance this is an array index.
5234 while (buffer->has_more() && hasher.is_array_index()) {
5235 hasher.AddCharacter(buffer->GetNext());
5236 }
5237
5238 // Process the remaining characters without updating the array
5239 // index.
5240 while (buffer->has_more()) {
5241 hasher.AddCharacterNoIndex(buffer->GetNext());
5242 }
5243
5244 return hasher.GetHashField();
5245}
5246
5247
John Reck59135872010-11-02 12:39:01 -07005248MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005249 if (start == 0 && end == length()) return this;
John Reck59135872010-11-02 12:39:01 -07005250 MaybeObject* result = Heap::AllocateSubString(this, start, end, pretenure);
Steve Blockd0582a62009-12-15 09:54:21 +00005251 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00005252}
5253
5254
5255void String::PrintOn(FILE* file) {
5256 int length = this->length();
5257 for (int i = 0; i < length; i++) {
5258 fprintf(file, "%c", Get(i));
5259 }
5260}
5261
5262
5263void Map::CreateBackPointers() {
5264 DescriptorArray* descriptors = instance_descriptors();
5265 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
Iain Merrick75681382010-08-19 15:07:18 +01005266 if (descriptors->GetType(i) == MAP_TRANSITION ||
5267 descriptors->GetType(i) == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005268 // Get target.
5269 Map* target = Map::cast(descriptors->GetValue(i));
5270#ifdef DEBUG
5271 // Verify target.
5272 Object* source_prototype = prototype();
5273 Object* target_prototype = target->prototype();
5274 ASSERT(source_prototype->IsJSObject() ||
5275 source_prototype->IsMap() ||
5276 source_prototype->IsNull());
5277 ASSERT(target_prototype->IsJSObject() ||
5278 target_prototype->IsNull());
5279 ASSERT(source_prototype->IsMap() ||
5280 source_prototype == target_prototype);
5281#endif
5282 // Point target back to source. set_prototype() will not let us set
5283 // the prototype to a map, as we do here.
5284 *RawField(target, kPrototypeOffset) = this;
5285 }
5286 }
5287}
5288
5289
5290void Map::ClearNonLiveTransitions(Object* real_prototype) {
5291 // Live DescriptorArray objects will be marked, so we must use
5292 // low-level accessors to get and modify their data.
5293 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
5294 *RawField(this, Map::kInstanceDescriptorsOffset));
5295 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
5296 Smi* NullDescriptorDetails =
5297 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
5298 FixedArray* contents = reinterpret_cast<FixedArray*>(
5299 d->get(DescriptorArray::kContentArrayIndex));
5300 ASSERT(contents->length() >= 2);
5301 for (int i = 0; i < contents->length(); i += 2) {
5302 // If the pair (value, details) is a map transition,
5303 // check if the target is live. If not, null the descriptor.
5304 // Also drop the back pointer for that map transition, so that this
5305 // map is not reached again by following a back pointer from a
5306 // non-live object.
5307 PropertyDetails details(Smi::cast(contents->get(i + 1)));
Iain Merrick75681382010-08-19 15:07:18 +01005308 if (details.type() == MAP_TRANSITION ||
5309 details.type() == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005310 Map* target = reinterpret_cast<Map*>(contents->get(i));
5311 ASSERT(target->IsHeapObject());
5312 if (!target->IsMarked()) {
5313 ASSERT(target->IsMap());
Iain Merrick75681382010-08-19 15:07:18 +01005314 contents->set_unchecked(i + 1, NullDescriptorDetails);
5315 contents->set_null_unchecked(i);
Steve Blocka7e24c12009-10-30 11:49:00 +00005316 ASSERT(target->prototype() == this ||
5317 target->prototype() == real_prototype);
5318 // Getter prototype() is read-only, set_prototype() has side effects.
5319 *RawField(target, Map::kPrototypeOffset) = real_prototype;
5320 }
5321 }
5322 }
5323}
5324
5325
Steve Block791712a2010-08-27 10:21:07 +01005326void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
5327 // Iterate over all fields in the body but take care in dealing with
5328 // the code entry.
5329 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
5330 v->VisitCodeEntry(this->address() + kCodeEntryOffset);
5331 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
5332}
5333
5334
Steve Blocka7e24c12009-10-30 11:49:00 +00005335Object* JSFunction::SetInstancePrototype(Object* value) {
5336 ASSERT(value->IsJSObject());
5337
5338 if (has_initial_map()) {
5339 initial_map()->set_prototype(value);
5340 } else {
5341 // Put the value in the initial map field until an initial map is
5342 // needed. At that point, a new initial map is created and the
5343 // prototype is put into the initial map where it belongs.
5344 set_prototype_or_initial_map(value);
5345 }
Kristian Monsen25f61362010-05-21 11:50:48 +01005346 Heap::ClearInstanceofCache();
Steve Blocka7e24c12009-10-30 11:49:00 +00005347 return value;
5348}
5349
5350
John Reck59135872010-11-02 12:39:01 -07005351MaybeObject* JSFunction::SetPrototype(Object* value) {
Steve Block6ded16b2010-05-10 14:33:55 +01005352 ASSERT(should_have_prototype());
Steve Blocka7e24c12009-10-30 11:49:00 +00005353 Object* construct_prototype = value;
5354
5355 // If the value is not a JSObject, store the value in the map's
5356 // constructor field so it can be accessed. Also, set the prototype
5357 // used for constructing objects to the original object prototype.
5358 // See ECMA-262 13.2.2.
5359 if (!value->IsJSObject()) {
5360 // Copy the map so this does not affect unrelated functions.
5361 // Remove map transitions because they point to maps with a
5362 // different prototype.
John Reck59135872010-11-02 12:39:01 -07005363 Object* new_map;
5364 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
5365 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
5366 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005367 set_map(Map::cast(new_map));
5368 map()->set_constructor(value);
5369 map()->set_non_instance_prototype(true);
5370 construct_prototype =
5371 Top::context()->global_context()->initial_object_prototype();
5372 } else {
5373 map()->set_non_instance_prototype(false);
5374 }
5375
5376 return SetInstancePrototype(construct_prototype);
5377}
5378
5379
Steve Block6ded16b2010-05-10 14:33:55 +01005380Object* JSFunction::RemovePrototype() {
5381 ASSERT(map() == context()->global_context()->function_map());
5382 set_map(context()->global_context()->function_without_prototype_map());
5383 set_prototype_or_initial_map(Heap::the_hole_value());
5384 return this;
5385}
5386
5387
Steve Blocka7e24c12009-10-30 11:49:00 +00005388Object* JSFunction::SetInstanceClassName(String* name) {
5389 shared()->set_instance_class_name(name);
5390 return this;
5391}
5392
5393
5394Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
5395 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
5396}
5397
5398
John Reck59135872010-11-02 12:39:01 -07005399MaybeObject* Oddball::Initialize(const char* to_string, Object* to_number) {
5400 Object* symbol;
5401 { MaybeObject* maybe_symbol = Heap::LookupAsciiSymbol(to_string);
5402 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
5403 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005404 set_to_string(String::cast(symbol));
5405 set_to_number(to_number);
5406 return this;
5407}
5408
5409
Ben Murdochf87a2032010-10-22 12:50:53 +01005410String* SharedFunctionInfo::DebugName() {
5411 Object* n = name();
5412 if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
5413 return String::cast(n);
5414}
5415
5416
Steve Blocka7e24c12009-10-30 11:49:00 +00005417bool SharedFunctionInfo::HasSourceCode() {
5418 return !script()->IsUndefined() &&
Iain Merrick75681382010-08-19 15:07:18 +01005419 !reinterpret_cast<Script*>(script())->source()->IsUndefined();
Steve Blocka7e24c12009-10-30 11:49:00 +00005420}
5421
5422
5423Object* SharedFunctionInfo::GetSourceCode() {
5424 HandleScope scope;
5425 if (script()->IsUndefined()) return Heap::undefined_value();
5426 Object* source = Script::cast(script())->source();
5427 if (source->IsUndefined()) return Heap::undefined_value();
5428 return *SubString(Handle<String>(String::cast(source)),
5429 start_position(), end_position());
5430}
5431
5432
5433int SharedFunctionInfo::CalculateInstanceSize() {
5434 int instance_size =
5435 JSObject::kHeaderSize +
5436 expected_nof_properties() * kPointerSize;
5437 if (instance_size > JSObject::kMaxInstanceSize) {
5438 instance_size = JSObject::kMaxInstanceSize;
5439 }
5440 return instance_size;
5441}
5442
5443
5444int SharedFunctionInfo::CalculateInObjectProperties() {
5445 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
5446}
5447
5448
Andrei Popescu402d9372010-02-26 13:31:12 +00005449bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
5450 // Check the basic conditions for generating inline constructor code.
5451 if (!FLAG_inline_new
5452 || !has_only_simple_this_property_assignments()
5453 || this_property_assignments_count() == 0) {
5454 return false;
5455 }
5456
5457 // If the prototype is null inline constructors cause no problems.
5458 if (!prototype->IsJSObject()) {
5459 ASSERT(prototype->IsNull());
5460 return true;
5461 }
5462
5463 // Traverse the proposed prototype chain looking for setters for properties of
5464 // the same names as are set by the inline constructor.
5465 for (Object* obj = prototype;
5466 obj != Heap::null_value();
5467 obj = obj->GetPrototype()) {
5468 JSObject* js_object = JSObject::cast(obj);
5469 for (int i = 0; i < this_property_assignments_count(); i++) {
5470 LookupResult result;
5471 String* name = GetThisPropertyAssignmentName(i);
5472 js_object->LocalLookupRealNamedProperty(name, &result);
5473 if (result.IsProperty() && result.type() == CALLBACKS) {
5474 return false;
5475 }
5476 }
5477 }
5478
5479 return true;
5480}
5481
5482
Kristian Monsen0d5e1162010-09-30 15:31:59 +01005483void SharedFunctionInfo::ForbidInlineConstructor() {
5484 set_compiler_hints(BooleanBit::set(compiler_hints(),
5485 kHasOnlySimpleThisPropertyAssignments,
5486 false));
5487}
5488
5489
Steve Blocka7e24c12009-10-30 11:49:00 +00005490void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +00005491 bool only_simple_this_property_assignments,
5492 FixedArray* assignments) {
5493 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005494 kHasOnlySimpleThisPropertyAssignments,
5495 only_simple_this_property_assignments));
5496 set_this_property_assignments(assignments);
5497 set_this_property_assignments_count(assignments->length() / 3);
5498}
5499
5500
5501void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
5502 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005503 kHasOnlySimpleThisPropertyAssignments,
5504 false));
5505 set_this_property_assignments(Heap::undefined_value());
5506 set_this_property_assignments_count(0);
5507}
5508
5509
5510String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
5511 Object* obj = this_property_assignments();
5512 ASSERT(obj->IsFixedArray());
5513 ASSERT(index < this_property_assignments_count());
5514 obj = FixedArray::cast(obj)->get(index * 3);
5515 ASSERT(obj->IsString());
5516 return String::cast(obj);
5517}
5518
5519
5520bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
5521 Object* obj = this_property_assignments();
5522 ASSERT(obj->IsFixedArray());
5523 ASSERT(index < this_property_assignments_count());
5524 obj = FixedArray::cast(obj)->get(index * 3 + 1);
5525 return Smi::cast(obj)->value() != -1;
5526}
5527
5528
5529int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
5530 ASSERT(IsThisPropertyAssignmentArgument(index));
5531 Object* obj =
5532 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
5533 return Smi::cast(obj)->value();
5534}
5535
5536
5537Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
5538 ASSERT(!IsThisPropertyAssignmentArgument(index));
5539 Object* obj =
5540 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
5541 return obj;
5542}
5543
5544
Steve Blocka7e24c12009-10-30 11:49:00 +00005545// Support function for printing the source code to a StringStream
5546// without any allocation in the heap.
5547void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
5548 int max_length) {
5549 // For some native functions there is no source.
5550 if (script()->IsUndefined() ||
5551 Script::cast(script())->source()->IsUndefined()) {
5552 accumulator->Add("<No Source>");
5553 return;
5554 }
5555
Steve Blockd0582a62009-12-15 09:54:21 +00005556 // Get the source for the script which this function came from.
Steve Blocka7e24c12009-10-30 11:49:00 +00005557 // Don't use String::cast because we don't want more assertion errors while
5558 // we are already creating a stack dump.
5559 String* script_source =
5560 reinterpret_cast<String*>(Script::cast(script())->source());
5561
5562 if (!script_source->LooksValid()) {
5563 accumulator->Add("<Invalid Source>");
5564 return;
5565 }
5566
5567 if (!is_toplevel()) {
5568 accumulator->Add("function ");
5569 Object* name = this->name();
5570 if (name->IsString() && String::cast(name)->length() > 0) {
5571 accumulator->PrintName(name);
5572 }
5573 }
5574
5575 int len = end_position() - start_position();
5576 if (len > max_length) {
5577 accumulator->Put(script_source,
5578 start_position(),
5579 start_position() + max_length);
5580 accumulator->Add("...\n");
5581 } else {
5582 accumulator->Put(script_source, start_position(), end_position());
5583 }
5584}
5585
5586
Kristian Monsen0d5e1162010-09-30 15:31:59 +01005587void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) {
5588 ASSERT(!IsInobjectSlackTrackingInProgress());
5589
5590 // Only initiate the tracking the first time.
5591 if (live_objects_may_exist()) return;
5592 set_live_objects_may_exist(true);
5593
5594 // No tracking during the snapshot construction phase.
5595 if (Serializer::enabled()) return;
5596
5597 if (map->unused_property_fields() == 0) return;
5598
5599 // Nonzero counter is a leftover from the previous attempt interrupted
5600 // by GC, keep it.
5601 if (construction_count() == 0) {
5602 set_construction_count(kGenerousAllocationCount);
5603 }
5604 set_initial_map(map);
5605 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubGeneric),
5606 construct_stub());
5607 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubCountdown));
5608}
5609
5610
5611// Called from GC, hence reinterpret_cast and unchecked accessors.
5612void SharedFunctionInfo::DetachInitialMap() {
5613 Map* map = reinterpret_cast<Map*>(initial_map());
5614
5615 // Make the map remember to restore the link if it survives the GC.
5616 map->set_bit_field2(
5617 map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo));
5618
5619 // Undo state changes made by StartInobjectTracking (except the
5620 // construction_count). This way if the initial map does not survive the GC
5621 // then StartInobjectTracking will be called again the next time the
5622 // constructor is called. The countdown will continue and (possibly after
5623 // several more GCs) CompleteInobjectSlackTracking will eventually be called.
5624 set_initial_map(Heap::raw_unchecked_undefined_value());
5625 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubCountdown),
5626 *RawField(this, kConstructStubOffset));
5627 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubGeneric));
5628 // It is safe to clear the flag: it will be set again if the map is live.
5629 set_live_objects_may_exist(false);
5630}
5631
5632
5633// Called from GC, hence reinterpret_cast and unchecked accessors.
5634void SharedFunctionInfo::AttachInitialMap(Map* map) {
5635 map->set_bit_field2(
5636 map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo));
5637
5638 // Resume inobject slack tracking.
5639 set_initial_map(map);
5640 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubGeneric),
5641 *RawField(this, kConstructStubOffset));
5642 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubCountdown));
5643 // The map survived the gc, so there may be objects referencing it.
5644 set_live_objects_may_exist(true);
5645}
5646
5647
5648static void GetMinInobjectSlack(Map* map, void* data) {
5649 int slack = map->unused_property_fields();
5650 if (*reinterpret_cast<int*>(data) > slack) {
5651 *reinterpret_cast<int*>(data) = slack;
5652 }
5653}
5654
5655
5656static void ShrinkInstanceSize(Map* map, void* data) {
5657 int slack = *reinterpret_cast<int*>(data);
5658 map->set_inobject_properties(map->inobject_properties() - slack);
5659 map->set_unused_property_fields(map->unused_property_fields() - slack);
5660 map->set_instance_size(map->instance_size() - slack * kPointerSize);
5661
5662 // Visitor id might depend on the instance size, recalculate it.
5663 map->set_visitor_id(StaticVisitorBase::GetVisitorId(map));
5664}
5665
5666
5667void SharedFunctionInfo::CompleteInobjectSlackTracking() {
5668 ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress());
5669 Map* map = Map::cast(initial_map());
5670
5671 set_initial_map(Heap::undefined_value());
5672 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubCountdown),
5673 construct_stub());
5674 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubGeneric));
5675
5676 int slack = map->unused_property_fields();
5677 map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
5678 if (slack != 0) {
5679 // Resize the initial map and all maps in its transition tree.
5680 map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
5681 // Give the correct expected_nof_properties to initial maps created later.
5682 ASSERT(expected_nof_properties() >= slack);
5683 set_expected_nof_properties(expected_nof_properties() - slack);
5684 }
5685}
5686
5687
Steve Blocka7e24c12009-10-30 11:49:00 +00005688void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
5689 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
5690 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
5691 Object* old_target = target;
5692 VisitPointer(&target);
5693 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5694}
5695
5696
Steve Block791712a2010-08-27 10:21:07 +01005697void ObjectVisitor::VisitCodeEntry(Address entry_address) {
5698 Object* code = Code::GetObjectFromEntryAddress(entry_address);
5699 Object* old_code = code;
5700 VisitPointer(&code);
5701 if (code != old_code) {
5702 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry();
5703 }
5704}
5705
5706
Steve Blocka7e24c12009-10-30 11:49:00 +00005707void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005708 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
5709 rinfo->IsPatchedReturnSequence()) ||
5710 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
5711 rinfo->IsPatchedDebugBreakSlotSequence()));
Steve Blocka7e24c12009-10-30 11:49:00 +00005712 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
5713 Object* old_target = target;
5714 VisitPointer(&target);
5715 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5716}
5717
5718
Steve Blockd0582a62009-12-15 09:54:21 +00005719void Code::Relocate(intptr_t delta) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005720 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
5721 it.rinfo()->apply(delta);
5722 }
5723 CPU::FlushICache(instruction_start(), instruction_size());
5724}
5725
5726
5727void Code::CopyFrom(const CodeDesc& desc) {
5728 // copy code
5729 memmove(instruction_start(), desc.buffer, desc.instr_size);
5730
Steve Blocka7e24c12009-10-30 11:49:00 +00005731 // copy reloc info
5732 memmove(relocation_start(),
5733 desc.buffer + desc.buffer_size - desc.reloc_size,
5734 desc.reloc_size);
5735
5736 // unbox handles and relocate
Steve Block3ce2e202009-11-05 08:53:23 +00005737 intptr_t delta = instruction_start() - desc.buffer;
Steve Blocka7e24c12009-10-30 11:49:00 +00005738 int mode_mask = RelocInfo::kCodeTargetMask |
5739 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
5740 RelocInfo::kApplyMask;
Steve Block3ce2e202009-11-05 08:53:23 +00005741 Assembler* origin = desc.origin; // Needed to find target_object on X64.
Steve Blocka7e24c12009-10-30 11:49:00 +00005742 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
5743 RelocInfo::Mode mode = it.rinfo()->rmode();
5744 if (mode == RelocInfo::EMBEDDED_OBJECT) {
Steve Block3ce2e202009-11-05 08:53:23 +00005745 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005746 it.rinfo()->set_target_object(*p);
5747 } else if (RelocInfo::IsCodeTarget(mode)) {
5748 // rewrite code handles in inline cache targets to direct
5749 // pointers to the first instruction in the code object
Steve Block3ce2e202009-11-05 08:53:23 +00005750 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005751 Code* code = Code::cast(*p);
5752 it.rinfo()->set_target_address(code->instruction_start());
5753 } else {
5754 it.rinfo()->apply(delta);
5755 }
5756 }
5757 CPU::FlushICache(instruction_start(), instruction_size());
5758}
5759
5760
5761// Locate the source position which is closest to the address in the code. This
5762// is using the source position information embedded in the relocation info.
5763// The position returned is relative to the beginning of the script where the
5764// source for this function is found.
5765int Code::SourcePosition(Address pc) {
5766 int distance = kMaxInt;
5767 int position = RelocInfo::kNoPosition; // Initially no position found.
5768 // Run through all the relocation info to find the best matching source
5769 // position. All the code needs to be considered as the sequence of the
5770 // instructions in the code does not necessarily follow the same order as the
5771 // source.
5772 RelocIterator it(this, RelocInfo::kPositionMask);
5773 while (!it.done()) {
5774 // Only look at positions after the current pc.
5775 if (it.rinfo()->pc() < pc) {
5776 // Get position and distance.
Steve Blockd0582a62009-12-15 09:54:21 +00005777
5778 int dist = static_cast<int>(pc - it.rinfo()->pc());
5779 int pos = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005780 // If this position is closer than the current candidate or if it has the
5781 // same distance as the current candidate and the position is higher then
5782 // this position is the new candidate.
5783 if ((dist < distance) ||
5784 (dist == distance && pos > position)) {
5785 position = pos;
5786 distance = dist;
5787 }
5788 }
5789 it.next();
5790 }
5791 return position;
5792}
5793
5794
5795// Same as Code::SourcePosition above except it only looks for statement
5796// positions.
5797int Code::SourceStatementPosition(Address pc) {
5798 // First find the position as close as possible using all position
5799 // information.
5800 int position = SourcePosition(pc);
5801 // Now find the closest statement position before the position.
5802 int statement_position = 0;
5803 RelocIterator it(this, RelocInfo::kPositionMask);
5804 while (!it.done()) {
5805 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
Steve Blockd0582a62009-12-15 09:54:21 +00005806 int p = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005807 if (statement_position < p && p <= position) {
5808 statement_position = p;
5809 }
5810 }
5811 it.next();
5812 }
5813 return statement_position;
5814}
5815
5816
5817#ifdef ENABLE_DISASSEMBLER
5818// Identify kind of code.
5819const char* Code::Kind2String(Kind kind) {
5820 switch (kind) {
5821 case FUNCTION: return "FUNCTION";
5822 case STUB: return "STUB";
5823 case BUILTIN: return "BUILTIN";
5824 case LOAD_IC: return "LOAD_IC";
5825 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
5826 case STORE_IC: return "STORE_IC";
5827 case KEYED_STORE_IC: return "KEYED_STORE_IC";
5828 case CALL_IC: return "CALL_IC";
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005829 case KEYED_CALL_IC: return "KEYED_CALL_IC";
Steve Block6ded16b2010-05-10 14:33:55 +01005830 case BINARY_OP_IC: return "BINARY_OP_IC";
Steve Blocka7e24c12009-10-30 11:49:00 +00005831 }
5832 UNREACHABLE();
5833 return NULL;
5834}
5835
5836
5837const char* Code::ICState2String(InlineCacheState state) {
5838 switch (state) {
5839 case UNINITIALIZED: return "UNINITIALIZED";
5840 case PREMONOMORPHIC: return "PREMONOMORPHIC";
5841 case MONOMORPHIC: return "MONOMORPHIC";
5842 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
5843 case MEGAMORPHIC: return "MEGAMORPHIC";
5844 case DEBUG_BREAK: return "DEBUG_BREAK";
5845 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
5846 }
5847 UNREACHABLE();
5848 return NULL;
5849}
5850
5851
5852const char* Code::PropertyType2String(PropertyType type) {
5853 switch (type) {
5854 case NORMAL: return "NORMAL";
5855 case FIELD: return "FIELD";
5856 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
5857 case CALLBACKS: return "CALLBACKS";
5858 case INTERCEPTOR: return "INTERCEPTOR";
5859 case MAP_TRANSITION: return "MAP_TRANSITION";
5860 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
5861 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
5862 }
5863 UNREACHABLE();
5864 return NULL;
5865}
5866
5867void Code::Disassemble(const char* name) {
5868 PrintF("kind = %s\n", Kind2String(kind()));
5869 if (is_inline_cache_stub()) {
5870 PrintF("ic_state = %s\n", ICState2String(ic_state()));
5871 PrintF("ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
5872 if (ic_state() == MONOMORPHIC) {
5873 PrintF("type = %s\n", PropertyType2String(type()));
5874 }
5875 }
5876 if ((name != NULL) && (name[0] != '\0')) {
5877 PrintF("name = %s\n", name);
5878 }
5879
5880 PrintF("Instructions (size = %d)\n", instruction_size());
5881 Disassembler::Decode(NULL, this);
5882 PrintF("\n");
5883
5884 PrintF("RelocInfo (size = %d)\n", relocation_size());
5885 for (RelocIterator it(this); !it.done(); it.next())
5886 it.rinfo()->Print();
5887 PrintF("\n");
5888}
5889#endif // ENABLE_DISASSEMBLER
5890
5891
John Reck59135872010-11-02 12:39:01 -07005892MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
5893 int length) {
Steve Block3ce2e202009-11-05 08:53:23 +00005894 // We should never end in here with a pixel or external array.
5895 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Block8defd9f2010-07-08 12:39:36 +01005896
John Reck59135872010-11-02 12:39:01 -07005897 Object* obj;
5898 { MaybeObject* maybe_obj = Heap::AllocateFixedArrayWithHoles(capacity);
5899 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
5900 }
Steve Block8defd9f2010-07-08 12:39:36 +01005901 FixedArray* elems = FixedArray::cast(obj);
5902
John Reck59135872010-11-02 12:39:01 -07005903 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
5904 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
5905 }
Steve Block8defd9f2010-07-08 12:39:36 +01005906 Map* new_map = Map::cast(obj);
5907
Leon Clarke4515c472010-02-03 11:58:03 +00005908 AssertNoAllocation no_gc;
5909 WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00005910 switch (GetElementsKind()) {
5911 case FAST_ELEMENTS: {
5912 FixedArray* old_elements = FixedArray::cast(elements());
5913 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
5914 // Fill out the new array with this content and array holes.
5915 for (uint32_t i = 0; i < old_length; i++) {
5916 elems->set(i, old_elements->get(i), mode);
5917 }
5918 break;
5919 }
5920 case DICTIONARY_ELEMENTS: {
5921 NumberDictionary* dictionary = NumberDictionary::cast(elements());
5922 for (int i = 0; i < dictionary->Capacity(); i++) {
5923 Object* key = dictionary->KeyAt(i);
5924 if (key->IsNumber()) {
5925 uint32_t entry = static_cast<uint32_t>(key->Number());
5926 elems->set(entry, dictionary->ValueAt(i), mode);
5927 }
5928 }
5929 break;
5930 }
5931 default:
5932 UNREACHABLE();
5933 break;
5934 }
Steve Block8defd9f2010-07-08 12:39:36 +01005935
5936 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00005937 set_elements(elems);
Steve Block8defd9f2010-07-08 12:39:36 +01005938
5939 if (IsJSArray()) {
5940 JSArray::cast(this)->set_length(Smi::FromInt(length));
5941 }
5942
5943 return this;
Steve Blocka7e24c12009-10-30 11:49:00 +00005944}
5945
5946
John Reck59135872010-11-02 12:39:01 -07005947MaybeObject* JSObject::SetSlowElements(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00005948 // We should never end in here with a pixel or external array.
5949 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00005950
5951 uint32_t new_length = static_cast<uint32_t>(len->Number());
5952
5953 switch (GetElementsKind()) {
5954 case FAST_ELEMENTS: {
5955 // Make sure we never try to shrink dense arrays into sparse arrays.
5956 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
5957 new_length);
John Reck59135872010-11-02 12:39:01 -07005958 Object* obj;
5959 { MaybeObject* maybe_obj = NormalizeElements();
5960 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
5961 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005962
5963 // Update length for JSArrays.
5964 if (IsJSArray()) JSArray::cast(this)->set_length(len);
5965 break;
5966 }
5967 case DICTIONARY_ELEMENTS: {
5968 if (IsJSArray()) {
5969 uint32_t old_length =
Steve Block6ded16b2010-05-10 14:33:55 +01005970 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
Steve Blocka7e24c12009-10-30 11:49:00 +00005971 element_dictionary()->RemoveNumberEntries(new_length, old_length),
5972 JSArray::cast(this)->set_length(len);
5973 }
5974 break;
5975 }
5976 default:
5977 UNREACHABLE();
5978 break;
5979 }
5980 return this;
5981}
5982
5983
John Reck59135872010-11-02 12:39:01 -07005984MaybeObject* JSArray::Initialize(int capacity) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005985 ASSERT(capacity >= 0);
Leon Clarke4515c472010-02-03 11:58:03 +00005986 set_length(Smi::FromInt(0));
Steve Blocka7e24c12009-10-30 11:49:00 +00005987 FixedArray* new_elements;
5988 if (capacity == 0) {
5989 new_elements = Heap::empty_fixed_array();
5990 } else {
John Reck59135872010-11-02 12:39:01 -07005991 Object* obj;
5992 { MaybeObject* maybe_obj = Heap::AllocateFixedArrayWithHoles(capacity);
5993 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
5994 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005995 new_elements = FixedArray::cast(obj);
5996 }
5997 set_elements(new_elements);
5998 return this;
5999}
6000
6001
6002void JSArray::Expand(int required_size) {
6003 Handle<JSArray> self(this);
6004 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
6005 int old_size = old_backing->length();
Steve Blockd0582a62009-12-15 09:54:21 +00006006 int new_size = required_size > old_size ? required_size : old_size;
Steve Blocka7e24c12009-10-30 11:49:00 +00006007 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
6008 // Can't use this any more now because we may have had a GC!
6009 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
6010 self->SetContent(*new_backing);
6011}
6012
6013
6014// Computes the new capacity when expanding the elements of a JSObject.
6015static int NewElementsCapacity(int old_capacity) {
6016 // (old_capacity + 50%) + 16
6017 return old_capacity + (old_capacity >> 1) + 16;
6018}
6019
6020
John Reck59135872010-11-02 12:39:01 -07006021static Failure* ArrayLengthRangeError() {
Steve Blocka7e24c12009-10-30 11:49:00 +00006022 HandleScope scope;
6023 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
6024 HandleVector<Object>(NULL, 0)));
6025}
6026
6027
John Reck59135872010-11-02 12:39:01 -07006028MaybeObject* JSObject::SetElementsLength(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00006029 // We should never end in here with a pixel or external array.
Steve Block6ded16b2010-05-10 14:33:55 +01006030 ASSERT(AllowsSetElementsLength());
Steve Blocka7e24c12009-10-30 11:49:00 +00006031
John Reck59135872010-11-02 12:39:01 -07006032 MaybeObject* maybe_smi_length = len->ToSmi();
6033 Object* smi_length = Smi::FromInt(0);
6034 if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) {
Steve Block8defd9f2010-07-08 12:39:36 +01006035 const int value = Smi::cast(smi_length)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00006036 if (value < 0) return ArrayLengthRangeError();
6037 switch (GetElementsKind()) {
6038 case FAST_ELEMENTS: {
6039 int old_capacity = FixedArray::cast(elements())->length();
6040 if (value <= old_capacity) {
6041 if (IsJSArray()) {
John Reck59135872010-11-02 12:39:01 -07006042 Object* obj;
6043 { MaybeObject* maybe_obj = EnsureWritableFastElements();
6044 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6045 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006046 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
6047 // NOTE: We may be able to optimize this by removing the
6048 // last part of the elements backing storage array and
6049 // setting the capacity to the new size.
6050 for (int i = value; i < old_length; i++) {
6051 FixedArray::cast(elements())->set_the_hole(i);
6052 }
Leon Clarke4515c472010-02-03 11:58:03 +00006053 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006054 }
6055 return this;
6056 }
6057 int min = NewElementsCapacity(old_capacity);
6058 int new_capacity = value > min ? value : min;
6059 if (new_capacity <= kMaxFastElementsLength ||
6060 !ShouldConvertToSlowElements(new_capacity)) {
John Reck59135872010-11-02 12:39:01 -07006061 Object* obj;
6062 { MaybeObject* maybe_obj =
6063 SetFastElementsCapacityAndLength(new_capacity, value);
6064 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6065 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006066 return this;
6067 }
6068 break;
6069 }
6070 case DICTIONARY_ELEMENTS: {
6071 if (IsJSArray()) {
6072 if (value == 0) {
6073 // If the length of a slow array is reset to zero, we clear
6074 // the array and flush backing storage. This has the added
6075 // benefit that the array returns to fast mode.
John Reck59135872010-11-02 12:39:01 -07006076 Object* obj;
6077 { MaybeObject* maybe_obj = ResetElements();
6078 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6079 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006080 } else {
6081 // Remove deleted elements.
6082 uint32_t old_length =
6083 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
6084 element_dictionary()->RemoveNumberEntries(value, old_length);
6085 }
Leon Clarke4515c472010-02-03 11:58:03 +00006086 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006087 }
6088 return this;
6089 }
6090 default:
6091 UNREACHABLE();
6092 break;
6093 }
6094 }
6095
6096 // General slow case.
6097 if (len->IsNumber()) {
6098 uint32_t length;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006099 if (len->ToArrayIndex(&length)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006100 return SetSlowElements(len);
6101 } else {
6102 return ArrayLengthRangeError();
6103 }
6104 }
6105
6106 // len is not a number so make the array size one and
6107 // set only element to len.
John Reck59135872010-11-02 12:39:01 -07006108 Object* obj;
6109 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(1);
6110 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6111 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006112 FixedArray::cast(obj)->set(0, len);
Leon Clarke4515c472010-02-03 11:58:03 +00006113 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006114 set_elements(FixedArray::cast(obj));
6115 return this;
6116}
6117
6118
John Reck59135872010-11-02 12:39:01 -07006119MaybeObject* JSObject::SetPrototype(Object* value,
6120 bool skip_hidden_prototypes) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006121 // Silently ignore the change if value is not a JSObject or null.
6122 // SpiderMonkey behaves this way.
6123 if (!value->IsJSObject() && !value->IsNull()) return value;
6124
6125 // Before we can set the prototype we need to be sure
6126 // prototype cycles are prevented.
6127 // It is sufficient to validate that the receiver is not in the new prototype
6128 // chain.
6129 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
6130 if (JSObject::cast(pt) == this) {
6131 // Cycle detected.
6132 HandleScope scope;
6133 return Top::Throw(*Factory::NewError("cyclic_proto",
6134 HandleVector<Object>(NULL, 0)));
6135 }
6136 }
6137
6138 JSObject* real_receiver = this;
6139
6140 if (skip_hidden_prototypes) {
6141 // Find the first object in the chain whose prototype object is not
6142 // hidden and set the new prototype on that object.
6143 Object* current_proto = real_receiver->GetPrototype();
6144 while (current_proto->IsJSObject() &&
6145 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
6146 real_receiver = JSObject::cast(current_proto);
6147 current_proto = current_proto->GetPrototype();
6148 }
6149 }
6150
6151 // Set the new prototype of the object.
John Reck59135872010-11-02 12:39:01 -07006152 Object* new_map;
6153 { MaybeObject* maybe_new_map = real_receiver->map()->CopyDropTransitions();
6154 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
6155 }
Andrei Popescu402d9372010-02-26 13:31:12 +00006156 Map::cast(new_map)->set_prototype(value);
6157 real_receiver->set_map(Map::cast(new_map));
6158
Kristian Monsen25f61362010-05-21 11:50:48 +01006159 Heap::ClearInstanceofCache();
6160
Andrei Popescu402d9372010-02-26 13:31:12 +00006161 return value;
6162}
6163
6164
Steve Blocka7e24c12009-10-30 11:49:00 +00006165bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
6166 switch (GetElementsKind()) {
6167 case FAST_ELEMENTS: {
6168 uint32_t length = IsJSArray() ?
6169 static_cast<uint32_t>
6170 (Smi::cast(JSArray::cast(this)->length())->value()) :
6171 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6172 if ((index < length) &&
6173 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
6174 return true;
6175 }
6176 break;
6177 }
6178 case PIXEL_ELEMENTS: {
6179 // TODO(iposva): Add testcase.
6180 PixelArray* pixels = PixelArray::cast(elements());
6181 if (index < static_cast<uint32_t>(pixels->length())) {
6182 return true;
6183 }
6184 break;
6185 }
Steve Block3ce2e202009-11-05 08:53:23 +00006186 case EXTERNAL_BYTE_ELEMENTS:
6187 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6188 case EXTERNAL_SHORT_ELEMENTS:
6189 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6190 case EXTERNAL_INT_ELEMENTS:
6191 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6192 case EXTERNAL_FLOAT_ELEMENTS: {
6193 // TODO(kbr): Add testcase.
6194 ExternalArray* array = ExternalArray::cast(elements());
6195 if (index < static_cast<uint32_t>(array->length())) {
6196 return true;
6197 }
6198 break;
6199 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006200 case DICTIONARY_ELEMENTS: {
6201 if (element_dictionary()->FindEntry(index)
6202 != NumberDictionary::kNotFound) {
6203 return true;
6204 }
6205 break;
6206 }
6207 default:
6208 UNREACHABLE();
6209 break;
6210 }
6211
6212 // Handle [] on String objects.
6213 if (this->IsStringObjectWithCharacterAt(index)) return true;
6214
6215 Object* pt = GetPrototype();
6216 if (pt == Heap::null_value()) return false;
6217 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
6218}
6219
6220
6221bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
6222 // Make sure that the top context does not change when doing
6223 // callbacks or interceptor calls.
6224 AssertNoContextChange ncc;
6225 HandleScope scope;
6226 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6227 Handle<JSObject> receiver_handle(receiver);
6228 Handle<JSObject> holder_handle(this);
6229 CustomArguments args(interceptor->data(), receiver, this);
6230 v8::AccessorInfo info(args.end());
6231 if (!interceptor->query()->IsUndefined()) {
6232 v8::IndexedPropertyQuery query =
6233 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
6234 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
Iain Merrick75681382010-08-19 15:07:18 +01006235 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00006236 {
6237 // Leaving JavaScript.
6238 VMState state(EXTERNAL);
6239 result = query(index, info);
6240 }
Iain Merrick75681382010-08-19 15:07:18 +01006241 if (!result.IsEmpty()) {
6242 ASSERT(result->IsInt32());
6243 return true; // absence of property is signaled by empty handle.
6244 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006245 } else if (!interceptor->getter()->IsUndefined()) {
6246 v8::IndexedPropertyGetter getter =
6247 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
6248 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
6249 v8::Handle<v8::Value> result;
6250 {
6251 // Leaving JavaScript.
6252 VMState state(EXTERNAL);
6253 result = getter(index, info);
6254 }
6255 if (!result.IsEmpty()) return true;
6256 }
6257 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
6258}
6259
6260
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006261JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006262 // Check access rights if needed.
6263 if (IsAccessCheckNeeded() &&
6264 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6265 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006266 return UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006267 }
6268
6269 // Check for lookup interceptor
6270 if (HasIndexedInterceptor()) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006271 return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
6272 : UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006273 }
6274
6275 // Handle [] on String objects.
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006276 if (this->IsStringObjectWithCharacterAt(index)) {
6277 return STRING_CHARACTER_ELEMENT;
6278 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006279
6280 switch (GetElementsKind()) {
6281 case FAST_ELEMENTS: {
6282 uint32_t length = IsJSArray() ?
6283 static_cast<uint32_t>
6284 (Smi::cast(JSArray::cast(this)->length())->value()) :
6285 static_cast<uint32_t>(FixedArray::cast(elements())->length());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006286 if ((index < length) &&
6287 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
6288 return FAST_ELEMENT;
6289 }
6290 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006291 }
6292 case PIXEL_ELEMENTS: {
6293 PixelArray* pixels = PixelArray::cast(elements());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006294 if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT;
6295 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006296 }
Steve Block3ce2e202009-11-05 08:53:23 +00006297 case EXTERNAL_BYTE_ELEMENTS:
6298 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6299 case EXTERNAL_SHORT_ELEMENTS:
6300 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6301 case EXTERNAL_INT_ELEMENTS:
6302 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6303 case EXTERNAL_FLOAT_ELEMENTS: {
6304 ExternalArray* array = ExternalArray::cast(elements());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006305 if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
6306 break;
Steve Block3ce2e202009-11-05 08:53:23 +00006307 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006308 case DICTIONARY_ELEMENTS: {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006309 if (element_dictionary()->FindEntry(index) !=
6310 NumberDictionary::kNotFound) {
6311 return DICTIONARY_ELEMENT;
6312 }
6313 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006314 }
6315 default:
6316 UNREACHABLE();
6317 break;
6318 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006319
6320 return UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006321}
6322
6323
6324bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
6325 // Check access rights if needed.
6326 if (IsAccessCheckNeeded() &&
6327 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6328 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6329 return false;
6330 }
6331
6332 // Check for lookup interceptor
6333 if (HasIndexedInterceptor()) {
6334 return HasElementWithInterceptor(receiver, index);
6335 }
6336
6337 switch (GetElementsKind()) {
6338 case FAST_ELEMENTS: {
6339 uint32_t length = IsJSArray() ?
6340 static_cast<uint32_t>
6341 (Smi::cast(JSArray::cast(this)->length())->value()) :
6342 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6343 if ((index < length) &&
6344 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
6345 break;
6346 }
6347 case PIXEL_ELEMENTS: {
6348 PixelArray* pixels = PixelArray::cast(elements());
6349 if (index < static_cast<uint32_t>(pixels->length())) {
6350 return true;
6351 }
6352 break;
6353 }
Steve Block3ce2e202009-11-05 08:53:23 +00006354 case EXTERNAL_BYTE_ELEMENTS:
6355 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6356 case EXTERNAL_SHORT_ELEMENTS:
6357 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6358 case EXTERNAL_INT_ELEMENTS:
6359 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6360 case EXTERNAL_FLOAT_ELEMENTS: {
6361 ExternalArray* array = ExternalArray::cast(elements());
6362 if (index < static_cast<uint32_t>(array->length())) {
6363 return true;
6364 }
6365 break;
6366 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006367 case DICTIONARY_ELEMENTS: {
6368 if (element_dictionary()->FindEntry(index)
6369 != NumberDictionary::kNotFound) {
6370 return true;
6371 }
6372 break;
6373 }
6374 default:
6375 UNREACHABLE();
6376 break;
6377 }
6378
6379 // Handle [] on String objects.
6380 if (this->IsStringObjectWithCharacterAt(index)) return true;
6381
6382 Object* pt = GetPrototype();
6383 if (pt == Heap::null_value()) return false;
6384 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
6385}
6386
6387
John Reck59135872010-11-02 12:39:01 -07006388MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
6389 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006390 // Make sure that the top context does not change when doing
6391 // callbacks or interceptor calls.
6392 AssertNoContextChange ncc;
6393 HandleScope scope;
6394 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6395 Handle<JSObject> this_handle(this);
6396 Handle<Object> value_handle(value);
6397 if (!interceptor->setter()->IsUndefined()) {
6398 v8::IndexedPropertySetter setter =
6399 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
6400 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
6401 CustomArguments args(interceptor->data(), this, this);
6402 v8::AccessorInfo info(args.end());
6403 v8::Handle<v8::Value> result;
6404 {
6405 // Leaving JavaScript.
6406 VMState state(EXTERNAL);
6407 result = setter(index, v8::Utils::ToLocal(value_handle), info);
6408 }
6409 RETURN_IF_SCHEDULED_EXCEPTION();
6410 if (!result.IsEmpty()) return *value_handle;
6411 }
John Reck59135872010-11-02 12:39:01 -07006412 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00006413 this_handle->SetElementWithoutInterceptor(index, *value_handle);
6414 RETURN_IF_SCHEDULED_EXCEPTION();
6415 return raw_result;
6416}
6417
6418
John Reck59135872010-11-02 12:39:01 -07006419MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
6420 Object* structure,
6421 uint32_t index,
6422 Object* holder) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006423 ASSERT(!structure->IsProxy());
6424
6425 // api style callbacks.
6426 if (structure->IsAccessorInfo()) {
6427 AccessorInfo* data = AccessorInfo::cast(structure);
6428 Object* fun_obj = data->getter();
6429 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
6430 HandleScope scope;
6431 Handle<JSObject> self(JSObject::cast(receiver));
6432 Handle<JSObject> holder_handle(JSObject::cast(holder));
6433 Handle<Object> number = Factory::NewNumberFromUint(index);
6434 Handle<String> key(Factory::NumberToString(number));
6435 LOG(ApiNamedPropertyAccess("load", *self, *key));
6436 CustomArguments args(data->data(), *self, *holder_handle);
6437 v8::AccessorInfo info(args.end());
6438 v8::Handle<v8::Value> result;
6439 {
6440 // Leaving JavaScript.
6441 VMState state(EXTERNAL);
6442 result = call_fun(v8::Utils::ToLocal(key), info);
6443 }
6444 RETURN_IF_SCHEDULED_EXCEPTION();
6445 if (result.IsEmpty()) return Heap::undefined_value();
6446 return *v8::Utils::OpenHandle(*result);
6447 }
6448
6449 // __defineGetter__ callback
6450 if (structure->IsFixedArray()) {
6451 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
6452 if (getter->IsJSFunction()) {
6453 return Object::GetPropertyWithDefinedGetter(receiver,
6454 JSFunction::cast(getter));
6455 }
6456 // Getter is not a function.
6457 return Heap::undefined_value();
6458 }
6459
6460 UNREACHABLE();
6461 return NULL;
6462}
6463
6464
John Reck59135872010-11-02 12:39:01 -07006465MaybeObject* JSObject::SetElementWithCallback(Object* structure,
6466 uint32_t index,
6467 Object* value,
6468 JSObject* holder) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006469 HandleScope scope;
6470
6471 // We should never get here to initialize a const with the hole
6472 // value since a const declaration would conflict with the setter.
6473 ASSERT(!value->IsTheHole());
6474 Handle<Object> value_handle(value);
6475
6476 // To accommodate both the old and the new api we switch on the
6477 // data structure used to store the callbacks. Eventually proxy
6478 // callbacks should be phased out.
6479 ASSERT(!structure->IsProxy());
6480
6481 if (structure->IsAccessorInfo()) {
6482 // api style callbacks
6483 AccessorInfo* data = AccessorInfo::cast(structure);
6484 Object* call_obj = data->setter();
6485 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
6486 if (call_fun == NULL) return value;
6487 Handle<Object> number = Factory::NewNumberFromUint(index);
6488 Handle<String> key(Factory::NumberToString(number));
6489 LOG(ApiNamedPropertyAccess("store", this, *key));
6490 CustomArguments args(data->data(), this, JSObject::cast(holder));
6491 v8::AccessorInfo info(args.end());
6492 {
6493 // Leaving JavaScript.
6494 VMState state(EXTERNAL);
6495 call_fun(v8::Utils::ToLocal(key),
6496 v8::Utils::ToLocal(value_handle),
6497 info);
6498 }
6499 RETURN_IF_SCHEDULED_EXCEPTION();
6500 return *value_handle;
6501 }
6502
6503 if (structure->IsFixedArray()) {
6504 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
6505 if (setter->IsJSFunction()) {
6506 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
6507 } else {
6508 Handle<Object> holder_handle(holder);
6509 Handle<Object> key(Factory::NewNumberFromUint(index));
6510 Handle<Object> args[2] = { key, holder_handle };
6511 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
6512 HandleVector(args, 2)));
6513 }
6514 }
6515
6516 UNREACHABLE();
6517 return NULL;
6518}
6519
6520
Steve Blocka7e24c12009-10-30 11:49:00 +00006521// Adding n elements in fast case is O(n*n).
6522// Note: revisit design to have dual undefined values to capture absent
6523// elements.
John Reck59135872010-11-02 12:39:01 -07006524MaybeObject* JSObject::SetFastElement(uint32_t index, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006525 ASSERT(HasFastElements());
6526
John Reck59135872010-11-02 12:39:01 -07006527 Object* elms_obj;
6528 { MaybeObject* maybe_elms_obj = EnsureWritableFastElements();
6529 if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
6530 }
Iain Merrick75681382010-08-19 15:07:18 +01006531 FixedArray* elms = FixedArray::cast(elms_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00006532 uint32_t elms_length = static_cast<uint32_t>(elms->length());
6533
6534 if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006535 if (SetElementWithCallbackSetterInPrototypes(index, value)) {
6536 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006537 }
6538 }
6539
6540 // Check whether there is extra space in fixed array..
6541 if (index < elms_length) {
6542 elms->set(index, value);
6543 if (IsJSArray()) {
6544 // Update the length of the array if needed.
6545 uint32_t array_length = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006546 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006547 if (index >= array_length) {
Leon Clarke4515c472010-02-03 11:58:03 +00006548 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006549 }
6550 }
6551 return value;
6552 }
6553
6554 // Allow gap in fast case.
6555 if ((index - elms_length) < kMaxGap) {
6556 // Try allocating extra space.
6557 int new_capacity = NewElementsCapacity(index+1);
6558 if (new_capacity <= kMaxFastElementsLength ||
6559 !ShouldConvertToSlowElements(new_capacity)) {
6560 ASSERT(static_cast<uint32_t>(new_capacity) > index);
John Reck59135872010-11-02 12:39:01 -07006561 Object* obj;
6562 { MaybeObject* maybe_obj =
6563 SetFastElementsCapacityAndLength(new_capacity, index + 1);
6564 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6565 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006566 FixedArray::cast(elements())->set(index, value);
6567 return value;
6568 }
6569 }
6570
6571 // Otherwise default to slow case.
John Reck59135872010-11-02 12:39:01 -07006572 Object* obj;
6573 { MaybeObject* maybe_obj = NormalizeElements();
6574 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6575 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006576 ASSERT(HasDictionaryElements());
6577 return SetElement(index, value);
6578}
6579
Iain Merrick75681382010-08-19 15:07:18 +01006580
John Reck59135872010-11-02 12:39:01 -07006581MaybeObject* JSObject::SetElement(uint32_t index, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006582 // Check access rights if needed.
6583 if (IsAccessCheckNeeded() &&
6584 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
Iain Merrick75681382010-08-19 15:07:18 +01006585 HandleScope scope;
6586 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00006587 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01006588 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00006589 }
6590
6591 if (IsJSGlobalProxy()) {
6592 Object* proto = GetPrototype();
6593 if (proto->IsNull()) return value;
6594 ASSERT(proto->IsJSGlobalObject());
6595 return JSObject::cast(proto)->SetElement(index, value);
6596 }
6597
6598 // Check for lookup interceptor
6599 if (HasIndexedInterceptor()) {
6600 return SetElementWithInterceptor(index, value);
6601 }
6602
6603 return SetElementWithoutInterceptor(index, value);
6604}
6605
6606
John Reck59135872010-11-02 12:39:01 -07006607MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
6608 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006609 switch (GetElementsKind()) {
6610 case FAST_ELEMENTS:
6611 // Fast case.
6612 return SetFastElement(index, value);
6613 case PIXEL_ELEMENTS: {
6614 PixelArray* pixels = PixelArray::cast(elements());
6615 return pixels->SetValue(index, value);
6616 }
Steve Block3ce2e202009-11-05 08:53:23 +00006617 case EXTERNAL_BYTE_ELEMENTS: {
6618 ExternalByteArray* array = ExternalByteArray::cast(elements());
6619 return array->SetValue(index, value);
6620 }
6621 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6622 ExternalUnsignedByteArray* array =
6623 ExternalUnsignedByteArray::cast(elements());
6624 return array->SetValue(index, value);
6625 }
6626 case EXTERNAL_SHORT_ELEMENTS: {
6627 ExternalShortArray* array = ExternalShortArray::cast(elements());
6628 return array->SetValue(index, value);
6629 }
6630 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6631 ExternalUnsignedShortArray* array =
6632 ExternalUnsignedShortArray::cast(elements());
6633 return array->SetValue(index, value);
6634 }
6635 case EXTERNAL_INT_ELEMENTS: {
6636 ExternalIntArray* array = ExternalIntArray::cast(elements());
6637 return array->SetValue(index, value);
6638 }
6639 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6640 ExternalUnsignedIntArray* array =
6641 ExternalUnsignedIntArray::cast(elements());
6642 return array->SetValue(index, value);
6643 }
6644 case EXTERNAL_FLOAT_ELEMENTS: {
6645 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
6646 return array->SetValue(index, value);
6647 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006648 case DICTIONARY_ELEMENTS: {
6649 // Insert element in the dictionary.
6650 FixedArray* elms = FixedArray::cast(elements());
6651 NumberDictionary* dictionary = NumberDictionary::cast(elms);
6652
6653 int entry = dictionary->FindEntry(index);
6654 if (entry != NumberDictionary::kNotFound) {
6655 Object* element = dictionary->ValueAt(entry);
6656 PropertyDetails details = dictionary->DetailsAt(entry);
6657 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006658 return SetElementWithCallback(element, index, value, this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006659 } else {
6660 dictionary->UpdateMaxNumberKey(index);
6661 dictionary->ValueAtPut(entry, value);
6662 }
6663 } else {
6664 // Index not already used. Look for an accessor in the prototype chain.
6665 if (!IsJSArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006666 if (SetElementWithCallbackSetterInPrototypes(index, value)) {
6667 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006668 }
6669 }
Steve Block8defd9f2010-07-08 12:39:36 +01006670 // When we set the is_extensible flag to false we always force
6671 // the element into dictionary mode (and force them to stay there).
6672 if (!map()->is_extensible()) {
Ben Murdochf87a2032010-10-22 12:50:53 +01006673 Handle<Object> number(Factory::NewNumberFromUint(index));
Steve Block8defd9f2010-07-08 12:39:36 +01006674 Handle<String> index_string(Factory::NumberToString(number));
6675 Handle<Object> args[1] = { index_string };
6676 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
6677 HandleVector(args, 1)));
6678 }
John Reck59135872010-11-02 12:39:01 -07006679 Object* result;
6680 { MaybeObject* maybe_result = dictionary->AtNumberPut(index, value);
6681 if (!maybe_result->ToObject(&result)) return maybe_result;
6682 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006683 if (elms != FixedArray::cast(result)) {
6684 set_elements(FixedArray::cast(result));
6685 }
6686 }
6687
6688 // Update the array length if this JSObject is an array.
6689 if (IsJSArray()) {
6690 JSArray* array = JSArray::cast(this);
John Reck59135872010-11-02 12:39:01 -07006691 Object* return_value;
6692 { MaybeObject* maybe_return_value =
6693 array->JSArrayUpdateLengthFromIndex(index, value);
6694 if (!maybe_return_value->ToObject(&return_value)) {
6695 return maybe_return_value;
6696 }
6697 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006698 }
6699
6700 // Attempt to put this object back in fast case.
6701 if (ShouldConvertToFastElements()) {
6702 uint32_t new_length = 0;
6703 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006704 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006705 } else {
6706 new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
6707 }
John Reck59135872010-11-02 12:39:01 -07006708 Object* obj;
6709 { MaybeObject* maybe_obj =
6710 SetFastElementsCapacityAndLength(new_length, new_length);
6711 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6712 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006713#ifdef DEBUG
6714 if (FLAG_trace_normalization) {
6715 PrintF("Object elements are fast case again:\n");
6716 Print();
6717 }
6718#endif
6719 }
6720
6721 return value;
6722 }
6723 default:
6724 UNREACHABLE();
6725 break;
6726 }
6727 // All possible cases have been handled above. Add a return to avoid the
6728 // complaints from the compiler.
6729 UNREACHABLE();
6730 return Heap::null_value();
6731}
6732
6733
John Reck59135872010-11-02 12:39:01 -07006734MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
6735 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006736 uint32_t old_len = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006737 CHECK(length()->ToArrayIndex(&old_len));
Steve Blocka7e24c12009-10-30 11:49:00 +00006738 // Check to see if we need to update the length. For now, we make
6739 // sure that the length stays within 32-bits (unsigned).
6740 if (index >= old_len && index != 0xffffffff) {
John Reck59135872010-11-02 12:39:01 -07006741 Object* len;
6742 { MaybeObject* maybe_len =
6743 Heap::NumberFromDouble(static_cast<double>(index) + 1);
6744 if (!maybe_len->ToObject(&len)) return maybe_len;
6745 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006746 set_length(len);
6747 }
6748 return value;
6749}
6750
6751
John Reck59135872010-11-02 12:39:01 -07006752MaybeObject* JSObject::GetElementPostInterceptor(JSObject* receiver,
6753 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006754 // Get element works for both JSObject and JSArray since
6755 // JSArray::length cannot change.
6756 switch (GetElementsKind()) {
6757 case FAST_ELEMENTS: {
6758 FixedArray* elms = FixedArray::cast(elements());
6759 if (index < static_cast<uint32_t>(elms->length())) {
6760 Object* value = elms->get(index);
6761 if (!value->IsTheHole()) return value;
6762 }
6763 break;
6764 }
6765 case PIXEL_ELEMENTS: {
6766 // TODO(iposva): Add testcase and implement.
6767 UNIMPLEMENTED();
6768 break;
6769 }
Steve Block3ce2e202009-11-05 08:53:23 +00006770 case EXTERNAL_BYTE_ELEMENTS:
6771 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6772 case EXTERNAL_SHORT_ELEMENTS:
6773 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6774 case EXTERNAL_INT_ELEMENTS:
6775 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6776 case EXTERNAL_FLOAT_ELEMENTS: {
6777 // TODO(kbr): Add testcase and implement.
6778 UNIMPLEMENTED();
6779 break;
6780 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006781 case DICTIONARY_ELEMENTS: {
6782 NumberDictionary* dictionary = element_dictionary();
6783 int entry = dictionary->FindEntry(index);
6784 if (entry != NumberDictionary::kNotFound) {
6785 Object* element = dictionary->ValueAt(entry);
6786 PropertyDetails details = dictionary->DetailsAt(entry);
6787 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006788 return GetElementWithCallback(receiver,
6789 element,
6790 index,
6791 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006792 }
6793 return element;
6794 }
6795 break;
6796 }
6797 default:
6798 UNREACHABLE();
6799 break;
6800 }
6801
6802 // Continue searching via the prototype chain.
6803 Object* pt = GetPrototype();
6804 if (pt == Heap::null_value()) return Heap::undefined_value();
6805 return pt->GetElementWithReceiver(receiver, index);
6806}
6807
6808
John Reck59135872010-11-02 12:39:01 -07006809MaybeObject* JSObject::GetElementWithInterceptor(JSObject* receiver,
6810 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006811 // Make sure that the top context does not change when doing
6812 // callbacks or interceptor calls.
6813 AssertNoContextChange ncc;
6814 HandleScope scope;
6815 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6816 Handle<JSObject> this_handle(receiver);
6817 Handle<JSObject> holder_handle(this);
6818
6819 if (!interceptor->getter()->IsUndefined()) {
6820 v8::IndexedPropertyGetter getter =
6821 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
6822 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
6823 CustomArguments args(interceptor->data(), receiver, this);
6824 v8::AccessorInfo info(args.end());
6825 v8::Handle<v8::Value> result;
6826 {
6827 // Leaving JavaScript.
6828 VMState state(EXTERNAL);
6829 result = getter(index, info);
6830 }
6831 RETURN_IF_SCHEDULED_EXCEPTION();
6832 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6833 }
6834
John Reck59135872010-11-02 12:39:01 -07006835 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00006836 holder_handle->GetElementPostInterceptor(*this_handle, index);
6837 RETURN_IF_SCHEDULED_EXCEPTION();
6838 return raw_result;
6839}
6840
6841
John Reck59135872010-11-02 12:39:01 -07006842MaybeObject* JSObject::GetElementWithReceiver(JSObject* receiver,
6843 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006844 // Check access rights if needed.
6845 if (IsAccessCheckNeeded() &&
6846 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
6847 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
6848 return Heap::undefined_value();
6849 }
6850
6851 if (HasIndexedInterceptor()) {
6852 return GetElementWithInterceptor(receiver, index);
6853 }
6854
6855 // Get element works for both JSObject and JSArray since
6856 // JSArray::length cannot change.
6857 switch (GetElementsKind()) {
6858 case FAST_ELEMENTS: {
6859 FixedArray* elms = FixedArray::cast(elements());
6860 if (index < static_cast<uint32_t>(elms->length())) {
6861 Object* value = elms->get(index);
6862 if (!value->IsTheHole()) return value;
6863 }
6864 break;
6865 }
6866 case PIXEL_ELEMENTS: {
6867 PixelArray* pixels = PixelArray::cast(elements());
6868 if (index < static_cast<uint32_t>(pixels->length())) {
6869 uint8_t value = pixels->get(index);
6870 return Smi::FromInt(value);
6871 }
6872 break;
6873 }
Steve Block3ce2e202009-11-05 08:53:23 +00006874 case EXTERNAL_BYTE_ELEMENTS: {
6875 ExternalByteArray* array = ExternalByteArray::cast(elements());
6876 if (index < static_cast<uint32_t>(array->length())) {
6877 int8_t value = array->get(index);
6878 return Smi::FromInt(value);
6879 }
6880 break;
6881 }
6882 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6883 ExternalUnsignedByteArray* array =
6884 ExternalUnsignedByteArray::cast(elements());
6885 if (index < static_cast<uint32_t>(array->length())) {
6886 uint8_t value = array->get(index);
6887 return Smi::FromInt(value);
6888 }
6889 break;
6890 }
6891 case EXTERNAL_SHORT_ELEMENTS: {
6892 ExternalShortArray* array = ExternalShortArray::cast(elements());
6893 if (index < static_cast<uint32_t>(array->length())) {
6894 int16_t value = array->get(index);
6895 return Smi::FromInt(value);
6896 }
6897 break;
6898 }
6899 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6900 ExternalUnsignedShortArray* array =
6901 ExternalUnsignedShortArray::cast(elements());
6902 if (index < static_cast<uint32_t>(array->length())) {
6903 uint16_t value = array->get(index);
6904 return Smi::FromInt(value);
6905 }
6906 break;
6907 }
6908 case EXTERNAL_INT_ELEMENTS: {
6909 ExternalIntArray* array = ExternalIntArray::cast(elements());
6910 if (index < static_cast<uint32_t>(array->length())) {
6911 int32_t value = array->get(index);
6912 return Heap::NumberFromInt32(value);
6913 }
6914 break;
6915 }
6916 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6917 ExternalUnsignedIntArray* array =
6918 ExternalUnsignedIntArray::cast(elements());
6919 if (index < static_cast<uint32_t>(array->length())) {
6920 uint32_t value = array->get(index);
6921 return Heap::NumberFromUint32(value);
6922 }
6923 break;
6924 }
6925 case EXTERNAL_FLOAT_ELEMENTS: {
6926 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
6927 if (index < static_cast<uint32_t>(array->length())) {
6928 float value = array->get(index);
6929 return Heap::AllocateHeapNumber(value);
6930 }
6931 break;
6932 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006933 case DICTIONARY_ELEMENTS: {
6934 NumberDictionary* dictionary = element_dictionary();
6935 int entry = dictionary->FindEntry(index);
6936 if (entry != NumberDictionary::kNotFound) {
6937 Object* element = dictionary->ValueAt(entry);
6938 PropertyDetails details = dictionary->DetailsAt(entry);
6939 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006940 return GetElementWithCallback(receiver,
6941 element,
6942 index,
6943 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00006944 }
6945 return element;
6946 }
6947 break;
6948 }
6949 }
6950
6951 Object* pt = GetPrototype();
6952 if (pt == Heap::null_value()) return Heap::undefined_value();
6953 return pt->GetElementWithReceiver(receiver, index);
6954}
6955
6956
6957bool JSObject::HasDenseElements() {
6958 int capacity = 0;
6959 int number_of_elements = 0;
6960
6961 switch (GetElementsKind()) {
6962 case FAST_ELEMENTS: {
6963 FixedArray* elms = FixedArray::cast(elements());
6964 capacity = elms->length();
6965 for (int i = 0; i < capacity; i++) {
6966 if (!elms->get(i)->IsTheHole()) number_of_elements++;
6967 }
6968 break;
6969 }
Steve Block3ce2e202009-11-05 08:53:23 +00006970 case PIXEL_ELEMENTS:
6971 case EXTERNAL_BYTE_ELEMENTS:
6972 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6973 case EXTERNAL_SHORT_ELEMENTS:
6974 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6975 case EXTERNAL_INT_ELEMENTS:
6976 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6977 case EXTERNAL_FLOAT_ELEMENTS: {
Steve Blocka7e24c12009-10-30 11:49:00 +00006978 return true;
6979 }
6980 case DICTIONARY_ELEMENTS: {
6981 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6982 capacity = dictionary->Capacity();
6983 number_of_elements = dictionary->NumberOfElements();
6984 break;
6985 }
6986 default:
6987 UNREACHABLE();
6988 break;
6989 }
6990
6991 if (capacity == 0) return true;
6992 return (number_of_elements > (capacity / 2));
6993}
6994
6995
6996bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
6997 ASSERT(HasFastElements());
6998 // Keep the array in fast case if the current backing storage is
6999 // almost filled and if the new capacity is no more than twice the
7000 // old capacity.
7001 int elements_length = FixedArray::cast(elements())->length();
7002 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
7003}
7004
7005
7006bool JSObject::ShouldConvertToFastElements() {
7007 ASSERT(HasDictionaryElements());
7008 NumberDictionary* dictionary = NumberDictionary::cast(elements());
7009 // If the elements are sparse, we should not go back to fast case.
7010 if (!HasDenseElements()) return false;
7011 // If an element has been added at a very high index in the elements
7012 // dictionary, we cannot go back to fast case.
7013 if (dictionary->requires_slow_elements()) return false;
7014 // An object requiring access checks is never allowed to have fast
7015 // elements. If it had fast elements we would skip security checks.
7016 if (IsAccessCheckNeeded()) return false;
7017 // If the dictionary backing storage takes up roughly half as much
7018 // space as a fast-case backing storage would the array should have
7019 // fast elements.
7020 uint32_t length = 0;
7021 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007022 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
Steve Blocka7e24c12009-10-30 11:49:00 +00007023 } else {
7024 length = dictionary->max_number_key();
7025 }
7026 return static_cast<uint32_t>(dictionary->Capacity()) >=
7027 (length / (2 * NumberDictionary::kEntrySize));
7028}
7029
7030
7031// Certain compilers request function template instantiation when they
7032// see the definition of the other template functions in the
7033// class. This requires us to have the template functions put
7034// together, so even though this function belongs in objects-debug.cc,
7035// we keep it here instead to satisfy certain compilers.
7036#ifdef DEBUG
7037template<typename Shape, typename Key>
7038void Dictionary<Shape, Key>::Print() {
7039 int capacity = HashTable<Shape, Key>::Capacity();
7040 for (int i = 0; i < capacity; i++) {
7041 Object* k = HashTable<Shape, Key>::KeyAt(i);
7042 if (HashTable<Shape, Key>::IsKey(k)) {
7043 PrintF(" ");
7044 if (k->IsString()) {
7045 String::cast(k)->StringPrint();
7046 } else {
7047 k->ShortPrint();
7048 }
7049 PrintF(": ");
7050 ValueAt(i)->ShortPrint();
7051 PrintF("\n");
7052 }
7053 }
7054}
7055#endif
7056
7057
7058template<typename Shape, typename Key>
7059void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
7060 int pos = 0;
7061 int capacity = HashTable<Shape, Key>::Capacity();
Leon Clarke4515c472010-02-03 11:58:03 +00007062 AssertNoAllocation no_gc;
7063 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007064 for (int i = 0; i < capacity; i++) {
7065 Object* k = Dictionary<Shape, Key>::KeyAt(i);
7066 if (Dictionary<Shape, Key>::IsKey(k)) {
7067 elements->set(pos++, ValueAt(i), mode);
7068 }
7069 }
7070 ASSERT(pos == elements->length());
7071}
7072
7073
7074InterceptorInfo* JSObject::GetNamedInterceptor() {
7075 ASSERT(map()->has_named_interceptor());
7076 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01007077 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00007078 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01007079 constructor->shared()->get_api_func_data()->named_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00007080 return InterceptorInfo::cast(result);
7081}
7082
7083
7084InterceptorInfo* JSObject::GetIndexedInterceptor() {
7085 ASSERT(map()->has_indexed_interceptor());
7086 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01007087 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00007088 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01007089 constructor->shared()->get_api_func_data()->indexed_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00007090 return InterceptorInfo::cast(result);
7091}
7092
7093
John Reck59135872010-11-02 12:39:01 -07007094MaybeObject* JSObject::GetPropertyPostInterceptor(
7095 JSObject* receiver,
7096 String* name,
7097 PropertyAttributes* attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007098 // Check local property in holder, ignore interceptor.
7099 LookupResult result;
7100 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007101 if (result.IsProperty()) {
7102 return GetProperty(receiver, &result, name, attributes);
7103 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007104 // Continue searching via the prototype chain.
7105 Object* pt = GetPrototype();
7106 *attributes = ABSENT;
7107 if (pt == Heap::null_value()) return Heap::undefined_value();
7108 return pt->GetPropertyWithReceiver(receiver, name, attributes);
7109}
7110
7111
John Reck59135872010-11-02 12:39:01 -07007112MaybeObject* JSObject::GetLocalPropertyPostInterceptor(
Steve Blockd0582a62009-12-15 09:54:21 +00007113 JSObject* receiver,
7114 String* name,
7115 PropertyAttributes* attributes) {
7116 // Check local property in holder, ignore interceptor.
7117 LookupResult result;
7118 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007119 if (result.IsProperty()) {
7120 return GetProperty(receiver, &result, name, attributes);
7121 }
7122 return Heap::undefined_value();
Steve Blockd0582a62009-12-15 09:54:21 +00007123}
7124
7125
John Reck59135872010-11-02 12:39:01 -07007126MaybeObject* JSObject::GetPropertyWithInterceptor(
Steve Blocka7e24c12009-10-30 11:49:00 +00007127 JSObject* receiver,
7128 String* name,
7129 PropertyAttributes* attributes) {
7130 InterceptorInfo* interceptor = GetNamedInterceptor();
7131 HandleScope scope;
7132 Handle<JSObject> receiver_handle(receiver);
7133 Handle<JSObject> holder_handle(this);
7134 Handle<String> name_handle(name);
7135
7136 if (!interceptor->getter()->IsUndefined()) {
7137 v8::NamedPropertyGetter getter =
7138 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
7139 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
7140 CustomArguments args(interceptor->data(), receiver, this);
7141 v8::AccessorInfo info(args.end());
7142 v8::Handle<v8::Value> result;
7143 {
7144 // Leaving JavaScript.
7145 VMState state(EXTERNAL);
7146 result = getter(v8::Utils::ToLocal(name_handle), info);
7147 }
7148 RETURN_IF_SCHEDULED_EXCEPTION();
7149 if (!result.IsEmpty()) {
7150 *attributes = NONE;
7151 return *v8::Utils::OpenHandle(*result);
7152 }
7153 }
7154
John Reck59135872010-11-02 12:39:01 -07007155 MaybeObject* result = holder_handle->GetPropertyPostInterceptor(
Steve Blocka7e24c12009-10-30 11:49:00 +00007156 *receiver_handle,
7157 *name_handle,
7158 attributes);
7159 RETURN_IF_SCHEDULED_EXCEPTION();
7160 return result;
7161}
7162
7163
7164bool JSObject::HasRealNamedProperty(String* key) {
7165 // Check access rights if needed.
7166 if (IsAccessCheckNeeded() &&
7167 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
7168 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7169 return false;
7170 }
7171
7172 LookupResult result;
7173 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007174 return result.IsProperty() && (result.type() != INTERCEPTOR);
Steve Blocka7e24c12009-10-30 11:49:00 +00007175}
7176
7177
7178bool JSObject::HasRealElementProperty(uint32_t index) {
7179 // Check access rights if needed.
7180 if (IsAccessCheckNeeded() &&
7181 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
7182 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7183 return false;
7184 }
7185
7186 // Handle [] on String objects.
7187 if (this->IsStringObjectWithCharacterAt(index)) return true;
7188
7189 switch (GetElementsKind()) {
7190 case FAST_ELEMENTS: {
7191 uint32_t length = IsJSArray() ?
7192 static_cast<uint32_t>(
7193 Smi::cast(JSArray::cast(this)->length())->value()) :
7194 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7195 return (index < length) &&
7196 !FixedArray::cast(elements())->get(index)->IsTheHole();
7197 }
7198 case PIXEL_ELEMENTS: {
7199 PixelArray* pixels = PixelArray::cast(elements());
7200 return index < static_cast<uint32_t>(pixels->length());
7201 }
Steve Block3ce2e202009-11-05 08:53:23 +00007202 case EXTERNAL_BYTE_ELEMENTS:
7203 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7204 case EXTERNAL_SHORT_ELEMENTS:
7205 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7206 case EXTERNAL_INT_ELEMENTS:
7207 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7208 case EXTERNAL_FLOAT_ELEMENTS: {
7209 ExternalArray* array = ExternalArray::cast(elements());
7210 return index < static_cast<uint32_t>(array->length());
7211 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007212 case DICTIONARY_ELEMENTS: {
7213 return element_dictionary()->FindEntry(index)
7214 != NumberDictionary::kNotFound;
7215 }
7216 default:
7217 UNREACHABLE();
7218 break;
7219 }
7220 // All possibilities have been handled above already.
7221 UNREACHABLE();
7222 return Heap::null_value();
7223}
7224
7225
7226bool JSObject::HasRealNamedCallbackProperty(String* key) {
7227 // Check access rights if needed.
7228 if (IsAccessCheckNeeded() &&
7229 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
7230 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7231 return false;
7232 }
7233
7234 LookupResult result;
7235 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007236 return result.IsProperty() && (result.type() == CALLBACKS);
Steve Blocka7e24c12009-10-30 11:49:00 +00007237}
7238
7239
7240int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
7241 if (HasFastProperties()) {
7242 DescriptorArray* descs = map()->instance_descriptors();
7243 int result = 0;
7244 for (int i = 0; i < descs->number_of_descriptors(); i++) {
7245 PropertyDetails details = descs->GetDetails(i);
7246 if (details.IsProperty() && (details.attributes() & filter) == 0) {
7247 result++;
7248 }
7249 }
7250 return result;
7251 } else {
7252 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
7253 }
7254}
7255
7256
7257int JSObject::NumberOfEnumProperties() {
7258 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
7259}
7260
7261
7262void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
7263 Object* temp = get(i);
7264 set(i, get(j));
7265 set(j, temp);
7266 if (this != numbers) {
7267 temp = numbers->get(i);
7268 numbers->set(i, numbers->get(j));
7269 numbers->set(j, temp);
7270 }
7271}
7272
7273
7274static void InsertionSortPairs(FixedArray* content,
7275 FixedArray* numbers,
7276 int len) {
7277 for (int i = 1; i < len; i++) {
7278 int j = i;
7279 while (j > 0 &&
7280 (NumberToUint32(numbers->get(j - 1)) >
7281 NumberToUint32(numbers->get(j)))) {
7282 content->SwapPairs(numbers, j - 1, j);
7283 j--;
7284 }
7285 }
7286}
7287
7288
7289void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
7290 // In-place heap sort.
7291 ASSERT(content->length() == numbers->length());
7292
7293 // Bottom-up max-heap construction.
7294 for (int i = 1; i < len; ++i) {
7295 int child_index = i;
7296 while (child_index > 0) {
7297 int parent_index = ((child_index + 1) >> 1) - 1;
7298 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
7299 uint32_t child_value = NumberToUint32(numbers->get(child_index));
7300 if (parent_value < child_value) {
7301 content->SwapPairs(numbers, parent_index, child_index);
7302 } else {
7303 break;
7304 }
7305 child_index = parent_index;
7306 }
7307 }
7308
7309 // Extract elements and create sorted array.
7310 for (int i = len - 1; i > 0; --i) {
7311 // Put max element at the back of the array.
7312 content->SwapPairs(numbers, 0, i);
7313 // Sift down the new top element.
7314 int parent_index = 0;
7315 while (true) {
7316 int child_index = ((parent_index + 1) << 1) - 1;
7317 if (child_index >= i) break;
7318 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
7319 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
7320 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
7321 if (child_index + 1 >= i || child1_value > child2_value) {
7322 if (parent_value > child1_value) break;
7323 content->SwapPairs(numbers, parent_index, child_index);
7324 parent_index = child_index;
7325 } else {
7326 if (parent_value > child2_value) break;
7327 content->SwapPairs(numbers, parent_index, child_index + 1);
7328 parent_index = child_index + 1;
7329 }
7330 }
7331 }
7332}
7333
7334
7335// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
7336void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
7337 ASSERT(this->length() == numbers->length());
7338 // For small arrays, simply use insertion sort.
7339 if (len <= 10) {
7340 InsertionSortPairs(this, numbers, len);
7341 return;
7342 }
7343 // Check the range of indices.
7344 uint32_t min_index = NumberToUint32(numbers->get(0));
7345 uint32_t max_index = min_index;
7346 uint32_t i;
7347 for (i = 1; i < len; i++) {
7348 if (NumberToUint32(numbers->get(i)) < min_index) {
7349 min_index = NumberToUint32(numbers->get(i));
7350 } else if (NumberToUint32(numbers->get(i)) > max_index) {
7351 max_index = NumberToUint32(numbers->get(i));
7352 }
7353 }
7354 if (max_index - min_index + 1 == len) {
7355 // Indices form a contiguous range, unless there are duplicates.
7356 // Do an in-place linear time sort assuming distinct numbers, but
7357 // avoid hanging in case they are not.
7358 for (i = 0; i < len; i++) {
7359 uint32_t p;
7360 uint32_t j = 0;
7361 // While the current element at i is not at its correct position p,
7362 // swap the elements at these two positions.
7363 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
7364 j++ < len) {
7365 SwapPairs(numbers, i, p);
7366 }
7367 }
7368 } else {
7369 HeapSortPairs(this, numbers, len);
7370 return;
7371 }
7372}
7373
7374
7375// Fill in the names of local properties into the supplied storage. The main
7376// purpose of this function is to provide reflection information for the object
7377// mirrors.
7378void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
7379 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
7380 if (HasFastProperties()) {
7381 DescriptorArray* descs = map()->instance_descriptors();
7382 for (int i = 0; i < descs->number_of_descriptors(); i++) {
7383 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
7384 }
7385 ASSERT(storage->length() >= index);
7386 } else {
7387 property_dictionary()->CopyKeysTo(storage);
7388 }
7389}
7390
7391
7392int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
7393 return GetLocalElementKeys(NULL, filter);
7394}
7395
7396
7397int JSObject::NumberOfEnumElements() {
Steve Blockd0582a62009-12-15 09:54:21 +00007398 // Fast case for objects with no elements.
7399 if (!IsJSValue() && HasFastElements()) {
7400 uint32_t length = IsJSArray() ?
7401 static_cast<uint32_t>(
7402 Smi::cast(JSArray::cast(this)->length())->value()) :
7403 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7404 if (length == 0) return 0;
7405 }
7406 // Compute the number of enumerable elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00007407 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
7408}
7409
7410
7411int JSObject::GetLocalElementKeys(FixedArray* storage,
7412 PropertyAttributes filter) {
7413 int counter = 0;
7414 switch (GetElementsKind()) {
7415 case FAST_ELEMENTS: {
7416 int length = IsJSArray() ?
7417 Smi::cast(JSArray::cast(this)->length())->value() :
7418 FixedArray::cast(elements())->length();
7419 for (int i = 0; i < length; i++) {
7420 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
7421 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007422 storage->set(counter, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00007423 }
7424 counter++;
7425 }
7426 }
7427 ASSERT(!storage || storage->length() >= counter);
7428 break;
7429 }
7430 case PIXEL_ELEMENTS: {
7431 int length = PixelArray::cast(elements())->length();
7432 while (counter < length) {
7433 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007434 storage->set(counter, Smi::FromInt(counter));
Steve Blocka7e24c12009-10-30 11:49:00 +00007435 }
7436 counter++;
7437 }
7438 ASSERT(!storage || storage->length() >= counter);
7439 break;
7440 }
Steve Block3ce2e202009-11-05 08:53:23 +00007441 case EXTERNAL_BYTE_ELEMENTS:
7442 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7443 case EXTERNAL_SHORT_ELEMENTS:
7444 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7445 case EXTERNAL_INT_ELEMENTS:
7446 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7447 case EXTERNAL_FLOAT_ELEMENTS: {
7448 int length = ExternalArray::cast(elements())->length();
7449 while (counter < length) {
7450 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007451 storage->set(counter, Smi::FromInt(counter));
Steve Block3ce2e202009-11-05 08:53:23 +00007452 }
7453 counter++;
7454 }
7455 ASSERT(!storage || storage->length() >= counter);
7456 break;
7457 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007458 case DICTIONARY_ELEMENTS: {
7459 if (storage != NULL) {
7460 element_dictionary()->CopyKeysTo(storage, filter);
7461 }
7462 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
7463 break;
7464 }
7465 default:
7466 UNREACHABLE();
7467 break;
7468 }
7469
7470 if (this->IsJSValue()) {
7471 Object* val = JSValue::cast(this)->value();
7472 if (val->IsString()) {
7473 String* str = String::cast(val);
7474 if (storage) {
7475 for (int i = 0; i < str->length(); i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00007476 storage->set(counter + i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00007477 }
7478 }
7479 counter += str->length();
7480 }
7481 }
7482 ASSERT(!storage || storage->length() == counter);
7483 return counter;
7484}
7485
7486
7487int JSObject::GetEnumElementKeys(FixedArray* storage) {
7488 return GetLocalElementKeys(storage,
7489 static_cast<PropertyAttributes>(DONT_ENUM));
7490}
7491
7492
7493bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
7494 ASSERT(other->IsNumber());
7495 return key == static_cast<uint32_t>(other->Number());
7496}
7497
7498
7499uint32_t NumberDictionaryShape::Hash(uint32_t key) {
7500 return ComputeIntegerHash(key);
7501}
7502
7503
7504uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) {
7505 ASSERT(other->IsNumber());
7506 return ComputeIntegerHash(static_cast<uint32_t>(other->Number()));
7507}
7508
7509
John Reck59135872010-11-02 12:39:01 -07007510MaybeObject* NumberDictionaryShape::AsObject(uint32_t key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007511 return Heap::NumberFromUint32(key);
7512}
7513
7514
7515bool StringDictionaryShape::IsMatch(String* key, Object* other) {
7516 // We know that all entries in a hash table had their hash keys created.
7517 // Use that knowledge to have fast failure.
7518 if (key->Hash() != String::cast(other)->Hash()) return false;
7519 return key->Equals(String::cast(other));
7520}
7521
7522
7523uint32_t StringDictionaryShape::Hash(String* key) {
7524 return key->Hash();
7525}
7526
7527
7528uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) {
7529 return String::cast(other)->Hash();
7530}
7531
7532
John Reck59135872010-11-02 12:39:01 -07007533MaybeObject* StringDictionaryShape::AsObject(String* key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007534 return key;
7535}
7536
7537
7538// StringKey simply carries a string object as key.
7539class StringKey : public HashTableKey {
7540 public:
7541 explicit StringKey(String* string) :
7542 string_(string),
7543 hash_(HashForObject(string)) { }
7544
7545 bool IsMatch(Object* string) {
7546 // We know that all entries in a hash table had their hash keys created.
7547 // Use that knowledge to have fast failure.
7548 if (hash_ != HashForObject(string)) {
7549 return false;
7550 }
7551 return string_->Equals(String::cast(string));
7552 }
7553
7554 uint32_t Hash() { return hash_; }
7555
7556 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
7557
7558 Object* AsObject() { return string_; }
7559
7560 String* string_;
7561 uint32_t hash_;
7562};
7563
7564
7565// StringSharedKeys are used as keys in the eval cache.
7566class StringSharedKey : public HashTableKey {
7567 public:
7568 StringSharedKey(String* source, SharedFunctionInfo* shared)
7569 : source_(source), shared_(shared) { }
7570
7571 bool IsMatch(Object* other) {
7572 if (!other->IsFixedArray()) return false;
7573 FixedArray* pair = FixedArray::cast(other);
7574 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7575 if (shared != shared_) return false;
7576 String* source = String::cast(pair->get(1));
7577 return source->Equals(source_);
7578 }
7579
7580 static uint32_t StringSharedHashHelper(String* source,
7581 SharedFunctionInfo* shared) {
7582 uint32_t hash = source->Hash();
7583 if (shared->HasSourceCode()) {
7584 // Instead of using the SharedFunctionInfo pointer in the hash
7585 // code computation, we use a combination of the hash of the
7586 // script source code and the start and end positions. We do
7587 // this to ensure that the cache entries can survive garbage
7588 // collection.
7589 Script* script = Script::cast(shared->script());
7590 hash ^= String::cast(script->source())->Hash();
7591 hash += shared->start_position();
7592 }
7593 return hash;
7594 }
7595
7596 uint32_t Hash() {
7597 return StringSharedHashHelper(source_, shared_);
7598 }
7599
7600 uint32_t HashForObject(Object* obj) {
7601 FixedArray* pair = FixedArray::cast(obj);
7602 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7603 String* source = String::cast(pair->get(1));
7604 return StringSharedHashHelper(source, shared);
7605 }
7606
John Reck59135872010-11-02 12:39:01 -07007607 MUST_USE_RESULT MaybeObject* AsObject() {
7608 Object* obj;
7609 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(2);
7610 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7611 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007612 FixedArray* pair = FixedArray::cast(obj);
7613 pair->set(0, shared_);
7614 pair->set(1, source_);
7615 return pair;
7616 }
7617
7618 private:
7619 String* source_;
7620 SharedFunctionInfo* shared_;
7621};
7622
7623
7624// RegExpKey carries the source and flags of a regular expression as key.
7625class RegExpKey : public HashTableKey {
7626 public:
7627 RegExpKey(String* string, JSRegExp::Flags flags)
7628 : string_(string),
7629 flags_(Smi::FromInt(flags.value())) { }
7630
Steve Block3ce2e202009-11-05 08:53:23 +00007631 // Rather than storing the key in the hash table, a pointer to the
7632 // stored value is stored where the key should be. IsMatch then
7633 // compares the search key to the found object, rather than comparing
7634 // a key to a key.
Steve Blocka7e24c12009-10-30 11:49:00 +00007635 bool IsMatch(Object* obj) {
7636 FixedArray* val = FixedArray::cast(obj);
7637 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
7638 && (flags_ == val->get(JSRegExp::kFlagsIndex));
7639 }
7640
7641 uint32_t Hash() { return RegExpHash(string_, flags_); }
7642
7643 Object* AsObject() {
7644 // Plain hash maps, which is where regexp keys are used, don't
7645 // use this function.
7646 UNREACHABLE();
7647 return NULL;
7648 }
7649
7650 uint32_t HashForObject(Object* obj) {
7651 FixedArray* val = FixedArray::cast(obj);
7652 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
7653 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
7654 }
7655
7656 static uint32_t RegExpHash(String* string, Smi* flags) {
7657 return string->Hash() + flags->value();
7658 }
7659
7660 String* string_;
7661 Smi* flags_;
7662};
7663
7664// Utf8SymbolKey carries a vector of chars as key.
7665class Utf8SymbolKey : public HashTableKey {
7666 public:
7667 explicit Utf8SymbolKey(Vector<const char> string)
Steve Blockd0582a62009-12-15 09:54:21 +00007668 : string_(string), hash_field_(0) { }
Steve Blocka7e24c12009-10-30 11:49:00 +00007669
7670 bool IsMatch(Object* string) {
7671 return String::cast(string)->IsEqualTo(string_);
7672 }
7673
7674 uint32_t Hash() {
Steve Blockd0582a62009-12-15 09:54:21 +00007675 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00007676 unibrow::Utf8InputBuffer<> buffer(string_.start(),
7677 static_cast<unsigned>(string_.length()));
7678 chars_ = buffer.Length();
Steve Blockd0582a62009-12-15 09:54:21 +00007679 hash_field_ = String::ComputeHashField(&buffer, chars_);
7680 uint32_t result = hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00007681 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
7682 return result;
7683 }
7684
7685 uint32_t HashForObject(Object* other) {
7686 return String::cast(other)->Hash();
7687 }
7688
John Reck59135872010-11-02 12:39:01 -07007689 MaybeObject* AsObject() {
Steve Blockd0582a62009-12-15 09:54:21 +00007690 if (hash_field_ == 0) Hash();
7691 return Heap::AllocateSymbol(string_, chars_, hash_field_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007692 }
7693
7694 Vector<const char> string_;
Steve Blockd0582a62009-12-15 09:54:21 +00007695 uint32_t hash_field_;
Steve Blocka7e24c12009-10-30 11:49:00 +00007696 int chars_; // Caches the number of characters when computing the hash code.
7697};
7698
7699
7700// SymbolKey carries a string/symbol object as key.
7701class SymbolKey : public HashTableKey {
7702 public:
7703 explicit SymbolKey(String* string) : string_(string) { }
7704
7705 bool IsMatch(Object* string) {
7706 return String::cast(string)->Equals(string_);
7707 }
7708
7709 uint32_t Hash() { return string_->Hash(); }
7710
7711 uint32_t HashForObject(Object* other) {
7712 return String::cast(other)->Hash();
7713 }
7714
John Reck59135872010-11-02 12:39:01 -07007715 MaybeObject* AsObject() {
Leon Clarkef7060e22010-06-03 12:02:55 +01007716 // Attempt to flatten the string, so that symbols will most often
7717 // be flat strings.
7718 string_ = string_->TryFlattenGetString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007719 // Transform string to symbol if possible.
7720 Map* map = Heap::SymbolMapForString(string_);
7721 if (map != NULL) {
7722 string_->set_map(map);
7723 ASSERT(string_->IsSymbol());
7724 return string_;
7725 }
7726 // Otherwise allocate a new symbol.
7727 StringInputBuffer buffer(string_);
7728 return Heap::AllocateInternalSymbol(&buffer,
7729 string_->length(),
Steve Blockd0582a62009-12-15 09:54:21 +00007730 string_->hash_field());
Steve Blocka7e24c12009-10-30 11:49:00 +00007731 }
7732
7733 static uint32_t StringHash(Object* obj) {
7734 return String::cast(obj)->Hash();
7735 }
7736
7737 String* string_;
7738};
7739
7740
7741template<typename Shape, typename Key>
7742void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
7743 IteratePointers(v, 0, kElementsStartOffset);
7744}
7745
7746
7747template<typename Shape, typename Key>
7748void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
7749 IteratePointers(v,
7750 kElementsStartOffset,
7751 kHeaderSize + length() * kPointerSize);
7752}
7753
7754
7755template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07007756MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
7757 PretenureFlag pretenure) {
Steve Block6ded16b2010-05-10 14:33:55 +01007758 const int kMinCapacity = 32;
7759 int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
7760 if (capacity < kMinCapacity) {
7761 capacity = kMinCapacity; // Guarantee min capacity.
Leon Clarkee46be812010-01-19 14:06:41 +00007762 } else if (capacity > HashTable::kMaxCapacity) {
7763 return Failure::OutOfMemoryException();
7764 }
7765
John Reck59135872010-11-02 12:39:01 -07007766 Object* obj;
7767 { MaybeObject* maybe_obj =
7768 Heap::AllocateHashTable(EntryToIndex(capacity), pretenure);
7769 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00007770 }
John Reck59135872010-11-02 12:39:01 -07007771 HashTable::cast(obj)->SetNumberOfElements(0);
7772 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
7773 HashTable::cast(obj)->SetCapacity(capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007774 return obj;
7775}
7776
7777
Leon Clarkee46be812010-01-19 14:06:41 +00007778// Find entry for key otherwise return kNotFound.
Steve Blocka7e24c12009-10-30 11:49:00 +00007779template<typename Shape, typename Key>
7780int HashTable<Shape, Key>::FindEntry(Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007781 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007782 uint32_t entry = FirstProbe(Shape::Hash(key), capacity);
7783 uint32_t count = 1;
7784 // EnsureCapacity will guarantee the hash table is never full.
7785 while (true) {
7786 Object* element = KeyAt(entry);
7787 if (element->IsUndefined()) break; // Empty entry.
7788 if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
7789 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007790 }
7791 return kNotFound;
7792}
7793
7794
Ben Murdoch3bec4d22010-07-22 14:51:16 +01007795// Find entry for key otherwise return kNotFound.
7796int StringDictionary::FindEntry(String* key) {
7797 if (!key->IsSymbol()) {
7798 return HashTable<StringDictionaryShape, String*>::FindEntry(key);
7799 }
7800
7801 // Optimized for symbol key. Knowledge of the key type allows:
7802 // 1. Move the check if the key is a symbol out of the loop.
7803 // 2. Avoid comparing hash codes in symbol to symbol comparision.
7804 // 3. Detect a case when a dictionary key is not a symbol but the key is.
7805 // In case of positive result the dictionary key may be replaced by
7806 // the symbol with minimal performance penalty. It gives a chance to
7807 // perform further lookups in code stubs (and significant performance boost
7808 // a certain style of code).
7809
7810 // EnsureCapacity will guarantee the hash table is never full.
7811 uint32_t capacity = Capacity();
7812 uint32_t entry = FirstProbe(key->Hash(), capacity);
7813 uint32_t count = 1;
7814
7815 while (true) {
7816 int index = EntryToIndex(entry);
7817 Object* element = get(index);
7818 if (element->IsUndefined()) break; // Empty entry.
7819 if (key == element) return entry;
7820 if (!element->IsSymbol() &&
7821 !element->IsNull() &&
7822 String::cast(element)->Equals(key)) {
7823 // Replace a non-symbol key by the equivalent symbol for faster further
7824 // lookups.
7825 set(index, key);
7826 return entry;
7827 }
7828 ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
7829 entry = NextProbe(entry, count++, capacity);
7830 }
7831 return kNotFound;
7832}
7833
7834
Steve Blocka7e24c12009-10-30 11:49:00 +00007835template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07007836MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007837 int capacity = Capacity();
7838 int nof = NumberOfElements() + n;
Leon Clarkee46be812010-01-19 14:06:41 +00007839 int nod = NumberOfDeletedElements();
7840 // Return if:
7841 // 50% is still free after adding n elements and
7842 // at most 50% of the free elements are deleted elements.
Steve Block6ded16b2010-05-10 14:33:55 +01007843 if (nod <= (capacity - nof) >> 1) {
7844 int needed_free = nof >> 1;
7845 if (nof + needed_free <= capacity) return this;
7846 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007847
Steve Block6ded16b2010-05-10 14:33:55 +01007848 const int kMinCapacityForPretenure = 256;
7849 bool pretenure =
7850 (capacity > kMinCapacityForPretenure) && !Heap::InNewSpace(this);
John Reck59135872010-11-02 12:39:01 -07007851 Object* obj;
7852 { MaybeObject* maybe_obj =
7853 Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
7854 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7855 }
Leon Clarke4515c472010-02-03 11:58:03 +00007856
7857 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00007858 HashTable* table = HashTable::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00007859 WriteBarrierMode mode = table->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007860
7861 // Copy prefix to new array.
7862 for (int i = kPrefixStartIndex;
7863 i < kPrefixStartIndex + Shape::kPrefixSize;
7864 i++) {
7865 table->set(i, get(i), mode);
7866 }
7867 // Rehash the elements.
7868 for (int i = 0; i < capacity; i++) {
7869 uint32_t from_index = EntryToIndex(i);
7870 Object* k = get(from_index);
7871 if (IsKey(k)) {
7872 uint32_t hash = Shape::HashForObject(key, k);
7873 uint32_t insertion_index =
7874 EntryToIndex(table->FindInsertionEntry(hash));
7875 for (int j = 0; j < Shape::kEntrySize; j++) {
7876 table->set(insertion_index + j, get(from_index + j), mode);
7877 }
7878 }
7879 }
7880 table->SetNumberOfElements(NumberOfElements());
Leon Clarkee46be812010-01-19 14:06:41 +00007881 table->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007882 return table;
7883}
7884
7885
7886template<typename Shape, typename Key>
7887uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
7888 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00007889 uint32_t entry = FirstProbe(hash, capacity);
7890 uint32_t count = 1;
7891 // EnsureCapacity will guarantee the hash table is never full.
7892 while (true) {
7893 Object* element = KeyAt(entry);
7894 if (element->IsUndefined() || element->IsNull()) break;
7895 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00007896 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007897 return entry;
7898}
7899
7900// Force instantiation of template instances class.
7901// Please note this list is compiler dependent.
7902
7903template class HashTable<SymbolTableShape, HashTableKey*>;
7904
7905template class HashTable<CompilationCacheShape, HashTableKey*>;
7906
7907template class HashTable<MapCacheShape, HashTableKey*>;
7908
7909template class Dictionary<StringDictionaryShape, String*>;
7910
7911template class Dictionary<NumberDictionaryShape, uint32_t>;
7912
John Reck59135872010-11-02 12:39:01 -07007913template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
Steve Blocka7e24c12009-10-30 11:49:00 +00007914 int);
7915
John Reck59135872010-11-02 12:39:01 -07007916template MaybeObject* Dictionary<StringDictionaryShape, String*>::Allocate(
Steve Blocka7e24c12009-10-30 11:49:00 +00007917 int);
7918
John Reck59135872010-11-02 12:39:01 -07007919template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
Steve Blocka7e24c12009-10-30 11:49:00 +00007920 uint32_t, Object*);
7921
7922template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
7923 Object*);
7924
7925template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
7926 Object*);
7927
7928template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
7929 FixedArray*, PropertyAttributes);
7930
7931template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
7932 int, JSObject::DeleteMode);
7933
7934template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
7935 int, JSObject::DeleteMode);
7936
7937template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
7938 FixedArray*);
7939
7940template int
7941Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
7942 PropertyAttributes);
7943
John Reck59135872010-11-02 12:39:01 -07007944template MaybeObject* Dictionary<StringDictionaryShape, String*>::Add(
Steve Blocka7e24c12009-10-30 11:49:00 +00007945 String*, Object*, PropertyDetails);
7946
John Reck59135872010-11-02 12:39:01 -07007947template MaybeObject*
Steve Blocka7e24c12009-10-30 11:49:00 +00007948Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
7949
7950template int
7951Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
7952 PropertyAttributes);
7953
John Reck59135872010-11-02 12:39:01 -07007954template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Add(
Steve Blocka7e24c12009-10-30 11:49:00 +00007955 uint32_t, Object*, PropertyDetails);
7956
John Reck59135872010-11-02 12:39:01 -07007957template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::
7958 EnsureCapacity(int, uint32_t);
Steve Blocka7e24c12009-10-30 11:49:00 +00007959
John Reck59135872010-11-02 12:39:01 -07007960template MaybeObject* Dictionary<StringDictionaryShape, String*>::
7961 EnsureCapacity(int, String*);
Steve Blocka7e24c12009-10-30 11:49:00 +00007962
John Reck59135872010-11-02 12:39:01 -07007963template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
Steve Blocka7e24c12009-10-30 11:49:00 +00007964 uint32_t, Object*, PropertyDetails, uint32_t);
7965
John Reck59135872010-11-02 12:39:01 -07007966template MaybeObject* Dictionary<StringDictionaryShape, String*>::AddEntry(
Steve Blocka7e24c12009-10-30 11:49:00 +00007967 String*, Object*, PropertyDetails, uint32_t);
7968
7969template
7970int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
7971
7972template
7973int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
7974
Leon Clarkee46be812010-01-19 14:06:41 +00007975template
7976int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
7977
7978
Steve Blocka7e24c12009-10-30 11:49:00 +00007979// Collates undefined and unexisting elements below limit from position
7980// zero of the elements. The object stays in Dictionary mode.
John Reck59135872010-11-02 12:39:01 -07007981MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007982 ASSERT(HasDictionaryElements());
7983 // Must stay in dictionary mode, either because of requires_slow_elements,
7984 // or because we are not going to sort (and therefore compact) all of the
7985 // elements.
7986 NumberDictionary* dict = element_dictionary();
7987 HeapNumber* result_double = NULL;
7988 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
7989 // Allocate space for result before we start mutating the object.
John Reck59135872010-11-02 12:39:01 -07007990 Object* new_double;
7991 { MaybeObject* maybe_new_double = Heap::AllocateHeapNumber(0.0);
7992 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
7993 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007994 result_double = HeapNumber::cast(new_double);
7995 }
7996
John Reck59135872010-11-02 12:39:01 -07007997 Object* obj;
7998 { MaybeObject* maybe_obj =
7999 NumberDictionary::Allocate(dict->NumberOfElements());
8000 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8001 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008002 NumberDictionary* new_dict = NumberDictionary::cast(obj);
8003
8004 AssertNoAllocation no_alloc;
8005
8006 uint32_t pos = 0;
8007 uint32_t undefs = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01008008 int capacity = dict->Capacity();
Steve Blocka7e24c12009-10-30 11:49:00 +00008009 for (int i = 0; i < capacity; i++) {
8010 Object* k = dict->KeyAt(i);
8011 if (dict->IsKey(k)) {
8012 ASSERT(k->IsNumber());
8013 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
8014 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
8015 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
8016 Object* value = dict->ValueAt(i);
8017 PropertyDetails details = dict->DetailsAt(i);
8018 if (details.type() == CALLBACKS) {
8019 // Bail out and do the sorting of undefineds and array holes in JS.
8020 return Smi::FromInt(-1);
8021 }
8022 uint32_t key = NumberToUint32(k);
John Reck59135872010-11-02 12:39:01 -07008023 // In the following we assert that adding the entry to the new dictionary
8024 // does not cause GC. This is the case because we made sure to allocate
8025 // the dictionary big enough above, so it need not grow.
Steve Blocka7e24c12009-10-30 11:49:00 +00008026 if (key < limit) {
8027 if (value->IsUndefined()) {
8028 undefs++;
8029 } else {
John Reck59135872010-11-02 12:39:01 -07008030 new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008031 pos++;
8032 }
8033 } else {
John Reck59135872010-11-02 12:39:01 -07008034 new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008035 }
8036 }
8037 }
8038
8039 uint32_t result = pos;
8040 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
8041 while (undefs > 0) {
John Reck59135872010-11-02 12:39:01 -07008042 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details)->
8043 ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008044 pos++;
8045 undefs--;
8046 }
8047
8048 set_elements(new_dict);
8049
8050 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
8051 return Smi::FromInt(static_cast<int>(result));
8052 }
8053
8054 ASSERT_NE(NULL, result_double);
8055 result_double->set_value(static_cast<double>(result));
8056 return result_double;
8057}
8058
8059
8060// Collects all defined (non-hole) and non-undefined (array) elements at
8061// the start of the elements array.
8062// If the object is in dictionary mode, it is converted to fast elements
8063// mode.
John Reck59135872010-11-02 12:39:01 -07008064MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
Steve Block3ce2e202009-11-05 08:53:23 +00008065 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00008066
8067 if (HasDictionaryElements()) {
8068 // Convert to fast elements containing only the existing properties.
8069 // Ordering is irrelevant, since we are going to sort anyway.
8070 NumberDictionary* dict = element_dictionary();
8071 if (IsJSArray() || dict->requires_slow_elements() ||
8072 dict->max_number_key() >= limit) {
8073 return PrepareSlowElementsForSort(limit);
8074 }
8075 // Convert to fast elements.
8076
John Reck59135872010-11-02 12:39:01 -07008077 Object* obj;
8078 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
8079 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8080 }
Steve Block8defd9f2010-07-08 12:39:36 +01008081 Map* new_map = Map::cast(obj);
8082
Steve Blocka7e24c12009-10-30 11:49:00 +00008083 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
John Reck59135872010-11-02 12:39:01 -07008084 Object* new_array;
8085 { MaybeObject* maybe_new_array =
8086 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
8087 if (!maybe_new_array->ToObject(&new_array)) return maybe_new_array;
8088 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008089 FixedArray* fast_elements = FixedArray::cast(new_array);
8090 dict->CopyValuesTo(fast_elements);
Steve Block8defd9f2010-07-08 12:39:36 +01008091
8092 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00008093 set_elements(fast_elements);
Iain Merrick75681382010-08-19 15:07:18 +01008094 } else {
John Reck59135872010-11-02 12:39:01 -07008095 Object* obj;
8096 { MaybeObject* maybe_obj = EnsureWritableFastElements();
8097 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8098 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008099 }
8100 ASSERT(HasFastElements());
8101
8102 // Collect holes at the end, undefined before that and the rest at the
8103 // start, and return the number of non-hole, non-undefined values.
8104
8105 FixedArray* elements = FixedArray::cast(this->elements());
8106 uint32_t elements_length = static_cast<uint32_t>(elements->length());
8107 if (limit > elements_length) {
8108 limit = elements_length ;
8109 }
8110 if (limit == 0) {
8111 return Smi::FromInt(0);
8112 }
8113
8114 HeapNumber* result_double = NULL;
8115 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
8116 // Pessimistically allocate space for return value before
8117 // we start mutating the array.
John Reck59135872010-11-02 12:39:01 -07008118 Object* new_double;
8119 { MaybeObject* maybe_new_double = Heap::AllocateHeapNumber(0.0);
8120 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
8121 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008122 result_double = HeapNumber::cast(new_double);
8123 }
8124
8125 AssertNoAllocation no_alloc;
8126
8127 // Split elements into defined, undefined and the_hole, in that order.
8128 // Only count locations for undefined and the hole, and fill them afterwards.
Leon Clarke4515c472010-02-03 11:58:03 +00008129 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
Steve Blocka7e24c12009-10-30 11:49:00 +00008130 unsigned int undefs = limit;
8131 unsigned int holes = limit;
8132 // Assume most arrays contain no holes and undefined values, so minimize the
8133 // number of stores of non-undefined, non-the-hole values.
8134 for (unsigned int i = 0; i < undefs; i++) {
8135 Object* current = elements->get(i);
8136 if (current->IsTheHole()) {
8137 holes--;
8138 undefs--;
8139 } else if (current->IsUndefined()) {
8140 undefs--;
8141 } else {
8142 continue;
8143 }
8144 // Position i needs to be filled.
8145 while (undefs > i) {
8146 current = elements->get(undefs);
8147 if (current->IsTheHole()) {
8148 holes--;
8149 undefs--;
8150 } else if (current->IsUndefined()) {
8151 undefs--;
8152 } else {
8153 elements->set(i, current, write_barrier);
8154 break;
8155 }
8156 }
8157 }
8158 uint32_t result = undefs;
8159 while (undefs < holes) {
8160 elements->set_undefined(undefs);
8161 undefs++;
8162 }
8163 while (holes < limit) {
8164 elements->set_the_hole(holes);
8165 holes++;
8166 }
8167
8168 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
8169 return Smi::FromInt(static_cast<int>(result));
8170 }
8171 ASSERT_NE(NULL, result_double);
8172 result_double->set_value(static_cast<double>(result));
8173 return result_double;
8174}
8175
8176
8177Object* PixelArray::SetValue(uint32_t index, Object* value) {
8178 uint8_t clamped_value = 0;
8179 if (index < static_cast<uint32_t>(length())) {
8180 if (value->IsSmi()) {
8181 int int_value = Smi::cast(value)->value();
8182 if (int_value < 0) {
8183 clamped_value = 0;
8184 } else if (int_value > 255) {
8185 clamped_value = 255;
8186 } else {
8187 clamped_value = static_cast<uint8_t>(int_value);
8188 }
8189 } else if (value->IsHeapNumber()) {
8190 double double_value = HeapNumber::cast(value)->value();
8191 if (!(double_value > 0)) {
8192 // NaN and less than zero clamp to zero.
8193 clamped_value = 0;
8194 } else if (double_value > 255) {
8195 // Greater than 255 clamp to 255.
8196 clamped_value = 255;
8197 } else {
8198 // Other doubles are rounded to the nearest integer.
8199 clamped_value = static_cast<uint8_t>(double_value + 0.5);
8200 }
8201 } else {
8202 // Clamp undefined to zero (default). All other types have been
8203 // converted to a number type further up in the call chain.
8204 ASSERT(value->IsUndefined());
8205 }
8206 set(index, clamped_value);
8207 }
8208 return Smi::FromInt(clamped_value);
8209}
8210
8211
Steve Block3ce2e202009-11-05 08:53:23 +00008212template<typename ExternalArrayClass, typename ValueType>
John Reck59135872010-11-02 12:39:01 -07008213static MaybeObject* ExternalArrayIntSetter(ExternalArrayClass* receiver,
8214 uint32_t index,
8215 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008216 ValueType cast_value = 0;
8217 if (index < static_cast<uint32_t>(receiver->length())) {
8218 if (value->IsSmi()) {
8219 int int_value = Smi::cast(value)->value();
8220 cast_value = static_cast<ValueType>(int_value);
8221 } else if (value->IsHeapNumber()) {
8222 double double_value = HeapNumber::cast(value)->value();
8223 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
8224 } else {
8225 // Clamp undefined to zero (default). All other types have been
8226 // converted to a number type further up in the call chain.
8227 ASSERT(value->IsUndefined());
8228 }
8229 receiver->set(index, cast_value);
8230 }
8231 return Heap::NumberFromInt32(cast_value);
8232}
8233
8234
John Reck59135872010-11-02 12:39:01 -07008235MaybeObject* ExternalByteArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008236 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
8237 (this, index, value);
8238}
8239
8240
John Reck59135872010-11-02 12:39:01 -07008241MaybeObject* ExternalUnsignedByteArray::SetValue(uint32_t index,
8242 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008243 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
8244 (this, index, value);
8245}
8246
8247
John Reck59135872010-11-02 12:39:01 -07008248MaybeObject* ExternalShortArray::SetValue(uint32_t index,
8249 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008250 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
8251 (this, index, value);
8252}
8253
8254
John Reck59135872010-11-02 12:39:01 -07008255MaybeObject* ExternalUnsignedShortArray::SetValue(uint32_t index,
8256 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008257 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
8258 (this, index, value);
8259}
8260
8261
John Reck59135872010-11-02 12:39:01 -07008262MaybeObject* ExternalIntArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008263 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
8264 (this, index, value);
8265}
8266
8267
John Reck59135872010-11-02 12:39:01 -07008268MaybeObject* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008269 uint32_t cast_value = 0;
8270 if (index < static_cast<uint32_t>(length())) {
8271 if (value->IsSmi()) {
8272 int int_value = Smi::cast(value)->value();
8273 cast_value = static_cast<uint32_t>(int_value);
8274 } else if (value->IsHeapNumber()) {
8275 double double_value = HeapNumber::cast(value)->value();
8276 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
8277 } else {
8278 // Clamp undefined to zero (default). All other types have been
8279 // converted to a number type further up in the call chain.
8280 ASSERT(value->IsUndefined());
8281 }
8282 set(index, cast_value);
8283 }
8284 return Heap::NumberFromUint32(cast_value);
8285}
8286
8287
John Reck59135872010-11-02 12:39:01 -07008288MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008289 float cast_value = 0;
8290 if (index < static_cast<uint32_t>(length())) {
8291 if (value->IsSmi()) {
8292 int int_value = Smi::cast(value)->value();
8293 cast_value = static_cast<float>(int_value);
8294 } else if (value->IsHeapNumber()) {
8295 double double_value = HeapNumber::cast(value)->value();
8296 cast_value = static_cast<float>(double_value);
8297 } else {
8298 // Clamp undefined to zero (default). All other types have been
8299 // converted to a number type further up in the call chain.
8300 ASSERT(value->IsUndefined());
8301 }
8302 set(index, cast_value);
8303 }
8304 return Heap::AllocateHeapNumber(cast_value);
8305}
8306
8307
Steve Blocka7e24c12009-10-30 11:49:00 +00008308Object* GlobalObject::GetPropertyCell(LookupResult* result) {
8309 ASSERT(!HasFastProperties());
8310 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
8311 ASSERT(value->IsJSGlobalPropertyCell());
8312 return value;
8313}
8314
8315
John Reck59135872010-11-02 12:39:01 -07008316MaybeObject* GlobalObject::EnsurePropertyCell(String* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008317 ASSERT(!HasFastProperties());
8318 int entry = property_dictionary()->FindEntry(name);
8319 if (entry == StringDictionary::kNotFound) {
John Reck59135872010-11-02 12:39:01 -07008320 Object* cell;
8321 { MaybeObject* maybe_cell =
8322 Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
8323 if (!maybe_cell->ToObject(&cell)) return maybe_cell;
8324 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008325 PropertyDetails details(NONE, NORMAL);
8326 details = details.AsDeleted();
John Reck59135872010-11-02 12:39:01 -07008327 Object* dictionary;
8328 { MaybeObject* maybe_dictionary =
8329 property_dictionary()->Add(name, cell, details);
8330 if (!maybe_dictionary->ToObject(&dictionary)) return maybe_dictionary;
8331 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008332 set_properties(StringDictionary::cast(dictionary));
8333 return cell;
8334 } else {
8335 Object* value = property_dictionary()->ValueAt(entry);
8336 ASSERT(value->IsJSGlobalPropertyCell());
8337 return value;
8338 }
8339}
8340
8341
John Reck59135872010-11-02 12:39:01 -07008342MaybeObject* SymbolTable::LookupString(String* string, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008343 SymbolKey key(string);
8344 return LookupKey(&key, s);
8345}
8346
8347
Steve Blockd0582a62009-12-15 09:54:21 +00008348// This class is used for looking up two character strings in the symbol table.
8349// If we don't have a hit we don't want to waste much time so we unroll the
8350// string hash calculation loop here for speed. Doesn't work if the two
8351// characters form a decimal integer, since such strings have a different hash
8352// algorithm.
8353class TwoCharHashTableKey : public HashTableKey {
8354 public:
8355 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
8356 : c1_(c1), c2_(c2) {
8357 // Char 1.
8358 uint32_t hash = c1 + (c1 << 10);
8359 hash ^= hash >> 6;
8360 // Char 2.
8361 hash += c2;
8362 hash += hash << 10;
8363 hash ^= hash >> 6;
8364 // GetHash.
8365 hash += hash << 3;
8366 hash ^= hash >> 11;
8367 hash += hash << 15;
8368 if (hash == 0) hash = 27;
8369#ifdef DEBUG
8370 StringHasher hasher(2);
8371 hasher.AddCharacter(c1);
8372 hasher.AddCharacter(c2);
8373 // If this assert fails then we failed to reproduce the two-character
8374 // version of the string hashing algorithm above. One reason could be
8375 // that we were passed two digits as characters, since the hash
8376 // algorithm is different in that case.
8377 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
8378#endif
8379 hash_ = hash;
8380 }
8381
8382 bool IsMatch(Object* o) {
8383 if (!o->IsString()) return false;
8384 String* other = String::cast(o);
8385 if (other->length() != 2) return false;
8386 if (other->Get(0) != c1_) return false;
8387 return other->Get(1) == c2_;
8388 }
8389
8390 uint32_t Hash() { return hash_; }
8391 uint32_t HashForObject(Object* key) {
8392 if (!key->IsString()) return 0;
8393 return String::cast(key)->Hash();
8394 }
8395
8396 Object* AsObject() {
8397 // The TwoCharHashTableKey is only used for looking in the symbol
8398 // table, not for adding to it.
8399 UNREACHABLE();
8400 return NULL;
8401 }
8402 private:
8403 uint32_t c1_;
8404 uint32_t c2_;
8405 uint32_t hash_;
8406};
8407
8408
Steve Blocka7e24c12009-10-30 11:49:00 +00008409bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
8410 SymbolKey key(string);
8411 int entry = FindEntry(&key);
8412 if (entry == kNotFound) {
8413 return false;
8414 } else {
8415 String* result = String::cast(KeyAt(entry));
8416 ASSERT(StringShape(result).IsSymbol());
8417 *symbol = result;
8418 return true;
8419 }
8420}
8421
8422
Steve Blockd0582a62009-12-15 09:54:21 +00008423bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
8424 uint32_t c2,
8425 String** symbol) {
8426 TwoCharHashTableKey key(c1, c2);
8427 int entry = FindEntry(&key);
8428 if (entry == kNotFound) {
8429 return false;
8430 } else {
8431 String* result = String::cast(KeyAt(entry));
8432 ASSERT(StringShape(result).IsSymbol());
8433 *symbol = result;
8434 return true;
8435 }
8436}
8437
8438
John Reck59135872010-11-02 12:39:01 -07008439MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008440 Utf8SymbolKey key(str);
8441 return LookupKey(&key, s);
8442}
8443
8444
John Reck59135872010-11-02 12:39:01 -07008445MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008446 int entry = FindEntry(key);
8447
8448 // Symbol already in table.
8449 if (entry != kNotFound) {
8450 *s = KeyAt(entry);
8451 return this;
8452 }
8453
8454 // Adding new symbol. Grow table if needed.
John Reck59135872010-11-02 12:39:01 -07008455 Object* obj;
8456 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
8457 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8458 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008459
8460 // Create symbol object.
John Reck59135872010-11-02 12:39:01 -07008461 Object* symbol;
8462 { MaybeObject* maybe_symbol = key->AsObject();
8463 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
8464 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008465
8466 // If the symbol table grew as part of EnsureCapacity, obj is not
8467 // the current symbol table and therefore we cannot use
8468 // SymbolTable::cast here.
8469 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
8470
8471 // Add the new symbol and return it along with the symbol table.
8472 entry = table->FindInsertionEntry(key->Hash());
8473 table->set(EntryToIndex(entry), symbol);
8474 table->ElementAdded();
8475 *s = symbol;
8476 return table;
8477}
8478
8479
8480Object* CompilationCacheTable::Lookup(String* src) {
8481 StringKey key(src);
8482 int entry = FindEntry(&key);
8483 if (entry == kNotFound) return Heap::undefined_value();
8484 return get(EntryToIndex(entry) + 1);
8485}
8486
8487
8488Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
8489 StringSharedKey key(src, context->closure()->shared());
8490 int entry = FindEntry(&key);
8491 if (entry == kNotFound) return Heap::undefined_value();
8492 return get(EntryToIndex(entry) + 1);
8493}
8494
8495
8496Object* CompilationCacheTable::LookupRegExp(String* src,
8497 JSRegExp::Flags flags) {
8498 RegExpKey key(src, flags);
8499 int entry = FindEntry(&key);
8500 if (entry == kNotFound) return Heap::undefined_value();
8501 return get(EntryToIndex(entry) + 1);
8502}
8503
8504
John Reck59135872010-11-02 12:39:01 -07008505MaybeObject* CompilationCacheTable::Put(String* src, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008506 StringKey key(src);
John Reck59135872010-11-02 12:39:01 -07008507 Object* obj;
8508 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
8509 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8510 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008511
8512 CompilationCacheTable* cache =
8513 reinterpret_cast<CompilationCacheTable*>(obj);
8514 int entry = cache->FindInsertionEntry(key.Hash());
8515 cache->set(EntryToIndex(entry), src);
8516 cache->set(EntryToIndex(entry) + 1, value);
8517 cache->ElementAdded();
8518 return cache;
8519}
8520
8521
John Reck59135872010-11-02 12:39:01 -07008522MaybeObject* CompilationCacheTable::PutEval(String* src,
8523 Context* context,
8524 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008525 StringSharedKey key(src, context->closure()->shared());
John Reck59135872010-11-02 12:39:01 -07008526 Object* obj;
8527 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
8528 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8529 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008530
8531 CompilationCacheTable* cache =
8532 reinterpret_cast<CompilationCacheTable*>(obj);
8533 int entry = cache->FindInsertionEntry(key.Hash());
8534
John Reck59135872010-11-02 12:39:01 -07008535 Object* k;
8536 { MaybeObject* maybe_k = key.AsObject();
8537 if (!maybe_k->ToObject(&k)) return maybe_k;
8538 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008539
8540 cache->set(EntryToIndex(entry), k);
8541 cache->set(EntryToIndex(entry) + 1, value);
8542 cache->ElementAdded();
8543 return cache;
8544}
8545
8546
John Reck59135872010-11-02 12:39:01 -07008547MaybeObject* CompilationCacheTable::PutRegExp(String* src,
8548 JSRegExp::Flags flags,
8549 FixedArray* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008550 RegExpKey key(src, flags);
John Reck59135872010-11-02 12:39:01 -07008551 Object* obj;
8552 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
8553 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8554 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008555
8556 CompilationCacheTable* cache =
8557 reinterpret_cast<CompilationCacheTable*>(obj);
8558 int entry = cache->FindInsertionEntry(key.Hash());
Steve Block3ce2e202009-11-05 08:53:23 +00008559 // We store the value in the key slot, and compare the search key
8560 // to the stored value with a custon IsMatch function during lookups.
Steve Blocka7e24c12009-10-30 11:49:00 +00008561 cache->set(EntryToIndex(entry), value);
8562 cache->set(EntryToIndex(entry) + 1, value);
8563 cache->ElementAdded();
8564 return cache;
8565}
8566
8567
8568// SymbolsKey used for HashTable where key is array of symbols.
8569class SymbolsKey : public HashTableKey {
8570 public:
8571 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
8572
8573 bool IsMatch(Object* symbols) {
8574 FixedArray* o = FixedArray::cast(symbols);
8575 int len = symbols_->length();
8576 if (o->length() != len) return false;
8577 for (int i = 0; i < len; i++) {
8578 if (o->get(i) != symbols_->get(i)) return false;
8579 }
8580 return true;
8581 }
8582
8583 uint32_t Hash() { return HashForObject(symbols_); }
8584
8585 uint32_t HashForObject(Object* obj) {
8586 FixedArray* symbols = FixedArray::cast(obj);
8587 int len = symbols->length();
8588 uint32_t hash = 0;
8589 for (int i = 0; i < len; i++) {
8590 hash ^= String::cast(symbols->get(i))->Hash();
8591 }
8592 return hash;
8593 }
8594
8595 Object* AsObject() { return symbols_; }
8596
8597 private:
8598 FixedArray* symbols_;
8599};
8600
8601
8602Object* MapCache::Lookup(FixedArray* array) {
8603 SymbolsKey key(array);
8604 int entry = FindEntry(&key);
8605 if (entry == kNotFound) return Heap::undefined_value();
8606 return get(EntryToIndex(entry) + 1);
8607}
8608
8609
John Reck59135872010-11-02 12:39:01 -07008610MaybeObject* MapCache::Put(FixedArray* array, Map* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008611 SymbolsKey key(array);
John Reck59135872010-11-02 12:39:01 -07008612 Object* obj;
8613 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
8614 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8615 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008616
8617 MapCache* cache = reinterpret_cast<MapCache*>(obj);
8618 int entry = cache->FindInsertionEntry(key.Hash());
8619 cache->set(EntryToIndex(entry), array);
8620 cache->set(EntryToIndex(entry) + 1, value);
8621 cache->ElementAdded();
8622 return cache;
8623}
8624
8625
8626template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008627MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
8628 Object* obj;
8629 { MaybeObject* maybe_obj =
8630 HashTable<Shape, Key>::Allocate(at_least_space_for);
8631 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00008632 }
John Reck59135872010-11-02 12:39:01 -07008633 // Initialize the next enumeration index.
8634 Dictionary<Shape, Key>::cast(obj)->
8635 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
Steve Blocka7e24c12009-10-30 11:49:00 +00008636 return obj;
8637}
8638
8639
8640template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008641MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
Steve Blocka7e24c12009-10-30 11:49:00 +00008642 int length = HashTable<Shape, Key>::NumberOfElements();
8643
8644 // Allocate and initialize iteration order array.
John Reck59135872010-11-02 12:39:01 -07008645 Object* obj;
8646 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(length);
8647 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8648 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008649 FixedArray* iteration_order = FixedArray::cast(obj);
8650 for (int i = 0; i < length; i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00008651 iteration_order->set(i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00008652 }
8653
8654 // Allocate array with enumeration order.
John Reck59135872010-11-02 12:39:01 -07008655 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(length);
8656 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8657 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008658 FixedArray* enumeration_order = FixedArray::cast(obj);
8659
8660 // Fill the enumeration order array with property details.
8661 int capacity = HashTable<Shape, Key>::Capacity();
8662 int pos = 0;
8663 for (int i = 0; i < capacity; i++) {
8664 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
Leon Clarke4515c472010-02-03 11:58:03 +00008665 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008666 }
8667 }
8668
8669 // Sort the arrays wrt. enumeration order.
8670 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
8671
8672 // Overwrite the enumeration_order with the enumeration indices.
8673 for (int i = 0; i < length; i++) {
8674 int index = Smi::cast(iteration_order->get(i))->value();
8675 int enum_index = PropertyDetails::kInitialIndex + i;
Leon Clarke4515c472010-02-03 11:58:03 +00008676 enumeration_order->set(index, Smi::FromInt(enum_index));
Steve Blocka7e24c12009-10-30 11:49:00 +00008677 }
8678
8679 // Update the dictionary with new indices.
8680 capacity = HashTable<Shape, Key>::Capacity();
8681 pos = 0;
8682 for (int i = 0; i < capacity; i++) {
8683 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
8684 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
8685 PropertyDetails details = DetailsAt(i);
8686 PropertyDetails new_details =
8687 PropertyDetails(details.attributes(), details.type(), enum_index);
8688 DetailsAtPut(i, new_details);
8689 }
8690 }
8691
8692 // Set the next enumeration index.
8693 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
8694 return this;
8695}
8696
8697template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008698MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008699 // Check whether there are enough enumeration indices to add n elements.
8700 if (Shape::kIsEnumerable &&
8701 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
8702 // If not, we generate new indices for the properties.
John Reck59135872010-11-02 12:39:01 -07008703 Object* result;
8704 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
8705 if (!maybe_result->ToObject(&result)) return maybe_result;
8706 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008707 }
8708 return HashTable<Shape, Key>::EnsureCapacity(n, key);
8709}
8710
8711
8712void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
8713 // Do nothing if the interval [from, to) is empty.
8714 if (from >= to) return;
8715
8716 int removed_entries = 0;
8717 Object* sentinel = Heap::null_value();
8718 int capacity = Capacity();
8719 for (int i = 0; i < capacity; i++) {
8720 Object* key = KeyAt(i);
8721 if (key->IsNumber()) {
8722 uint32_t number = static_cast<uint32_t>(key->Number());
8723 if (from <= number && number < to) {
8724 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
8725 removed_entries++;
8726 }
8727 }
8728 }
8729
8730 // Update the number of elements.
Leon Clarkee46be812010-01-19 14:06:41 +00008731 ElementsRemoved(removed_entries);
Steve Blocka7e24c12009-10-30 11:49:00 +00008732}
8733
8734
8735template<typename Shape, typename Key>
8736Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
8737 JSObject::DeleteMode mode) {
8738 PropertyDetails details = DetailsAt(entry);
8739 // Ignore attributes if forcing a deletion.
8740 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
8741 return Heap::false_value();
8742 }
8743 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
8744 HashTable<Shape, Key>::ElementRemoved();
8745 return Heap::true_value();
8746}
8747
8748
8749template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008750MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008751 int entry = this->FindEntry(key);
Steve Blocka7e24c12009-10-30 11:49:00 +00008752
8753 // If the entry is present set the value;
8754 if (entry != Dictionary<Shape, Key>::kNotFound) {
8755 ValueAtPut(entry, value);
8756 return this;
8757 }
8758
8759 // Check whether the dictionary should be extended.
John Reck59135872010-11-02 12:39:01 -07008760 Object* obj;
8761 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
8762 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8763 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008764
John Reck59135872010-11-02 12:39:01 -07008765 Object* k;
8766 { MaybeObject* maybe_k = Shape::AsObject(key);
8767 if (!maybe_k->ToObject(&k)) return maybe_k;
8768 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008769 PropertyDetails details = PropertyDetails(NONE, NORMAL);
8770 return Dictionary<Shape, Key>::cast(obj)->
8771 AddEntry(key, value, details, Shape::Hash(key));
8772}
8773
8774
8775template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008776MaybeObject* Dictionary<Shape, Key>::Add(Key key,
8777 Object* value,
8778 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008779 // Valdate key is absent.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008780 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
Steve Blocka7e24c12009-10-30 11:49:00 +00008781 // Check whether the dictionary should be extended.
John Reck59135872010-11-02 12:39:01 -07008782 Object* obj;
8783 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
8784 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8785 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008786 return Dictionary<Shape, Key>::cast(obj)->
8787 AddEntry(key, value, details, Shape::Hash(key));
8788}
8789
8790
8791// Add a key, value pair to the dictionary.
8792template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008793MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key,
8794 Object* value,
8795 PropertyDetails details,
8796 uint32_t hash) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008797 // Compute the key object.
John Reck59135872010-11-02 12:39:01 -07008798 Object* k;
8799 { MaybeObject* maybe_k = Shape::AsObject(key);
8800 if (!maybe_k->ToObject(&k)) return maybe_k;
8801 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008802
8803 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
8804 // Insert element at empty or deleted entry
8805 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
8806 // Assign an enumeration index to the property and update
8807 // SetNextEnumerationIndex.
8808 int index = NextEnumerationIndex();
8809 details = PropertyDetails(details.attributes(), details.type(), index);
8810 SetNextEnumerationIndex(index + 1);
8811 }
8812 SetEntry(entry, k, value, details);
8813 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
8814 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
8815 HashTable<Shape, Key>::ElementAdded();
8816 return this;
8817}
8818
8819
8820void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
8821 // If the dictionary requires slow elements an element has already
8822 // been added at a high index.
8823 if (requires_slow_elements()) return;
8824 // Check if this index is high enough that we should require slow
8825 // elements.
8826 if (key > kRequiresSlowElementsLimit) {
8827 set_requires_slow_elements();
8828 return;
8829 }
8830 // Update max key value.
8831 Object* max_index_object = get(kMaxNumberKeyIndex);
8832 if (!max_index_object->IsSmi() || max_number_key() < key) {
8833 FixedArray::set(kMaxNumberKeyIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00008834 Smi::FromInt(key << kRequiresSlowElementsTagSize));
Steve Blocka7e24c12009-10-30 11:49:00 +00008835 }
8836}
8837
8838
John Reck59135872010-11-02 12:39:01 -07008839MaybeObject* NumberDictionary::AddNumberEntry(uint32_t key,
8840 Object* value,
8841 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008842 UpdateMaxNumberKey(key);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01008843 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
Steve Blocka7e24c12009-10-30 11:49:00 +00008844 return Add(key, value, details);
8845}
8846
8847
John Reck59135872010-11-02 12:39:01 -07008848MaybeObject* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008849 UpdateMaxNumberKey(key);
8850 return AtPut(key, value);
8851}
8852
8853
John Reck59135872010-11-02 12:39:01 -07008854MaybeObject* NumberDictionary::Set(uint32_t key,
8855 Object* value,
8856 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008857 int entry = FindEntry(key);
8858 if (entry == kNotFound) return AddNumberEntry(key, value, details);
8859 // Preserve enumeration index.
8860 details = PropertyDetails(details.attributes(),
8861 details.type(),
8862 DetailsAt(entry).index());
John Reck59135872010-11-02 12:39:01 -07008863 MaybeObject* maybe_object_key = NumberDictionaryShape::AsObject(key);
8864 Object* object_key;
8865 if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
Ben Murdochf87a2032010-10-22 12:50:53 +01008866 SetEntry(entry, object_key, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00008867 return this;
8868}
8869
8870
8871
8872template<typename Shape, typename Key>
8873int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
8874 PropertyAttributes filter) {
8875 int capacity = HashTable<Shape, Key>::Capacity();
8876 int result = 0;
8877 for (int i = 0; i < capacity; i++) {
8878 Object* k = HashTable<Shape, Key>::KeyAt(i);
8879 if (HashTable<Shape, Key>::IsKey(k)) {
8880 PropertyDetails details = DetailsAt(i);
8881 if (details.IsDeleted()) continue;
8882 PropertyAttributes attr = details.attributes();
8883 if ((attr & filter) == 0) result++;
8884 }
8885 }
8886 return result;
8887}
8888
8889
8890template<typename Shape, typename Key>
8891int Dictionary<Shape, Key>::NumberOfEnumElements() {
8892 return NumberOfElementsFilterAttributes(
8893 static_cast<PropertyAttributes>(DONT_ENUM));
8894}
8895
8896
8897template<typename Shape, typename Key>
8898void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
8899 PropertyAttributes filter) {
8900 ASSERT(storage->length() >= NumberOfEnumElements());
8901 int capacity = HashTable<Shape, Key>::Capacity();
8902 int index = 0;
8903 for (int i = 0; i < capacity; i++) {
8904 Object* k = HashTable<Shape, Key>::KeyAt(i);
8905 if (HashTable<Shape, Key>::IsKey(k)) {
8906 PropertyDetails details = DetailsAt(i);
8907 if (details.IsDeleted()) continue;
8908 PropertyAttributes attr = details.attributes();
8909 if ((attr & filter) == 0) storage->set(index++, k);
8910 }
8911 }
8912 storage->SortPairs(storage, index);
8913 ASSERT(storage->length() >= index);
8914}
8915
8916
8917void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
8918 FixedArray* sort_array) {
8919 ASSERT(storage->length() >= NumberOfEnumElements());
8920 int capacity = Capacity();
8921 int index = 0;
8922 for (int i = 0; i < capacity; i++) {
8923 Object* k = KeyAt(i);
8924 if (IsKey(k)) {
8925 PropertyDetails details = DetailsAt(i);
8926 if (details.IsDeleted() || details.IsDontEnum()) continue;
8927 storage->set(index, k);
Leon Clarke4515c472010-02-03 11:58:03 +00008928 sort_array->set(index, Smi::FromInt(details.index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00008929 index++;
8930 }
8931 }
8932 storage->SortPairs(sort_array, sort_array->length());
8933 ASSERT(storage->length() >= index);
8934}
8935
8936
8937template<typename Shape, typename Key>
8938void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
8939 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
8940 static_cast<PropertyAttributes>(NONE)));
8941 int capacity = HashTable<Shape, Key>::Capacity();
8942 int index = 0;
8943 for (int i = 0; i < capacity; i++) {
8944 Object* k = HashTable<Shape, Key>::KeyAt(i);
8945 if (HashTable<Shape, Key>::IsKey(k)) {
8946 PropertyDetails details = DetailsAt(i);
8947 if (details.IsDeleted()) continue;
8948 storage->set(index++, k);
8949 }
8950 }
8951 ASSERT(storage->length() >= index);
8952}
8953
8954
8955// Backwards lookup (slow).
8956template<typename Shape, typename Key>
8957Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
8958 int capacity = HashTable<Shape, Key>::Capacity();
8959 for (int i = 0; i < capacity; i++) {
8960 Object* k = HashTable<Shape, Key>::KeyAt(i);
8961 if (Dictionary<Shape, Key>::IsKey(k)) {
8962 Object* e = ValueAt(i);
8963 if (e->IsJSGlobalPropertyCell()) {
8964 e = JSGlobalPropertyCell::cast(e)->value();
8965 }
8966 if (e == value) return k;
8967 }
8968 }
8969 return Heap::undefined_value();
8970}
8971
8972
John Reck59135872010-11-02 12:39:01 -07008973MaybeObject* StringDictionary::TransformPropertiesToFastFor(
Steve Blocka7e24c12009-10-30 11:49:00 +00008974 JSObject* obj, int unused_property_fields) {
8975 // Make sure we preserve dictionary representation if there are too many
8976 // descriptors.
8977 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
8978
8979 // Figure out if it is necessary to generate new enumeration indices.
8980 int max_enumeration_index =
8981 NextEnumerationIndex() +
8982 (DescriptorArray::kMaxNumberOfDescriptors -
8983 NumberOfElements());
8984 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
John Reck59135872010-11-02 12:39:01 -07008985 Object* result;
8986 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
8987 if (!maybe_result->ToObject(&result)) return maybe_result;
8988 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008989 }
8990
8991 int instance_descriptor_length = 0;
8992 int number_of_fields = 0;
8993
8994 // Compute the length of the instance descriptor.
8995 int capacity = Capacity();
8996 for (int i = 0; i < capacity; i++) {
8997 Object* k = KeyAt(i);
8998 if (IsKey(k)) {
8999 Object* value = ValueAt(i);
9000 PropertyType type = DetailsAt(i).type();
9001 ASSERT(type != FIELD);
9002 instance_descriptor_length++;
Leon Clarkee46be812010-01-19 14:06:41 +00009003 if (type == NORMAL &&
9004 (!value->IsJSFunction() || Heap::InNewSpace(value))) {
9005 number_of_fields += 1;
9006 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009007 }
9008 }
9009
9010 // Allocate the instance descriptor.
John Reck59135872010-11-02 12:39:01 -07009011 Object* descriptors_unchecked;
9012 { MaybeObject* maybe_descriptors_unchecked =
9013 DescriptorArray::Allocate(instance_descriptor_length);
9014 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
9015 return maybe_descriptors_unchecked;
9016 }
9017 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009018 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
9019
9020 int inobject_props = obj->map()->inobject_properties();
9021 int number_of_allocated_fields =
9022 number_of_fields + unused_property_fields - inobject_props;
Ben Murdochf87a2032010-10-22 12:50:53 +01009023 if (number_of_allocated_fields < 0) {
9024 // There is enough inobject space for all fields (including unused).
9025 number_of_allocated_fields = 0;
9026 unused_property_fields = inobject_props - number_of_fields;
9027 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009028
9029 // Allocate the fixed array for the fields.
John Reck59135872010-11-02 12:39:01 -07009030 Object* fields;
9031 { MaybeObject* maybe_fields =
9032 Heap::AllocateFixedArray(number_of_allocated_fields);
9033 if (!maybe_fields->ToObject(&fields)) return maybe_fields;
9034 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009035
9036 // Fill in the instance descriptor and the fields.
9037 int next_descriptor = 0;
9038 int current_offset = 0;
9039 for (int i = 0; i < capacity; i++) {
9040 Object* k = KeyAt(i);
9041 if (IsKey(k)) {
9042 Object* value = ValueAt(i);
9043 // Ensure the key is a symbol before writing into the instance descriptor.
John Reck59135872010-11-02 12:39:01 -07009044 Object* key;
9045 { MaybeObject* maybe_key = Heap::LookupSymbol(String::cast(k));
9046 if (!maybe_key->ToObject(&key)) return maybe_key;
9047 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009048 PropertyDetails details = DetailsAt(i);
9049 PropertyType type = details.type();
9050
Leon Clarkee46be812010-01-19 14:06:41 +00009051 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009052 ConstantFunctionDescriptor d(String::cast(key),
9053 JSFunction::cast(value),
9054 details.attributes(),
9055 details.index());
9056 descriptors->Set(next_descriptor++, &d);
9057 } else if (type == NORMAL) {
9058 if (current_offset < inobject_props) {
9059 obj->InObjectPropertyAtPut(current_offset,
9060 value,
9061 UPDATE_WRITE_BARRIER);
9062 } else {
9063 int offset = current_offset - inobject_props;
9064 FixedArray::cast(fields)->set(offset, value);
9065 }
9066 FieldDescriptor d(String::cast(key),
9067 current_offset++,
9068 details.attributes(),
9069 details.index());
9070 descriptors->Set(next_descriptor++, &d);
9071 } else if (type == CALLBACKS) {
9072 CallbacksDescriptor d(String::cast(key),
9073 value,
9074 details.attributes(),
9075 details.index());
9076 descriptors->Set(next_descriptor++, &d);
9077 } else {
9078 UNREACHABLE();
9079 }
9080 }
9081 }
9082 ASSERT(current_offset == number_of_fields);
9083
9084 descriptors->Sort();
9085 // Allocate new map.
John Reck59135872010-11-02 12:39:01 -07009086 Object* new_map;
9087 { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
9088 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
9089 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009090
9091 // Transform the object.
9092 obj->set_map(Map::cast(new_map));
9093 obj->map()->set_instance_descriptors(descriptors);
9094 obj->map()->set_unused_property_fields(unused_property_fields);
9095
9096 obj->set_properties(FixedArray::cast(fields));
9097 ASSERT(obj->IsJSObject());
9098
9099 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
9100 // Check that it really works.
9101 ASSERT(obj->HasFastProperties());
9102
9103 return obj;
9104}
9105
9106
9107#ifdef ENABLE_DEBUGGER_SUPPORT
9108// Check if there is a break point at this code position.
9109bool DebugInfo::HasBreakPoint(int code_position) {
9110 // Get the break point info object for this code position.
9111 Object* break_point_info = GetBreakPointInfo(code_position);
9112
9113 // If there is no break point info object or no break points in the break
9114 // point info object there is no break point at this code position.
9115 if (break_point_info->IsUndefined()) return false;
9116 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
9117}
9118
9119
9120// Get the break point info object for this code position.
9121Object* DebugInfo::GetBreakPointInfo(int code_position) {
9122 // Find the index of the break point info object for this code position.
9123 int index = GetBreakPointInfoIndex(code_position);
9124
9125 // Return the break point info object if any.
9126 if (index == kNoBreakPointInfo) return Heap::undefined_value();
9127 return BreakPointInfo::cast(break_points()->get(index));
9128}
9129
9130
9131// Clear a break point at the specified code position.
9132void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
9133 int code_position,
9134 Handle<Object> break_point_object) {
9135 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
9136 if (break_point_info->IsUndefined()) return;
9137 BreakPointInfo::ClearBreakPoint(
9138 Handle<BreakPointInfo>::cast(break_point_info),
9139 break_point_object);
9140}
9141
9142
9143void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
9144 int code_position,
9145 int source_position,
9146 int statement_position,
9147 Handle<Object> break_point_object) {
9148 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
9149 if (!break_point_info->IsUndefined()) {
9150 BreakPointInfo::SetBreakPoint(
9151 Handle<BreakPointInfo>::cast(break_point_info),
9152 break_point_object);
9153 return;
9154 }
9155
9156 // Adding a new break point for a code position which did not have any
9157 // break points before. Try to find a free slot.
9158 int index = kNoBreakPointInfo;
9159 for (int i = 0; i < debug_info->break_points()->length(); i++) {
9160 if (debug_info->break_points()->get(i)->IsUndefined()) {
9161 index = i;
9162 break;
9163 }
9164 }
9165 if (index == kNoBreakPointInfo) {
9166 // No free slot - extend break point info array.
9167 Handle<FixedArray> old_break_points =
9168 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
Steve Blocka7e24c12009-10-30 11:49:00 +00009169 Handle<FixedArray> new_break_points =
Kristian Monsen0d5e1162010-09-30 15:31:59 +01009170 Factory::NewFixedArray(old_break_points->length() +
9171 Debug::kEstimatedNofBreakPointsInFunction);
9172
9173 debug_info->set_break_points(*new_break_points);
Steve Blocka7e24c12009-10-30 11:49:00 +00009174 for (int i = 0; i < old_break_points->length(); i++) {
9175 new_break_points->set(i, old_break_points->get(i));
9176 }
9177 index = old_break_points->length();
9178 }
9179 ASSERT(index != kNoBreakPointInfo);
9180
9181 // Allocate new BreakPointInfo object and set the break point.
9182 Handle<BreakPointInfo> new_break_point_info =
9183 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
9184 new_break_point_info->set_code_position(Smi::FromInt(code_position));
9185 new_break_point_info->set_source_position(Smi::FromInt(source_position));
9186 new_break_point_info->
9187 set_statement_position(Smi::FromInt(statement_position));
9188 new_break_point_info->set_break_point_objects(Heap::undefined_value());
9189 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
9190 debug_info->break_points()->set(index, *new_break_point_info);
9191}
9192
9193
9194// Get the break point objects for a code position.
9195Object* DebugInfo::GetBreakPointObjects(int code_position) {
9196 Object* break_point_info = GetBreakPointInfo(code_position);
9197 if (break_point_info->IsUndefined()) {
9198 return Heap::undefined_value();
9199 }
9200 return BreakPointInfo::cast(break_point_info)->break_point_objects();
9201}
9202
9203
9204// Get the total number of break points.
9205int DebugInfo::GetBreakPointCount() {
9206 if (break_points()->IsUndefined()) return 0;
9207 int count = 0;
9208 for (int i = 0; i < break_points()->length(); i++) {
9209 if (!break_points()->get(i)->IsUndefined()) {
9210 BreakPointInfo* break_point_info =
9211 BreakPointInfo::cast(break_points()->get(i));
9212 count += break_point_info->GetBreakPointCount();
9213 }
9214 }
9215 return count;
9216}
9217
9218
9219Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
9220 Handle<Object> break_point_object) {
9221 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
9222 for (int i = 0; i < debug_info->break_points()->length(); i++) {
9223 if (!debug_info->break_points()->get(i)->IsUndefined()) {
9224 Handle<BreakPointInfo> break_point_info =
9225 Handle<BreakPointInfo>(BreakPointInfo::cast(
9226 debug_info->break_points()->get(i)));
9227 if (BreakPointInfo::HasBreakPointObject(break_point_info,
9228 break_point_object)) {
9229 return *break_point_info;
9230 }
9231 }
9232 }
9233 return Heap::undefined_value();
9234}
9235
9236
9237// Find the index of the break point info object for the specified code
9238// position.
9239int DebugInfo::GetBreakPointInfoIndex(int code_position) {
9240 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
9241 for (int i = 0; i < break_points()->length(); i++) {
9242 if (!break_points()->get(i)->IsUndefined()) {
9243 BreakPointInfo* break_point_info =
9244 BreakPointInfo::cast(break_points()->get(i));
9245 if (break_point_info->code_position()->value() == code_position) {
9246 return i;
9247 }
9248 }
9249 }
9250 return kNoBreakPointInfo;
9251}
9252
9253
9254// Remove the specified break point object.
9255void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
9256 Handle<Object> break_point_object) {
9257 // If there are no break points just ignore.
9258 if (break_point_info->break_point_objects()->IsUndefined()) return;
9259 // If there is a single break point clear it if it is the same.
9260 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9261 if (break_point_info->break_point_objects() == *break_point_object) {
9262 break_point_info->set_break_point_objects(Heap::undefined_value());
9263 }
9264 return;
9265 }
9266 // If there are multiple break points shrink the array
9267 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
9268 Handle<FixedArray> old_array =
9269 Handle<FixedArray>(
9270 FixedArray::cast(break_point_info->break_point_objects()));
9271 Handle<FixedArray> new_array =
9272 Factory::NewFixedArray(old_array->length() - 1);
9273 int found_count = 0;
9274 for (int i = 0; i < old_array->length(); i++) {
9275 if (old_array->get(i) == *break_point_object) {
9276 ASSERT(found_count == 0);
9277 found_count++;
9278 } else {
9279 new_array->set(i - found_count, old_array->get(i));
9280 }
9281 }
9282 // If the break point was found in the list change it.
9283 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
9284}
9285
9286
9287// Add the specified break point object.
9288void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
9289 Handle<Object> break_point_object) {
9290 // If there was no break point objects before just set it.
9291 if (break_point_info->break_point_objects()->IsUndefined()) {
9292 break_point_info->set_break_point_objects(*break_point_object);
9293 return;
9294 }
9295 // If the break point object is the same as before just ignore.
9296 if (break_point_info->break_point_objects() == *break_point_object) return;
9297 // If there was one break point object before replace with array.
9298 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9299 Handle<FixedArray> array = Factory::NewFixedArray(2);
9300 array->set(0, break_point_info->break_point_objects());
9301 array->set(1, *break_point_object);
9302 break_point_info->set_break_point_objects(*array);
9303 return;
9304 }
9305 // If there was more than one break point before extend array.
9306 Handle<FixedArray> old_array =
9307 Handle<FixedArray>(
9308 FixedArray::cast(break_point_info->break_point_objects()));
9309 Handle<FixedArray> new_array =
9310 Factory::NewFixedArray(old_array->length() + 1);
9311 for (int i = 0; i < old_array->length(); i++) {
9312 // If the break point was there before just ignore.
9313 if (old_array->get(i) == *break_point_object) return;
9314 new_array->set(i, old_array->get(i));
9315 }
9316 // Add the new break point.
9317 new_array->set(old_array->length(), *break_point_object);
9318 break_point_info->set_break_point_objects(*new_array);
9319}
9320
9321
9322bool BreakPointInfo::HasBreakPointObject(
9323 Handle<BreakPointInfo> break_point_info,
9324 Handle<Object> break_point_object) {
9325 // No break point.
9326 if (break_point_info->break_point_objects()->IsUndefined()) return false;
9327 // Single beak point.
9328 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9329 return break_point_info->break_point_objects() == *break_point_object;
9330 }
9331 // Multiple break points.
9332 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
9333 for (int i = 0; i < array->length(); i++) {
9334 if (array->get(i) == *break_point_object) {
9335 return true;
9336 }
9337 }
9338 return false;
9339}
9340
9341
9342// Get the number of break points.
9343int BreakPointInfo::GetBreakPointCount() {
9344 // No break point.
9345 if (break_point_objects()->IsUndefined()) return 0;
9346 // Single beak point.
9347 if (!break_point_objects()->IsFixedArray()) return 1;
9348 // Multiple break points.
9349 return FixedArray::cast(break_point_objects())->length();
9350}
9351#endif
9352
9353
9354} } // namespace v8::internal