blob: 4c0059188d3e339e0501822daba55e3f34f1ec8e [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"
Ben Murdochb0fe1622011-05-05 13:52:32 +010033#include "codegen.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000034#include "debug.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010035#include "deoptimizer.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000036#include "execution.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010037#include "full-codegen.h"
38#include "hydrogen.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000039#include "objects-inl.h"
Iain Merrick75681382010-08-19 15:07:18 +010040#include "objects-visiting.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000041#include "macro-assembler.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010042#include "safepoint-table.h"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080043#include "scanner-base.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000044#include "scopeinfo.h"
45#include "string-stream.h"
Steve Blockd0582a62009-12-15 09:54:21 +000046#include "utils.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010047#include "vm-state-inl.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000048
49#ifdef ENABLE_DISASSEMBLER
Ben Murdochb0fe1622011-05-05 13:52:32 +010050#include "disasm.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000051#include "disassembler.h"
52#endif
53
54
55namespace v8 {
56namespace internal {
57
58// Getters and setters are stored in a fixed array property. These are
59// constants for their indices.
60const int kGetterIndex = 0;
61const int kSetterIndex = 1;
62
63
John Reck59135872010-11-02 12:39:01 -070064MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor,
65 Object* value) {
66 Object* result;
67 { MaybeObject* maybe_result = Heap::AllocateJSObject(constructor);
68 if (!maybe_result->ToObject(&result)) return maybe_result;
69 }
Steve Blocka7e24c12009-10-30 11:49:00 +000070 JSValue::cast(result)->set_value(value);
71 return result;
72}
73
74
John Reck59135872010-11-02 12:39:01 -070075MaybeObject* Object::ToObject(Context* global_context) {
Steve Blocka7e24c12009-10-30 11:49:00 +000076 if (IsNumber()) {
77 return CreateJSValue(global_context->number_function(), this);
78 } else if (IsBoolean()) {
79 return CreateJSValue(global_context->boolean_function(), this);
80 } else if (IsString()) {
81 return CreateJSValue(global_context->string_function(), this);
82 }
83 ASSERT(IsJSObject());
84 return this;
85}
86
87
John Reck59135872010-11-02 12:39:01 -070088MaybeObject* Object::ToObject() {
Steve Blocka7e24c12009-10-30 11:49:00 +000089 Context* global_context = Top::context()->global_context();
90 if (IsJSObject()) {
91 return this;
92 } else if (IsNumber()) {
93 return CreateJSValue(global_context->number_function(), this);
94 } else if (IsBoolean()) {
95 return CreateJSValue(global_context->boolean_function(), this);
96 } else if (IsString()) {
97 return CreateJSValue(global_context->string_function(), this);
98 }
99
100 // Throw a type error.
101 return Failure::InternalError();
102}
103
104
105Object* Object::ToBoolean() {
106 if (IsTrue()) return Heap::true_value();
107 if (IsFalse()) return Heap::false_value();
108 if (IsSmi()) {
109 return Heap::ToBoolean(Smi::cast(this)->value() != 0);
110 }
111 if (IsUndefined() || IsNull()) return Heap::false_value();
112 // Undetectable object is false
113 if (IsUndetectableObject()) {
114 return Heap::false_value();
115 }
116 if (IsString()) {
117 return Heap::ToBoolean(String::cast(this)->length() != 0);
118 }
119 if (IsHeapNumber()) {
120 return HeapNumber::cast(this)->HeapNumberToBoolean();
121 }
122 return Heap::true_value();
123}
124
125
126void Object::Lookup(String* name, LookupResult* result) {
127 if (IsJSObject()) return JSObject::cast(this)->Lookup(name, result);
128 Object* holder = NULL;
129 Context* global_context = Top::context()->global_context();
130 if (IsString()) {
131 holder = global_context->string_function()->instance_prototype();
132 } else if (IsNumber()) {
133 holder = global_context->number_function()->instance_prototype();
134 } else if (IsBoolean()) {
135 holder = global_context->boolean_function()->instance_prototype();
136 }
137 ASSERT(holder != NULL); // Cannot handle null or undefined.
138 JSObject::cast(holder)->Lookup(name, result);
139}
140
141
John Reck59135872010-11-02 12:39:01 -0700142MaybeObject* Object::GetPropertyWithReceiver(Object* receiver,
143 String* name,
144 PropertyAttributes* attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000145 LookupResult result;
146 Lookup(name, &result);
John Reck59135872010-11-02 12:39:01 -0700147 MaybeObject* value = GetProperty(receiver, &result, name, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +0000148 ASSERT(*attributes <= ABSENT);
149 return value;
150}
151
152
John Reck59135872010-11-02 12:39:01 -0700153MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
154 Object* structure,
155 String* name,
156 Object* holder) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000157 // To accommodate both the old and the new api we switch on the
158 // data structure used to store the callbacks. Eventually proxy
159 // callbacks should be phased out.
160 if (structure->IsProxy()) {
161 AccessorDescriptor* callback =
162 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
John Reck59135872010-11-02 12:39:01 -0700163 MaybeObject* value = (callback->getter)(receiver, callback->data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000164 RETURN_IF_SCHEDULED_EXCEPTION();
165 return value;
166 }
167
168 // api style callbacks.
169 if (structure->IsAccessorInfo()) {
170 AccessorInfo* data = AccessorInfo::cast(structure);
171 Object* fun_obj = data->getter();
172 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
173 HandleScope scope;
174 JSObject* self = JSObject::cast(receiver);
175 JSObject* holder_handle = JSObject::cast(holder);
176 Handle<String> key(name);
177 LOG(ApiNamedPropertyAccess("load", self, name));
178 CustomArguments args(data->data(), self, holder_handle);
179 v8::AccessorInfo info(args.end());
180 v8::Handle<v8::Value> result;
181 {
182 // Leaving JavaScript.
183 VMState state(EXTERNAL);
184 result = call_fun(v8::Utils::ToLocal(key), info);
185 }
186 RETURN_IF_SCHEDULED_EXCEPTION();
187 if (result.IsEmpty()) return Heap::undefined_value();
188 return *v8::Utils::OpenHandle(*result);
189 }
190
191 // __defineGetter__ callback
192 if (structure->IsFixedArray()) {
193 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
194 if (getter->IsJSFunction()) {
195 return Object::GetPropertyWithDefinedGetter(receiver,
196 JSFunction::cast(getter));
197 }
198 // Getter is not a function.
199 return Heap::undefined_value();
200 }
201
202 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +0100203 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000204}
205
206
John Reck59135872010-11-02 12:39:01 -0700207MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver,
208 JSFunction* getter) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000209 HandleScope scope;
210 Handle<JSFunction> fun(JSFunction::cast(getter));
211 Handle<Object> self(receiver);
212#ifdef ENABLE_DEBUGGER_SUPPORT
213 // Handle stepping into a getter if step into is active.
214 if (Debug::StepInActive()) {
215 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
216 }
217#endif
218 bool has_pending_exception;
219 Handle<Object> result =
220 Execution::Call(fun, self, 0, NULL, &has_pending_exception);
221 // Check for pending exception and return the result.
222 if (has_pending_exception) return Failure::Exception();
223 return *result;
224}
225
226
227// Only deal with CALLBACKS and INTERCEPTOR
John Reck59135872010-11-02 12:39:01 -0700228MaybeObject* JSObject::GetPropertyWithFailedAccessCheck(
Steve Blocka7e24c12009-10-30 11:49:00 +0000229 Object* receiver,
230 LookupResult* result,
231 String* name,
232 PropertyAttributes* attributes) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000233 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000234 switch (result->type()) {
235 case CALLBACKS: {
236 // Only allow API accessors.
237 Object* obj = result->GetCallbackObject();
238 if (obj->IsAccessorInfo()) {
239 AccessorInfo* info = AccessorInfo::cast(obj);
240 if (info->all_can_read()) {
241 *attributes = result->GetAttributes();
242 return GetPropertyWithCallback(receiver,
243 result->GetCallbackObject(),
244 name,
245 result->holder());
246 }
247 }
248 break;
249 }
250 case NORMAL:
251 case FIELD:
252 case CONSTANT_FUNCTION: {
253 // Search ALL_CAN_READ accessors in prototype chain.
254 LookupResult r;
255 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000256 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000257 return GetPropertyWithFailedAccessCheck(receiver,
258 &r,
259 name,
260 attributes);
261 }
262 break;
263 }
264 case INTERCEPTOR: {
265 // If the object has an interceptor, try real named properties.
266 // No access check in GetPropertyAttributeWithInterceptor.
267 LookupResult r;
268 result->holder()->LookupRealNamedProperty(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000269 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000270 return GetPropertyWithFailedAccessCheck(receiver,
271 &r,
272 name,
273 attributes);
274 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000275 break;
276 }
Andrei Popescu402d9372010-02-26 13:31:12 +0000277 default:
278 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +0000279 }
280 }
281
282 // No accessible property found.
283 *attributes = ABSENT;
284 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
285 return Heap::undefined_value();
286}
287
288
289PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
290 Object* receiver,
291 LookupResult* result,
292 String* name,
293 bool continue_search) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000294 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000295 switch (result->type()) {
296 case CALLBACKS: {
297 // Only allow API accessors.
298 Object* obj = result->GetCallbackObject();
299 if (obj->IsAccessorInfo()) {
300 AccessorInfo* info = AccessorInfo::cast(obj);
301 if (info->all_can_read()) {
302 return result->GetAttributes();
303 }
304 }
305 break;
306 }
307
308 case NORMAL:
309 case FIELD:
310 case CONSTANT_FUNCTION: {
311 if (!continue_search) break;
312 // Search ALL_CAN_READ accessors in prototype chain.
313 LookupResult r;
314 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
Andrei Popescu402d9372010-02-26 13:31:12 +0000315 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000316 return GetPropertyAttributeWithFailedAccessCheck(receiver,
317 &r,
318 name,
319 continue_search);
320 }
321 break;
322 }
323
324 case INTERCEPTOR: {
325 // If the object has an interceptor, try real named properties.
326 // No access check in GetPropertyAttributeWithInterceptor.
327 LookupResult r;
328 if (continue_search) {
329 result->holder()->LookupRealNamedProperty(name, &r);
330 } else {
331 result->holder()->LocalLookupRealNamedProperty(name, &r);
332 }
Andrei Popescu402d9372010-02-26 13:31:12 +0000333 if (r.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000334 return GetPropertyAttributeWithFailedAccessCheck(receiver,
335 &r,
336 name,
337 continue_search);
338 }
339 break;
340 }
341
Andrei Popescu402d9372010-02-26 13:31:12 +0000342 default:
343 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +0000344 }
345 }
346
347 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
348 return ABSENT;
349}
350
351
Steve Blocka7e24c12009-10-30 11:49:00 +0000352Object* JSObject::GetNormalizedProperty(LookupResult* result) {
353 ASSERT(!HasFastProperties());
354 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
355 if (IsGlobalObject()) {
356 value = JSGlobalPropertyCell::cast(value)->value();
357 }
358 ASSERT(!value->IsJSGlobalPropertyCell());
359 return value;
360}
361
362
363Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) {
364 ASSERT(!HasFastProperties());
365 if (IsGlobalObject()) {
366 JSGlobalPropertyCell* cell =
367 JSGlobalPropertyCell::cast(
368 property_dictionary()->ValueAt(result->GetDictionaryEntry()));
369 cell->set_value(value);
370 } else {
371 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
372 }
373 return value;
374}
375
376
John Reck59135872010-11-02 12:39:01 -0700377MaybeObject* JSObject::SetNormalizedProperty(String* name,
378 Object* value,
379 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000380 ASSERT(!HasFastProperties());
381 int entry = property_dictionary()->FindEntry(name);
382 if (entry == StringDictionary::kNotFound) {
383 Object* store_value = value;
384 if (IsGlobalObject()) {
John Reck59135872010-11-02 12:39:01 -0700385 { MaybeObject* maybe_store_value =
386 Heap::AllocateJSGlobalPropertyCell(value);
387 if (!maybe_store_value->ToObject(&store_value)) {
388 return maybe_store_value;
389 }
390 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000391 }
John Reck59135872010-11-02 12:39:01 -0700392 Object* dict;
393 { MaybeObject* maybe_dict =
394 property_dictionary()->Add(name, store_value, details);
395 if (!maybe_dict->ToObject(&dict)) return maybe_dict;
396 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000397 set_properties(StringDictionary::cast(dict));
398 return value;
399 }
400 // Preserve enumeration index.
401 details = PropertyDetails(details.attributes(),
402 details.type(),
403 property_dictionary()->DetailsAt(entry).index());
404 if (IsGlobalObject()) {
405 JSGlobalPropertyCell* cell =
406 JSGlobalPropertyCell::cast(property_dictionary()->ValueAt(entry));
407 cell->set_value(value);
408 // Please note we have to update the property details.
409 property_dictionary()->DetailsAtPut(entry, details);
410 } else {
411 property_dictionary()->SetEntry(entry, name, value, details);
412 }
413 return value;
414}
415
416
John Reck59135872010-11-02 12:39:01 -0700417MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000418 ASSERT(!HasFastProperties());
419 StringDictionary* dictionary = property_dictionary();
420 int entry = dictionary->FindEntry(name);
421 if (entry != StringDictionary::kNotFound) {
422 // If we have a global object set the cell to the hole.
423 if (IsGlobalObject()) {
424 PropertyDetails details = dictionary->DetailsAt(entry);
425 if (details.IsDontDelete()) {
426 if (mode != FORCE_DELETION) return Heap::false_value();
427 // When forced to delete global properties, we have to make a
428 // map change to invalidate any ICs that think they can load
429 // from the DontDelete cell without checking if it contains
430 // the hole value.
John Reck59135872010-11-02 12:39:01 -0700431 Object* new_map;
432 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
433 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
434 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000435 set_map(Map::cast(new_map));
436 }
437 JSGlobalPropertyCell* cell =
438 JSGlobalPropertyCell::cast(dictionary->ValueAt(entry));
439 cell->set_value(Heap::the_hole_value());
440 dictionary->DetailsAtPut(entry, details.AsDeleted());
441 } else {
442 return dictionary->DeleteProperty(entry, mode);
443 }
444 }
445 return Heap::true_value();
446}
447
448
449bool JSObject::IsDirty() {
450 Object* cons_obj = map()->constructor();
451 if (!cons_obj->IsJSFunction())
452 return true;
453 JSFunction* fun = JSFunction::cast(cons_obj);
Steve Block6ded16b2010-05-10 14:33:55 +0100454 if (!fun->shared()->IsApiFunction())
Steve Blocka7e24c12009-10-30 11:49:00 +0000455 return true;
456 // If the object is fully fast case and has the same map it was
457 // created with then no changes can have been made to it.
458 return map() != fun->initial_map()
459 || !HasFastElements()
460 || !HasFastProperties();
461}
462
463
John Reck59135872010-11-02 12:39:01 -0700464MaybeObject* Object::GetProperty(Object* receiver,
465 LookupResult* result,
466 String* name,
467 PropertyAttributes* attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000468 // Make sure that the top context does not change when doing
469 // callbacks or interceptor calls.
470 AssertNoContextChange ncc;
471
472 // Traverse the prototype chain from the current object (this) to
473 // the holder and check for access rights. This avoid traversing the
474 // objects more than once in case of interceptors, because the
475 // holder will always be the interceptor holder and the search may
476 // only continue with a current object just after the interceptor
477 // holder in the prototype chain.
Andrei Popescu402d9372010-02-26 13:31:12 +0000478 Object* last = result->IsProperty() ? result->holder() : Heap::null_value();
Steve Blocka7e24c12009-10-30 11:49:00 +0000479 for (Object* current = this; true; current = current->GetPrototype()) {
480 if (current->IsAccessCheckNeeded()) {
481 // Check if we're allowed to read from the current object. Note
482 // that even though we may not actually end up loading the named
483 // property from the current object, we still check that we have
484 // access to it.
485 JSObject* checked = JSObject::cast(current);
486 if (!Top::MayNamedAccess(checked, name, v8::ACCESS_GET)) {
487 return checked->GetPropertyWithFailedAccessCheck(receiver,
488 result,
489 name,
490 attributes);
491 }
492 }
493 // Stop traversing the chain once we reach the last object in the
494 // chain; either the holder of the result or null in case of an
495 // absent property.
496 if (current == last) break;
497 }
498
499 if (!result->IsProperty()) {
500 *attributes = ABSENT;
501 return Heap::undefined_value();
502 }
503 *attributes = result->GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +0000504 Object* value;
505 JSObject* holder = result->holder();
506 switch (result->type()) {
507 case NORMAL:
508 value = holder->GetNormalizedProperty(result);
509 ASSERT(!value->IsTheHole() || result->IsReadOnly());
510 return value->IsTheHole() ? Heap::undefined_value() : value;
511 case FIELD:
512 value = holder->FastPropertyAt(result->GetFieldIndex());
513 ASSERT(!value->IsTheHole() || result->IsReadOnly());
514 return value->IsTheHole() ? Heap::undefined_value() : value;
515 case CONSTANT_FUNCTION:
516 return result->GetConstantFunction();
517 case CALLBACKS:
518 return GetPropertyWithCallback(receiver,
519 result->GetCallbackObject(),
520 name,
521 holder);
522 case INTERCEPTOR: {
523 JSObject* recvr = JSObject::cast(receiver);
524 return holder->GetPropertyWithInterceptor(recvr, name, attributes);
525 }
526 default:
527 UNREACHABLE();
528 return NULL;
529 }
530}
531
532
John Reck59135872010-11-02 12:39:01 -0700533MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100534 if (IsJSObject()) {
535 return JSObject::cast(this)->GetElementWithReceiver(receiver, index);
536 }
537
538 Object* holder = NULL;
539 Context* global_context = Top::context()->global_context();
540 if (IsString()) {
541 holder = global_context->string_function()->instance_prototype();
542 } else if (IsNumber()) {
543 holder = global_context->number_function()->instance_prototype();
544 } else if (IsBoolean()) {
545 holder = global_context->boolean_function()->instance_prototype();
546 } else {
547 // Undefined and null have no indexed properties.
548 ASSERT(IsUndefined() || IsNull());
549 return Heap::undefined_value();
550 }
551
552 return JSObject::cast(holder)->GetElementWithReceiver(receiver, index);
Steve Blocka7e24c12009-10-30 11:49:00 +0000553}
554
555
556Object* Object::GetPrototype() {
557 // The object is either a number, a string, a boolean, or a real JS object.
558 if (IsJSObject()) return JSObject::cast(this)->map()->prototype();
559 Context* context = Top::context()->global_context();
560
561 if (IsNumber()) return context->number_function()->instance_prototype();
562 if (IsString()) return context->string_function()->instance_prototype();
563 if (IsBoolean()) {
564 return context->boolean_function()->instance_prototype();
565 } else {
566 return Heap::null_value();
567 }
568}
569
570
Ben Murdochb0fe1622011-05-05 13:52:32 +0100571void Object::ShortPrint(FILE* out) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000572 HeapStringAllocator allocator;
573 StringStream accumulator(&allocator);
574 ShortPrint(&accumulator);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100575 accumulator.OutputToFile(out);
Steve Blocka7e24c12009-10-30 11:49:00 +0000576}
577
578
579void Object::ShortPrint(StringStream* accumulator) {
580 if (IsSmi()) {
581 Smi::cast(this)->SmiPrint(accumulator);
582 } else if (IsFailure()) {
583 Failure::cast(this)->FailurePrint(accumulator);
584 } else {
585 HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
586 }
587}
588
589
Ben Murdochb0fe1622011-05-05 13:52:32 +0100590void Smi::SmiPrint(FILE* out) {
591 PrintF(out, "%d", value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000592}
593
594
595void Smi::SmiPrint(StringStream* accumulator) {
596 accumulator->Add("%d", value());
597}
598
599
600void Failure::FailurePrint(StringStream* accumulator) {
Steve Block3ce2e202009-11-05 08:53:23 +0000601 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000602}
603
604
Ben Murdochb0fe1622011-05-05 13:52:32 +0100605void Failure::FailurePrint(FILE* out) {
606 PrintF(out, "Failure(%p)", reinterpret_cast<void*>(value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000607}
608
609
Steve Blocka7e24c12009-10-30 11:49:00 +0000610// Should a word be prefixed by 'a' or 'an' in order to read naturally in
611// English? Returns false for non-ASCII or words that don't start with
612// a capital letter. The a/an rule follows pronunciation in English.
613// We don't use the BBC's overcorrect "an historic occasion" though if
614// you speak a dialect you may well say "an 'istoric occasion".
615static bool AnWord(String* str) {
616 if (str->length() == 0) return false; // A nothing.
617 int c0 = str->Get(0);
618 int c1 = str->length() > 1 ? str->Get(1) : 0;
619 if (c0 == 'U') {
620 if (c1 > 'Z') {
621 return true; // An Umpire, but a UTF8String, a U.
622 }
623 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
624 return true; // An Ape, an ABCBook.
625 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
626 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
627 c0 == 'S' || c0 == 'X')) {
628 return true; // An MP3File, an M.
629 }
630 return false;
631}
632
633
John Reck59135872010-11-02 12:39:01 -0700634MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000635#ifdef DEBUG
636 // Do not attempt to flatten in debug mode when allocation is not
637 // allowed. This is to avoid an assertion failure when allocating.
638 // Flattening strings is the only case where we always allow
639 // allocation because no GC is performed if the allocation fails.
640 if (!Heap::IsAllocationAllowed()) return this;
641#endif
642
643 switch (StringShape(this).representation_tag()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000644 case kConsStringTag: {
645 ConsString* cs = ConsString::cast(this);
646 if (cs->second()->length() == 0) {
Leon Clarkef7060e22010-06-03 12:02:55 +0100647 return cs->first();
Steve Blocka7e24c12009-10-30 11:49:00 +0000648 }
649 // There's little point in putting the flat string in new space if the
650 // cons string is in old space. It can never get GCed until there is
651 // an old space GC.
Steve Block6ded16b2010-05-10 14:33:55 +0100652 PretenureFlag tenure = Heap::InNewSpace(this) ? pretenure : TENURED;
Steve Blocka7e24c12009-10-30 11:49:00 +0000653 int len = length();
654 Object* object;
655 String* result;
656 if (IsAsciiRepresentation()) {
John Reck59135872010-11-02 12:39:01 -0700657 { MaybeObject* maybe_object = Heap::AllocateRawAsciiString(len, tenure);
658 if (!maybe_object->ToObject(&object)) return maybe_object;
659 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000660 result = String::cast(object);
661 String* first = cs->first();
662 int first_length = first->length();
663 char* dest = SeqAsciiString::cast(result)->GetChars();
664 WriteToFlat(first, dest, 0, first_length);
665 String* second = cs->second();
666 WriteToFlat(second,
667 dest + first_length,
668 0,
669 len - first_length);
670 } else {
John Reck59135872010-11-02 12:39:01 -0700671 { MaybeObject* maybe_object =
672 Heap::AllocateRawTwoByteString(len, tenure);
673 if (!maybe_object->ToObject(&object)) return maybe_object;
674 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000675 result = String::cast(object);
676 uc16* dest = SeqTwoByteString::cast(result)->GetChars();
677 String* first = cs->first();
678 int first_length = first->length();
679 WriteToFlat(first, dest, 0, first_length);
680 String* second = cs->second();
681 WriteToFlat(second,
682 dest + first_length,
683 0,
684 len - first_length);
685 }
686 cs->set_first(result);
687 cs->set_second(Heap::empty_string());
Leon Clarkef7060e22010-06-03 12:02:55 +0100688 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +0000689 }
690 default:
691 return this;
692 }
693}
694
695
696bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
Steve Block8defd9f2010-07-08 12:39:36 +0100697 // Externalizing twice leaks the external resource, so it's
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100698 // prohibited by the API.
699 ASSERT(!this->IsExternalString());
Steve Blocka7e24c12009-10-30 11:49:00 +0000700#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000701 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000702 // Assert that the resource and the string are equivalent.
703 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100704 ScopedVector<uc16> smart_chars(this->length());
705 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
706 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000707 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100708 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000709 }
710#endif // DEBUG
711
712 int size = this->Size(); // Byte size of the original string.
713 if (size < ExternalString::kSize) {
714 // The string is too small to fit an external String in its place. This can
715 // only happen for zero length strings.
716 return false;
717 }
718 ASSERT(size >= ExternalString::kSize);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100719 bool is_ascii = this->IsAsciiRepresentation();
Steve Blocka7e24c12009-10-30 11:49:00 +0000720 bool is_symbol = this->IsSymbol();
721 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000722 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000723
724 // Morph the object to an external string by adjusting the map and
725 // reinitializing the fields.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100726 this->set_map(is_ascii ?
727 Heap::external_string_with_ascii_data_map() :
728 Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000729 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
730 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000731 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000732 self->set_resource(resource);
733 // Additionally make the object into an external symbol if the original string
734 // was a symbol to start with.
735 if (is_symbol) {
736 self->Hash(); // Force regeneration of the hash value.
737 // Now morph this external string into a external symbol.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100738 this->set_map(is_ascii ?
739 Heap::external_symbol_with_ascii_data_map() :
740 Heap::external_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000741 }
742
743 // Fill the remainder of the string with dead wood.
744 int new_size = this->Size(); // Byte size of the external String object.
745 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
746 return true;
747}
748
749
750bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
751#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000752 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000753 // Assert that the resource and the string are equivalent.
754 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100755 ScopedVector<char> smart_chars(this->length());
756 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
757 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000758 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100759 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000760 }
761#endif // DEBUG
762
763 int size = this->Size(); // Byte size of the original string.
764 if (size < ExternalString::kSize) {
765 // The string is too small to fit an external String in its place. This can
766 // only happen for zero length strings.
767 return false;
768 }
769 ASSERT(size >= ExternalString::kSize);
770 bool is_symbol = this->IsSymbol();
771 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000772 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000773
774 // Morph the object to an external string by adjusting the map and
775 // reinitializing the fields.
Steve Blockd0582a62009-12-15 09:54:21 +0000776 this->set_map(Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000777 ExternalAsciiString* self = ExternalAsciiString::cast(this);
778 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000779 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000780 self->set_resource(resource);
781 // Additionally make the object into an external symbol if the original string
782 // was a symbol to start with.
783 if (is_symbol) {
784 self->Hash(); // Force regeneration of the hash value.
785 // Now morph this external string into a external symbol.
Steve Blockd0582a62009-12-15 09:54:21 +0000786 this->set_map(Heap::external_ascii_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000787 }
788
789 // Fill the remainder of the string with dead wood.
790 int new_size = this->Size(); // Byte size of the external String object.
791 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
792 return true;
793}
794
795
796void String::StringShortPrint(StringStream* accumulator) {
797 int len = length();
Steve Blockd0582a62009-12-15 09:54:21 +0000798 if (len > kMaxShortPrintLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000799 accumulator->Add("<Very long string[%u]>", len);
800 return;
801 }
802
803 if (!LooksValid()) {
804 accumulator->Add("<Invalid String>");
805 return;
806 }
807
808 StringInputBuffer buf(this);
809
810 bool truncated = false;
811 if (len > kMaxShortPrintLength) {
812 len = kMaxShortPrintLength;
813 truncated = true;
814 }
815 bool ascii = true;
816 for (int i = 0; i < len; i++) {
817 int c = buf.GetNext();
818
819 if (c < 32 || c >= 127) {
820 ascii = false;
821 }
822 }
823 buf.Reset(this);
824 if (ascii) {
825 accumulator->Add("<String[%u]: ", length());
826 for (int i = 0; i < len; i++) {
827 accumulator->Put(buf.GetNext());
828 }
829 accumulator->Put('>');
830 } else {
831 // Backslash indicates that the string contains control
832 // characters and that backslashes are therefore escaped.
833 accumulator->Add("<String[%u]\\: ", length());
834 for (int i = 0; i < len; i++) {
835 int c = buf.GetNext();
836 if (c == '\n') {
837 accumulator->Add("\\n");
838 } else if (c == '\r') {
839 accumulator->Add("\\r");
840 } else if (c == '\\') {
841 accumulator->Add("\\\\");
842 } else if (c < 32 || c > 126) {
843 accumulator->Add("\\x%02x", c);
844 } else {
845 accumulator->Put(c);
846 }
847 }
848 if (truncated) {
849 accumulator->Put('.');
850 accumulator->Put('.');
851 accumulator->Put('.');
852 }
853 accumulator->Put('>');
854 }
855 return;
856}
857
858
859void JSObject::JSObjectShortPrint(StringStream* accumulator) {
860 switch (map()->instance_type()) {
861 case JS_ARRAY_TYPE: {
862 double length = JSArray::cast(this)->length()->Number();
863 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
864 break;
865 }
866 case JS_REGEXP_TYPE: {
867 accumulator->Add("<JS RegExp>");
868 break;
869 }
870 case JS_FUNCTION_TYPE: {
871 Object* fun_name = JSFunction::cast(this)->shared()->name();
872 bool printed = false;
873 if (fun_name->IsString()) {
874 String* str = String::cast(fun_name);
875 if (str->length() > 0) {
876 accumulator->Add("<JS Function ");
877 accumulator->Put(str);
878 accumulator->Put('>');
879 printed = true;
880 }
881 }
882 if (!printed) {
883 accumulator->Add("<JS Function>");
884 }
885 break;
886 }
887 // All other JSObjects are rather similar to each other (JSObject,
888 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
889 default: {
890 Object* constructor = map()->constructor();
891 bool printed = false;
892 if (constructor->IsHeapObject() &&
893 !Heap::Contains(HeapObject::cast(constructor))) {
894 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
895 } else {
896 bool global_object = IsJSGlobalProxy();
897 if (constructor->IsJSFunction()) {
898 if (!Heap::Contains(JSFunction::cast(constructor)->shared())) {
899 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
900 } else {
901 Object* constructor_name =
902 JSFunction::cast(constructor)->shared()->name();
903 if (constructor_name->IsString()) {
904 String* str = String::cast(constructor_name);
905 if (str->length() > 0) {
906 bool vowel = AnWord(str);
907 accumulator->Add("<%sa%s ",
908 global_object ? "Global Object: " : "",
909 vowel ? "n" : "");
910 accumulator->Put(str);
911 accumulator->Put('>');
912 printed = true;
913 }
914 }
915 }
916 }
917 if (!printed) {
918 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
919 }
920 }
921 if (IsJSValue()) {
922 accumulator->Add(" value = ");
923 JSValue::cast(this)->value()->ShortPrint(accumulator);
924 }
925 accumulator->Put('>');
926 break;
927 }
928 }
929}
930
931
932void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
933 // if (!Heap::InNewSpace(this)) PrintF("*", this);
934 if (!Heap::Contains(this)) {
935 accumulator->Add("!!!INVALID POINTER!!!");
936 return;
937 }
938 if (!Heap::Contains(map())) {
939 accumulator->Add("!!!INVALID MAP!!!");
940 return;
941 }
942
943 accumulator->Add("%p ", this);
944
945 if (IsString()) {
946 String::cast(this)->StringShortPrint(accumulator);
947 return;
948 }
949 if (IsJSObject()) {
950 JSObject::cast(this)->JSObjectShortPrint(accumulator);
951 return;
952 }
953 switch (map()->instance_type()) {
954 case MAP_TYPE:
955 accumulator->Add("<Map>");
956 break;
957 case FIXED_ARRAY_TYPE:
958 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
959 break;
960 case BYTE_ARRAY_TYPE:
961 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
962 break;
963 case PIXEL_ARRAY_TYPE:
964 accumulator->Add("<PixelArray[%u]>", PixelArray::cast(this)->length());
965 break;
Steve Block3ce2e202009-11-05 08:53:23 +0000966 case EXTERNAL_BYTE_ARRAY_TYPE:
967 accumulator->Add("<ExternalByteArray[%u]>",
968 ExternalByteArray::cast(this)->length());
969 break;
970 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
971 accumulator->Add("<ExternalUnsignedByteArray[%u]>",
972 ExternalUnsignedByteArray::cast(this)->length());
973 break;
974 case EXTERNAL_SHORT_ARRAY_TYPE:
975 accumulator->Add("<ExternalShortArray[%u]>",
976 ExternalShortArray::cast(this)->length());
977 break;
978 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
979 accumulator->Add("<ExternalUnsignedShortArray[%u]>",
980 ExternalUnsignedShortArray::cast(this)->length());
981 break;
982 case EXTERNAL_INT_ARRAY_TYPE:
983 accumulator->Add("<ExternalIntArray[%u]>",
984 ExternalIntArray::cast(this)->length());
985 break;
986 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
987 accumulator->Add("<ExternalUnsignedIntArray[%u]>",
988 ExternalUnsignedIntArray::cast(this)->length());
989 break;
990 case EXTERNAL_FLOAT_ARRAY_TYPE:
991 accumulator->Add("<ExternalFloatArray[%u]>",
992 ExternalFloatArray::cast(this)->length());
993 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000994 case SHARED_FUNCTION_INFO_TYPE:
995 accumulator->Add("<SharedFunctionInfo>");
996 break;
Steve Block1e0659c2011-05-24 12:43:12 +0100997 case JS_MESSAGE_OBJECT_TYPE:
998 accumulator->Add("<JSMessageObject>");
999 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001000#define MAKE_STRUCT_CASE(NAME, Name, name) \
1001 case NAME##_TYPE: \
1002 accumulator->Put('<'); \
1003 accumulator->Add(#Name); \
1004 accumulator->Put('>'); \
1005 break;
1006 STRUCT_LIST(MAKE_STRUCT_CASE)
1007#undef MAKE_STRUCT_CASE
1008 case CODE_TYPE:
1009 accumulator->Add("<Code>");
1010 break;
1011 case ODDBALL_TYPE: {
1012 if (IsUndefined())
1013 accumulator->Add("<undefined>");
1014 else if (IsTheHole())
1015 accumulator->Add("<the hole>");
1016 else if (IsNull())
1017 accumulator->Add("<null>");
1018 else if (IsTrue())
1019 accumulator->Add("<true>");
1020 else if (IsFalse())
1021 accumulator->Add("<false>");
1022 else
1023 accumulator->Add("<Odd Oddball>");
1024 break;
1025 }
1026 case HEAP_NUMBER_TYPE:
1027 accumulator->Add("<Number: ");
1028 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
1029 accumulator->Put('>');
1030 break;
1031 case PROXY_TYPE:
1032 accumulator->Add("<Proxy>");
1033 break;
1034 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1035 accumulator->Add("Cell for ");
1036 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator);
1037 break;
1038 default:
1039 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
1040 break;
1041 }
1042}
1043
1044
Steve Blocka7e24c12009-10-30 11:49:00 +00001045void HeapObject::Iterate(ObjectVisitor* v) {
1046 // Handle header
1047 IteratePointer(v, kMapOffset);
1048 // Handle object body
1049 Map* m = map();
1050 IterateBody(m->instance_type(), SizeFromMap(m), v);
1051}
1052
1053
1054void HeapObject::IterateBody(InstanceType type, int object_size,
1055 ObjectVisitor* v) {
1056 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
1057 // During GC, the map pointer field is encoded.
1058 if (type < FIRST_NONSTRING_TYPE) {
1059 switch (type & kStringRepresentationMask) {
1060 case kSeqStringTag:
1061 break;
1062 case kConsStringTag:
Iain Merrick75681382010-08-19 15:07:18 +01001063 ConsString::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001064 break;
Steve Blockd0582a62009-12-15 09:54:21 +00001065 case kExternalStringTag:
1066 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1067 reinterpret_cast<ExternalAsciiString*>(this)->
1068 ExternalAsciiStringIterateBody(v);
1069 } else {
1070 reinterpret_cast<ExternalTwoByteString*>(this)->
1071 ExternalTwoByteStringIterateBody(v);
1072 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001073 break;
1074 }
1075 return;
1076 }
1077
1078 switch (type) {
1079 case FIXED_ARRAY_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001080 FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001081 break;
1082 case JS_OBJECT_TYPE:
1083 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
1084 case JS_VALUE_TYPE:
1085 case JS_ARRAY_TYPE:
1086 case JS_REGEXP_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001087 case JS_GLOBAL_PROXY_TYPE:
1088 case JS_GLOBAL_OBJECT_TYPE:
1089 case JS_BUILTINS_OBJECT_TYPE:
Steve Block1e0659c2011-05-24 12:43:12 +01001090 case JS_MESSAGE_OBJECT_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001091 JSObject::BodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001092 break;
Steve Block791712a2010-08-27 10:21:07 +01001093 case JS_FUNCTION_TYPE:
1094 reinterpret_cast<JSFunction*>(this)
1095 ->JSFunctionIterateBody(object_size, v);
1096 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001097 case ODDBALL_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001098 Oddball::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001099 break;
1100 case PROXY_TYPE:
1101 reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
1102 break;
1103 case MAP_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001104 Map::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001105 break;
1106 case CODE_TYPE:
1107 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1108 break;
1109 case JS_GLOBAL_PROPERTY_CELL_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001110 JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001111 break;
1112 case HEAP_NUMBER_TYPE:
1113 case FILLER_TYPE:
1114 case BYTE_ARRAY_TYPE:
1115 case PIXEL_ARRAY_TYPE:
Steve Block3ce2e202009-11-05 08:53:23 +00001116 case EXTERNAL_BYTE_ARRAY_TYPE:
1117 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1118 case EXTERNAL_SHORT_ARRAY_TYPE:
1119 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1120 case EXTERNAL_INT_ARRAY_TYPE:
1121 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1122 case EXTERNAL_FLOAT_ARRAY_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001123 break;
Iain Merrick75681382010-08-19 15:07:18 +01001124 case SHARED_FUNCTION_INFO_TYPE:
1125 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001126 break;
Iain Merrick75681382010-08-19 15:07:18 +01001127
Steve Blocka7e24c12009-10-30 11:49:00 +00001128#define MAKE_STRUCT_CASE(NAME, Name, name) \
1129 case NAME##_TYPE:
1130 STRUCT_LIST(MAKE_STRUCT_CASE)
1131#undef MAKE_STRUCT_CASE
Iain Merrick75681382010-08-19 15:07:18 +01001132 StructBodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001133 break;
1134 default:
1135 PrintF("Unknown type: %d\n", type);
1136 UNREACHABLE();
1137 }
1138}
1139
1140
Steve Blocka7e24c12009-10-30 11:49:00 +00001141Object* HeapNumber::HeapNumberToBoolean() {
1142 // NaN, +0, and -0 should return the false object
Iain Merrick75681382010-08-19 15:07:18 +01001143#if __BYTE_ORDER == __LITTLE_ENDIAN
1144 union IeeeDoubleLittleEndianArchType u;
1145#elif __BYTE_ORDER == __BIG_ENDIAN
1146 union IeeeDoubleBigEndianArchType u;
1147#endif
1148 u.d = value();
1149 if (u.bits.exp == 2047) {
1150 // Detect NaN for IEEE double precision floating point.
1151 if ((u.bits.man_low | u.bits.man_high) != 0)
1152 return Heap::false_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001153 }
Iain Merrick75681382010-08-19 15:07:18 +01001154 if (u.bits.exp == 0) {
1155 // Detect +0, and -0 for IEEE double precision floating point.
1156 if ((u.bits.man_low | u.bits.man_high) == 0)
1157 return Heap::false_value();
1158 }
1159 return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001160}
1161
1162
Ben Murdochb0fe1622011-05-05 13:52:32 +01001163void HeapNumber::HeapNumberPrint(FILE* out) {
1164 PrintF(out, "%.16g", Number());
Steve Blocka7e24c12009-10-30 11:49:00 +00001165}
1166
1167
1168void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1169 // The Windows version of vsnprintf can allocate when printing a %g string
1170 // into a buffer that may not be big enough. We don't want random memory
1171 // allocation when producing post-crash stack traces, so we print into a
1172 // buffer that is plenty big enough for any floating point number, then
1173 // print that using vsnprintf (which may truncate but never allocate if
1174 // there is no more space in the buffer).
1175 EmbeddedVector<char, 100> buffer;
1176 OS::SNPrintF(buffer, "%.16g", Number());
1177 accumulator->Add("%s", buffer.start());
1178}
1179
1180
1181String* JSObject::class_name() {
1182 if (IsJSFunction()) {
1183 return Heap::function_class_symbol();
1184 }
1185 if (map()->constructor()->IsJSFunction()) {
1186 JSFunction* constructor = JSFunction::cast(map()->constructor());
1187 return String::cast(constructor->shared()->instance_class_name());
1188 }
1189 // If the constructor is not present, return "Object".
1190 return Heap::Object_symbol();
1191}
1192
1193
1194String* JSObject::constructor_name() {
Steve Blocka7e24c12009-10-30 11:49:00 +00001195 if (map()->constructor()->IsJSFunction()) {
1196 JSFunction* constructor = JSFunction::cast(map()->constructor());
1197 String* name = String::cast(constructor->shared()->name());
Ben Murdochf87a2032010-10-22 12:50:53 +01001198 if (name->length() > 0) return name;
1199 String* inferred_name = constructor->shared()->inferred_name();
1200 if (inferred_name->length() > 0) return inferred_name;
1201 Object* proto = GetPrototype();
1202 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
Steve Blocka7e24c12009-10-30 11:49:00 +00001203 }
1204 // If the constructor is not present, return "Object".
1205 return Heap::Object_symbol();
1206}
1207
1208
John Reck59135872010-11-02 12:39:01 -07001209MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
1210 String* name,
1211 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001212 int index = new_map->PropertyIndexFor(name);
1213 if (map()->unused_property_fields() == 0) {
1214 ASSERT(map()->unused_property_fields() == 0);
1215 int new_unused = new_map->unused_property_fields();
John Reck59135872010-11-02 12:39:01 -07001216 Object* values;
1217 { MaybeObject* maybe_values =
1218 properties()->CopySize(properties()->length() + new_unused + 1);
1219 if (!maybe_values->ToObject(&values)) return maybe_values;
1220 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001221 set_properties(FixedArray::cast(values));
1222 }
1223 set_map(new_map);
1224 return FastPropertyAtPut(index, value);
1225}
1226
1227
John Reck59135872010-11-02 12:39:01 -07001228MaybeObject* JSObject::AddFastProperty(String* name,
1229 Object* value,
1230 PropertyAttributes attributes) {
Steve Block1e0659c2011-05-24 12:43:12 +01001231 ASSERT(!IsJSGlobalProxy());
1232
Steve Blocka7e24c12009-10-30 11:49:00 +00001233 // Normalize the object if the name is an actual string (not the
1234 // hidden symbols) and is not a real identifier.
1235 StringInputBuffer buffer(name);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001236 if (!ScannerConstants::IsIdentifier(&buffer)
1237 && name != Heap::hidden_symbol()) {
John Reck59135872010-11-02 12:39:01 -07001238 Object* obj;
1239 { MaybeObject* maybe_obj =
1240 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1241 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1242 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001243 return AddSlowProperty(name, value, attributes);
1244 }
1245
1246 DescriptorArray* old_descriptors = map()->instance_descriptors();
1247 // Compute the new index for new field.
1248 int index = map()->NextFreePropertyIndex();
1249
1250 // Allocate new instance descriptors with (name, index) added
1251 FieldDescriptor new_field(name, index, attributes);
John Reck59135872010-11-02 12:39:01 -07001252 Object* new_descriptors;
1253 { MaybeObject* maybe_new_descriptors =
1254 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1255 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1256 return maybe_new_descriptors;
1257 }
1258 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001259
1260 // Only allow map transition if the object's map is NOT equal to the
1261 // global object_function's map and there is not a transition for name.
1262 bool allow_map_transition =
1263 !old_descriptors->Contains(name) &&
1264 (Top::context()->global_context()->object_function()->map() != map());
1265
1266 ASSERT(index < map()->inobject_properties() ||
1267 (index - map()->inobject_properties()) < properties()->length() ||
1268 map()->unused_property_fields() == 0);
1269 // Allocate a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001270 Object* r;
1271 { MaybeObject* maybe_r = map()->CopyDropDescriptors();
1272 if (!maybe_r->ToObject(&r)) return maybe_r;
1273 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001274 Map* new_map = Map::cast(r);
1275 if (allow_map_transition) {
1276 // Allocate new instance descriptors for the old map with map transition.
1277 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
John Reck59135872010-11-02 12:39:01 -07001278 Object* r;
1279 { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1280 if (!maybe_r->ToObject(&r)) return maybe_r;
1281 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001282 old_descriptors = DescriptorArray::cast(r);
1283 }
1284
1285 if (map()->unused_property_fields() == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01001286 if (properties()->length() > MaxFastProperties()) {
John Reck59135872010-11-02 12:39:01 -07001287 Object* obj;
1288 { MaybeObject* maybe_obj =
1289 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1290 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1291 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001292 return AddSlowProperty(name, value, attributes);
1293 }
1294 // Make room for the new value
John Reck59135872010-11-02 12:39:01 -07001295 Object* values;
1296 { MaybeObject* maybe_values =
1297 properties()->CopySize(properties()->length() + kFieldsAdded);
1298 if (!maybe_values->ToObject(&values)) return maybe_values;
1299 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001300 set_properties(FixedArray::cast(values));
1301 new_map->set_unused_property_fields(kFieldsAdded - 1);
1302 } else {
1303 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
1304 }
1305 // We have now allocated all the necessary objects.
1306 // All the changes can be applied at once, so they are atomic.
1307 map()->set_instance_descriptors(old_descriptors);
1308 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1309 set_map(new_map);
1310 return FastPropertyAtPut(index, value);
1311}
1312
1313
John Reck59135872010-11-02 12:39:01 -07001314MaybeObject* JSObject::AddConstantFunctionProperty(
1315 String* name,
1316 JSFunction* function,
1317 PropertyAttributes attributes) {
Leon Clarkee46be812010-01-19 14:06:41 +00001318 ASSERT(!Heap::InNewSpace(function));
1319
Steve Blocka7e24c12009-10-30 11:49:00 +00001320 // Allocate new instance descriptors with (name, function) added
1321 ConstantFunctionDescriptor d(name, function, attributes);
John Reck59135872010-11-02 12:39:01 -07001322 Object* new_descriptors;
1323 { MaybeObject* maybe_new_descriptors =
1324 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1325 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1326 return maybe_new_descriptors;
1327 }
1328 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001329
1330 // Allocate a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001331 Object* new_map;
1332 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
1333 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
1334 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001335
1336 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1337 Map::cast(new_map)->set_instance_descriptors(descriptors);
1338 Map* old_map = map();
1339 set_map(Map::cast(new_map));
1340
1341 // If the old map is the global object map (from new Object()),
1342 // then transitions are not added to it, so we are done.
1343 if (old_map == Top::context()->global_context()->object_function()->map()) {
1344 return function;
1345 }
1346
1347 // Do not add CONSTANT_TRANSITIONS to global objects
1348 if (IsGlobalObject()) {
1349 return function;
1350 }
1351
1352 // Add a CONSTANT_TRANSITION descriptor to the old map,
1353 // so future assignments to this property on other objects
1354 // of the same type will create a normal field, not a constant function.
1355 // Don't do this for special properties, with non-trival attributes.
1356 if (attributes != NONE) {
1357 return function;
1358 }
Iain Merrick75681382010-08-19 15:07:18 +01001359 ConstTransitionDescriptor mark(name, Map::cast(new_map));
John Reck59135872010-11-02 12:39:01 -07001360 { MaybeObject* maybe_new_descriptors =
1361 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1362 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1363 // We have accomplished the main goal, so return success.
1364 return function;
1365 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001366 }
1367 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1368
1369 return function;
1370}
1371
1372
1373// Add property in slow mode
John Reck59135872010-11-02 12:39:01 -07001374MaybeObject* JSObject::AddSlowProperty(String* name,
1375 Object* value,
1376 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001377 ASSERT(!HasFastProperties());
1378 StringDictionary* dict = property_dictionary();
1379 Object* store_value = value;
1380 if (IsGlobalObject()) {
1381 // In case name is an orphaned property reuse the cell.
1382 int entry = dict->FindEntry(name);
1383 if (entry != StringDictionary::kNotFound) {
1384 store_value = dict->ValueAt(entry);
1385 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1386 // Assign an enumeration index to the property and update
1387 // SetNextEnumerationIndex.
1388 int index = dict->NextEnumerationIndex();
1389 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1390 dict->SetNextEnumerationIndex(index + 1);
1391 dict->SetEntry(entry, name, store_value, details);
1392 return value;
1393 }
John Reck59135872010-11-02 12:39:01 -07001394 { MaybeObject* maybe_store_value =
1395 Heap::AllocateJSGlobalPropertyCell(value);
1396 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
1397 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001398 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1399 }
1400 PropertyDetails details = PropertyDetails(attributes, NORMAL);
John Reck59135872010-11-02 12:39:01 -07001401 Object* result;
1402 { MaybeObject* maybe_result = dict->Add(name, store_value, details);
1403 if (!maybe_result->ToObject(&result)) return maybe_result;
1404 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001405 if (dict != result) set_properties(StringDictionary::cast(result));
1406 return value;
1407}
1408
1409
John Reck59135872010-11-02 12:39:01 -07001410MaybeObject* JSObject::AddProperty(String* name,
1411 Object* value,
1412 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001413 ASSERT(!IsJSGlobalProxy());
Steve Block8defd9f2010-07-08 12:39:36 +01001414 if (!map()->is_extensible()) {
1415 Handle<Object> args[1] = {Handle<String>(name)};
1416 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001417 HandleVector(args, 1)));
Steve Block8defd9f2010-07-08 12:39:36 +01001418 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001419 if (HasFastProperties()) {
1420 // Ensure the descriptor array does not get too big.
1421 if (map()->instance_descriptors()->number_of_descriptors() <
1422 DescriptorArray::kMaxNumberOfDescriptors) {
Leon Clarkee46be812010-01-19 14:06:41 +00001423 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001424 return AddConstantFunctionProperty(name,
1425 JSFunction::cast(value),
1426 attributes);
1427 } else {
1428 return AddFastProperty(name, value, attributes);
1429 }
1430 } else {
1431 // Normalize the object to prevent very large instance descriptors.
1432 // This eliminates unwanted N^2 allocation and lookup behavior.
John Reck59135872010-11-02 12:39:01 -07001433 Object* obj;
1434 { MaybeObject* maybe_obj =
1435 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1436 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1437 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001438 }
1439 }
1440 return AddSlowProperty(name, value, attributes);
1441}
1442
1443
John Reck59135872010-11-02 12:39:01 -07001444MaybeObject* JSObject::SetPropertyPostInterceptor(
1445 String* name,
1446 Object* value,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001447 PropertyAttributes attributes,
1448 StrictModeFlag strict_mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001449 // Check local property, ignore interceptor.
1450 LookupResult result;
1451 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001452 if (result.IsFound()) {
1453 // An existing property, a map transition or a null descriptor was
1454 // found. Use set property to handle all these cases.
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001455 return SetProperty(&result, name, value, attributes, strict_mode);
Andrei Popescu402d9372010-02-26 13:31:12 +00001456 }
1457 // Add a new real property.
Steve Blocka7e24c12009-10-30 11:49:00 +00001458 return AddProperty(name, value, attributes);
1459}
1460
1461
John Reck59135872010-11-02 12:39:01 -07001462MaybeObject* JSObject::ReplaceSlowProperty(String* name,
1463 Object* value,
1464 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001465 StringDictionary* dictionary = property_dictionary();
1466 int old_index = dictionary->FindEntry(name);
1467 int new_enumeration_index = 0; // 0 means "Use the next available index."
1468 if (old_index != -1) {
1469 // All calls to ReplaceSlowProperty have had all transitions removed.
1470 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1471 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1472 }
1473
1474 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
1475 return SetNormalizedProperty(name, value, new_details);
1476}
1477
Steve Blockd0582a62009-12-15 09:54:21 +00001478
John Reck59135872010-11-02 12:39:01 -07001479MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
Steve Blocka7e24c12009-10-30 11:49:00 +00001480 String* name,
1481 Object* new_value,
1482 PropertyAttributes attributes) {
1483 Map* old_map = map();
John Reck59135872010-11-02 12:39:01 -07001484 Object* result;
1485 { MaybeObject* maybe_result =
1486 ConvertDescriptorToField(name, new_value, attributes);
1487 if (!maybe_result->ToObject(&result)) return maybe_result;
1488 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001489 // If we get to this point we have succeeded - do not return failure
1490 // after this point. Later stuff is optional.
1491 if (!HasFastProperties()) {
1492 return result;
1493 }
1494 // Do not add transitions to the map of "new Object()".
1495 if (map() == Top::context()->global_context()->object_function()->map()) {
1496 return result;
1497 }
1498
1499 MapTransitionDescriptor transition(name,
1500 map(),
1501 attributes);
John Reck59135872010-11-02 12:39:01 -07001502 Object* new_descriptors;
1503 { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
1504 CopyInsert(&transition, KEEP_TRANSITIONS);
1505 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1506 return result; // Yes, return _result_.
1507 }
1508 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001509 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1510 return result;
1511}
1512
1513
John Reck59135872010-11-02 12:39:01 -07001514MaybeObject* JSObject::ConvertDescriptorToField(String* name,
1515 Object* new_value,
1516 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001517 if (map()->unused_property_fields() == 0 &&
Steve Block8defd9f2010-07-08 12:39:36 +01001518 properties()->length() > MaxFastProperties()) {
John Reck59135872010-11-02 12:39:01 -07001519 Object* obj;
1520 { MaybeObject* maybe_obj =
1521 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1522 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1523 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001524 return ReplaceSlowProperty(name, new_value, attributes);
1525 }
1526
1527 int index = map()->NextFreePropertyIndex();
1528 FieldDescriptor new_field(name, index, attributes);
1529 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
John Reck59135872010-11-02 12:39:01 -07001530 Object* descriptors_unchecked;
1531 { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
1532 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1533 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
1534 return maybe_descriptors_unchecked;
1535 }
1536 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001537 DescriptorArray* new_descriptors =
1538 DescriptorArray::cast(descriptors_unchecked);
1539
1540 // Make a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001541 Object* new_map_unchecked;
1542 { MaybeObject* maybe_new_map_unchecked = map()->CopyDropDescriptors();
1543 if (!maybe_new_map_unchecked->ToObject(&new_map_unchecked)) {
1544 return maybe_new_map_unchecked;
1545 }
1546 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001547 Map* new_map = Map::cast(new_map_unchecked);
1548 new_map->set_instance_descriptors(new_descriptors);
1549
1550 // Make new properties array if necessary.
1551 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1552 int new_unused_property_fields = map()->unused_property_fields() - 1;
1553 if (map()->unused_property_fields() == 0) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001554 new_unused_property_fields = kFieldsAdded - 1;
John Reck59135872010-11-02 12:39:01 -07001555 Object* new_properties_object;
1556 { MaybeObject* maybe_new_properties_object =
1557 properties()->CopySize(properties()->length() + kFieldsAdded);
1558 if (!maybe_new_properties_object->ToObject(&new_properties_object)) {
1559 return maybe_new_properties_object;
1560 }
1561 }
1562 new_properties = FixedArray::cast(new_properties_object);
Steve Blocka7e24c12009-10-30 11:49:00 +00001563 }
1564
1565 // Update pointers to commit changes.
1566 // Object points to the new map.
1567 new_map->set_unused_property_fields(new_unused_property_fields);
1568 set_map(new_map);
1569 if (new_properties) {
1570 set_properties(FixedArray::cast(new_properties));
1571 }
1572 return FastPropertyAtPut(index, new_value);
1573}
1574
1575
1576
John Reck59135872010-11-02 12:39:01 -07001577MaybeObject* JSObject::SetPropertyWithInterceptor(
1578 String* name,
1579 Object* value,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001580 PropertyAttributes attributes,
1581 StrictModeFlag strict_mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001582 HandleScope scope;
1583 Handle<JSObject> this_handle(this);
1584 Handle<String> name_handle(name);
1585 Handle<Object> value_handle(value);
1586 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1587 if (!interceptor->setter()->IsUndefined()) {
1588 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name));
1589 CustomArguments args(interceptor->data(), this, this);
1590 v8::AccessorInfo info(args.end());
1591 v8::NamedPropertySetter setter =
1592 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1593 v8::Handle<v8::Value> result;
1594 {
1595 // Leaving JavaScript.
1596 VMState state(EXTERNAL);
1597 Handle<Object> value_unhole(value->IsTheHole() ?
1598 Heap::undefined_value() :
1599 value);
1600 result = setter(v8::Utils::ToLocal(name_handle),
1601 v8::Utils::ToLocal(value_unhole),
1602 info);
1603 }
1604 RETURN_IF_SCHEDULED_EXCEPTION();
1605 if (!result.IsEmpty()) return *value_handle;
1606 }
John Reck59135872010-11-02 12:39:01 -07001607 MaybeObject* raw_result =
1608 this_handle->SetPropertyPostInterceptor(*name_handle,
1609 *value_handle,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001610 attributes,
1611 strict_mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00001612 RETURN_IF_SCHEDULED_EXCEPTION();
1613 return raw_result;
1614}
1615
1616
John Reck59135872010-11-02 12:39:01 -07001617MaybeObject* JSObject::SetProperty(String* name,
1618 Object* value,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001619 PropertyAttributes attributes,
1620 StrictModeFlag strict_mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001621 LookupResult result;
1622 LocalLookup(name, &result);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001623 return SetProperty(&result, name, value, attributes, strict_mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00001624}
1625
1626
John Reck59135872010-11-02 12:39:01 -07001627MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
1628 String* name,
1629 Object* value,
1630 JSObject* holder) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001631 HandleScope scope;
1632
1633 // We should never get here to initialize a const with the hole
1634 // value since a const declaration would conflict with the setter.
1635 ASSERT(!value->IsTheHole());
1636 Handle<Object> value_handle(value);
1637
1638 // To accommodate both the old and the new api we switch on the
1639 // data structure used to store the callbacks. Eventually proxy
1640 // callbacks should be phased out.
1641 if (structure->IsProxy()) {
1642 AccessorDescriptor* callback =
1643 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
John Reck59135872010-11-02 12:39:01 -07001644 MaybeObject* obj = (callback->setter)(this, value, callback->data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001645 RETURN_IF_SCHEDULED_EXCEPTION();
1646 if (obj->IsFailure()) return obj;
1647 return *value_handle;
1648 }
1649
1650 if (structure->IsAccessorInfo()) {
1651 // api style callbacks
1652 AccessorInfo* data = AccessorInfo::cast(structure);
1653 Object* call_obj = data->setter();
1654 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1655 if (call_fun == NULL) return value;
1656 Handle<String> key(name);
1657 LOG(ApiNamedPropertyAccess("store", this, name));
1658 CustomArguments args(data->data(), this, JSObject::cast(holder));
1659 v8::AccessorInfo info(args.end());
1660 {
1661 // Leaving JavaScript.
1662 VMState state(EXTERNAL);
1663 call_fun(v8::Utils::ToLocal(key),
1664 v8::Utils::ToLocal(value_handle),
1665 info);
1666 }
1667 RETURN_IF_SCHEDULED_EXCEPTION();
1668 return *value_handle;
1669 }
1670
1671 if (structure->IsFixedArray()) {
1672 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1673 if (setter->IsJSFunction()) {
1674 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
1675 } else {
1676 Handle<String> key(name);
1677 Handle<Object> holder_handle(holder);
1678 Handle<Object> args[2] = { key, holder_handle };
1679 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
1680 HandleVector(args, 2)));
1681 }
1682 }
1683
1684 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +01001685 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001686}
1687
1688
John Reck59135872010-11-02 12:39:01 -07001689MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1690 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001691 Handle<Object> value_handle(value);
1692 Handle<JSFunction> fun(JSFunction::cast(setter));
1693 Handle<JSObject> self(this);
1694#ifdef ENABLE_DEBUGGER_SUPPORT
1695 // Handle stepping into a setter if step into is active.
1696 if (Debug::StepInActive()) {
1697 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
1698 }
1699#endif
1700 bool has_pending_exception;
1701 Object** argv[] = { value_handle.location() };
1702 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1703 // Check for pending exception and return the result.
1704 if (has_pending_exception) return Failure::Exception();
1705 return *value_handle;
1706}
1707
1708
1709void JSObject::LookupCallbackSetterInPrototypes(String* name,
1710 LookupResult* result) {
1711 for (Object* pt = GetPrototype();
1712 pt != Heap::null_value();
1713 pt = pt->GetPrototype()) {
1714 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001715 if (result->IsProperty()) {
1716 if (result->IsReadOnly()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001717 result->NotFound();
1718 return;
1719 }
1720 if (result->type() == CALLBACKS) {
1721 return;
1722 }
1723 }
1724 }
1725 result->NotFound();
1726}
1727
1728
Steve Block1e0659c2011-05-24 12:43:12 +01001729MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
1730 Object* value,
1731 bool* found) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001732 for (Object* pt = GetPrototype();
1733 pt != Heap::null_value();
1734 pt = pt->GetPrototype()) {
1735 if (!JSObject::cast(pt)->HasDictionaryElements()) {
1736 continue;
1737 }
1738 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1739 int entry = dictionary->FindEntry(index);
1740 if (entry != NumberDictionary::kNotFound) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001741 PropertyDetails details = dictionary->DetailsAt(entry);
1742 if (details.type() == CALLBACKS) {
Steve Block1e0659c2011-05-24 12:43:12 +01001743 *found = true;
1744 return SetElementWithCallback(
1745 dictionary->ValueAt(entry), index, value, JSObject::cast(pt));
Steve Blocka7e24c12009-10-30 11:49:00 +00001746 }
1747 }
1748 }
Steve Block1e0659c2011-05-24 12:43:12 +01001749 *found = false;
1750 return Heap::the_hole_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001751}
1752
1753
1754void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1755 DescriptorArray* descriptors = map()->instance_descriptors();
Iain Merrick75681382010-08-19 15:07:18 +01001756 int number = descriptors->SearchWithCache(name);
Steve Blocka7e24c12009-10-30 11:49:00 +00001757 if (number != DescriptorArray::kNotFound) {
1758 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1759 } else {
1760 result->NotFound();
1761 }
1762}
1763
1764
Ben Murdochb0fe1622011-05-05 13:52:32 +01001765void Map::LookupInDescriptors(JSObject* holder,
1766 String* name,
1767 LookupResult* result) {
1768 DescriptorArray* descriptors = instance_descriptors();
1769 int number = DescriptorLookupCache::Lookup(descriptors, name);
1770 if (number == DescriptorLookupCache::kAbsent) {
1771 number = descriptors->Search(name);
1772 DescriptorLookupCache::Update(descriptors, name, number);
1773 }
1774 if (number != DescriptorArray::kNotFound) {
1775 result->DescriptorResult(holder, descriptors->GetDetails(number), number);
1776 } else {
1777 result->NotFound();
1778 }
1779}
1780
1781
Steve Blocka7e24c12009-10-30 11:49:00 +00001782void JSObject::LocalLookupRealNamedProperty(String* name,
1783 LookupResult* result) {
1784 if (IsJSGlobalProxy()) {
1785 Object* proto = GetPrototype();
1786 if (proto->IsNull()) return result->NotFound();
1787 ASSERT(proto->IsJSGlobalObject());
1788 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
1789 }
1790
1791 if (HasFastProperties()) {
1792 LookupInDescriptor(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001793 if (result->IsFound()) {
1794 // A property, a map transition or a null descriptor was found.
1795 // We return all of these result types because
1796 // LocalLookupRealNamedProperty is used when setting properties
1797 // where map transitions and null descriptors are handled.
Steve Blocka7e24c12009-10-30 11:49:00 +00001798 ASSERT(result->holder() == this && result->type() != NORMAL);
1799 // Disallow caching for uninitialized constants. These can only
1800 // occur as fields.
1801 if (result->IsReadOnly() && result->type() == FIELD &&
1802 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
1803 result->DisallowCaching();
1804 }
1805 return;
1806 }
1807 } else {
1808 int entry = property_dictionary()->FindEntry(name);
1809 if (entry != StringDictionary::kNotFound) {
1810 Object* value = property_dictionary()->ValueAt(entry);
1811 if (IsGlobalObject()) {
1812 PropertyDetails d = property_dictionary()->DetailsAt(entry);
1813 if (d.IsDeleted()) {
1814 result->NotFound();
1815 return;
1816 }
1817 value = JSGlobalPropertyCell::cast(value)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001818 }
1819 // Make sure to disallow caching for uninitialized constants
1820 // found in the dictionary-mode objects.
1821 if (value->IsTheHole()) result->DisallowCaching();
1822 result->DictionaryResult(this, entry);
1823 return;
1824 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001825 }
1826 result->NotFound();
1827}
1828
1829
1830void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
1831 LocalLookupRealNamedProperty(name, result);
1832 if (result->IsProperty()) return;
1833
1834 LookupRealNamedPropertyInPrototypes(name, result);
1835}
1836
1837
1838void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
1839 LookupResult* result) {
1840 for (Object* pt = GetPrototype();
1841 pt != Heap::null_value();
1842 pt = JSObject::cast(pt)->GetPrototype()) {
1843 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001844 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001845 }
1846 result->NotFound();
1847}
1848
1849
1850// We only need to deal with CALLBACKS and INTERCEPTORS
John Reck59135872010-11-02 12:39:01 -07001851MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
1852 String* name,
Ben Murdoch086aeea2011-05-13 15:57:08 +01001853 Object* value,
1854 bool check_prototype) {
1855 if (check_prototype && !result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001856 LookupCallbackSetterInPrototypes(name, result);
1857 }
1858
1859 if (result->IsProperty()) {
1860 if (!result->IsReadOnly()) {
1861 switch (result->type()) {
1862 case CALLBACKS: {
1863 Object* obj = result->GetCallbackObject();
1864 if (obj->IsAccessorInfo()) {
1865 AccessorInfo* info = AccessorInfo::cast(obj);
1866 if (info->all_can_write()) {
1867 return SetPropertyWithCallback(result->GetCallbackObject(),
1868 name,
1869 value,
1870 result->holder());
1871 }
1872 }
1873 break;
1874 }
1875 case INTERCEPTOR: {
1876 // Try lookup real named properties. Note that only property can be
1877 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
1878 LookupResult r;
1879 LookupRealNamedProperty(name, &r);
1880 if (r.IsProperty()) {
Ben Murdoch086aeea2011-05-13 15:57:08 +01001881 return SetPropertyWithFailedAccessCheck(&r, name, value,
1882 check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00001883 }
1884 break;
1885 }
1886 default: {
1887 break;
1888 }
1889 }
1890 }
1891 }
1892
Iain Merrick75681382010-08-19 15:07:18 +01001893 HandleScope scope;
1894 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001895 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01001896 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00001897}
1898
1899
John Reck59135872010-11-02 12:39:01 -07001900MaybeObject* JSObject::SetProperty(LookupResult* result,
1901 String* name,
1902 Object* value,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001903 PropertyAttributes attributes,
1904 StrictModeFlag strict_mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001905 // Make sure that the top context does not change when doing callbacks or
1906 // interceptor calls.
1907 AssertNoContextChange ncc;
1908
Steve Blockd0582a62009-12-15 09:54:21 +00001909 // Optimization for 2-byte strings often used as keys in a decompression
1910 // dictionary. We make these short keys into symbols to avoid constantly
1911 // reallocating them.
1912 if (!name->IsSymbol() && name->length() <= 2) {
John Reck59135872010-11-02 12:39:01 -07001913 Object* symbol_version;
1914 { MaybeObject* maybe_symbol_version = Heap::LookupSymbol(name);
1915 if (maybe_symbol_version->ToObject(&symbol_version)) {
1916 name = String::cast(symbol_version);
1917 }
1918 }
Steve Blockd0582a62009-12-15 09:54:21 +00001919 }
1920
Steve Blocka7e24c12009-10-30 11:49:00 +00001921 // Check access rights if needed.
1922 if (IsAccessCheckNeeded()
1923 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
Ben Murdoch086aeea2011-05-13 15:57:08 +01001924 return SetPropertyWithFailedAccessCheck(result, name, value, true);
Steve Blocka7e24c12009-10-30 11:49:00 +00001925 }
1926
1927 if (IsJSGlobalProxy()) {
1928 Object* proto = GetPrototype();
1929 if (proto->IsNull()) return value;
1930 ASSERT(proto->IsJSGlobalObject());
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001931 return JSObject::cast(proto)->SetProperty(
1932 result, name, value, attributes, strict_mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00001933 }
1934
1935 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
1936 // We could not find a local property so let's check whether there is an
1937 // accessor that wants to handle the property.
1938 LookupResult accessor_result;
1939 LookupCallbackSetterInPrototypes(name, &accessor_result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001940 if (accessor_result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001941 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
1942 name,
1943 value,
1944 accessor_result.holder());
1945 }
1946 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001947 if (!result->IsFound()) {
1948 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001949 return AddProperty(name, value, attributes);
1950 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001951 if (result->IsReadOnly() && result->IsProperty()) {
1952 if (strict_mode == kStrictMode) {
1953 HandleScope scope;
1954 Handle<String> key(name);
1955 Handle<Object> holder(this);
1956 Handle<Object> args[2] = { key, holder };
1957 return Top::Throw(*Factory::NewTypeError("strict_read_only_property",
1958 HandleVector(args, 2)));
1959 } else {
1960 return value;
1961 }
1962 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001963 // This is a real property that is not read-only, or it is a
1964 // transition or null descriptor and there are no setters in the prototypes.
1965 switch (result->type()) {
1966 case NORMAL:
1967 return SetNormalizedProperty(result, value);
1968 case FIELD:
1969 return FastPropertyAtPut(result->GetFieldIndex(), value);
1970 case MAP_TRANSITION:
1971 if (attributes == result->GetAttributes()) {
1972 // Only use map transition if the attributes match.
1973 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1974 name,
1975 value);
1976 }
1977 return ConvertDescriptorToField(name, value, attributes);
1978 case CONSTANT_FUNCTION:
1979 // Only replace the function if necessary.
1980 if (value == result->GetConstantFunction()) return value;
1981 // Preserve the attributes of this existing property.
1982 attributes = result->GetAttributes();
1983 return ConvertDescriptorToField(name, value, attributes);
1984 case CALLBACKS:
1985 return SetPropertyWithCallback(result->GetCallbackObject(),
1986 name,
1987 value,
1988 result->holder());
1989 case INTERCEPTOR:
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001990 return SetPropertyWithInterceptor(name, value, attributes, strict_mode);
Iain Merrick75681382010-08-19 15:07:18 +01001991 case CONSTANT_TRANSITION: {
1992 // If the same constant function is being added we can simply
1993 // transition to the target map.
1994 Map* target_map = result->GetTransitionMap();
1995 DescriptorArray* target_descriptors = target_map->instance_descriptors();
1996 int number = target_descriptors->SearchWithCache(name);
1997 ASSERT(number != DescriptorArray::kNotFound);
1998 ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
1999 JSFunction* function =
2000 JSFunction::cast(target_descriptors->GetValue(number));
2001 ASSERT(!Heap::InNewSpace(function));
2002 if (value == function) {
2003 set_map(target_map);
2004 return value;
2005 }
2006 // Otherwise, replace with a MAP_TRANSITION to a new map with a
2007 // FIELD, even if the value is a constant function.
Steve Blocka7e24c12009-10-30 11:49:00 +00002008 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01002009 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002010 case NULL_DESCRIPTOR:
2011 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2012 default:
2013 UNREACHABLE();
2014 }
2015 UNREACHABLE();
2016 return value;
2017}
2018
2019
2020// Set a real local property, even if it is READ_ONLY. If the property is not
2021// present, add it with attributes NONE. This code is an exact clone of
2022// SetProperty, with the check for IsReadOnly and the check for a
2023// callback setter removed. The two lines looking up the LookupResult
2024// result are also added. If one of the functions is changed, the other
2025// should be.
Ben Murdoch086aeea2011-05-13 15:57:08 +01002026MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
Steve Blocka7e24c12009-10-30 11:49:00 +00002027 String* name,
2028 Object* value,
2029 PropertyAttributes attributes) {
2030 // Make sure that the top context does not change when doing callbacks or
2031 // interceptor calls.
2032 AssertNoContextChange ncc;
Andrei Popescu402d9372010-02-26 13:31:12 +00002033 LookupResult result;
2034 LocalLookup(name, &result);
Steve Blocka7e24c12009-10-30 11:49:00 +00002035 // Check access rights if needed.
2036 if (IsAccessCheckNeeded()
Andrei Popescu402d9372010-02-26 13:31:12 +00002037 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
Ben Murdoch086aeea2011-05-13 15:57:08 +01002038 return SetPropertyWithFailedAccessCheck(&result, name, value, false);
Steve Blocka7e24c12009-10-30 11:49:00 +00002039 }
2040
2041 if (IsJSGlobalProxy()) {
2042 Object* proto = GetPrototype();
2043 if (proto->IsNull()) return value;
2044 ASSERT(proto->IsJSGlobalObject());
Ben Murdoch086aeea2011-05-13 15:57:08 +01002045 return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes(
Steve Blocka7e24c12009-10-30 11:49:00 +00002046 name,
2047 value,
2048 attributes);
2049 }
2050
2051 // Check for accessor in prototype chain removed here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00002052 if (!result.IsFound()) {
2053 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00002054 return AddProperty(name, value, attributes);
2055 }
Steve Block6ded16b2010-05-10 14:33:55 +01002056
Andrei Popescu402d9372010-02-26 13:31:12 +00002057 PropertyDetails details = PropertyDetails(attributes, NORMAL);
2058
Steve Blocka7e24c12009-10-30 11:49:00 +00002059 // Check of IsReadOnly removed from here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00002060 switch (result.type()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002061 case NORMAL:
Andrei Popescu402d9372010-02-26 13:31:12 +00002062 return SetNormalizedProperty(name, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00002063 case FIELD:
Andrei Popescu402d9372010-02-26 13:31:12 +00002064 return FastPropertyAtPut(result.GetFieldIndex(), value);
Steve Blocka7e24c12009-10-30 11:49:00 +00002065 case MAP_TRANSITION:
Andrei Popescu402d9372010-02-26 13:31:12 +00002066 if (attributes == result.GetAttributes()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002067 // Only use map transition if the attributes match.
Andrei Popescu402d9372010-02-26 13:31:12 +00002068 return AddFastPropertyUsingMap(result.GetTransitionMap(),
Steve Blocka7e24c12009-10-30 11:49:00 +00002069 name,
2070 value);
2071 }
2072 return ConvertDescriptorToField(name, value, attributes);
2073 case CONSTANT_FUNCTION:
2074 // Only replace the function if necessary.
Andrei Popescu402d9372010-02-26 13:31:12 +00002075 if (value == result.GetConstantFunction()) return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00002076 // Preserve the attributes of this existing property.
Andrei Popescu402d9372010-02-26 13:31:12 +00002077 attributes = result.GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +00002078 return ConvertDescriptorToField(name, value, attributes);
2079 case CALLBACKS:
2080 case INTERCEPTOR:
2081 // Override callback in clone
2082 return ConvertDescriptorToField(name, value, attributes);
2083 case CONSTANT_TRANSITION:
2084 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
2085 // if the value is a function.
2086 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2087 case NULL_DESCRIPTOR:
2088 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2089 default:
2090 UNREACHABLE();
2091 }
2092 UNREACHABLE();
2093 return value;
2094}
2095
2096
2097PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
2098 JSObject* receiver,
2099 String* name,
2100 bool continue_search) {
2101 // Check local property, ignore interceptor.
2102 LookupResult result;
2103 LocalLookupRealNamedProperty(name, &result);
2104 if (result.IsProperty()) return result.GetAttributes();
2105
2106 if (continue_search) {
2107 // Continue searching via the prototype chain.
2108 Object* pt = GetPrototype();
2109 if (pt != Heap::null_value()) {
2110 return JSObject::cast(pt)->
2111 GetPropertyAttributeWithReceiver(receiver, name);
2112 }
2113 }
2114 return ABSENT;
2115}
2116
2117
2118PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
2119 JSObject* receiver,
2120 String* name,
2121 bool continue_search) {
2122 // Make sure that the top context does not change when doing
2123 // callbacks or interceptor calls.
2124 AssertNoContextChange ncc;
2125
2126 HandleScope scope;
2127 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2128 Handle<JSObject> receiver_handle(receiver);
2129 Handle<JSObject> holder_handle(this);
2130 Handle<String> name_handle(name);
2131 CustomArguments args(interceptor->data(), receiver, this);
2132 v8::AccessorInfo info(args.end());
2133 if (!interceptor->query()->IsUndefined()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002134 v8::NamedPropertyQuery query =
2135 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
Steve Blocka7e24c12009-10-30 11:49:00 +00002136 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002137 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00002138 {
2139 // Leaving JavaScript.
2140 VMState state(EXTERNAL);
2141 result = query(v8::Utils::ToLocal(name_handle), info);
2142 }
2143 if (!result.IsEmpty()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002144 ASSERT(result->IsInt32());
2145 return static_cast<PropertyAttributes>(result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002146 }
2147 } else if (!interceptor->getter()->IsUndefined()) {
2148 v8::NamedPropertyGetter getter =
2149 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
2150 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
2151 v8::Handle<v8::Value> result;
2152 {
2153 // Leaving JavaScript.
2154 VMState state(EXTERNAL);
2155 result = getter(v8::Utils::ToLocal(name_handle), info);
2156 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002157 if (!result.IsEmpty()) return DONT_ENUM;
Steve Blocka7e24c12009-10-30 11:49:00 +00002158 }
2159 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2160 *name_handle,
2161 continue_search);
2162}
2163
2164
2165PropertyAttributes JSObject::GetPropertyAttributeWithReceiver(
2166 JSObject* receiver,
2167 String* key) {
2168 uint32_t index = 0;
2169 if (key->AsArrayIndex(&index)) {
2170 if (HasElementWithReceiver(receiver, index)) return NONE;
2171 return ABSENT;
2172 }
2173 // Named property.
2174 LookupResult result;
2175 Lookup(key, &result);
2176 return GetPropertyAttribute(receiver, &result, key, true);
2177}
2178
2179
2180PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
2181 LookupResult* result,
2182 String* name,
2183 bool continue_search) {
2184 // Check access rights if needed.
2185 if (IsAccessCheckNeeded() &&
2186 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2187 return GetPropertyAttributeWithFailedAccessCheck(receiver,
2188 result,
2189 name,
2190 continue_search);
2191 }
Andrei Popescu402d9372010-02-26 13:31:12 +00002192 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002193 switch (result->type()) {
2194 case NORMAL: // fall through
2195 case FIELD:
2196 case CONSTANT_FUNCTION:
2197 case CALLBACKS:
2198 return result->GetAttributes();
2199 case INTERCEPTOR:
2200 return result->holder()->
2201 GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
Steve Blocka7e24c12009-10-30 11:49:00 +00002202 default:
2203 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +00002204 }
2205 }
2206 return ABSENT;
2207}
2208
2209
2210PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) {
2211 // Check whether the name is an array index.
2212 uint32_t index = 0;
2213 if (name->AsArrayIndex(&index)) {
2214 if (HasLocalElement(index)) return NONE;
2215 return ABSENT;
2216 }
2217 // Named property.
2218 LookupResult result;
2219 LocalLookup(name, &result);
2220 return GetPropertyAttribute(this, &result, name, false);
2221}
2222
2223
John Reck59135872010-11-02 12:39:01 -07002224MaybeObject* NormalizedMapCache::Get(JSObject* obj,
2225 PropertyNormalizationMode mode) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002226 Map* fast = obj->map();
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002227 int index = Hash(fast) % kEntries;
2228 Object* result = get(index);
2229 if (result->IsMap() && CheckHit(Map::cast(result), fast, mode)) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002230#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002231 if (FLAG_enable_slow_asserts) {
2232 // The cached map should match newly created normalized map bit-by-bit.
John Reck59135872010-11-02 12:39:01 -07002233 Object* fresh;
2234 { MaybeObject* maybe_fresh =
2235 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2236 if (maybe_fresh->ToObject(&fresh)) {
2237 ASSERT(memcmp(Map::cast(fresh)->address(),
2238 Map::cast(result)->address(),
2239 Map::kSize) == 0);
2240 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002241 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002242 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002243#endif
2244 return result;
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002245 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002246
John Reck59135872010-11-02 12:39:01 -07002247 { MaybeObject* maybe_result =
2248 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2249 if (!maybe_result->ToObject(&result)) return maybe_result;
2250 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002251 set(index, result);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002252 Counters::normalized_maps.Increment();
2253
2254 return result;
2255}
2256
2257
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002258void NormalizedMapCache::Clear() {
2259 int entries = length();
2260 for (int i = 0; i != entries; i++) {
2261 set_undefined(i);
2262 }
2263}
2264
2265
2266int NormalizedMapCache::Hash(Map* fast) {
2267 // For performance reasons we only hash the 3 most variable fields of a map:
2268 // constructor, prototype and bit_field2.
2269
2270 // Shift away the tag.
2271 int hash = (static_cast<uint32_t>(
2272 reinterpret_cast<uintptr_t>(fast->constructor())) >> 2);
2273
2274 // XOR-ing the prototype and constructor directly yields too many zero bits
2275 // when the two pointers are close (which is fairly common).
2276 // To avoid this we shift the prototype 4 bits relatively to the constructor.
2277 hash ^= (static_cast<uint32_t>(
2278 reinterpret_cast<uintptr_t>(fast->prototype())) << 2);
2279
2280 return hash ^ (hash >> 16) ^ fast->bit_field2();
2281}
2282
2283
2284bool NormalizedMapCache::CheckHit(Map* slow,
2285 Map* fast,
2286 PropertyNormalizationMode mode) {
2287#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002288 slow->SharedMapVerify();
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002289#endif
2290 return
2291 slow->constructor() == fast->constructor() &&
2292 slow->prototype() == fast->prototype() &&
2293 slow->inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ?
2294 0 :
2295 fast->inobject_properties()) &&
2296 slow->instance_type() == fast->instance_type() &&
2297 slow->bit_field() == fast->bit_field() &&
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002298 (slow->bit_field2() & ~(1<<Map::kIsShared)) == fast->bit_field2();
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002299}
2300
2301
John Reck59135872010-11-02 12:39:01 -07002302MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002303 if (map()->is_shared()) {
2304 // Fast case maps are never marked as shared.
2305 ASSERT(!HasFastProperties());
2306 // Replace the map with an identical copy that can be safely modified.
John Reck59135872010-11-02 12:39:01 -07002307 Object* obj;
2308 { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES,
2309 UNIQUE_NORMALIZED_MAP);
2310 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2311 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002312 Counters::normalized_maps.Increment();
2313
2314 set_map(Map::cast(obj));
2315 }
2316 return map()->UpdateCodeCache(name, code);
2317}
2318
2319
John Reck59135872010-11-02 12:39:01 -07002320MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2321 int expected_additional_properties) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002322 if (!HasFastProperties()) return this;
2323
2324 // The global object is always normalized.
2325 ASSERT(!IsGlobalObject());
2326
Steve Block1e0659c2011-05-24 12:43:12 +01002327 // JSGlobalProxy must never be normalized
2328 ASSERT(!IsJSGlobalProxy());
2329
Steve Blocka7e24c12009-10-30 11:49:00 +00002330 // Allocate new content.
2331 int property_count = map()->NumberOfDescribedProperties();
2332 if (expected_additional_properties > 0) {
2333 property_count += expected_additional_properties;
2334 } else {
2335 property_count += 2; // Make space for two more properties.
2336 }
John Reck59135872010-11-02 12:39:01 -07002337 Object* obj;
2338 { MaybeObject* maybe_obj =
2339 StringDictionary::Allocate(property_count);
2340 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2341 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002342 StringDictionary* dictionary = StringDictionary::cast(obj);
2343
2344 DescriptorArray* descs = map()->instance_descriptors();
2345 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2346 PropertyDetails details = descs->GetDetails(i);
2347 switch (details.type()) {
2348 case CONSTANT_FUNCTION: {
2349 PropertyDetails d =
2350 PropertyDetails(details.attributes(), NORMAL, details.index());
2351 Object* value = descs->GetConstantFunction(i);
John Reck59135872010-11-02 12:39:01 -07002352 Object* result;
2353 { MaybeObject* maybe_result =
2354 dictionary->Add(descs->GetKey(i), value, d);
2355 if (!maybe_result->ToObject(&result)) return maybe_result;
2356 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002357 dictionary = StringDictionary::cast(result);
2358 break;
2359 }
2360 case FIELD: {
2361 PropertyDetails d =
2362 PropertyDetails(details.attributes(), NORMAL, details.index());
2363 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
John Reck59135872010-11-02 12:39:01 -07002364 Object* result;
2365 { MaybeObject* maybe_result =
2366 dictionary->Add(descs->GetKey(i), value, d);
2367 if (!maybe_result->ToObject(&result)) return maybe_result;
2368 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002369 dictionary = StringDictionary::cast(result);
2370 break;
2371 }
2372 case CALLBACKS: {
2373 PropertyDetails d =
2374 PropertyDetails(details.attributes(), CALLBACKS, details.index());
2375 Object* value = descs->GetCallbacksObject(i);
John Reck59135872010-11-02 12:39:01 -07002376 Object* result;
2377 { MaybeObject* maybe_result =
2378 dictionary->Add(descs->GetKey(i), value, d);
2379 if (!maybe_result->ToObject(&result)) return maybe_result;
2380 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002381 dictionary = StringDictionary::cast(result);
2382 break;
2383 }
2384 case MAP_TRANSITION:
2385 case CONSTANT_TRANSITION:
2386 case NULL_DESCRIPTOR:
2387 case INTERCEPTOR:
2388 break;
2389 default:
2390 UNREACHABLE();
2391 }
2392 }
2393
2394 // Copy the next enumeration index from instance descriptor.
2395 int index = map()->instance_descriptors()->NextEnumerationIndex();
2396 dictionary->SetNextEnumerationIndex(index);
2397
John Reck59135872010-11-02 12:39:01 -07002398 { MaybeObject* maybe_obj = Top::context()->global_context()->
2399 normalized_map_cache()->Get(this, mode);
2400 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2401 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002402 Map* new_map = Map::cast(obj);
2403
Steve Blocka7e24c12009-10-30 11:49:00 +00002404 // We have now successfully allocated all the necessary objects.
2405 // Changes can now be made with the guarantee that all of them take effect.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002406
2407 // Resize the object in the heap if necessary.
2408 int new_instance_size = new_map->instance_size();
2409 int instance_size_delta = map()->instance_size() - new_instance_size;
2410 ASSERT(instance_size_delta >= 0);
2411 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2412 instance_size_delta);
2413
Steve Blocka7e24c12009-10-30 11:49:00 +00002414 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002415
2416 set_properties(dictionary);
2417
2418 Counters::props_to_dictionary.Increment();
2419
2420#ifdef DEBUG
2421 if (FLAG_trace_normalization) {
2422 PrintF("Object properties have been normalized:\n");
2423 Print();
2424 }
2425#endif
2426 return this;
2427}
2428
2429
John Reck59135872010-11-02 12:39:01 -07002430MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002431 if (HasFastProperties()) return this;
2432 ASSERT(!IsGlobalObject());
2433 return property_dictionary()->
2434 TransformPropertiesToFastFor(this, unused_property_fields);
2435}
2436
2437
John Reck59135872010-11-02 12:39:01 -07002438MaybeObject* JSObject::NormalizeElements() {
Steve Block3ce2e202009-11-05 08:53:23 +00002439 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002440 if (HasDictionaryElements()) return this;
Steve Block8defd9f2010-07-08 12:39:36 +01002441 ASSERT(map()->has_fast_elements());
2442
John Reck59135872010-11-02 12:39:01 -07002443 Object* obj;
2444 { MaybeObject* maybe_obj = map()->GetSlowElementsMap();
2445 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2446 }
Steve Block8defd9f2010-07-08 12:39:36 +01002447 Map* new_map = Map::cast(obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00002448
2449 // Get number of entries.
2450 FixedArray* array = FixedArray::cast(elements());
2451
2452 // Compute the effective length.
2453 int length = IsJSArray() ?
2454 Smi::cast(JSArray::cast(this)->length())->value() :
2455 array->length();
John Reck59135872010-11-02 12:39:01 -07002456 { MaybeObject* maybe_obj = NumberDictionary::Allocate(length);
2457 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2458 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002459 NumberDictionary* dictionary = NumberDictionary::cast(obj);
2460 // Copy entries.
2461 for (int i = 0; i < length; i++) {
2462 Object* value = array->get(i);
2463 if (!value->IsTheHole()) {
2464 PropertyDetails details = PropertyDetails(NONE, NORMAL);
John Reck59135872010-11-02 12:39:01 -07002465 Object* result;
2466 { MaybeObject* maybe_result =
2467 dictionary->AddNumberEntry(i, array->get(i), details);
2468 if (!maybe_result->ToObject(&result)) return maybe_result;
2469 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002470 dictionary = NumberDictionary::cast(result);
2471 }
2472 }
Steve Block8defd9f2010-07-08 12:39:36 +01002473 // Switch to using the dictionary as the backing storage for
2474 // elements. Set the new map first to satify the elements type
2475 // assert in set_elements().
2476 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002477 set_elements(dictionary);
2478
2479 Counters::elements_to_dictionary.Increment();
2480
2481#ifdef DEBUG
2482 if (FLAG_trace_normalization) {
2483 PrintF("Object elements have been normalized:\n");
2484 Print();
2485 }
2486#endif
2487
2488 return this;
2489}
2490
2491
John Reck59135872010-11-02 12:39:01 -07002492MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
2493 DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002494 // Check local property, ignore interceptor.
2495 LookupResult result;
2496 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002497 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002498
2499 // Normalize object if needed.
John Reck59135872010-11-02 12:39:01 -07002500 Object* obj;
2501 { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2502 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2503 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002504
2505 return DeleteNormalizedProperty(name, mode);
2506}
2507
2508
John Reck59135872010-11-02 12:39:01 -07002509MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002510 HandleScope scope;
2511 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2512 Handle<String> name_handle(name);
2513 Handle<JSObject> this_handle(this);
2514 if (!interceptor->deleter()->IsUndefined()) {
2515 v8::NamedPropertyDeleter deleter =
2516 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2517 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2518 CustomArguments args(interceptor->data(), this, this);
2519 v8::AccessorInfo info(args.end());
2520 v8::Handle<v8::Boolean> result;
2521 {
2522 // Leaving JavaScript.
2523 VMState state(EXTERNAL);
2524 result = deleter(v8::Utils::ToLocal(name_handle), info);
2525 }
2526 RETURN_IF_SCHEDULED_EXCEPTION();
2527 if (!result.IsEmpty()) {
2528 ASSERT(result->IsBoolean());
2529 return *v8::Utils::OpenHandle(*result);
2530 }
2531 }
John Reck59135872010-11-02 12:39:01 -07002532 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00002533 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
2534 RETURN_IF_SCHEDULED_EXCEPTION();
2535 return raw_result;
2536}
2537
2538
John Reck59135872010-11-02 12:39:01 -07002539MaybeObject* JSObject::DeleteElementPostInterceptor(uint32_t index,
2540 DeleteMode mode) {
Steve Block3ce2e202009-11-05 08:53:23 +00002541 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002542 switch (GetElementsKind()) {
2543 case FAST_ELEMENTS: {
John Reck59135872010-11-02 12:39:01 -07002544 Object* obj;
2545 { MaybeObject* maybe_obj = EnsureWritableFastElements();
2546 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2547 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002548 uint32_t length = IsJSArray() ?
2549 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2550 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2551 if (index < length) {
2552 FixedArray::cast(elements())->set_the_hole(index);
2553 }
2554 break;
2555 }
2556 case DICTIONARY_ELEMENTS: {
2557 NumberDictionary* dictionary = element_dictionary();
2558 int entry = dictionary->FindEntry(index);
2559 if (entry != NumberDictionary::kNotFound) {
2560 return dictionary->DeleteProperty(entry, mode);
2561 }
2562 break;
2563 }
2564 default:
2565 UNREACHABLE();
2566 break;
2567 }
2568 return Heap::true_value();
2569}
2570
2571
John Reck59135872010-11-02 12:39:01 -07002572MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002573 // Make sure that the top context does not change when doing
2574 // callbacks or interceptor calls.
2575 AssertNoContextChange ncc;
2576 HandleScope scope;
2577 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2578 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2579 v8::IndexedPropertyDeleter deleter =
2580 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2581 Handle<JSObject> this_handle(this);
2582 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2583 CustomArguments args(interceptor->data(), this, this);
2584 v8::AccessorInfo info(args.end());
2585 v8::Handle<v8::Boolean> result;
2586 {
2587 // Leaving JavaScript.
2588 VMState state(EXTERNAL);
2589 result = deleter(index, info);
2590 }
2591 RETURN_IF_SCHEDULED_EXCEPTION();
2592 if (!result.IsEmpty()) {
2593 ASSERT(result->IsBoolean());
2594 return *v8::Utils::OpenHandle(*result);
2595 }
John Reck59135872010-11-02 12:39:01 -07002596 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00002597 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
2598 RETURN_IF_SCHEDULED_EXCEPTION();
2599 return raw_result;
2600}
2601
2602
John Reck59135872010-11-02 12:39:01 -07002603MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002604 // Check access rights if needed.
2605 if (IsAccessCheckNeeded() &&
2606 !Top::MayIndexedAccess(this, index, 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)->DeleteElement(index, mode);
2616 }
2617
2618 if (HasIndexedInterceptor()) {
2619 // Skip interceptor if forcing deletion.
2620 if (mode == FORCE_DELETION) {
2621 return DeleteElementPostInterceptor(index, mode);
2622 }
2623 return DeleteElementWithInterceptor(index);
2624 }
2625
2626 switch (GetElementsKind()) {
2627 case FAST_ELEMENTS: {
John Reck59135872010-11-02 12:39:01 -07002628 Object* obj;
2629 { MaybeObject* maybe_obj = EnsureWritableFastElements();
2630 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2631 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002632 uint32_t length = IsJSArray() ?
2633 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2634 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2635 if (index < length) {
2636 FixedArray::cast(elements())->set_the_hole(index);
2637 }
2638 break;
2639 }
Steve Block3ce2e202009-11-05 08:53:23 +00002640 case PIXEL_ELEMENTS:
2641 case EXTERNAL_BYTE_ELEMENTS:
2642 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2643 case EXTERNAL_SHORT_ELEMENTS:
2644 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2645 case EXTERNAL_INT_ELEMENTS:
2646 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2647 case EXTERNAL_FLOAT_ELEMENTS:
2648 // Pixel and external array elements cannot be deleted. Just
2649 // silently ignore here.
Steve Blocka7e24c12009-10-30 11:49:00 +00002650 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00002651 case DICTIONARY_ELEMENTS: {
2652 NumberDictionary* dictionary = element_dictionary();
2653 int entry = dictionary->FindEntry(index);
2654 if (entry != NumberDictionary::kNotFound) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01002655 Object* result = dictionary->DeleteProperty(entry, mode);
2656 if (mode == STRICT_DELETION && result == Heap::false_value()) {
2657 // In strict mode, deleting a non-configurable property throws
2658 // exception. dictionary->DeleteProperty will return false_value()
2659 // if a non-configurable property is being deleted.
2660 HandleScope scope;
2661 Handle<Object> i = Factory::NewNumberFromUint(index);
2662 Handle<Object> args[2] = { i, Handle<Object>(this) };
2663 return Top::Throw(*Factory::NewTypeError("strict_delete_property",
2664 HandleVector(args, 2)));
2665 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002666 }
2667 break;
2668 }
2669 default:
2670 UNREACHABLE();
2671 break;
2672 }
2673 return Heap::true_value();
2674}
2675
2676
John Reck59135872010-11-02 12:39:01 -07002677MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002678 // ECMA-262, 3rd, 8.6.2.5
2679 ASSERT(name->IsString());
2680
2681 // Check access rights if needed.
2682 if (IsAccessCheckNeeded() &&
2683 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2684 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2685 return Heap::false_value();
2686 }
2687
2688 if (IsJSGlobalProxy()) {
2689 Object* proto = GetPrototype();
2690 if (proto->IsNull()) return Heap::false_value();
2691 ASSERT(proto->IsJSGlobalObject());
2692 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
2693 }
2694
2695 uint32_t index = 0;
2696 if (name->AsArrayIndex(&index)) {
2697 return DeleteElement(index, mode);
2698 } else {
2699 LookupResult result;
2700 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002701 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002702 // Ignore attributes if forcing a deletion.
2703 if (result.IsDontDelete() && mode != FORCE_DELETION) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01002704 if (mode == STRICT_DELETION) {
2705 // Deleting a non-configurable property in strict mode.
2706 HandleScope scope;
2707 Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) };
2708 return Top::Throw(*Factory::NewTypeError("strict_delete_property",
2709 HandleVector(args, 2)));
2710 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002711 return Heap::false_value();
2712 }
2713 // Check for interceptor.
2714 if (result.type() == INTERCEPTOR) {
2715 // Skip interceptor if forcing a deletion.
2716 if (mode == FORCE_DELETION) {
2717 return DeletePropertyPostInterceptor(name, mode);
2718 }
2719 return DeletePropertyWithInterceptor(name);
2720 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002721 // Normalize object if needed.
John Reck59135872010-11-02 12:39:01 -07002722 Object* obj;
2723 { MaybeObject* maybe_obj =
2724 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2725 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2726 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002727 // Make sure the properties are normalized before removing the entry.
2728 return DeleteNormalizedProperty(name, mode);
2729 }
2730}
2731
2732
2733// Check whether this object references another object.
2734bool JSObject::ReferencesObject(Object* obj) {
2735 AssertNoAllocation no_alloc;
2736
2737 // Is the object the constructor for this object?
2738 if (map()->constructor() == obj) {
2739 return true;
2740 }
2741
2742 // Is the object the prototype for this object?
2743 if (map()->prototype() == obj) {
2744 return true;
2745 }
2746
2747 // Check if the object is among the named properties.
2748 Object* key = SlowReverseLookup(obj);
2749 if (key != Heap::undefined_value()) {
2750 return true;
2751 }
2752
2753 // Check if the object is among the indexed properties.
2754 switch (GetElementsKind()) {
2755 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002756 case EXTERNAL_BYTE_ELEMENTS:
2757 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2758 case EXTERNAL_SHORT_ELEMENTS:
2759 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2760 case EXTERNAL_INT_ELEMENTS:
2761 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2762 case EXTERNAL_FLOAT_ELEMENTS:
2763 // Raw pixels and external arrays do not reference other
2764 // objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00002765 break;
2766 case FAST_ELEMENTS: {
2767 int length = IsJSArray() ?
2768 Smi::cast(JSArray::cast(this)->length())->value() :
2769 FixedArray::cast(elements())->length();
2770 for (int i = 0; i < length; i++) {
2771 Object* element = FixedArray::cast(elements())->get(i);
2772 if (!element->IsTheHole() && element == obj) {
2773 return true;
2774 }
2775 }
2776 break;
2777 }
2778 case DICTIONARY_ELEMENTS: {
2779 key = element_dictionary()->SlowReverseLookup(obj);
2780 if (key != Heap::undefined_value()) {
2781 return true;
2782 }
2783 break;
2784 }
2785 default:
2786 UNREACHABLE();
2787 break;
2788 }
2789
Steve Block6ded16b2010-05-10 14:33:55 +01002790 // For functions check the context.
2791 if (IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002792 // Get the constructor function for arguments array.
2793 JSObject* arguments_boilerplate =
2794 Top::context()->global_context()->arguments_boilerplate();
2795 JSFunction* arguments_function =
2796 JSFunction::cast(arguments_boilerplate->map()->constructor());
2797
2798 // Get the context and don't check if it is the global context.
2799 JSFunction* f = JSFunction::cast(this);
2800 Context* context = f->context();
2801 if (context->IsGlobalContext()) {
2802 return false;
2803 }
2804
2805 // Check the non-special context slots.
2806 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2807 // Only check JS objects.
2808 if (context->get(i)->IsJSObject()) {
2809 JSObject* ctxobj = JSObject::cast(context->get(i));
2810 // If it is an arguments array check the content.
2811 if (ctxobj->map()->constructor() == arguments_function) {
2812 if (ctxobj->ReferencesObject(obj)) {
2813 return true;
2814 }
2815 } else if (ctxobj == obj) {
2816 return true;
2817 }
2818 }
2819 }
2820
2821 // Check the context extension if any.
2822 if (context->has_extension()) {
2823 return context->extension()->ReferencesObject(obj);
2824 }
2825 }
2826
2827 // No references to object.
2828 return false;
2829}
2830
2831
John Reck59135872010-11-02 12:39:01 -07002832MaybeObject* JSObject::PreventExtensions() {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01002833 if (IsAccessCheckNeeded() &&
2834 !Top::MayNamedAccess(this, Heap::undefined_value(), v8::ACCESS_KEYS)) {
2835 Top::ReportFailedAccessCheck(this, v8::ACCESS_KEYS);
2836 return Heap::false_value();
2837 }
2838
Steve Block1e0659c2011-05-24 12:43:12 +01002839 if (IsJSGlobalProxy()) {
2840 Object* proto = GetPrototype();
2841 if (proto->IsNull()) return this;
2842 ASSERT(proto->IsJSGlobalObject());
2843 return JSObject::cast(proto)->PreventExtensions();
2844 }
2845
Steve Block8defd9f2010-07-08 12:39:36 +01002846 // If there are fast elements we normalize.
2847 if (HasFastElements()) {
John Reck59135872010-11-02 12:39:01 -07002848 Object* ok;
2849 { MaybeObject* maybe_ok = NormalizeElements();
2850 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
2851 }
Steve Block8defd9f2010-07-08 12:39:36 +01002852 }
2853 // Make sure that we never go back to fast case.
2854 element_dictionary()->set_requires_slow_elements();
2855
2856 // Do a map transition, other objects with this map may still
2857 // be extensible.
John Reck59135872010-11-02 12:39:01 -07002858 Object* new_map;
2859 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
2860 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
2861 }
Steve Block8defd9f2010-07-08 12:39:36 +01002862 Map::cast(new_map)->set_is_extensible(false);
2863 set_map(Map::cast(new_map));
2864 ASSERT(!map()->is_extensible());
2865 return new_map;
2866}
2867
2868
Steve Blocka7e24c12009-10-30 11:49:00 +00002869// Tests for the fast common case for property enumeration:
Steve Blockd0582a62009-12-15 09:54:21 +00002870// - This object and all prototypes has an enum cache (which means that it has
2871// no interceptors and needs no access checks).
2872// - This object has no elements.
2873// - No prototype has enumerable properties/elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002874bool JSObject::IsSimpleEnum() {
Steve Blocka7e24c12009-10-30 11:49:00 +00002875 for (Object* o = this;
2876 o != Heap::null_value();
2877 o = JSObject::cast(o)->GetPrototype()) {
2878 JSObject* curr = JSObject::cast(o);
Steve Blocka7e24c12009-10-30 11:49:00 +00002879 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00002880 ASSERT(!curr->HasNamedInterceptor());
2881 ASSERT(!curr->HasIndexedInterceptor());
2882 ASSERT(!curr->IsAccessCheckNeeded());
Steve Blocka7e24c12009-10-30 11:49:00 +00002883 if (curr->NumberOfEnumElements() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002884 if (curr != this) {
2885 FixedArray* curr_fixed_array =
2886 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
Steve Blockd0582a62009-12-15 09:54:21 +00002887 if (curr_fixed_array->length() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002888 }
2889 }
2890 return true;
2891}
2892
2893
2894int Map::NumberOfDescribedProperties() {
2895 int result = 0;
2896 DescriptorArray* descs = instance_descriptors();
2897 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2898 if (descs->IsProperty(i)) result++;
2899 }
2900 return result;
2901}
2902
2903
2904int Map::PropertyIndexFor(String* name) {
2905 DescriptorArray* descs = instance_descriptors();
2906 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2907 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
2908 return descs->GetFieldIndex(i);
2909 }
2910 }
2911 return -1;
2912}
2913
2914
2915int Map::NextFreePropertyIndex() {
2916 int max_index = -1;
2917 DescriptorArray* descs = instance_descriptors();
2918 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2919 if (descs->GetType(i) == FIELD) {
2920 int current_index = descs->GetFieldIndex(i);
2921 if (current_index > max_index) max_index = current_index;
2922 }
2923 }
2924 return max_index + 1;
2925}
2926
2927
2928AccessorDescriptor* Map::FindAccessor(String* name) {
2929 DescriptorArray* descs = instance_descriptors();
2930 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2931 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
2932 return descs->GetCallbacks(i);
2933 }
2934 }
2935 return NULL;
2936}
2937
2938
2939void JSObject::LocalLookup(String* name, LookupResult* result) {
2940 ASSERT(name->IsString());
2941
2942 if (IsJSGlobalProxy()) {
2943 Object* proto = GetPrototype();
2944 if (proto->IsNull()) return result->NotFound();
2945 ASSERT(proto->IsJSGlobalObject());
2946 return JSObject::cast(proto)->LocalLookup(name, result);
2947 }
2948
2949 // Do not use inline caching if the object is a non-global object
2950 // that requires access checks.
2951 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
2952 result->DisallowCaching();
2953 }
2954
2955 // Check __proto__ before interceptor.
2956 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
2957 result->ConstantResult(this);
2958 return;
2959 }
2960
2961 // Check for lookup interceptor except when bootstrapping.
2962 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2963 result->InterceptorResult(this);
2964 return;
2965 }
2966
2967 LocalLookupRealNamedProperty(name, result);
2968}
2969
2970
2971void JSObject::Lookup(String* name, LookupResult* result) {
2972 // Ecma-262 3rd 8.6.2.4
2973 for (Object* current = this;
2974 current != Heap::null_value();
2975 current = JSObject::cast(current)->GetPrototype()) {
2976 JSObject::cast(current)->LocalLookup(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002977 if (result->IsProperty()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002978 }
2979 result->NotFound();
2980}
2981
2982
2983// Search object and it's prototype chain for callback properties.
2984void JSObject::LookupCallback(String* name, LookupResult* result) {
2985 for (Object* current = this;
2986 current != Heap::null_value();
2987 current = JSObject::cast(current)->GetPrototype()) {
2988 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002989 if (result->IsProperty() && result->type() == CALLBACKS) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002990 }
2991 result->NotFound();
2992}
2993
2994
John Reck59135872010-11-02 12:39:01 -07002995MaybeObject* JSObject::DefineGetterSetter(String* name,
2996 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002997 // Make sure that the top context does not change when doing callbacks or
2998 // interceptor calls.
2999 AssertNoContextChange ncc;
3000
Steve Blocka7e24c12009-10-30 11:49:00 +00003001 // Try to flatten before operating on the string.
Steve Block6ded16b2010-05-10 14:33:55 +01003002 name->TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00003003
Leon Clarkef7060e22010-06-03 12:02:55 +01003004 if (!CanSetCallback(name)) {
3005 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00003006 }
3007
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01003008 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00003009 bool is_element = name->AsArrayIndex(&index);
Steve Blocka7e24c12009-10-30 11:49:00 +00003010
3011 if (is_element) {
3012 switch (GetElementsKind()) {
3013 case FAST_ELEMENTS:
3014 break;
3015 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00003016 case EXTERNAL_BYTE_ELEMENTS:
3017 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3018 case EXTERNAL_SHORT_ELEMENTS:
3019 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3020 case EXTERNAL_INT_ELEMENTS:
3021 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3022 case EXTERNAL_FLOAT_ELEMENTS:
3023 // Ignore getters and setters on pixel and external array
3024 // elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00003025 return Heap::undefined_value();
3026 case DICTIONARY_ELEMENTS: {
3027 // Lookup the index.
3028 NumberDictionary* dictionary = element_dictionary();
3029 int entry = dictionary->FindEntry(index);
3030 if (entry != NumberDictionary::kNotFound) {
3031 Object* result = dictionary->ValueAt(entry);
3032 PropertyDetails details = dictionary->DetailsAt(entry);
3033 if (details.IsReadOnly()) return Heap::undefined_value();
3034 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003035 if (result->IsFixedArray()) {
3036 return result;
3037 }
3038 // Otherwise allow to override it.
Steve Blocka7e24c12009-10-30 11:49:00 +00003039 }
3040 }
3041 break;
3042 }
3043 default:
3044 UNREACHABLE();
3045 break;
3046 }
3047 } else {
3048 // Lookup the name.
3049 LookupResult result;
3050 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00003051 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003052 if (result.IsReadOnly()) return Heap::undefined_value();
3053 if (result.type() == CALLBACKS) {
3054 Object* obj = result.GetCallbackObject();
Leon Clarkef7060e22010-06-03 12:02:55 +01003055 // Need to preserve old getters/setters.
Leon Clarked91b9f72010-01-27 17:25:45 +00003056 if (obj->IsFixedArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003057 // Use set to update attributes.
3058 return SetPropertyCallback(name, obj, attributes);
Leon Clarked91b9f72010-01-27 17:25:45 +00003059 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003060 }
3061 }
3062 }
3063
3064 // Allocate the fixed array to hold getter and setter.
John Reck59135872010-11-02 12:39:01 -07003065 Object* structure;
3066 { MaybeObject* maybe_structure = Heap::AllocateFixedArray(2, TENURED);
3067 if (!maybe_structure->ToObject(&structure)) return maybe_structure;
3068 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003069
3070 if (is_element) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003071 return SetElementCallback(index, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00003072 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01003073 return SetPropertyCallback(name, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00003074 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003075}
3076
3077
3078bool JSObject::CanSetCallback(String* name) {
3079 ASSERT(!IsAccessCheckNeeded()
3080 || Top::MayNamedAccess(this, name, v8::ACCESS_SET));
3081
3082 // Check if there is an API defined callback object which prohibits
3083 // callback overwriting in this object or it's prototype chain.
3084 // This mechanism is needed for instance in a browser setting, where
3085 // certain accessors such as window.location should not be allowed
3086 // to be overwritten because allowing overwriting could potentially
3087 // cause security problems.
3088 LookupResult callback_result;
3089 LookupCallback(name, &callback_result);
3090 if (callback_result.IsProperty()) {
3091 Object* obj = callback_result.GetCallbackObject();
3092 if (obj->IsAccessorInfo() &&
3093 AccessorInfo::cast(obj)->prohibits_overwriting()) {
3094 return false;
3095 }
3096 }
3097
3098 return true;
3099}
3100
3101
John Reck59135872010-11-02 12:39:01 -07003102MaybeObject* JSObject::SetElementCallback(uint32_t index,
3103 Object* structure,
3104 PropertyAttributes attributes) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003105 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3106
3107 // Normalize elements to make this operation simple.
John Reck59135872010-11-02 12:39:01 -07003108 Object* ok;
3109 { MaybeObject* maybe_ok = NormalizeElements();
3110 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3111 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003112
3113 // Update the dictionary with the new CALLBACKS property.
John Reck59135872010-11-02 12:39:01 -07003114 Object* dict;
3115 { MaybeObject* maybe_dict =
3116 element_dictionary()->Set(index, structure, details);
3117 if (!maybe_dict->ToObject(&dict)) return maybe_dict;
3118 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003119
3120 NumberDictionary* elements = NumberDictionary::cast(dict);
3121 elements->set_requires_slow_elements();
3122 // Set the potential new dictionary on the object.
3123 set_elements(elements);
Steve Blocka7e24c12009-10-30 11:49:00 +00003124
3125 return structure;
3126}
3127
3128
John Reck59135872010-11-02 12:39:01 -07003129MaybeObject* JSObject::SetPropertyCallback(String* name,
3130 Object* structure,
3131 PropertyAttributes attributes) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003132 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3133
3134 bool convert_back_to_fast = HasFastProperties() &&
3135 (map()->instance_descriptors()->number_of_descriptors()
3136 < DescriptorArray::kMaxNumberOfDescriptors);
3137
3138 // Normalize object to make this operation simple.
John Reck59135872010-11-02 12:39:01 -07003139 Object* ok;
3140 { MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3141 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3142 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003143
3144 // For the global object allocate a new map to invalidate the global inline
3145 // caches which have a global property cell reference directly in the code.
3146 if (IsGlobalObject()) {
John Reck59135872010-11-02 12:39:01 -07003147 Object* new_map;
3148 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
3149 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3150 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003151 set_map(Map::cast(new_map));
Ben Murdochb0fe1622011-05-05 13:52:32 +01003152 // When running crankshaft, changing the map is not enough. We
3153 // need to deoptimize all functions that rely on this global
3154 // object.
3155 Deoptimizer::DeoptimizeGlobalObject(this);
Leon Clarkef7060e22010-06-03 12:02:55 +01003156 }
3157
3158 // Update the dictionary with the new CALLBACKS property.
John Reck59135872010-11-02 12:39:01 -07003159 Object* result;
3160 { MaybeObject* maybe_result = SetNormalizedProperty(name, structure, details);
3161 if (!maybe_result->ToObject(&result)) return maybe_result;
3162 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003163
3164 if (convert_back_to_fast) {
John Reck59135872010-11-02 12:39:01 -07003165 { MaybeObject* maybe_ok = TransformToFastProperties(0);
3166 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3167 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003168 }
3169 return result;
3170}
3171
John Reck59135872010-11-02 12:39:01 -07003172MaybeObject* JSObject::DefineAccessor(String* name,
3173 bool is_getter,
Ben Murdochb0fe1622011-05-05 13:52:32 +01003174 Object* fun,
John Reck59135872010-11-02 12:39:01 -07003175 PropertyAttributes attributes) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01003176 ASSERT(fun->IsJSFunction() || fun->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00003177 // Check access rights if needed.
3178 if (IsAccessCheckNeeded() &&
Leon Clarkef7060e22010-06-03 12:02:55 +01003179 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
3180 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Steve Blocka7e24c12009-10-30 11:49:00 +00003181 return Heap::undefined_value();
3182 }
3183
3184 if (IsJSGlobalProxy()) {
3185 Object* proto = GetPrototype();
3186 if (proto->IsNull()) return this;
3187 ASSERT(proto->IsJSGlobalObject());
3188 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
3189 fun, attributes);
3190 }
3191
John Reck59135872010-11-02 12:39:01 -07003192 Object* array;
3193 { MaybeObject* maybe_array = DefineGetterSetter(name, attributes);
3194 if (!maybe_array->ToObject(&array)) return maybe_array;
3195 }
3196 if (array->IsUndefined()) return array;
Steve Blocka7e24c12009-10-30 11:49:00 +00003197 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
3198 return this;
3199}
3200
3201
John Reck59135872010-11-02 12:39:01 -07003202MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003203 String* name = String::cast(info->name());
3204 // Check access rights if needed.
3205 if (IsAccessCheckNeeded() &&
3206 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
3207 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
3208 return Heap::undefined_value();
3209 }
3210
3211 if (IsJSGlobalProxy()) {
3212 Object* proto = GetPrototype();
3213 if (proto->IsNull()) return this;
3214 ASSERT(proto->IsJSGlobalObject());
3215 return JSObject::cast(proto)->DefineAccessor(info);
3216 }
3217
3218 // Make sure that the top context does not change when doing callbacks or
3219 // interceptor calls.
3220 AssertNoContextChange ncc;
3221
3222 // Try to flatten before operating on the string.
3223 name->TryFlatten();
3224
3225 if (!CanSetCallback(name)) {
3226 return Heap::undefined_value();
3227 }
3228
3229 uint32_t index = 0;
3230 bool is_element = name->AsArrayIndex(&index);
3231
3232 if (is_element) {
3233 if (IsJSArray()) return Heap::undefined_value();
3234
3235 // Accessors overwrite previous callbacks (cf. with getters/setters).
3236 switch (GetElementsKind()) {
3237 case FAST_ELEMENTS:
3238 break;
3239 case PIXEL_ELEMENTS:
3240 case EXTERNAL_BYTE_ELEMENTS:
3241 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3242 case EXTERNAL_SHORT_ELEMENTS:
3243 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3244 case EXTERNAL_INT_ELEMENTS:
3245 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3246 case EXTERNAL_FLOAT_ELEMENTS:
3247 // Ignore getters and setters on pixel and external array
3248 // elements.
3249 return Heap::undefined_value();
3250 case DICTIONARY_ELEMENTS:
3251 break;
3252 default:
3253 UNREACHABLE();
3254 break;
3255 }
3256
John Reck59135872010-11-02 12:39:01 -07003257 Object* ok;
3258 { MaybeObject* maybe_ok =
3259 SetElementCallback(index, info, info->property_attributes());
3260 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3261 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003262 } else {
3263 // Lookup the name.
3264 LookupResult result;
3265 LocalLookup(name, &result);
3266 // ES5 forbids turning a property into an accessor if it's not
3267 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
3268 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
3269 return Heap::undefined_value();
3270 }
John Reck59135872010-11-02 12:39:01 -07003271 Object* ok;
3272 { MaybeObject* maybe_ok =
3273 SetPropertyCallback(name, info, info->property_attributes());
3274 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3275 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003276 }
3277
3278 return this;
3279}
3280
3281
Steve Blocka7e24c12009-10-30 11:49:00 +00003282Object* JSObject::LookupAccessor(String* name, bool is_getter) {
3283 // Make sure that the top context does not change when doing callbacks or
3284 // interceptor calls.
3285 AssertNoContextChange ncc;
3286
3287 // Check access rights if needed.
3288 if (IsAccessCheckNeeded() &&
3289 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
3290 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
3291 return Heap::undefined_value();
3292 }
3293
3294 // Make the lookup and include prototypes.
3295 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01003296 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00003297 if (name->AsArrayIndex(&index)) {
3298 for (Object* obj = this;
3299 obj != Heap::null_value();
3300 obj = JSObject::cast(obj)->GetPrototype()) {
3301 JSObject* js_object = JSObject::cast(obj);
3302 if (js_object->HasDictionaryElements()) {
3303 NumberDictionary* dictionary = js_object->element_dictionary();
3304 int entry = dictionary->FindEntry(index);
3305 if (entry != NumberDictionary::kNotFound) {
3306 Object* element = dictionary->ValueAt(entry);
3307 PropertyDetails details = dictionary->DetailsAt(entry);
3308 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003309 if (element->IsFixedArray()) {
3310 return FixedArray::cast(element)->get(accessor_index);
3311 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003312 }
3313 }
3314 }
3315 }
3316 } else {
3317 for (Object* obj = this;
3318 obj != Heap::null_value();
3319 obj = JSObject::cast(obj)->GetPrototype()) {
3320 LookupResult result;
3321 JSObject::cast(obj)->LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00003322 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003323 if (result.IsReadOnly()) return Heap::undefined_value();
3324 if (result.type() == CALLBACKS) {
3325 Object* obj = result.GetCallbackObject();
3326 if (obj->IsFixedArray()) {
3327 return FixedArray::cast(obj)->get(accessor_index);
3328 }
3329 }
3330 }
3331 }
3332 }
3333 return Heap::undefined_value();
3334}
3335
3336
3337Object* JSObject::SlowReverseLookup(Object* value) {
3338 if (HasFastProperties()) {
3339 DescriptorArray* descs = map()->instance_descriptors();
3340 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3341 if (descs->GetType(i) == FIELD) {
3342 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3343 return descs->GetKey(i);
3344 }
3345 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3346 if (descs->GetConstantFunction(i) == value) {
3347 return descs->GetKey(i);
3348 }
3349 }
3350 }
3351 return Heap::undefined_value();
3352 } else {
3353 return property_dictionary()->SlowReverseLookup(value);
3354 }
3355}
3356
3357
John Reck59135872010-11-02 12:39:01 -07003358MaybeObject* Map::CopyDropDescriptors() {
3359 Object* result;
3360 { MaybeObject* maybe_result =
3361 Heap::AllocateMap(instance_type(), instance_size());
3362 if (!maybe_result->ToObject(&result)) return maybe_result;
3363 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003364 Map::cast(result)->set_prototype(prototype());
3365 Map::cast(result)->set_constructor(constructor());
3366 // Don't copy descriptors, so map transitions always remain a forest.
3367 // If we retained the same descriptors we would have two maps
3368 // pointing to the same transition which is bad because the garbage
3369 // collector relies on being able to reverse pointers from transitions
3370 // to maps. If properties need to be retained use CopyDropTransitions.
3371 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
3372 // Please note instance_type and instance_size are set when allocated.
3373 Map::cast(result)->set_inobject_properties(inobject_properties());
3374 Map::cast(result)->set_unused_property_fields(unused_property_fields());
3375
3376 // If the map has pre-allocated properties always start out with a descriptor
3377 // array describing these properties.
3378 if (pre_allocated_property_fields() > 0) {
3379 ASSERT(constructor()->IsJSFunction());
3380 JSFunction* ctor = JSFunction::cast(constructor());
John Reck59135872010-11-02 12:39:01 -07003381 Object* descriptors;
3382 { MaybeObject* maybe_descriptors =
3383 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
3384 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
3385 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003386 Map::cast(result)->set_instance_descriptors(
3387 DescriptorArray::cast(descriptors));
3388 Map::cast(result)->set_pre_allocated_property_fields(
3389 pre_allocated_property_fields());
3390 }
3391 Map::cast(result)->set_bit_field(bit_field());
3392 Map::cast(result)->set_bit_field2(bit_field2());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003393 Map::cast(result)->set_is_shared(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00003394 Map::cast(result)->ClearCodeCache();
3395 return result;
3396}
3397
3398
John Reck59135872010-11-02 12:39:01 -07003399MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
3400 NormalizedMapSharingMode sharing) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003401 int new_instance_size = instance_size();
3402 if (mode == CLEAR_INOBJECT_PROPERTIES) {
3403 new_instance_size -= inobject_properties() * kPointerSize;
3404 }
3405
John Reck59135872010-11-02 12:39:01 -07003406 Object* result;
3407 { MaybeObject* maybe_result =
3408 Heap::AllocateMap(instance_type(), new_instance_size);
3409 if (!maybe_result->ToObject(&result)) return maybe_result;
3410 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003411
3412 if (mode != CLEAR_INOBJECT_PROPERTIES) {
3413 Map::cast(result)->set_inobject_properties(inobject_properties());
3414 }
3415
3416 Map::cast(result)->set_prototype(prototype());
3417 Map::cast(result)->set_constructor(constructor());
3418
3419 Map::cast(result)->set_bit_field(bit_field());
3420 Map::cast(result)->set_bit_field2(bit_field2());
3421
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003422 Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
3423
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003424#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003425 if (Map::cast(result)->is_shared()) {
3426 Map::cast(result)->SharedMapVerify();
3427 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003428#endif
3429
3430 return result;
3431}
3432
3433
John Reck59135872010-11-02 12:39:01 -07003434MaybeObject* Map::CopyDropTransitions() {
3435 Object* new_map;
3436 { MaybeObject* maybe_new_map = CopyDropDescriptors();
3437 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3438 }
3439 Object* descriptors;
3440 { MaybeObject* maybe_descriptors =
3441 instance_descriptors()->RemoveTransitions();
3442 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
3443 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003444 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
Steve Block8defd9f2010-07-08 12:39:36 +01003445 return new_map;
Steve Blocka7e24c12009-10-30 11:49:00 +00003446}
3447
3448
John Reck59135872010-11-02 12:39:01 -07003449MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003450 // Allocate the code cache if not present.
3451 if (code_cache()->IsFixedArray()) {
John Reck59135872010-11-02 12:39:01 -07003452 Object* result;
3453 { MaybeObject* maybe_result = Heap::AllocateCodeCache();
3454 if (!maybe_result->ToObject(&result)) return maybe_result;
3455 }
Steve Block6ded16b2010-05-10 14:33:55 +01003456 set_code_cache(result);
3457 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003458
Steve Block6ded16b2010-05-10 14:33:55 +01003459 // Update the code cache.
3460 return CodeCache::cast(code_cache())->Update(name, code);
3461}
3462
3463
3464Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3465 // Do a lookup if a code cache exists.
3466 if (!code_cache()->IsFixedArray()) {
3467 return CodeCache::cast(code_cache())->Lookup(name, flags);
3468 } else {
3469 return Heap::undefined_value();
3470 }
3471}
3472
3473
3474int Map::IndexInCodeCache(Object* name, Code* code) {
3475 // Get the internal index if a code cache exists.
3476 if (!code_cache()->IsFixedArray()) {
3477 return CodeCache::cast(code_cache())->GetIndex(name, code);
3478 }
3479 return -1;
3480}
3481
3482
3483void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
3484 // No GC is supposed to happen between a call to IndexInCodeCache and
3485 // RemoveFromCodeCache so the code cache must be there.
3486 ASSERT(!code_cache()->IsFixedArray());
3487 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
3488}
3489
3490
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003491void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
3492 Map* current = this;
3493 while (current != Heap::meta_map()) {
3494 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
3495 *RawField(current, Map::kInstanceDescriptorsOffset));
3496 if (d == Heap::empty_descriptor_array()) {
3497 Map* prev = current->map();
3498 current->set_map(Heap::meta_map());
3499 callback(current, data);
3500 current = prev;
3501 continue;
3502 }
3503
3504 FixedArray* contents = reinterpret_cast<FixedArray*>(
3505 d->get(DescriptorArray::kContentArrayIndex));
3506 Object** map_or_index_field = RawField(contents, HeapObject::kMapOffset);
3507 Object* map_or_index = *map_or_index_field;
3508 bool map_done = true;
3509 for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0;
3510 i < contents->length();
3511 i += 2) {
3512 PropertyDetails details(Smi::cast(contents->get(i + 1)));
3513 if (details.IsTransition()) {
3514 Map* next = reinterpret_cast<Map*>(contents->get(i));
3515 next->set_map(current);
3516 *map_or_index_field = Smi::FromInt(i + 2);
3517 current = next;
3518 map_done = false;
3519 break;
3520 }
3521 }
3522 if (!map_done) continue;
3523 *map_or_index_field = Heap::fixed_array_map();
3524 Map* prev = current->map();
3525 current->set_map(Heap::meta_map());
3526 callback(current, data);
3527 current = prev;
3528 }
3529}
3530
3531
John Reck59135872010-11-02 12:39:01 -07003532MaybeObject* CodeCache::Update(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003533 ASSERT(code->ic_state() == MONOMORPHIC);
3534
3535 // The number of monomorphic stubs for normal load/store/call IC's can grow to
3536 // a large number and therefore they need to go into a hash table. They are
3537 // used to load global properties from cells.
3538 if (code->type() == NORMAL) {
3539 // Make sure that a hash table is allocated for the normal load code cache.
3540 if (normal_type_cache()->IsUndefined()) {
John Reck59135872010-11-02 12:39:01 -07003541 Object* result;
3542 { MaybeObject* maybe_result =
3543 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
3544 if (!maybe_result->ToObject(&result)) return maybe_result;
3545 }
Steve Block6ded16b2010-05-10 14:33:55 +01003546 set_normal_type_cache(result);
3547 }
3548 return UpdateNormalTypeCache(name, code);
3549 } else {
3550 ASSERT(default_cache()->IsFixedArray());
3551 return UpdateDefaultCache(name, code);
3552 }
3553}
3554
3555
John Reck59135872010-11-02 12:39:01 -07003556MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003557 // When updating the default code cache we disregard the type encoded in the
Steve Blocka7e24c12009-10-30 11:49:00 +00003558 // flags. This allows call constant stubs to overwrite call field
3559 // stubs, etc.
3560 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
3561
3562 // First check whether we can update existing code cache without
3563 // extending it.
Steve Block6ded16b2010-05-10 14:33:55 +01003564 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003565 int length = cache->length();
3566 int deleted_index = -1;
Steve Block6ded16b2010-05-10 14:33:55 +01003567 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003568 Object* key = cache->get(i);
3569 if (key->IsNull()) {
3570 if (deleted_index < 0) deleted_index = i;
3571 continue;
3572 }
3573 if (key->IsUndefined()) {
3574 if (deleted_index >= 0) i = deleted_index;
Steve Block6ded16b2010-05-10 14:33:55 +01003575 cache->set(i + kCodeCacheEntryNameOffset, name);
3576 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003577 return this;
3578 }
3579 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003580 Code::Flags found =
3581 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
Steve Blocka7e24c12009-10-30 11:49:00 +00003582 if (Code::RemoveTypeFromFlags(found) == flags) {
Steve Block6ded16b2010-05-10 14:33:55 +01003583 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003584 return this;
3585 }
3586 }
3587 }
3588
3589 // Reached the end of the code cache. If there were deleted
3590 // elements, reuse the space for the first of them.
3591 if (deleted_index >= 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01003592 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
3593 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003594 return this;
3595 }
3596
Steve Block6ded16b2010-05-10 14:33:55 +01003597 // Extend the code cache with some new entries (at least one). Must be a
3598 // multiple of the entry size.
3599 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
3600 new_length = new_length - new_length % kCodeCacheEntrySize;
3601 ASSERT((new_length % kCodeCacheEntrySize) == 0);
John Reck59135872010-11-02 12:39:01 -07003602 Object* result;
3603 { MaybeObject* maybe_result = cache->CopySize(new_length);
3604 if (!maybe_result->ToObject(&result)) return maybe_result;
3605 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003606
3607 // Add the (name, code) pair to the new cache.
3608 cache = FixedArray::cast(result);
Steve Block6ded16b2010-05-10 14:33:55 +01003609 cache->set(length + kCodeCacheEntryNameOffset, name);
3610 cache->set(length + kCodeCacheEntryCodeOffset, code);
3611 set_default_cache(cache);
Steve Blocka7e24c12009-10-30 11:49:00 +00003612 return this;
3613}
3614
3615
John Reck59135872010-11-02 12:39:01 -07003616MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003617 // Adding a new entry can cause a new cache to be allocated.
3618 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
John Reck59135872010-11-02 12:39:01 -07003619 Object* new_cache;
3620 { MaybeObject* maybe_new_cache = cache->Put(name, code);
3621 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
3622 }
Steve Block6ded16b2010-05-10 14:33:55 +01003623 set_normal_type_cache(new_cache);
3624 return this;
3625}
3626
3627
3628Object* CodeCache::Lookup(String* name, Code::Flags flags) {
3629 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
3630 return LookupNormalTypeCache(name, flags);
3631 } else {
3632 return LookupDefaultCache(name, flags);
3633 }
3634}
3635
3636
3637Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
3638 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003639 int length = cache->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003640 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
3641 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
Steve Blocka7e24c12009-10-30 11:49:00 +00003642 // Skip deleted elements.
3643 if (key->IsNull()) continue;
3644 if (key->IsUndefined()) return key;
3645 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003646 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
3647 if (code->flags() == flags) {
3648 return code;
3649 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003650 }
3651 }
3652 return Heap::undefined_value();
3653}
3654
3655
Steve Block6ded16b2010-05-10 14:33:55 +01003656Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
3657 if (!normal_type_cache()->IsUndefined()) {
3658 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3659 return cache->Lookup(name, flags);
3660 } else {
3661 return Heap::undefined_value();
3662 }
3663}
3664
3665
3666int CodeCache::GetIndex(Object* name, Code* code) {
3667 if (code->type() == NORMAL) {
3668 if (normal_type_cache()->IsUndefined()) return -1;
3669 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3670 return cache->GetIndex(String::cast(name), code->flags());
3671 }
3672
3673 FixedArray* array = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003674 int len = array->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003675 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
3676 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003677 }
3678 return -1;
3679}
3680
3681
Steve Block6ded16b2010-05-10 14:33:55 +01003682void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
3683 if (code->type() == NORMAL) {
3684 ASSERT(!normal_type_cache()->IsUndefined());
3685 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3686 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
3687 cache->RemoveByIndex(index);
3688 } else {
3689 FixedArray* array = default_cache();
3690 ASSERT(array->length() >= index && array->get(index)->IsCode());
3691 // Use null instead of undefined for deleted elements to distinguish
3692 // deleted elements from unused elements. This distinction is used
3693 // when looking up in the cache and when updating the cache.
3694 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
3695 array->set_null(index - 1); // Name.
3696 array->set_null(index); // Code.
3697 }
3698}
3699
3700
3701// The key in the code cache hash table consists of the property name and the
3702// code object. The actual match is on the name and the code flags. If a key
3703// is created using the flags and not a code object it can only be used for
3704// lookup not to create a new entry.
3705class CodeCacheHashTableKey : public HashTableKey {
3706 public:
3707 CodeCacheHashTableKey(String* name, Code::Flags flags)
3708 : name_(name), flags_(flags), code_(NULL) { }
3709
3710 CodeCacheHashTableKey(String* name, Code* code)
3711 : name_(name),
3712 flags_(code->flags()),
3713 code_(code) { }
3714
3715
3716 bool IsMatch(Object* other) {
3717 if (!other->IsFixedArray()) return false;
3718 FixedArray* pair = FixedArray::cast(other);
3719 String* name = String::cast(pair->get(0));
3720 Code::Flags flags = Code::cast(pair->get(1))->flags();
3721 if (flags != flags_) {
3722 return false;
3723 }
3724 return name_->Equals(name);
3725 }
3726
3727 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
3728 return name->Hash() ^ flags;
3729 }
3730
3731 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
3732
3733 uint32_t HashForObject(Object* obj) {
3734 FixedArray* pair = FixedArray::cast(obj);
3735 String* name = String::cast(pair->get(0));
3736 Code* code = Code::cast(pair->get(1));
3737 return NameFlagsHashHelper(name, code->flags());
3738 }
3739
John Reck59135872010-11-02 12:39:01 -07003740 MUST_USE_RESULT MaybeObject* AsObject() {
Steve Block6ded16b2010-05-10 14:33:55 +01003741 ASSERT(code_ != NULL);
John Reck59135872010-11-02 12:39:01 -07003742 Object* obj;
3743 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(2);
3744 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3745 }
Steve Block6ded16b2010-05-10 14:33:55 +01003746 FixedArray* pair = FixedArray::cast(obj);
3747 pair->set(0, name_);
3748 pair->set(1, code_);
3749 return pair;
3750 }
3751
3752 private:
3753 String* name_;
3754 Code::Flags flags_;
3755 Code* code_;
3756};
3757
3758
3759Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
3760 CodeCacheHashTableKey key(name, flags);
3761 int entry = FindEntry(&key);
3762 if (entry == kNotFound) return Heap::undefined_value();
3763 return get(EntryToIndex(entry) + 1);
3764}
3765
3766
John Reck59135872010-11-02 12:39:01 -07003767MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003768 CodeCacheHashTableKey key(name, code);
John Reck59135872010-11-02 12:39:01 -07003769 Object* obj;
3770 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
3771 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3772 }
Steve Block6ded16b2010-05-10 14:33:55 +01003773
3774 // Don't use this, as the table might have grown.
3775 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
3776
3777 int entry = cache->FindInsertionEntry(key.Hash());
John Reck59135872010-11-02 12:39:01 -07003778 Object* k;
3779 { MaybeObject* maybe_k = key.AsObject();
3780 if (!maybe_k->ToObject(&k)) return maybe_k;
3781 }
Steve Block6ded16b2010-05-10 14:33:55 +01003782
3783 cache->set(EntryToIndex(entry), k);
3784 cache->set(EntryToIndex(entry) + 1, code);
3785 cache->ElementAdded();
3786 return cache;
3787}
3788
3789
3790int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
3791 CodeCacheHashTableKey key(name, flags);
3792 int entry = FindEntry(&key);
3793 return (entry == kNotFound) ? -1 : entry;
3794}
3795
3796
3797void CodeCacheHashTable::RemoveByIndex(int index) {
3798 ASSERT(index >= 0);
3799 set(EntryToIndex(index), Heap::null_value());
3800 set(EntryToIndex(index) + 1, Heap::null_value());
3801 ElementRemoved();
Steve Blocka7e24c12009-10-30 11:49:00 +00003802}
3803
3804
Steve Blocka7e24c12009-10-30 11:49:00 +00003805static bool HasKey(FixedArray* array, Object* key) {
3806 int len0 = array->length();
3807 for (int i = 0; i < len0; i++) {
3808 Object* element = array->get(i);
3809 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3810 if (element->IsString() &&
3811 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3812 return true;
3813 }
3814 }
3815 return false;
3816}
3817
3818
John Reck59135872010-11-02 12:39:01 -07003819MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
Steve Block3ce2e202009-11-05 08:53:23 +00003820 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00003821 switch (array->GetElementsKind()) {
3822 case JSObject::FAST_ELEMENTS:
3823 return UnionOfKeys(FixedArray::cast(array->elements()));
3824 case JSObject::DICTIONARY_ELEMENTS: {
3825 NumberDictionary* dict = array->element_dictionary();
3826 int size = dict->NumberOfElements();
3827
3828 // Allocate a temporary fixed array.
John Reck59135872010-11-02 12:39:01 -07003829 Object* object;
3830 { MaybeObject* maybe_object = Heap::AllocateFixedArray(size);
3831 if (!maybe_object->ToObject(&object)) return maybe_object;
3832 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003833 FixedArray* key_array = FixedArray::cast(object);
3834
3835 int capacity = dict->Capacity();
3836 int pos = 0;
3837 // Copy the elements from the JSArray to the temporary fixed array.
3838 for (int i = 0; i < capacity; i++) {
3839 if (dict->IsKey(dict->KeyAt(i))) {
3840 key_array->set(pos++, dict->ValueAt(i));
3841 }
3842 }
3843 // Compute the union of this and the temporary fixed array.
3844 return UnionOfKeys(key_array);
3845 }
3846 default:
3847 UNREACHABLE();
3848 }
3849 UNREACHABLE();
3850 return Heap::null_value(); // Failure case needs to "return" a value.
3851}
3852
3853
John Reck59135872010-11-02 12:39:01 -07003854MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003855 int len0 = length();
Ben Murdochf87a2032010-10-22 12:50:53 +01003856#ifdef DEBUG
3857 if (FLAG_enable_slow_asserts) {
3858 for (int i = 0; i < len0; i++) {
3859 ASSERT(get(i)->IsString() || get(i)->IsNumber());
3860 }
3861 }
3862#endif
Steve Blocka7e24c12009-10-30 11:49:00 +00003863 int len1 = other->length();
Ben Murdochf87a2032010-10-22 12:50:53 +01003864 // Optimize if 'other' is empty.
3865 // We cannot optimize if 'this' is empty, as other may have holes
3866 // or non keys.
Steve Blocka7e24c12009-10-30 11:49:00 +00003867 if (len1 == 0) return this;
3868
3869 // Compute how many elements are not in this.
3870 int extra = 0;
3871 for (int y = 0; y < len1; y++) {
3872 Object* value = other->get(y);
3873 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3874 }
3875
3876 if (extra == 0) return this;
3877
3878 // Allocate the result
John Reck59135872010-11-02 12:39:01 -07003879 Object* obj;
3880 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(len0 + extra);
3881 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3882 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003883 // Fill in the content
Leon Clarke4515c472010-02-03 11:58:03 +00003884 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003885 FixedArray* result = FixedArray::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00003886 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003887 for (int i = 0; i < len0; i++) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003888 Object* e = get(i);
3889 ASSERT(e->IsString() || e->IsNumber());
3890 result->set(i, e, mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00003891 }
3892 // Fill in the extra keys.
3893 int index = 0;
3894 for (int y = 0; y < len1; y++) {
3895 Object* value = other->get(y);
3896 if (!value->IsTheHole() && !HasKey(this, value)) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003897 Object* e = other->get(y);
3898 ASSERT(e->IsString() || e->IsNumber());
3899 result->set(len0 + index, e, mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00003900 index++;
3901 }
3902 }
3903 ASSERT(extra == index);
3904 return result;
3905}
3906
3907
John Reck59135872010-11-02 12:39:01 -07003908MaybeObject* FixedArray::CopySize(int new_length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003909 if (new_length == 0) return Heap::empty_fixed_array();
John Reck59135872010-11-02 12:39:01 -07003910 Object* obj;
3911 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(new_length);
3912 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3913 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003914 FixedArray* result = FixedArray::cast(obj);
3915 // Copy the content
Leon Clarke4515c472010-02-03 11:58:03 +00003916 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003917 int len = length();
3918 if (new_length < len) len = new_length;
3919 result->set_map(map());
Leon Clarke4515c472010-02-03 11:58:03 +00003920 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003921 for (int i = 0; i < len; i++) {
3922 result->set(i, get(i), mode);
3923 }
3924 return result;
3925}
3926
3927
3928void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
Leon Clarke4515c472010-02-03 11:58:03 +00003929 AssertNoAllocation no_gc;
3930 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003931 for (int index = 0; index < len; index++) {
3932 dest->set(dest_pos+index, get(pos+index), mode);
3933 }
3934}
3935
3936
3937#ifdef DEBUG
3938bool FixedArray::IsEqualTo(FixedArray* other) {
3939 if (length() != other->length()) return false;
3940 for (int i = 0 ; i < length(); ++i) {
3941 if (get(i) != other->get(i)) return false;
3942 }
3943 return true;
3944}
3945#endif
3946
3947
John Reck59135872010-11-02 12:39:01 -07003948MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003949 if (number_of_descriptors == 0) {
3950 return Heap::empty_descriptor_array();
3951 }
3952 // Allocate the array of keys.
John Reck59135872010-11-02 12:39:01 -07003953 Object* array;
3954 { MaybeObject* maybe_array =
3955 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
3956 if (!maybe_array->ToObject(&array)) return maybe_array;
3957 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003958 // Do not use DescriptorArray::cast on incomplete object.
3959 FixedArray* result = FixedArray::cast(array);
3960
3961 // Allocate the content array and set it in the descriptor array.
John Reck59135872010-11-02 12:39:01 -07003962 { MaybeObject* maybe_array =
3963 Heap::AllocateFixedArray(number_of_descriptors << 1);
3964 if (!maybe_array->ToObject(&array)) return maybe_array;
3965 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003966 result->set(kContentArrayIndex, array);
3967 result->set(kEnumerationIndexIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00003968 Smi::FromInt(PropertyDetails::kInitialIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +00003969 return result;
3970}
3971
3972
3973void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3974 FixedArray* new_cache) {
3975 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3976 if (HasEnumCache()) {
3977 FixedArray::cast(get(kEnumerationIndexIndex))->
3978 set(kEnumCacheBridgeCacheIndex, new_cache);
3979 } else {
3980 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3981 FixedArray::cast(bridge_storage)->
3982 set(kEnumCacheBridgeCacheIndex, new_cache);
3983 fast_set(FixedArray::cast(bridge_storage),
3984 kEnumCacheBridgeEnumIndex,
3985 get(kEnumerationIndexIndex));
3986 set(kEnumerationIndexIndex, bridge_storage);
3987 }
3988}
3989
3990
John Reck59135872010-11-02 12:39:01 -07003991MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
3992 TransitionFlag transition_flag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003993 // Transitions are only kept when inserting another transition.
3994 // This precondition is not required by this function's implementation, but
3995 // is currently required by the semantics of maps, so we check it.
3996 // Conversely, we filter after replacing, so replacing a transition and
3997 // removing all other transitions is not supported.
3998 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3999 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
4000 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
4001
4002 // Ensure the key is a symbol.
John Reck59135872010-11-02 12:39:01 -07004003 Object* result;
4004 { MaybeObject* maybe_result = descriptor->KeyToSymbol();
4005 if (!maybe_result->ToObject(&result)) return maybe_result;
4006 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004007
4008 int transitions = 0;
4009 int null_descriptors = 0;
4010 if (remove_transitions) {
4011 for (int i = 0; i < number_of_descriptors(); i++) {
4012 if (IsTransition(i)) transitions++;
4013 if (IsNullDescriptor(i)) null_descriptors++;
4014 }
4015 } else {
4016 for (int i = 0; i < number_of_descriptors(); i++) {
4017 if (IsNullDescriptor(i)) null_descriptors++;
4018 }
4019 }
4020 int new_size = number_of_descriptors() - transitions - null_descriptors;
4021
4022 // If key is in descriptor, we replace it in-place when filtering.
4023 // Count a null descriptor for key as inserted, not replaced.
4024 int index = Search(descriptor->GetKey());
4025 const bool inserting = (index == kNotFound);
4026 const bool replacing = !inserting;
4027 bool keep_enumeration_index = false;
4028 if (inserting) {
4029 ++new_size;
4030 }
4031 if (replacing) {
4032 // We are replacing an existing descriptor. We keep the enumeration
4033 // index of a visible property.
4034 PropertyType t = PropertyDetails(GetDetails(index)).type();
4035 if (t == CONSTANT_FUNCTION ||
4036 t == FIELD ||
4037 t == CALLBACKS ||
4038 t == INTERCEPTOR) {
4039 keep_enumeration_index = true;
4040 } else if (remove_transitions) {
4041 // Replaced descriptor has been counted as removed if it is
4042 // a transition that will be replaced. Adjust count in this case.
4043 ++new_size;
4044 }
4045 }
John Reck59135872010-11-02 12:39:01 -07004046 { MaybeObject* maybe_result = Allocate(new_size);
4047 if (!maybe_result->ToObject(&result)) return maybe_result;
4048 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004049 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
4050 // Set the enumeration index in the descriptors and set the enumeration index
4051 // in the result.
4052 int enumeration_index = NextEnumerationIndex();
4053 if (!descriptor->GetDetails().IsTransition()) {
4054 if (keep_enumeration_index) {
4055 descriptor->SetEnumerationIndex(
4056 PropertyDetails(GetDetails(index)).index());
4057 } else {
4058 descriptor->SetEnumerationIndex(enumeration_index);
4059 ++enumeration_index;
4060 }
4061 }
4062 new_descriptors->SetNextEnumerationIndex(enumeration_index);
4063
4064 // Copy the descriptors, filtering out transitions and null descriptors,
4065 // and inserting or replacing a descriptor.
4066 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
4067 int from_index = 0;
4068 int to_index = 0;
4069
4070 for (; from_index < number_of_descriptors(); from_index++) {
4071 String* key = GetKey(from_index);
4072 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
4073 break;
4074 }
4075 if (IsNullDescriptor(from_index)) continue;
4076 if (remove_transitions && IsTransition(from_index)) continue;
4077 new_descriptors->CopyFrom(to_index++, this, from_index);
4078 }
4079
4080 new_descriptors->Set(to_index++, descriptor);
4081 if (replacing) from_index++;
4082
4083 for (; from_index < number_of_descriptors(); from_index++) {
4084 if (IsNullDescriptor(from_index)) continue;
4085 if (remove_transitions && IsTransition(from_index)) continue;
4086 new_descriptors->CopyFrom(to_index++, this, from_index);
4087 }
4088
4089 ASSERT(to_index == new_descriptors->number_of_descriptors());
4090 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
4091
4092 return new_descriptors;
4093}
4094
4095
John Reck59135872010-11-02 12:39:01 -07004096MaybeObject* DescriptorArray::RemoveTransitions() {
Steve Blocka7e24c12009-10-30 11:49:00 +00004097 // Remove all transitions and null descriptors. Return a copy of the array
4098 // with all transitions removed, or a Failure object if the new array could
4099 // not be allocated.
4100
4101 // Compute the size of the map transition entries to be removed.
4102 int num_removed = 0;
4103 for (int i = 0; i < number_of_descriptors(); i++) {
4104 if (!IsProperty(i)) num_removed++;
4105 }
4106
4107 // Allocate the new descriptor array.
John Reck59135872010-11-02 12:39:01 -07004108 Object* result;
4109 { MaybeObject* maybe_result = Allocate(number_of_descriptors() - num_removed);
4110 if (!maybe_result->ToObject(&result)) return maybe_result;
4111 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004112 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
4113
4114 // Copy the content.
4115 int next_descriptor = 0;
4116 for (int i = 0; i < number_of_descriptors(); i++) {
4117 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
4118 }
4119 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
4120
4121 return new_descriptors;
4122}
4123
4124
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004125void DescriptorArray::SortUnchecked() {
Steve Blocka7e24c12009-10-30 11:49:00 +00004126 // In-place heap sort.
4127 int len = number_of_descriptors();
4128
4129 // Bottom-up max-heap construction.
Steve Block6ded16b2010-05-10 14:33:55 +01004130 // Index of the last node with children
4131 const int max_parent_index = (len / 2) - 1;
4132 for (int i = max_parent_index; i >= 0; --i) {
4133 int parent_index = i;
4134 const uint32_t parent_hash = GetKey(i)->Hash();
4135 while (parent_index <= max_parent_index) {
4136 int child_index = 2 * parent_index + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00004137 uint32_t child_hash = GetKey(child_index)->Hash();
Steve Block6ded16b2010-05-10 14:33:55 +01004138 if (child_index + 1 < len) {
4139 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4140 if (right_child_hash > child_hash) {
4141 child_index++;
4142 child_hash = right_child_hash;
4143 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004144 }
Steve Block6ded16b2010-05-10 14:33:55 +01004145 if (child_hash <= parent_hash) break;
4146 Swap(parent_index, child_index);
4147 // Now element at child_index could be < its children.
4148 parent_index = child_index; // parent_hash remains correct.
Steve Blocka7e24c12009-10-30 11:49:00 +00004149 }
4150 }
4151
4152 // Extract elements and create sorted array.
4153 for (int i = len - 1; i > 0; --i) {
4154 // Put max element at the back of the array.
4155 Swap(0, i);
4156 // Sift down the new top element.
4157 int parent_index = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01004158 const uint32_t parent_hash = GetKey(parent_index)->Hash();
4159 const int max_parent_index = (i / 2) - 1;
4160 while (parent_index <= max_parent_index) {
4161 int child_index = parent_index * 2 + 1;
4162 uint32_t child_hash = GetKey(child_index)->Hash();
4163 if (child_index + 1 < i) {
4164 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4165 if (right_child_hash > child_hash) {
4166 child_index++;
4167 child_hash = right_child_hash;
4168 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004169 }
Steve Block6ded16b2010-05-10 14:33:55 +01004170 if (child_hash <= parent_hash) break;
4171 Swap(parent_index, child_index);
4172 parent_index = child_index;
Steve Blocka7e24c12009-10-30 11:49:00 +00004173 }
4174 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004175}
Steve Blocka7e24c12009-10-30 11:49:00 +00004176
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004177
4178void DescriptorArray::Sort() {
4179 SortUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00004180 SLOW_ASSERT(IsSortedNoDuplicates());
4181}
4182
4183
4184int DescriptorArray::BinarySearch(String* name, int low, int high) {
4185 uint32_t hash = name->Hash();
4186
4187 while (low <= high) {
4188 int mid = (low + high) / 2;
4189 String* mid_name = GetKey(mid);
4190 uint32_t mid_hash = mid_name->Hash();
4191
4192 if (mid_hash > hash) {
4193 high = mid - 1;
4194 continue;
4195 }
4196 if (mid_hash < hash) {
4197 low = mid + 1;
4198 continue;
4199 }
4200 // Found an element with the same hash-code.
4201 ASSERT(hash == mid_hash);
4202 // There might be more, so we find the first one and
4203 // check them all to see if we have a match.
4204 if (name == mid_name && !is_null_descriptor(mid)) return mid;
4205 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
4206 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
4207 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
4208 }
4209 break;
4210 }
4211 return kNotFound;
4212}
4213
4214
4215int DescriptorArray::LinearSearch(String* name, int len) {
4216 uint32_t hash = name->Hash();
4217 for (int number = 0; number < len; number++) {
4218 String* entry = GetKey(number);
4219 if ((entry->Hash() == hash) &&
4220 name->Equals(entry) &&
4221 !is_null_descriptor(number)) {
4222 return number;
4223 }
4224 }
4225 return kNotFound;
4226}
4227
4228
Ben Murdochb0fe1622011-05-05 13:52:32 +01004229MaybeObject* DeoptimizationInputData::Allocate(int deopt_entry_count,
4230 PretenureFlag pretenure) {
4231 ASSERT(deopt_entry_count > 0);
4232 return Heap::AllocateFixedArray(LengthFor(deopt_entry_count),
4233 pretenure);
4234}
4235
4236
4237MaybeObject* DeoptimizationOutputData::Allocate(int number_of_deopt_points,
4238 PretenureFlag pretenure) {
4239 if (number_of_deopt_points == 0) return Heap::empty_fixed_array();
4240 return Heap::AllocateFixedArray(LengthOfFixedArray(number_of_deopt_points),
4241 pretenure);
4242}
4243
4244
Steve Blocka7e24c12009-10-30 11:49:00 +00004245#ifdef DEBUG
4246bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
4247 if (IsEmpty()) return other->IsEmpty();
4248 if (other->IsEmpty()) return false;
4249 if (length() != other->length()) return false;
4250 for (int i = 0; i < length(); ++i) {
4251 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
4252 }
4253 return GetContentArray()->IsEqualTo(other->GetContentArray());
4254}
4255#endif
4256
4257
4258static StaticResource<StringInputBuffer> string_input_buffer;
4259
4260
4261bool String::LooksValid() {
4262 if (!Heap::Contains(this)) return false;
4263 return true;
4264}
4265
4266
4267int String::Utf8Length() {
4268 if (IsAsciiRepresentation()) return length();
4269 // Attempt to flatten before accessing the string. It probably
4270 // doesn't make Utf8Length faster, but it is very likely that
4271 // the string will be accessed later (for example by WriteUtf8)
4272 // so it's still a good idea.
Steve Block6ded16b2010-05-10 14:33:55 +01004273 TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00004274 Access<StringInputBuffer> buffer(&string_input_buffer);
4275 buffer->Reset(0, this);
4276 int result = 0;
4277 while (buffer->has_more())
4278 result += unibrow::Utf8::Length(buffer->GetNext());
4279 return result;
4280}
4281
4282
4283Vector<const char> String::ToAsciiVector() {
4284 ASSERT(IsAsciiRepresentation());
4285 ASSERT(IsFlat());
4286
4287 int offset = 0;
4288 int length = this->length();
4289 StringRepresentationTag string_tag = StringShape(this).representation_tag();
4290 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00004291 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004292 ConsString* cons = ConsString::cast(string);
4293 ASSERT(cons->second()->length() == 0);
4294 string = cons->first();
4295 string_tag = StringShape(string).representation_tag();
4296 }
4297 if (string_tag == kSeqStringTag) {
4298 SeqAsciiString* seq = SeqAsciiString::cast(string);
4299 char* start = seq->GetChars();
4300 return Vector<const char>(start + offset, length);
4301 }
4302 ASSERT(string_tag == kExternalStringTag);
4303 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
4304 const char* start = ext->resource()->data();
4305 return Vector<const char>(start + offset, length);
4306}
4307
4308
4309Vector<const uc16> String::ToUC16Vector() {
4310 ASSERT(IsTwoByteRepresentation());
4311 ASSERT(IsFlat());
4312
4313 int offset = 0;
4314 int length = this->length();
4315 StringRepresentationTag string_tag = StringShape(this).representation_tag();
4316 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00004317 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004318 ConsString* cons = ConsString::cast(string);
4319 ASSERT(cons->second()->length() == 0);
4320 string = cons->first();
4321 string_tag = StringShape(string).representation_tag();
4322 }
4323 if (string_tag == kSeqStringTag) {
4324 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
4325 return Vector<const uc16>(seq->GetChars() + offset, length);
4326 }
4327 ASSERT(string_tag == kExternalStringTag);
4328 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
4329 const uc16* start =
4330 reinterpret_cast<const uc16*>(ext->resource()->data());
4331 return Vector<const uc16>(start + offset, length);
4332}
4333
4334
4335SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
4336 RobustnessFlag robust_flag,
4337 int offset,
4338 int length,
4339 int* length_return) {
4340 ASSERT(NativeAllocationChecker::allocation_allowed());
4341 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
4342 return SmartPointer<char>(NULL);
4343 }
4344
4345 // Negative length means the to the end of the string.
4346 if (length < 0) length = kMaxInt - offset;
4347
4348 // Compute the size of the UTF-8 string. Start at the specified offset.
4349 Access<StringInputBuffer> buffer(&string_input_buffer);
4350 buffer->Reset(offset, this);
4351 int character_position = offset;
4352 int utf8_bytes = 0;
4353 while (buffer->has_more()) {
4354 uint16_t character = buffer->GetNext();
4355 if (character_position < offset + length) {
4356 utf8_bytes += unibrow::Utf8::Length(character);
4357 }
4358 character_position++;
4359 }
4360
4361 if (length_return) {
4362 *length_return = utf8_bytes;
4363 }
4364
4365 char* result = NewArray<char>(utf8_bytes + 1);
4366
4367 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
4368 buffer->Rewind();
4369 buffer->Seek(offset);
4370 character_position = offset;
4371 int utf8_byte_position = 0;
4372 while (buffer->has_more()) {
4373 uint16_t character = buffer->GetNext();
4374 if (character_position < offset + length) {
4375 if (allow_nulls == DISALLOW_NULLS && character == 0) {
4376 character = ' ';
4377 }
4378 utf8_byte_position +=
4379 unibrow::Utf8::Encode(result + utf8_byte_position, character);
4380 }
4381 character_position++;
4382 }
4383 result[utf8_byte_position] = 0;
4384 return SmartPointer<char>(result);
4385}
4386
4387
4388SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
4389 RobustnessFlag robust_flag,
4390 int* length_return) {
4391 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
4392}
4393
4394
4395const uc16* String::GetTwoByteData() {
4396 return GetTwoByteData(0);
4397}
4398
4399
4400const uc16* String::GetTwoByteData(unsigned start) {
4401 ASSERT(!IsAsciiRepresentation());
4402 switch (StringShape(this).representation_tag()) {
4403 case kSeqStringTag:
4404 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
4405 case kExternalStringTag:
4406 return ExternalTwoByteString::cast(this)->
4407 ExternalTwoByteStringGetData(start);
Steve Blocka7e24c12009-10-30 11:49:00 +00004408 case kConsStringTag:
4409 UNREACHABLE();
4410 return NULL;
4411 }
4412 UNREACHABLE();
4413 return NULL;
4414}
4415
4416
4417SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
4418 ASSERT(NativeAllocationChecker::allocation_allowed());
4419
4420 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
4421 return SmartPointer<uc16>();
4422 }
4423
4424 Access<StringInputBuffer> buffer(&string_input_buffer);
4425 buffer->Reset(this);
4426
4427 uc16* result = NewArray<uc16>(length() + 1);
4428
4429 int i = 0;
4430 while (buffer->has_more()) {
4431 uint16_t character = buffer->GetNext();
4432 result[i++] = character;
4433 }
4434 result[i] = 0;
4435 return SmartPointer<uc16>(result);
4436}
4437
4438
4439const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
4440 return reinterpret_cast<uc16*>(
4441 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
4442}
4443
4444
4445void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4446 unsigned* offset_ptr,
4447 unsigned max_chars) {
4448 unsigned chars_read = 0;
4449 unsigned offset = *offset_ptr;
4450 while (chars_read < max_chars) {
4451 uint16_t c = *reinterpret_cast<uint16_t*>(
4452 reinterpret_cast<char*>(this) -
4453 kHeapObjectTag + kHeaderSize + offset * kShortSize);
4454 if (c <= kMaxAsciiCharCode) {
4455 // Fast case for ASCII characters. Cursor is an input output argument.
4456 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4457 rbb->util_buffer,
4458 rbb->capacity,
4459 rbb->cursor)) {
4460 break;
4461 }
4462 } else {
4463 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4464 rbb->util_buffer,
4465 rbb->capacity,
4466 rbb->cursor)) {
4467 break;
4468 }
4469 }
4470 offset++;
4471 chars_read++;
4472 }
4473 *offset_ptr = offset;
4474 rbb->remaining += chars_read;
4475}
4476
4477
4478const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
4479 unsigned* remaining,
4480 unsigned* offset_ptr,
4481 unsigned max_chars) {
4482 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
4483 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
4484 *remaining = max_chars;
4485 *offset_ptr += max_chars;
4486 return b;
4487}
4488
4489
4490// This will iterate unless the block of string data spans two 'halves' of
4491// a ConsString, in which case it will recurse. Since the block of string
4492// data to be read has a maximum size this limits the maximum recursion
4493// depth to something sane. Since C++ does not have tail call recursion
4494// elimination, the iteration must be explicit. Since this is not an
4495// -IntoBuffer method it can delegate to one of the efficient
4496// *AsciiStringReadBlock routines.
4497const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
4498 unsigned* offset_ptr,
4499 unsigned max_chars) {
4500 ConsString* current = this;
4501 unsigned offset = *offset_ptr;
4502 int offset_correction = 0;
4503
4504 while (true) {
4505 String* left = current->first();
4506 unsigned left_length = (unsigned)left->length();
4507 if (left_length > offset &&
4508 (max_chars <= left_length - offset ||
4509 (rbb->capacity <= left_length - offset &&
4510 (max_chars = left_length - offset, true)))) { // comma operator!
4511 // Left hand side only - iterate unless we have reached the bottom of
4512 // the cons tree. The assignment on the left of the comma operator is
4513 // in order to make use of the fact that the -IntoBuffer routines can
4514 // produce at most 'capacity' characters. This enables us to postpone
4515 // the point where we switch to the -IntoBuffer routines (below) in order
4516 // to maximize the chances of delegating a big chunk of work to the
4517 // efficient *AsciiStringReadBlock routines.
4518 if (StringShape(left).IsCons()) {
4519 current = ConsString::cast(left);
4520 continue;
4521 } else {
4522 const unibrow::byte* answer =
4523 String::ReadBlock(left, rbb, &offset, max_chars);
4524 *offset_ptr = offset + offset_correction;
4525 return answer;
4526 }
4527 } else if (left_length <= offset) {
4528 // Right hand side only - iterate unless we have reached the bottom of
4529 // the cons tree.
4530 String* right = current->second();
4531 offset -= left_length;
4532 offset_correction += left_length;
4533 if (StringShape(right).IsCons()) {
4534 current = ConsString::cast(right);
4535 continue;
4536 } else {
4537 const unibrow::byte* answer =
4538 String::ReadBlock(right, rbb, &offset, max_chars);
4539 *offset_ptr = offset + offset_correction;
4540 return answer;
4541 }
4542 } else {
4543 // The block to be read spans two sides of the ConsString, so we call the
4544 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
4545 // are able to assemble data from several part strings because they use
4546 // the util_buffer to store their data and never return direct pointers
4547 // to their storage. We don't try to read more than the buffer capacity
4548 // here or we can get too much recursion.
4549 ASSERT(rbb->remaining == 0);
4550 ASSERT(rbb->cursor == 0);
4551 current->ConsStringReadBlockIntoBuffer(
4552 rbb,
4553 &offset,
4554 max_chars > rbb->capacity ? rbb->capacity : max_chars);
4555 *offset_ptr = offset + offset_correction;
4556 return rbb->util_buffer;
4557 }
4558 }
4559}
4560
4561
Steve Blocka7e24c12009-10-30 11:49:00 +00004562uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
4563 ASSERT(index >= 0 && index < length());
4564 return resource()->data()[index];
4565}
4566
4567
4568const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
4569 unsigned* remaining,
4570 unsigned* offset_ptr,
4571 unsigned max_chars) {
4572 // Cast const char* to unibrow::byte* (signedness difference).
4573 const unibrow::byte* b =
4574 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
4575 *remaining = max_chars;
4576 *offset_ptr += max_chars;
4577 return b;
4578}
4579
4580
4581const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
4582 unsigned start) {
4583 return resource()->data() + start;
4584}
4585
4586
4587uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
4588 ASSERT(index >= 0 && index < length());
4589 return resource()->data()[index];
4590}
4591
4592
4593void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
4594 ReadBlockBuffer* rbb,
4595 unsigned* offset_ptr,
4596 unsigned max_chars) {
4597 unsigned chars_read = 0;
4598 unsigned offset = *offset_ptr;
4599 const uint16_t* data = resource()->data();
4600 while (chars_read < max_chars) {
4601 uint16_t c = data[offset];
4602 if (c <= kMaxAsciiCharCode) {
4603 // Fast case for ASCII characters. Cursor is an input output argument.
4604 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4605 rbb->util_buffer,
4606 rbb->capacity,
4607 rbb->cursor))
4608 break;
4609 } else {
4610 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4611 rbb->util_buffer,
4612 rbb->capacity,
4613 rbb->cursor))
4614 break;
4615 }
4616 offset++;
4617 chars_read++;
4618 }
4619 *offset_ptr = offset;
4620 rbb->remaining += chars_read;
4621}
4622
4623
4624void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4625 unsigned* offset_ptr,
4626 unsigned max_chars) {
4627 unsigned capacity = rbb->capacity - rbb->cursor;
4628 if (max_chars > capacity) max_chars = capacity;
4629 memcpy(rbb->util_buffer + rbb->cursor,
4630 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
4631 *offset_ptr * kCharSize,
4632 max_chars);
4633 rbb->remaining += max_chars;
4634 *offset_ptr += max_chars;
4635 rbb->cursor += max_chars;
4636}
4637
4638
4639void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
4640 ReadBlockBuffer* rbb,
4641 unsigned* offset_ptr,
4642 unsigned max_chars) {
4643 unsigned capacity = rbb->capacity - rbb->cursor;
4644 if (max_chars > capacity) max_chars = capacity;
4645 memcpy(rbb->util_buffer + rbb->cursor,
4646 resource()->data() + *offset_ptr,
4647 max_chars);
4648 rbb->remaining += max_chars;
4649 *offset_ptr += max_chars;
4650 rbb->cursor += max_chars;
4651}
4652
4653
4654// This method determines the type of string involved and then copies
4655// a whole chunk of characters into a buffer, or returns a pointer to a buffer
4656// where they can be found. The pointer is not necessarily valid across a GC
4657// (see AsciiStringReadBlock).
4658const unibrow::byte* String::ReadBlock(String* input,
4659 ReadBlockBuffer* rbb,
4660 unsigned* offset_ptr,
4661 unsigned max_chars) {
4662 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
4663 if (max_chars == 0) {
4664 rbb->remaining = 0;
4665 return NULL;
4666 }
4667 switch (StringShape(input).representation_tag()) {
4668 case kSeqStringTag:
4669 if (input->IsAsciiRepresentation()) {
4670 SeqAsciiString* str = SeqAsciiString::cast(input);
4671 return str->SeqAsciiStringReadBlock(&rbb->remaining,
4672 offset_ptr,
4673 max_chars);
4674 } else {
4675 SeqTwoByteString* str = SeqTwoByteString::cast(input);
4676 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4677 offset_ptr,
4678 max_chars);
4679 return rbb->util_buffer;
4680 }
4681 case kConsStringTag:
4682 return ConsString::cast(input)->ConsStringReadBlock(rbb,
4683 offset_ptr,
4684 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004685 case kExternalStringTag:
4686 if (input->IsAsciiRepresentation()) {
4687 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
4688 &rbb->remaining,
4689 offset_ptr,
4690 max_chars);
4691 } else {
4692 ExternalTwoByteString::cast(input)->
4693 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4694 offset_ptr,
4695 max_chars);
4696 return rbb->util_buffer;
4697 }
4698 default:
4699 break;
4700 }
4701
4702 UNREACHABLE();
4703 return 0;
4704}
4705
4706
4707Relocatable* Relocatable::top_ = NULL;
4708
4709
4710void Relocatable::PostGarbageCollectionProcessing() {
4711 Relocatable* current = top_;
4712 while (current != NULL) {
4713 current->PostGarbageCollection();
4714 current = current->prev_;
4715 }
4716}
4717
4718
4719// Reserve space for statics needing saving and restoring.
4720int Relocatable::ArchiveSpacePerThread() {
4721 return sizeof(top_);
4722}
4723
4724
4725// Archive statics that are thread local.
4726char* Relocatable::ArchiveState(char* to) {
4727 *reinterpret_cast<Relocatable**>(to) = top_;
4728 top_ = NULL;
4729 return to + ArchiveSpacePerThread();
4730}
4731
4732
4733// Restore statics that are thread local.
4734char* Relocatable::RestoreState(char* from) {
4735 top_ = *reinterpret_cast<Relocatable**>(from);
4736 return from + ArchiveSpacePerThread();
4737}
4738
4739
4740char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
4741 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
4742 Iterate(v, top);
4743 return thread_storage + ArchiveSpacePerThread();
4744}
4745
4746
4747void Relocatable::Iterate(ObjectVisitor* v) {
4748 Iterate(v, top_);
4749}
4750
4751
4752void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
4753 Relocatable* current = top;
4754 while (current != NULL) {
4755 current->IterateInstance(v);
4756 current = current->prev_;
4757 }
4758}
4759
4760
4761FlatStringReader::FlatStringReader(Handle<String> str)
4762 : str_(str.location()),
4763 length_(str->length()) {
4764 PostGarbageCollection();
4765}
4766
4767
4768FlatStringReader::FlatStringReader(Vector<const char> input)
4769 : str_(0),
4770 is_ascii_(true),
4771 length_(input.length()),
4772 start_(input.start()) { }
4773
4774
4775void FlatStringReader::PostGarbageCollection() {
4776 if (str_ == NULL) return;
4777 Handle<String> str(str_);
4778 ASSERT(str->IsFlat());
4779 is_ascii_ = str->IsAsciiRepresentation();
4780 if (is_ascii_) {
4781 start_ = str->ToAsciiVector().start();
4782 } else {
4783 start_ = str->ToUC16Vector().start();
4784 }
4785}
4786
4787
4788void StringInputBuffer::Seek(unsigned pos) {
4789 Reset(pos, input_);
4790}
4791
4792
4793void SafeStringInputBuffer::Seek(unsigned pos) {
4794 Reset(pos, input_);
4795}
4796
4797
4798// This method determines the type of string involved and then copies
4799// a whole chunk of characters into a buffer. It can be used with strings
4800// that have been glued together to form a ConsString and which must cooperate
4801// to fill up a buffer.
4802void String::ReadBlockIntoBuffer(String* input,
4803 ReadBlockBuffer* rbb,
4804 unsigned* offset_ptr,
4805 unsigned max_chars) {
4806 ASSERT(*offset_ptr <= (unsigned)input->length());
4807 if (max_chars == 0) return;
4808
4809 switch (StringShape(input).representation_tag()) {
4810 case kSeqStringTag:
4811 if (input->IsAsciiRepresentation()) {
4812 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4813 offset_ptr,
4814 max_chars);
4815 return;
4816 } else {
4817 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4818 offset_ptr,
4819 max_chars);
4820 return;
4821 }
4822 case kConsStringTag:
4823 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4824 offset_ptr,
4825 max_chars);
4826 return;
Steve Blocka7e24c12009-10-30 11:49:00 +00004827 case kExternalStringTag:
4828 if (input->IsAsciiRepresentation()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004829 ExternalAsciiString::cast(input)->
4830 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4831 } else {
4832 ExternalTwoByteString::cast(input)->
4833 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4834 offset_ptr,
4835 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004836 }
4837 return;
4838 default:
4839 break;
4840 }
4841
4842 UNREACHABLE();
4843 return;
4844}
4845
4846
4847const unibrow::byte* String::ReadBlock(String* input,
4848 unibrow::byte* util_buffer,
4849 unsigned capacity,
4850 unsigned* remaining,
4851 unsigned* offset_ptr) {
4852 ASSERT(*offset_ptr <= (unsigned)input->length());
4853 unsigned chars = input->length() - *offset_ptr;
4854 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4855 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4856 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4857 *remaining = rbb.remaining;
4858 return answer;
4859}
4860
4861
4862const unibrow::byte* String::ReadBlock(String** raw_input,
4863 unibrow::byte* util_buffer,
4864 unsigned capacity,
4865 unsigned* remaining,
4866 unsigned* offset_ptr) {
4867 Handle<String> input(raw_input);
4868 ASSERT(*offset_ptr <= (unsigned)input->length());
4869 unsigned chars = input->length() - *offset_ptr;
4870 if (chars > capacity) chars = capacity;
4871 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4872 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4873 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4874 *remaining = rbb.remaining;
4875 return rbb.util_buffer;
4876}
4877
4878
4879// This will iterate unless the block of string data spans two 'halves' of
4880// a ConsString, in which case it will recurse. Since the block of string
4881// data to be read has a maximum size this limits the maximum recursion
4882// depth to something sane. Since C++ does not have tail call recursion
4883// elimination, the iteration must be explicit.
4884void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4885 unsigned* offset_ptr,
4886 unsigned max_chars) {
4887 ConsString* current = this;
4888 unsigned offset = *offset_ptr;
4889 int offset_correction = 0;
4890
4891 while (true) {
4892 String* left = current->first();
4893 unsigned left_length = (unsigned)left->length();
4894 if (left_length > offset &&
4895 max_chars <= left_length - offset) {
4896 // Left hand side only - iterate unless we have reached the bottom of
4897 // the cons tree.
4898 if (StringShape(left).IsCons()) {
4899 current = ConsString::cast(left);
4900 continue;
4901 } else {
4902 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4903 *offset_ptr = offset + offset_correction;
4904 return;
4905 }
4906 } else if (left_length <= offset) {
4907 // Right hand side only - iterate unless we have reached the bottom of
4908 // the cons tree.
4909 offset -= left_length;
4910 offset_correction += left_length;
4911 String* right = current->second();
4912 if (StringShape(right).IsCons()) {
4913 current = ConsString::cast(right);
4914 continue;
4915 } else {
4916 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4917 *offset_ptr = offset + offset_correction;
4918 return;
4919 }
4920 } else {
4921 // The block to be read spans two sides of the ConsString, so we recurse.
4922 // First recurse on the left.
4923 max_chars -= left_length - offset;
4924 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4925 // We may have reached the max or there may not have been enough space
4926 // in the buffer for the characters in the left hand side.
4927 if (offset == left_length) {
4928 // Recurse on the right.
4929 String* right = String::cast(current->second());
4930 offset -= left_length;
4931 offset_correction += left_length;
4932 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4933 }
4934 *offset_ptr = offset + offset_correction;
4935 return;
4936 }
4937 }
4938}
4939
4940
Steve Blocka7e24c12009-10-30 11:49:00 +00004941uint16_t ConsString::ConsStringGet(int index) {
4942 ASSERT(index >= 0 && index < this->length());
4943
4944 // Check for a flattened cons string
4945 if (second()->length() == 0) {
4946 String* left = first();
4947 return left->Get(index);
4948 }
4949
4950 String* string = String::cast(this);
4951
4952 while (true) {
4953 if (StringShape(string).IsCons()) {
4954 ConsString* cons_string = ConsString::cast(string);
4955 String* left = cons_string->first();
4956 if (left->length() > index) {
4957 string = left;
4958 } else {
4959 index -= left->length();
4960 string = cons_string->second();
4961 }
4962 } else {
4963 return string->Get(index);
4964 }
4965 }
4966
4967 UNREACHABLE();
4968 return 0;
4969}
4970
4971
4972template <typename sinkchar>
4973void String::WriteToFlat(String* src,
4974 sinkchar* sink,
4975 int f,
4976 int t) {
4977 String* source = src;
4978 int from = f;
4979 int to = t;
4980 while (true) {
4981 ASSERT(0 <= from && from <= to && to <= source->length());
4982 switch (StringShape(source).full_representation_tag()) {
4983 case kAsciiStringTag | kExternalStringTag: {
4984 CopyChars(sink,
4985 ExternalAsciiString::cast(source)->resource()->data() + from,
4986 to - from);
4987 return;
4988 }
4989 case kTwoByteStringTag | kExternalStringTag: {
4990 const uc16* data =
4991 ExternalTwoByteString::cast(source)->resource()->data();
4992 CopyChars(sink,
4993 data + from,
4994 to - from);
4995 return;
4996 }
4997 case kAsciiStringTag | kSeqStringTag: {
4998 CopyChars(sink,
4999 SeqAsciiString::cast(source)->GetChars() + from,
5000 to - from);
5001 return;
5002 }
5003 case kTwoByteStringTag | kSeqStringTag: {
5004 CopyChars(sink,
5005 SeqTwoByteString::cast(source)->GetChars() + from,
5006 to - from);
5007 return;
5008 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005009 case kAsciiStringTag | kConsStringTag:
5010 case kTwoByteStringTag | kConsStringTag: {
5011 ConsString* cons_string = ConsString::cast(source);
5012 String* first = cons_string->first();
5013 int boundary = first->length();
5014 if (to - boundary >= boundary - from) {
5015 // Right hand side is longer. Recurse over left.
5016 if (from < boundary) {
5017 WriteToFlat(first, sink, from, boundary);
5018 sink += boundary - from;
5019 from = 0;
5020 } else {
5021 from -= boundary;
5022 }
5023 to -= boundary;
5024 source = cons_string->second();
5025 } else {
5026 // Left hand side is longer. Recurse over right.
5027 if (to > boundary) {
5028 String* second = cons_string->second();
5029 WriteToFlat(second,
5030 sink + boundary - from,
5031 0,
5032 to - boundary);
5033 to = boundary;
5034 }
5035 source = first;
5036 }
5037 break;
5038 }
5039 }
5040 }
5041}
5042
5043
Steve Blocka7e24c12009-10-30 11:49:00 +00005044template <typename IteratorA, typename IteratorB>
5045static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
5046 // General slow case check. We know that the ia and ib iterators
5047 // have the same length.
5048 while (ia->has_more()) {
5049 uc32 ca = ia->GetNext();
5050 uc32 cb = ib->GetNext();
5051 if (ca != cb)
5052 return false;
5053 }
5054 return true;
5055}
5056
5057
5058// Compares the contents of two strings by reading and comparing
5059// int-sized blocks of characters.
5060template <typename Char>
5061static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
5062 int length = a.length();
5063 ASSERT_EQ(length, b.length());
5064 const Char* pa = a.start();
5065 const Char* pb = b.start();
5066 int i = 0;
5067#ifndef V8_HOST_CAN_READ_UNALIGNED
5068 // If this architecture isn't comfortable reading unaligned ints
5069 // then we have to check that the strings are aligned before
5070 // comparing them blockwise.
5071 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
5072 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
5073 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
5074 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
5075#endif
5076 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
5077 int endpoint = length - kStepSize;
5078 // Compare blocks until we reach near the end of the string.
5079 for (; i <= endpoint; i += kStepSize) {
5080 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
5081 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
5082 if (wa != wb) {
5083 return false;
5084 }
5085 }
5086#ifndef V8_HOST_CAN_READ_UNALIGNED
5087 }
5088#endif
5089 // Compare the remaining characters that didn't fit into a block.
5090 for (; i < length; i++) {
5091 if (a[i] != b[i]) {
5092 return false;
5093 }
5094 }
5095 return true;
5096}
5097
5098
5099static StringInputBuffer string_compare_buffer_b;
5100
5101
5102template <typename IteratorA>
5103static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
5104 if (b->IsFlat()) {
5105 if (b->IsAsciiRepresentation()) {
5106 VectorIterator<char> ib(b->ToAsciiVector());
5107 return CompareStringContents(ia, &ib);
5108 } else {
5109 VectorIterator<uc16> ib(b->ToUC16Vector());
5110 return CompareStringContents(ia, &ib);
5111 }
5112 } else {
5113 string_compare_buffer_b.Reset(0, b);
5114 return CompareStringContents(ia, &string_compare_buffer_b);
5115 }
5116}
5117
5118
5119static StringInputBuffer string_compare_buffer_a;
5120
5121
5122bool String::SlowEquals(String* other) {
5123 // Fast check: negative check with lengths.
5124 int len = length();
5125 if (len != other->length()) return false;
5126 if (len == 0) return true;
5127
5128 // Fast check: if hash code is computed for both strings
5129 // a fast negative check can be performed.
5130 if (HasHashCode() && other->HasHashCode()) {
5131 if (Hash() != other->Hash()) return false;
5132 }
5133
Leon Clarkef7060e22010-06-03 12:02:55 +01005134 // We know the strings are both non-empty. Compare the first chars
5135 // before we try to flatten the strings.
5136 if (this->Get(0) != other->Get(0)) return false;
5137
5138 String* lhs = this->TryFlattenGetString();
5139 String* rhs = other->TryFlattenGetString();
5140
5141 if (StringShape(lhs).IsSequentialAscii() &&
5142 StringShape(rhs).IsSequentialAscii()) {
5143 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
5144 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
Steve Blocka7e24c12009-10-30 11:49:00 +00005145 return CompareRawStringContents(Vector<const char>(str1, len),
5146 Vector<const char>(str2, len));
5147 }
5148
Leon Clarkef7060e22010-06-03 12:02:55 +01005149 if (lhs->IsFlat()) {
Ben Murdochbb769b22010-08-11 14:56:33 +01005150 if (lhs->IsAsciiRepresentation()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01005151 Vector<const char> vec1 = lhs->ToAsciiVector();
5152 if (rhs->IsFlat()) {
5153 if (rhs->IsAsciiRepresentation()) {
5154 Vector<const char> vec2 = rhs->ToAsciiVector();
Steve Blocka7e24c12009-10-30 11:49:00 +00005155 return CompareRawStringContents(vec1, vec2);
5156 } else {
5157 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005158 VectorIterator<uc16> ib(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005159 return CompareStringContents(&buf1, &ib);
5160 }
5161 } else {
5162 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005163 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005164 return CompareStringContents(&buf1, &string_compare_buffer_b);
5165 }
5166 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005167 Vector<const uc16> vec1 = lhs->ToUC16Vector();
5168 if (rhs->IsFlat()) {
5169 if (rhs->IsAsciiRepresentation()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005170 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005171 VectorIterator<char> ib(rhs->ToAsciiVector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005172 return CompareStringContents(&buf1, &ib);
5173 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005174 Vector<const uc16> vec2(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005175 return CompareRawStringContents(vec1, vec2);
5176 }
5177 } else {
5178 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005179 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005180 return CompareStringContents(&buf1, &string_compare_buffer_b);
5181 }
5182 }
5183 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005184 string_compare_buffer_a.Reset(0, lhs);
5185 return CompareStringContentsPartial(&string_compare_buffer_a, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005186 }
5187}
5188
5189
5190bool String::MarkAsUndetectable() {
5191 if (StringShape(this).IsSymbol()) return false;
5192
5193 Map* map = this->map();
Steve Blockd0582a62009-12-15 09:54:21 +00005194 if (map == Heap::string_map()) {
5195 this->set_map(Heap::undetectable_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00005196 return true;
Steve Blockd0582a62009-12-15 09:54:21 +00005197 } else if (map == Heap::ascii_string_map()) {
5198 this->set_map(Heap::undetectable_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00005199 return true;
5200 }
5201 // Rest cannot be marked as undetectable
5202 return false;
5203}
5204
5205
5206bool String::IsEqualTo(Vector<const char> str) {
5207 int slen = length();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08005208 Access<ScannerConstants::Utf8Decoder>
5209 decoder(ScannerConstants::utf8_decoder());
Steve Blocka7e24c12009-10-30 11:49:00 +00005210 decoder->Reset(str.start(), str.length());
5211 int i;
5212 for (i = 0; i < slen && decoder->has_more(); i++) {
5213 uc32 r = decoder->GetNext();
5214 if (Get(i) != r) return false;
5215 }
5216 return i == slen && !decoder->has_more();
5217}
5218
5219
Steve Block9fac8402011-05-12 15:51:54 +01005220bool String::IsAsciiEqualTo(Vector<const char> str) {
5221 int slen = length();
5222 if (str.length() != slen) return false;
5223 for (int i = 0; i < slen; i++) {
5224 if (Get(i) != static_cast<uint16_t>(str[i])) return false;
5225 }
5226 return true;
5227}
5228
5229
5230bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
5231 int slen = length();
5232 if (str.length() != slen) return false;
5233 for (int i = 0; i < slen; i++) {
5234 if (Get(i) != str[i]) return false;
5235 }
5236 return true;
5237}
5238
5239
Steve Block6ded16b2010-05-10 14:33:55 +01005240template <typename schar>
5241static inline uint32_t HashSequentialString(const schar* chars, int length) {
5242 StringHasher hasher(length);
5243 if (!hasher.has_trivial_hash()) {
5244 int i;
5245 for (i = 0; hasher.is_array_index() && (i < length); i++) {
5246 hasher.AddCharacter(chars[i]);
5247 }
5248 for (; i < length; i++) {
5249 hasher.AddCharacterNoIndex(chars[i]);
5250 }
5251 }
5252 return hasher.GetHashField();
5253}
5254
5255
Steve Blocka7e24c12009-10-30 11:49:00 +00005256uint32_t String::ComputeAndSetHash() {
5257 // Should only be called if hash code has not yet been computed.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005258 ASSERT(!HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00005259
Steve Block6ded16b2010-05-10 14:33:55 +01005260 const int len = length();
5261
Steve Blocka7e24c12009-10-30 11:49:00 +00005262 // Compute the hash code.
Steve Block6ded16b2010-05-10 14:33:55 +01005263 uint32_t field = 0;
5264 if (StringShape(this).IsSequentialAscii()) {
5265 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
5266 } else if (StringShape(this).IsSequentialTwoByte()) {
5267 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
5268 } else {
5269 StringInputBuffer buffer(this);
5270 field = ComputeHashField(&buffer, len);
5271 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005272
5273 // Store the hash code in the object.
Steve Blockd0582a62009-12-15 09:54:21 +00005274 set_hash_field(field);
Steve Blocka7e24c12009-10-30 11:49:00 +00005275
5276 // Check the hash code is there.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005277 ASSERT(HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00005278 uint32_t result = field >> kHashShift;
5279 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
5280 return result;
5281}
5282
5283
5284bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
5285 uint32_t* index,
5286 int length) {
5287 if (length == 0 || length > kMaxArrayIndexSize) return false;
5288 uc32 ch = buffer->GetNext();
5289
5290 // If the string begins with a '0' character, it must only consist
5291 // of it to be a legal array index.
5292 if (ch == '0') {
5293 *index = 0;
5294 return length == 1;
5295 }
5296
5297 // Convert string to uint32 array index; character by character.
5298 int d = ch - '0';
5299 if (d < 0 || d > 9) return false;
5300 uint32_t result = d;
5301 while (buffer->has_more()) {
5302 d = buffer->GetNext() - '0';
5303 if (d < 0 || d > 9) return false;
5304 // Check that the new result is below the 32 bit limit.
5305 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
5306 result = (result * 10) + d;
5307 }
5308
5309 *index = result;
5310 return true;
5311}
5312
5313
5314bool String::SlowAsArrayIndex(uint32_t* index) {
5315 if (length() <= kMaxCachedArrayIndexLength) {
5316 Hash(); // force computation of hash code
Steve Blockd0582a62009-12-15 09:54:21 +00005317 uint32_t field = hash_field();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005318 if ((field & kIsNotArrayIndexMask) != 0) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00005319 // Isolate the array index form the full hash field.
5320 *index = (kArrayIndexHashMask & field) >> kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00005321 return true;
5322 } else {
5323 StringInputBuffer buffer(this);
5324 return ComputeArrayIndex(&buffer, index, length());
5325 }
5326}
5327
5328
Iain Merrick9ac36c92010-09-13 15:29:50 +01005329uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005330 // For array indexes mix the length into the hash as an array index could
5331 // be zero.
5332 ASSERT(length > 0);
5333 ASSERT(length <= String::kMaxArrayIndexSize);
5334 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
5335 (1 << String::kArrayIndexValueBits));
Iain Merrick9ac36c92010-09-13 15:29:50 +01005336
5337 value <<= String::kHashShift;
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005338 value |= length << String::kArrayIndexHashLengthShift;
Iain Merrick9ac36c92010-09-13 15:29:50 +01005339
5340 ASSERT((value & String::kIsNotArrayIndexMask) == 0);
5341 ASSERT((length > String::kMaxCachedArrayIndexLength) ||
5342 (value & String::kContainsCachedArrayIndexMask) == 0);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005343 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00005344}
5345
5346
5347uint32_t StringHasher::GetHashField() {
5348 ASSERT(is_valid());
Steve Blockd0582a62009-12-15 09:54:21 +00005349 if (length_ <= String::kMaxHashCalcLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005350 if (is_array_index()) {
Iain Merrick9ac36c92010-09-13 15:29:50 +01005351 return MakeArrayIndexHash(array_index(), length_);
Steve Blocka7e24c12009-10-30 11:49:00 +00005352 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005353 return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask;
Steve Blocka7e24c12009-10-30 11:49:00 +00005354 } else {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005355 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
Steve Blocka7e24c12009-10-30 11:49:00 +00005356 }
5357}
5358
5359
Steve Blockd0582a62009-12-15 09:54:21 +00005360uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
5361 int length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005362 StringHasher hasher(length);
5363
5364 // Very long strings have a trivial hash that doesn't inspect the
5365 // string contents.
5366 if (hasher.has_trivial_hash()) {
5367 return hasher.GetHashField();
5368 }
5369
5370 // Do the iterative array index computation as long as there is a
5371 // chance this is an array index.
5372 while (buffer->has_more() && hasher.is_array_index()) {
5373 hasher.AddCharacter(buffer->GetNext());
5374 }
5375
5376 // Process the remaining characters without updating the array
5377 // index.
5378 while (buffer->has_more()) {
5379 hasher.AddCharacterNoIndex(buffer->GetNext());
5380 }
5381
5382 return hasher.GetHashField();
5383}
5384
5385
John Reck59135872010-11-02 12:39:01 -07005386MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005387 if (start == 0 && end == length()) return this;
John Reck59135872010-11-02 12:39:01 -07005388 MaybeObject* result = Heap::AllocateSubString(this, start, end, pretenure);
Steve Blockd0582a62009-12-15 09:54:21 +00005389 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00005390}
5391
5392
5393void String::PrintOn(FILE* file) {
5394 int length = this->length();
5395 for (int i = 0; i < length; i++) {
5396 fprintf(file, "%c", Get(i));
5397 }
5398}
5399
5400
5401void Map::CreateBackPointers() {
5402 DescriptorArray* descriptors = instance_descriptors();
5403 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
Iain Merrick75681382010-08-19 15:07:18 +01005404 if (descriptors->GetType(i) == MAP_TRANSITION ||
5405 descriptors->GetType(i) == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005406 // Get target.
5407 Map* target = Map::cast(descriptors->GetValue(i));
5408#ifdef DEBUG
5409 // Verify target.
5410 Object* source_prototype = prototype();
5411 Object* target_prototype = target->prototype();
5412 ASSERT(source_prototype->IsJSObject() ||
5413 source_prototype->IsMap() ||
5414 source_prototype->IsNull());
5415 ASSERT(target_prototype->IsJSObject() ||
5416 target_prototype->IsNull());
5417 ASSERT(source_prototype->IsMap() ||
5418 source_prototype == target_prototype);
5419#endif
5420 // Point target back to source. set_prototype() will not let us set
5421 // the prototype to a map, as we do here.
5422 *RawField(target, kPrototypeOffset) = this;
5423 }
5424 }
5425}
5426
5427
5428void Map::ClearNonLiveTransitions(Object* real_prototype) {
5429 // Live DescriptorArray objects will be marked, so we must use
5430 // low-level accessors to get and modify their data.
5431 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
5432 *RawField(this, Map::kInstanceDescriptorsOffset));
5433 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
5434 Smi* NullDescriptorDetails =
5435 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
5436 FixedArray* contents = reinterpret_cast<FixedArray*>(
5437 d->get(DescriptorArray::kContentArrayIndex));
5438 ASSERT(contents->length() >= 2);
5439 for (int i = 0; i < contents->length(); i += 2) {
5440 // If the pair (value, details) is a map transition,
5441 // check if the target is live. If not, null the descriptor.
5442 // Also drop the back pointer for that map transition, so that this
5443 // map is not reached again by following a back pointer from a
5444 // non-live object.
5445 PropertyDetails details(Smi::cast(contents->get(i + 1)));
Iain Merrick75681382010-08-19 15:07:18 +01005446 if (details.type() == MAP_TRANSITION ||
5447 details.type() == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005448 Map* target = reinterpret_cast<Map*>(contents->get(i));
5449 ASSERT(target->IsHeapObject());
5450 if (!target->IsMarked()) {
5451 ASSERT(target->IsMap());
Iain Merrick75681382010-08-19 15:07:18 +01005452 contents->set_unchecked(i + 1, NullDescriptorDetails);
5453 contents->set_null_unchecked(i);
Steve Blocka7e24c12009-10-30 11:49:00 +00005454 ASSERT(target->prototype() == this ||
5455 target->prototype() == real_prototype);
5456 // Getter prototype() is read-only, set_prototype() has side effects.
5457 *RawField(target, Map::kPrototypeOffset) = real_prototype;
5458 }
5459 }
5460 }
5461}
5462
5463
Steve Block791712a2010-08-27 10:21:07 +01005464void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
5465 // Iterate over all fields in the body but take care in dealing with
5466 // the code entry.
5467 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
5468 v->VisitCodeEntry(this->address() + kCodeEntryOffset);
5469 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
5470}
5471
5472
Ben Murdochb0fe1622011-05-05 13:52:32 +01005473void JSFunction::MarkForLazyRecompilation() {
5474 ASSERT(is_compiled() && !IsOptimized());
Ben Murdochb8e0da22011-05-16 14:20:40 +01005475 ASSERT(shared()->allows_lazy_compilation() ||
5476 code()->optimizable());
Ben Murdochb0fe1622011-05-05 13:52:32 +01005477 ReplaceCode(Builtins::builtin(Builtins::LazyRecompile));
5478}
5479
5480
5481uint32_t JSFunction::SourceHash() {
5482 uint32_t hash = 0;
5483 Object* script = shared()->script();
5484 if (!script->IsUndefined()) {
5485 Object* source = Script::cast(script)->source();
5486 if (source->IsUndefined()) hash = String::cast(source)->Hash();
5487 }
5488 hash ^= ComputeIntegerHash(shared()->start_position_and_type());
5489 hash += ComputeIntegerHash(shared()->end_position());
5490 return hash;
5491}
5492
5493
5494bool JSFunction::IsInlineable() {
5495 if (IsBuiltin()) return false;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005496 SharedFunctionInfo* shared_info = shared();
Ben Murdochb0fe1622011-05-05 13:52:32 +01005497 // Check that the function has a script associated with it.
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005498 if (!shared_info->script()->IsScript()) return false;
5499 if (shared_info->optimization_disabled()) return false;
5500 Code* code = shared_info->code();
Ben Murdochb0fe1622011-05-05 13:52:32 +01005501 if (code->kind() == Code::OPTIMIZED_FUNCTION) return true;
5502 // If we never ran this (unlikely) then lets try to optimize it.
5503 if (code->kind() != Code::FUNCTION) return true;
5504 return code->optimizable();
5505}
5506
5507
Steve Blocka7e24c12009-10-30 11:49:00 +00005508Object* JSFunction::SetInstancePrototype(Object* value) {
5509 ASSERT(value->IsJSObject());
5510
5511 if (has_initial_map()) {
5512 initial_map()->set_prototype(value);
5513 } else {
5514 // Put the value in the initial map field until an initial map is
5515 // needed. At that point, a new initial map is created and the
5516 // prototype is put into the initial map where it belongs.
5517 set_prototype_or_initial_map(value);
5518 }
Kristian Monsen25f61362010-05-21 11:50:48 +01005519 Heap::ClearInstanceofCache();
Steve Blocka7e24c12009-10-30 11:49:00 +00005520 return value;
5521}
5522
5523
John Reck59135872010-11-02 12:39:01 -07005524MaybeObject* JSFunction::SetPrototype(Object* value) {
Steve Block6ded16b2010-05-10 14:33:55 +01005525 ASSERT(should_have_prototype());
Steve Blocka7e24c12009-10-30 11:49:00 +00005526 Object* construct_prototype = value;
5527
5528 // If the value is not a JSObject, store the value in the map's
5529 // constructor field so it can be accessed. Also, set the prototype
5530 // used for constructing objects to the original object prototype.
5531 // See ECMA-262 13.2.2.
5532 if (!value->IsJSObject()) {
5533 // Copy the map so this does not affect unrelated functions.
5534 // Remove map transitions because they point to maps with a
5535 // different prototype.
John Reck59135872010-11-02 12:39:01 -07005536 Object* new_map;
5537 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
5538 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
5539 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005540 set_map(Map::cast(new_map));
5541 map()->set_constructor(value);
5542 map()->set_non_instance_prototype(true);
5543 construct_prototype =
5544 Top::context()->global_context()->initial_object_prototype();
5545 } else {
5546 map()->set_non_instance_prototype(false);
5547 }
5548
5549 return SetInstancePrototype(construct_prototype);
5550}
5551
5552
Steve Block6ded16b2010-05-10 14:33:55 +01005553Object* JSFunction::RemovePrototype() {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005554 if (map() == context()->global_context()->function_without_prototype_map()) {
5555 // Be idempotent.
5556 return this;
5557 }
Steve Block6ded16b2010-05-10 14:33:55 +01005558 ASSERT(map() == context()->global_context()->function_map());
5559 set_map(context()->global_context()->function_without_prototype_map());
5560 set_prototype_or_initial_map(Heap::the_hole_value());
5561 return this;
5562}
5563
5564
Steve Blocka7e24c12009-10-30 11:49:00 +00005565Object* JSFunction::SetInstanceClassName(String* name) {
5566 shared()->set_instance_class_name(name);
5567 return this;
5568}
5569
5570
Ben Murdochb0fe1622011-05-05 13:52:32 +01005571void JSFunction::PrintName(FILE* out) {
5572 SmartPointer<char> name = shared()->DebugName()->ToCString();
5573 PrintF(out, "%s", *name);
5574}
5575
5576
Steve Blocka7e24c12009-10-30 11:49:00 +00005577Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
5578 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
5579}
5580
5581
John Reck59135872010-11-02 12:39:01 -07005582MaybeObject* Oddball::Initialize(const char* to_string, Object* to_number) {
5583 Object* symbol;
5584 { MaybeObject* maybe_symbol = Heap::LookupAsciiSymbol(to_string);
5585 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
5586 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005587 set_to_string(String::cast(symbol));
5588 set_to_number(to_number);
5589 return this;
5590}
5591
5592
Ben Murdochf87a2032010-10-22 12:50:53 +01005593String* SharedFunctionInfo::DebugName() {
5594 Object* n = name();
5595 if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
5596 return String::cast(n);
5597}
5598
5599
Steve Blocka7e24c12009-10-30 11:49:00 +00005600bool SharedFunctionInfo::HasSourceCode() {
5601 return !script()->IsUndefined() &&
Iain Merrick75681382010-08-19 15:07:18 +01005602 !reinterpret_cast<Script*>(script())->source()->IsUndefined();
Steve Blocka7e24c12009-10-30 11:49:00 +00005603}
5604
5605
5606Object* SharedFunctionInfo::GetSourceCode() {
Ben Murdochb0fe1622011-05-05 13:52:32 +01005607 if (!HasSourceCode()) return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00005608 HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +00005609 Object* source = Script::cast(script())->source();
Steve Blocka7e24c12009-10-30 11:49:00 +00005610 return *SubString(Handle<String>(String::cast(source)),
5611 start_position(), end_position());
5612}
5613
5614
Ben Murdochb0fe1622011-05-05 13:52:32 +01005615int SharedFunctionInfo::SourceSize() {
5616 return end_position() - start_position();
5617}
5618
5619
Steve Blocka7e24c12009-10-30 11:49:00 +00005620int SharedFunctionInfo::CalculateInstanceSize() {
5621 int instance_size =
5622 JSObject::kHeaderSize +
5623 expected_nof_properties() * kPointerSize;
5624 if (instance_size > JSObject::kMaxInstanceSize) {
5625 instance_size = JSObject::kMaxInstanceSize;
5626 }
5627 return instance_size;
5628}
5629
5630
5631int SharedFunctionInfo::CalculateInObjectProperties() {
5632 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
5633}
5634
5635
Andrei Popescu402d9372010-02-26 13:31:12 +00005636bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
5637 // Check the basic conditions for generating inline constructor code.
5638 if (!FLAG_inline_new
5639 || !has_only_simple_this_property_assignments()
5640 || this_property_assignments_count() == 0) {
5641 return false;
5642 }
5643
5644 // If the prototype is null inline constructors cause no problems.
5645 if (!prototype->IsJSObject()) {
5646 ASSERT(prototype->IsNull());
5647 return true;
5648 }
5649
5650 // Traverse the proposed prototype chain looking for setters for properties of
5651 // the same names as are set by the inline constructor.
5652 for (Object* obj = prototype;
5653 obj != Heap::null_value();
5654 obj = obj->GetPrototype()) {
5655 JSObject* js_object = JSObject::cast(obj);
5656 for (int i = 0; i < this_property_assignments_count(); i++) {
5657 LookupResult result;
5658 String* name = GetThisPropertyAssignmentName(i);
5659 js_object->LocalLookupRealNamedProperty(name, &result);
5660 if (result.IsProperty() && result.type() == CALLBACKS) {
5661 return false;
5662 }
5663 }
5664 }
5665
5666 return true;
5667}
5668
5669
Kristian Monsen0d5e1162010-09-30 15:31:59 +01005670void SharedFunctionInfo::ForbidInlineConstructor() {
5671 set_compiler_hints(BooleanBit::set(compiler_hints(),
5672 kHasOnlySimpleThisPropertyAssignments,
5673 false));
5674}
5675
5676
Steve Blocka7e24c12009-10-30 11:49:00 +00005677void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +00005678 bool only_simple_this_property_assignments,
5679 FixedArray* assignments) {
5680 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005681 kHasOnlySimpleThisPropertyAssignments,
5682 only_simple_this_property_assignments));
5683 set_this_property_assignments(assignments);
5684 set_this_property_assignments_count(assignments->length() / 3);
5685}
5686
5687
5688void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
5689 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005690 kHasOnlySimpleThisPropertyAssignments,
5691 false));
5692 set_this_property_assignments(Heap::undefined_value());
5693 set_this_property_assignments_count(0);
5694}
5695
5696
5697String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
5698 Object* obj = this_property_assignments();
5699 ASSERT(obj->IsFixedArray());
5700 ASSERT(index < this_property_assignments_count());
5701 obj = FixedArray::cast(obj)->get(index * 3);
5702 ASSERT(obj->IsString());
5703 return String::cast(obj);
5704}
5705
5706
5707bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
5708 Object* obj = this_property_assignments();
5709 ASSERT(obj->IsFixedArray());
5710 ASSERT(index < this_property_assignments_count());
5711 obj = FixedArray::cast(obj)->get(index * 3 + 1);
5712 return Smi::cast(obj)->value() != -1;
5713}
5714
5715
5716int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
5717 ASSERT(IsThisPropertyAssignmentArgument(index));
5718 Object* obj =
5719 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
5720 return Smi::cast(obj)->value();
5721}
5722
5723
5724Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
5725 ASSERT(!IsThisPropertyAssignmentArgument(index));
5726 Object* obj =
5727 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
5728 return obj;
5729}
5730
5731
Steve Blocka7e24c12009-10-30 11:49:00 +00005732// Support function for printing the source code to a StringStream
5733// without any allocation in the heap.
5734void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
5735 int max_length) {
5736 // For some native functions there is no source.
Ben Murdochb0fe1622011-05-05 13:52:32 +01005737 if (!HasSourceCode()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005738 accumulator->Add("<No Source>");
5739 return;
5740 }
5741
Steve Blockd0582a62009-12-15 09:54:21 +00005742 // Get the source for the script which this function came from.
Steve Blocka7e24c12009-10-30 11:49:00 +00005743 // Don't use String::cast because we don't want more assertion errors while
5744 // we are already creating a stack dump.
5745 String* script_source =
5746 reinterpret_cast<String*>(Script::cast(script())->source());
5747
5748 if (!script_source->LooksValid()) {
5749 accumulator->Add("<Invalid Source>");
5750 return;
5751 }
5752
5753 if (!is_toplevel()) {
5754 accumulator->Add("function ");
5755 Object* name = this->name();
5756 if (name->IsString() && String::cast(name)->length() > 0) {
5757 accumulator->PrintName(name);
5758 }
5759 }
5760
5761 int len = end_position() - start_position();
Ben Murdochb0fe1622011-05-05 13:52:32 +01005762 if (len <= max_length || max_length < 0) {
5763 accumulator->Put(script_source, start_position(), end_position());
5764 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +00005765 accumulator->Put(script_source,
5766 start_position(),
5767 start_position() + max_length);
5768 accumulator->Add("...\n");
Steve Blocka7e24c12009-10-30 11:49:00 +00005769 }
5770}
5771
5772
Ben Murdochb0fe1622011-05-05 13:52:32 +01005773static bool IsCodeEquivalent(Code* code, Code* recompiled) {
5774 if (code->instruction_size() != recompiled->instruction_size()) return false;
5775 ByteArray* code_relocation = code->relocation_info();
5776 ByteArray* recompiled_relocation = recompiled->relocation_info();
5777 int length = code_relocation->length();
5778 if (length != recompiled_relocation->length()) return false;
5779 int compare = memcmp(code_relocation->GetDataStartAddress(),
5780 recompiled_relocation->GetDataStartAddress(),
5781 length);
5782 return compare == 0;
5783}
5784
5785
5786void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
5787 ASSERT(!has_deoptimization_support());
5788 AssertNoAllocation no_allocation;
5789 Code* code = this->code();
5790 if (IsCodeEquivalent(code, recompiled)) {
5791 // Copy the deoptimization data from the recompiled code.
5792 code->set_deoptimization_data(recompiled->deoptimization_data());
5793 code->set_has_deoptimization_support(true);
5794 } else {
5795 // TODO(3025757): In case the recompiled isn't equivalent to the
5796 // old code, we have to replace it. We should try to avoid this
5797 // altogether because it flushes valuable type feedback by
5798 // effectively resetting all IC state.
5799 set_code(recompiled);
5800 }
5801 ASSERT(has_deoptimization_support());
5802}
5803
5804
5805bool SharedFunctionInfo::VerifyBailoutId(int id) {
5806 // TODO(srdjan): debugging ARM crashes in hydrogen. OK to disable while
5807 // we are always bailing out on ARM.
5808
5809 ASSERT(id != AstNode::kNoNumber);
5810 Code* unoptimized = code();
5811 DeoptimizationOutputData* data =
5812 DeoptimizationOutputData::cast(unoptimized->deoptimization_data());
5813 unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this);
5814 USE(ignore);
5815 return true; // Return true if there was no ASSERT.
5816}
5817
5818
Kristian Monsen0d5e1162010-09-30 15:31:59 +01005819void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) {
5820 ASSERT(!IsInobjectSlackTrackingInProgress());
5821
5822 // Only initiate the tracking the first time.
5823 if (live_objects_may_exist()) return;
5824 set_live_objects_may_exist(true);
5825
5826 // No tracking during the snapshot construction phase.
5827 if (Serializer::enabled()) return;
5828
5829 if (map->unused_property_fields() == 0) return;
5830
5831 // Nonzero counter is a leftover from the previous attempt interrupted
5832 // by GC, keep it.
5833 if (construction_count() == 0) {
5834 set_construction_count(kGenerousAllocationCount);
5835 }
5836 set_initial_map(map);
5837 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubGeneric),
5838 construct_stub());
5839 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubCountdown));
5840}
5841
5842
5843// Called from GC, hence reinterpret_cast and unchecked accessors.
5844void SharedFunctionInfo::DetachInitialMap() {
5845 Map* map = reinterpret_cast<Map*>(initial_map());
5846
5847 // Make the map remember to restore the link if it survives the GC.
5848 map->set_bit_field2(
5849 map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo));
5850
5851 // Undo state changes made by StartInobjectTracking (except the
5852 // construction_count). This way if the initial map does not survive the GC
5853 // then StartInobjectTracking will be called again the next time the
5854 // constructor is called. The countdown will continue and (possibly after
5855 // several more GCs) CompleteInobjectSlackTracking will eventually be called.
5856 set_initial_map(Heap::raw_unchecked_undefined_value());
5857 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubCountdown),
5858 *RawField(this, kConstructStubOffset));
5859 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubGeneric));
5860 // It is safe to clear the flag: it will be set again if the map is live.
5861 set_live_objects_may_exist(false);
5862}
5863
5864
5865// Called from GC, hence reinterpret_cast and unchecked accessors.
5866void SharedFunctionInfo::AttachInitialMap(Map* map) {
5867 map->set_bit_field2(
5868 map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo));
5869
5870 // Resume inobject slack tracking.
5871 set_initial_map(map);
5872 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubGeneric),
5873 *RawField(this, kConstructStubOffset));
5874 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubCountdown));
5875 // The map survived the gc, so there may be objects referencing it.
5876 set_live_objects_may_exist(true);
5877}
5878
5879
5880static void GetMinInobjectSlack(Map* map, void* data) {
5881 int slack = map->unused_property_fields();
5882 if (*reinterpret_cast<int*>(data) > slack) {
5883 *reinterpret_cast<int*>(data) = slack;
5884 }
5885}
5886
5887
5888static void ShrinkInstanceSize(Map* map, void* data) {
5889 int slack = *reinterpret_cast<int*>(data);
5890 map->set_inobject_properties(map->inobject_properties() - slack);
5891 map->set_unused_property_fields(map->unused_property_fields() - slack);
5892 map->set_instance_size(map->instance_size() - slack * kPointerSize);
5893
5894 // Visitor id might depend on the instance size, recalculate it.
5895 map->set_visitor_id(StaticVisitorBase::GetVisitorId(map));
5896}
5897
5898
5899void SharedFunctionInfo::CompleteInobjectSlackTracking() {
5900 ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress());
5901 Map* map = Map::cast(initial_map());
5902
5903 set_initial_map(Heap::undefined_value());
5904 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubCountdown),
5905 construct_stub());
5906 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubGeneric));
5907
5908 int slack = map->unused_property_fields();
5909 map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
5910 if (slack != 0) {
5911 // Resize the initial map and all maps in its transition tree.
5912 map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
5913 // Give the correct expected_nof_properties to initial maps created later.
5914 ASSERT(expected_nof_properties() >= slack);
5915 set_expected_nof_properties(expected_nof_properties() - slack);
5916 }
5917}
5918
5919
Steve Blocka7e24c12009-10-30 11:49:00 +00005920void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
5921 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
5922 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
5923 Object* old_target = target;
5924 VisitPointer(&target);
5925 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5926}
5927
5928
Steve Block791712a2010-08-27 10:21:07 +01005929void ObjectVisitor::VisitCodeEntry(Address entry_address) {
5930 Object* code = Code::GetObjectFromEntryAddress(entry_address);
5931 Object* old_code = code;
5932 VisitPointer(&code);
5933 if (code != old_code) {
5934 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry();
5935 }
5936}
5937
5938
Ben Murdochb0fe1622011-05-05 13:52:32 +01005939void ObjectVisitor::VisitGlobalPropertyCell(RelocInfo* rinfo) {
5940 ASSERT(rinfo->rmode() == RelocInfo::GLOBAL_PROPERTY_CELL);
5941 Object* cell = rinfo->target_cell();
5942 Object* old_cell = cell;
5943 VisitPointer(&cell);
5944 if (cell != old_cell) {
5945 rinfo->set_target_cell(reinterpret_cast<JSGlobalPropertyCell*>(cell));
5946 }
5947}
5948
5949
Steve Blocka7e24c12009-10-30 11:49:00 +00005950void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005951 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
5952 rinfo->IsPatchedReturnSequence()) ||
5953 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
5954 rinfo->IsPatchedDebugBreakSlotSequence()));
Steve Blocka7e24c12009-10-30 11:49:00 +00005955 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
5956 Object* old_target = target;
5957 VisitPointer(&target);
5958 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5959}
5960
5961
Ben Murdochb0fe1622011-05-05 13:52:32 +01005962void Code::InvalidateRelocation() {
5963 HandleScope scope;
5964 set_relocation_info(Heap::empty_byte_array());
5965}
5966
5967
Steve Blockd0582a62009-12-15 09:54:21 +00005968void Code::Relocate(intptr_t delta) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005969 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
5970 it.rinfo()->apply(delta);
5971 }
5972 CPU::FlushICache(instruction_start(), instruction_size());
5973}
5974
5975
5976void Code::CopyFrom(const CodeDesc& desc) {
5977 // copy code
5978 memmove(instruction_start(), desc.buffer, desc.instr_size);
5979
Steve Blocka7e24c12009-10-30 11:49:00 +00005980 // copy reloc info
5981 memmove(relocation_start(),
5982 desc.buffer + desc.buffer_size - desc.reloc_size,
5983 desc.reloc_size);
5984
5985 // unbox handles and relocate
Steve Block3ce2e202009-11-05 08:53:23 +00005986 intptr_t delta = instruction_start() - desc.buffer;
Steve Blocka7e24c12009-10-30 11:49:00 +00005987 int mode_mask = RelocInfo::kCodeTargetMask |
5988 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
Ben Murdochb0fe1622011-05-05 13:52:32 +01005989 RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) |
Steve Blocka7e24c12009-10-30 11:49:00 +00005990 RelocInfo::kApplyMask;
Steve Block3ce2e202009-11-05 08:53:23 +00005991 Assembler* origin = desc.origin; // Needed to find target_object on X64.
Steve Blocka7e24c12009-10-30 11:49:00 +00005992 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
5993 RelocInfo::Mode mode = it.rinfo()->rmode();
5994 if (mode == RelocInfo::EMBEDDED_OBJECT) {
Steve Block3ce2e202009-11-05 08:53:23 +00005995 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005996 it.rinfo()->set_target_object(*p);
Ben Murdochb0fe1622011-05-05 13:52:32 +01005997 } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
Steve Block1e0659c2011-05-24 12:43:12 +01005998 Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle();
Ben Murdochb0fe1622011-05-05 13:52:32 +01005999 it.rinfo()->set_target_cell(*cell);
Steve Blocka7e24c12009-10-30 11:49:00 +00006000 } else if (RelocInfo::IsCodeTarget(mode)) {
6001 // rewrite code handles in inline cache targets to direct
6002 // pointers to the first instruction in the code object
Steve Block3ce2e202009-11-05 08:53:23 +00006003 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00006004 Code* code = Code::cast(*p);
6005 it.rinfo()->set_target_address(code->instruction_start());
6006 } else {
6007 it.rinfo()->apply(delta);
6008 }
6009 }
6010 CPU::FlushICache(instruction_start(), instruction_size());
6011}
6012
6013
6014// Locate the source position which is closest to the address in the code. This
6015// is using the source position information embedded in the relocation info.
6016// The position returned is relative to the beginning of the script where the
6017// source for this function is found.
6018int Code::SourcePosition(Address pc) {
6019 int distance = kMaxInt;
6020 int position = RelocInfo::kNoPosition; // Initially no position found.
6021 // Run through all the relocation info to find the best matching source
6022 // position. All the code needs to be considered as the sequence of the
6023 // instructions in the code does not necessarily follow the same order as the
6024 // source.
6025 RelocIterator it(this, RelocInfo::kPositionMask);
6026 while (!it.done()) {
6027 // Only look at positions after the current pc.
6028 if (it.rinfo()->pc() < pc) {
6029 // Get position and distance.
Steve Blockd0582a62009-12-15 09:54:21 +00006030
6031 int dist = static_cast<int>(pc - it.rinfo()->pc());
6032 int pos = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00006033 // If this position is closer than the current candidate or if it has the
6034 // same distance as the current candidate and the position is higher then
6035 // this position is the new candidate.
6036 if ((dist < distance) ||
6037 (dist == distance && pos > position)) {
6038 position = pos;
6039 distance = dist;
6040 }
6041 }
6042 it.next();
6043 }
6044 return position;
6045}
6046
6047
6048// Same as Code::SourcePosition above except it only looks for statement
6049// positions.
6050int Code::SourceStatementPosition(Address pc) {
6051 // First find the position as close as possible using all position
6052 // information.
6053 int position = SourcePosition(pc);
6054 // Now find the closest statement position before the position.
6055 int statement_position = 0;
6056 RelocIterator it(this, RelocInfo::kPositionMask);
6057 while (!it.done()) {
6058 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
Steve Blockd0582a62009-12-15 09:54:21 +00006059 int p = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00006060 if (statement_position < p && p <= position) {
6061 statement_position = p;
6062 }
6063 }
6064 it.next();
6065 }
6066 return statement_position;
6067}
6068
6069
Ben Murdochb8e0da22011-05-16 14:20:40 +01006070SafepointEntry Code::GetSafepointEntry(Address pc) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01006071 SafepointTable table(this);
Ben Murdochb8e0da22011-05-16 14:20:40 +01006072 return table.FindEntry(pc);
Ben Murdochb0fe1622011-05-05 13:52:32 +01006073}
6074
6075
6076void Code::SetNoStackCheckTable() {
6077 // Indicate the absence of a stack-check table by a table start after the
6078 // end of the instructions. Table start must be aligned, so round up.
Steve Block1e0659c2011-05-24 12:43:12 +01006079 set_stack_check_table_offset(RoundUp(instruction_size(), kIntSize));
Ben Murdochb0fe1622011-05-05 13:52:32 +01006080}
6081
6082
6083Map* Code::FindFirstMap() {
6084 ASSERT(is_inline_cache_stub());
6085 AssertNoAllocation no_allocation;
6086 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
6087 for (RelocIterator it(this, mask); !it.done(); it.next()) {
6088 RelocInfo* info = it.rinfo();
6089 Object* object = info->target_object();
6090 if (object->IsMap()) return Map::cast(object);
6091 }
6092 return NULL;
6093}
6094
6095
Steve Blocka7e24c12009-10-30 11:49:00 +00006096#ifdef ENABLE_DISASSEMBLER
Ben Murdochb0fe1622011-05-05 13:52:32 +01006097
6098#ifdef OBJECT_PRINT
6099
6100void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
6101 disasm::NameConverter converter;
6102 int deopt_count = DeoptCount();
6103 PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count);
6104 if (0 == deopt_count) return;
6105
6106 PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc", "commands");
6107 for (int i = 0; i < deopt_count; i++) {
6108 int command_count = 0;
6109 PrintF(out, "%6d %6d %6d",
6110 i, AstId(i)->value(), ArgumentsStackHeight(i)->value());
6111 int translation_index = TranslationIndex(i)->value();
6112 TranslationIterator iterator(TranslationByteArray(), translation_index);
6113 Translation::Opcode opcode =
6114 static_cast<Translation::Opcode>(iterator.Next());
6115 ASSERT(Translation::BEGIN == opcode);
6116 int frame_count = iterator.Next();
6117 if (FLAG_print_code_verbose) {
6118 PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode),
6119 frame_count);
6120 }
6121
6122 for (int i = 0; i < frame_count; ++i) {
6123 opcode = static_cast<Translation::Opcode>(iterator.Next());
6124 ASSERT(Translation::FRAME == opcode);
6125 int ast_id = iterator.Next();
6126 int function_id = iterator.Next();
6127 JSFunction* function =
6128 JSFunction::cast(LiteralArray()->get(function_id));
6129 unsigned height = iterator.Next();
6130 if (FLAG_print_code_verbose) {
6131 PrintF(out, "%24s %s {ast_id=%d, function=",
6132 "", Translation::StringFor(opcode), ast_id);
6133 function->PrintName(out);
6134 PrintF(out, ", height=%u}\n", height);
6135 }
6136
6137 // Size of translation is height plus all incoming arguments including
6138 // receiver.
6139 int size = height + function->shared()->formal_parameter_count() + 1;
6140 command_count += size;
6141 for (int j = 0; j < size; ++j) {
6142 opcode = static_cast<Translation::Opcode>(iterator.Next());
6143 if (FLAG_print_code_verbose) {
6144 PrintF(out, "%24s %s ", "", Translation::StringFor(opcode));
6145 }
6146
6147 if (opcode == Translation::DUPLICATE) {
6148 opcode = static_cast<Translation::Opcode>(iterator.Next());
6149 if (FLAG_print_code_verbose) {
6150 PrintF(out, "%s ", Translation::StringFor(opcode));
6151 }
6152 --j; // Two commands share the same frame index.
6153 }
6154
6155 switch (opcode) {
6156 case Translation::BEGIN:
6157 case Translation::FRAME:
6158 case Translation::DUPLICATE:
6159 UNREACHABLE();
6160 break;
6161
6162 case Translation::REGISTER: {
6163 int reg_code = iterator.Next();
6164 if (FLAG_print_code_verbose) {
6165 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
6166 }
6167 break;
6168 }
6169
6170 case Translation::INT32_REGISTER: {
6171 int reg_code = iterator.Next();
6172 if (FLAG_print_code_verbose) {
6173 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
6174 }
6175 break;
6176 }
6177
6178 case Translation::DOUBLE_REGISTER: {
6179 int reg_code = iterator.Next();
6180 if (FLAG_print_code_verbose) {
6181 PrintF(out, "{input=%s}",
6182 DoubleRegister::AllocationIndexToString(reg_code));
6183 }
6184 break;
6185 }
6186
6187 case Translation::STACK_SLOT: {
6188 int input_slot_index = iterator.Next();
6189 if (FLAG_print_code_verbose) {
6190 PrintF(out, "{input=%d}", input_slot_index);
6191 }
6192 break;
6193 }
6194
6195 case Translation::INT32_STACK_SLOT: {
6196 int input_slot_index = iterator.Next();
6197 if (FLAG_print_code_verbose) {
6198 PrintF(out, "{input=%d}", input_slot_index);
6199 }
6200 break;
6201 }
6202
6203 case Translation::DOUBLE_STACK_SLOT: {
6204 int input_slot_index = iterator.Next();
6205 if (FLAG_print_code_verbose) {
6206 PrintF(out, "{input=%d}", input_slot_index);
6207 }
6208 break;
6209 }
6210
6211 case Translation::LITERAL: {
6212 unsigned literal_index = iterator.Next();
6213 if (FLAG_print_code_verbose) {
6214 PrintF(out, "{literal_id=%u}", literal_index);
6215 }
6216 break;
6217 }
6218
6219 case Translation::ARGUMENTS_OBJECT:
6220 break;
6221 }
6222 if (FLAG_print_code_verbose) PrintF(out, "\n");
6223 }
6224 }
6225 if (!FLAG_print_code_verbose) PrintF(out, " %12d\n", command_count);
6226 }
6227}
6228
6229
6230void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) {
6231 PrintF(out, "Deoptimization Output Data (deopt points = %d)\n",
6232 this->DeoptPoints());
6233 if (this->DeoptPoints() == 0) return;
6234
6235 PrintF("%6s %8s %s\n", "ast id", "pc", "state");
6236 for (int i = 0; i < this->DeoptPoints(); i++) {
6237 int pc_and_state = this->PcAndState(i)->value();
6238 PrintF("%6d %8d %s\n",
6239 this->AstId(i)->value(),
6240 FullCodeGenerator::PcField::decode(pc_and_state),
6241 FullCodeGenerator::State2String(
6242 FullCodeGenerator::StateField::decode(pc_and_state)));
6243 }
6244}
6245
6246#endif
6247
6248
Steve Blocka7e24c12009-10-30 11:49:00 +00006249// Identify kind of code.
6250const char* Code::Kind2String(Kind kind) {
6251 switch (kind) {
6252 case FUNCTION: return "FUNCTION";
Ben Murdochb0fe1622011-05-05 13:52:32 +01006253 case OPTIMIZED_FUNCTION: return "OPTIMIZED_FUNCTION";
Steve Blocka7e24c12009-10-30 11:49:00 +00006254 case STUB: return "STUB";
6255 case BUILTIN: return "BUILTIN";
6256 case LOAD_IC: return "LOAD_IC";
6257 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
6258 case STORE_IC: return "STORE_IC";
6259 case KEYED_STORE_IC: return "KEYED_STORE_IC";
6260 case CALL_IC: return "CALL_IC";
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006261 case KEYED_CALL_IC: return "KEYED_CALL_IC";
Steve Block6ded16b2010-05-10 14:33:55 +01006262 case BINARY_OP_IC: return "BINARY_OP_IC";
Ben Murdochb0fe1622011-05-05 13:52:32 +01006263 case TYPE_RECORDING_BINARY_OP_IC: return "TYPE_RECORDING_BINARY_OP_IC";
6264 case COMPARE_IC: return "COMPARE_IC";
Steve Blocka7e24c12009-10-30 11:49:00 +00006265 }
6266 UNREACHABLE();
6267 return NULL;
6268}
6269
6270
6271const char* Code::ICState2String(InlineCacheState state) {
6272 switch (state) {
6273 case UNINITIALIZED: return "UNINITIALIZED";
6274 case PREMONOMORPHIC: return "PREMONOMORPHIC";
6275 case MONOMORPHIC: return "MONOMORPHIC";
6276 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
6277 case MEGAMORPHIC: return "MEGAMORPHIC";
6278 case DEBUG_BREAK: return "DEBUG_BREAK";
6279 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
6280 }
6281 UNREACHABLE();
6282 return NULL;
6283}
6284
6285
6286const char* Code::PropertyType2String(PropertyType type) {
6287 switch (type) {
6288 case NORMAL: return "NORMAL";
6289 case FIELD: return "FIELD";
6290 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
6291 case CALLBACKS: return "CALLBACKS";
6292 case INTERCEPTOR: return "INTERCEPTOR";
6293 case MAP_TRANSITION: return "MAP_TRANSITION";
6294 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
6295 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
6296 }
6297 UNREACHABLE();
6298 return NULL;
6299}
6300
Ben Murdochb0fe1622011-05-05 13:52:32 +01006301
Steve Block1e0659c2011-05-24 12:43:12 +01006302void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) {
6303 const char* name = NULL;
6304 switch (kind) {
6305 case CALL_IC:
6306 if (extra == STRING_INDEX_OUT_OF_BOUNDS) {
6307 name = "STRING_INDEX_OUT_OF_BOUNDS";
6308 }
6309 break;
6310 case STORE_IC:
Ben Murdoche0cee9b2011-05-25 10:26:03 +01006311 case KEYED_STORE_IC:
6312 if (extra == kStrictMode) {
Steve Block1e0659c2011-05-24 12:43:12 +01006313 name = "STRICT";
6314 }
6315 break;
6316 default:
6317 break;
6318 }
6319 if (name != NULL) {
6320 PrintF(out, "extra_ic_state = %s\n", name);
6321 } else {
6322 PrintF(out, "etra_ic_state = %d\n", extra);
6323 }
6324}
6325
6326
Ben Murdochb0fe1622011-05-05 13:52:32 +01006327void Code::Disassemble(const char* name, FILE* out) {
6328 PrintF(out, "kind = %s\n", Kind2String(kind()));
Steve Blocka7e24c12009-10-30 11:49:00 +00006329 if (is_inline_cache_stub()) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01006330 PrintF(out, "ic_state = %s\n", ICState2String(ic_state()));
Steve Block1e0659c2011-05-24 12:43:12 +01006331 PrintExtraICState(out, kind(), extra_ic_state());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006332 PrintF(out, "ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
Steve Blocka7e24c12009-10-30 11:49:00 +00006333 if (ic_state() == MONOMORPHIC) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01006334 PrintF(out, "type = %s\n", PropertyType2String(type()));
Steve Blocka7e24c12009-10-30 11:49:00 +00006335 }
6336 }
6337 if ((name != NULL) && (name[0] != '\0')) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01006338 PrintF(out, "name = %s\n", name);
6339 }
6340 if (kind() == OPTIMIZED_FUNCTION) {
6341 PrintF(out, "stack_slots = %d\n", stack_slots());
Steve Blocka7e24c12009-10-30 11:49:00 +00006342 }
6343
Ben Murdochb0fe1622011-05-05 13:52:32 +01006344 PrintF(out, "Instructions (size = %d)\n", instruction_size());
6345 Disassembler::Decode(out, this);
6346 PrintF(out, "\n");
6347
6348#ifdef DEBUG
6349 if (kind() == FUNCTION) {
6350 DeoptimizationOutputData* data =
6351 DeoptimizationOutputData::cast(this->deoptimization_data());
6352 data->DeoptimizationOutputDataPrint(out);
6353 } else if (kind() == OPTIMIZED_FUNCTION) {
6354 DeoptimizationInputData* data =
6355 DeoptimizationInputData::cast(this->deoptimization_data());
6356 data->DeoptimizationInputDataPrint(out);
6357 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006358 PrintF("\n");
Ben Murdochb0fe1622011-05-05 13:52:32 +01006359#endif
6360
6361 if (kind() == OPTIMIZED_FUNCTION) {
6362 SafepointTable table(this);
6363 PrintF(out, "Safepoints (size = %u)\n", table.size());
6364 for (unsigned i = 0; i < table.length(); i++) {
6365 unsigned pc_offset = table.GetPcOffset(i);
6366 PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset);
6367 table.PrintEntry(i);
6368 PrintF(out, " (sp -> fp)");
Ben Murdochb8e0da22011-05-16 14:20:40 +01006369 SafepointEntry entry = table.GetEntry(i);
6370 if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
6371 PrintF(out, " %6d", entry.deoptimization_index());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006372 } else {
6373 PrintF(out, " <none>");
6374 }
Ben Murdochb8e0da22011-05-16 14:20:40 +01006375 if (entry.argument_count() > 0) {
6376 PrintF(out, " argc: %d", entry.argument_count());
6377 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01006378 PrintF(out, "\n");
6379 }
6380 PrintF(out, "\n");
6381 } else if (kind() == FUNCTION) {
Steve Block1e0659c2011-05-24 12:43:12 +01006382 unsigned offset = stack_check_table_offset();
Ben Murdochb0fe1622011-05-05 13:52:32 +01006383 // If there is no stack check table, the "table start" will at or after
6384 // (due to alignment) the end of the instruction stream.
6385 if (static_cast<int>(offset) < instruction_size()) {
6386 unsigned* address =
6387 reinterpret_cast<unsigned*>(instruction_start() + offset);
6388 unsigned length = address[0];
6389 PrintF(out, "Stack checks (size = %u)\n", length);
6390 PrintF(out, "ast_id pc_offset\n");
6391 for (unsigned i = 0; i < length; ++i) {
6392 unsigned index = (2 * i) + 1;
6393 PrintF(out, "%6u %9u\n", address[index], address[index + 1]);
6394 }
6395 PrintF(out, "\n");
6396 }
6397 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006398
6399 PrintF("RelocInfo (size = %d)\n", relocation_size());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006400 for (RelocIterator it(this); !it.done(); it.next()) it.rinfo()->Print(out);
6401 PrintF(out, "\n");
Steve Blocka7e24c12009-10-30 11:49:00 +00006402}
6403#endif // ENABLE_DISASSEMBLER
6404
6405
John Reck59135872010-11-02 12:39:01 -07006406MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
6407 int length) {
Steve Block3ce2e202009-11-05 08:53:23 +00006408 // We should never end in here with a pixel or external array.
6409 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Block8defd9f2010-07-08 12:39:36 +01006410
John Reck59135872010-11-02 12:39:01 -07006411 Object* obj;
6412 { MaybeObject* maybe_obj = Heap::AllocateFixedArrayWithHoles(capacity);
6413 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6414 }
Steve Block8defd9f2010-07-08 12:39:36 +01006415 FixedArray* elems = FixedArray::cast(obj);
6416
John Reck59135872010-11-02 12:39:01 -07006417 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
6418 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6419 }
Steve Block8defd9f2010-07-08 12:39:36 +01006420 Map* new_map = Map::cast(obj);
6421
Leon Clarke4515c472010-02-03 11:58:03 +00006422 AssertNoAllocation no_gc;
6423 WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00006424 switch (GetElementsKind()) {
6425 case FAST_ELEMENTS: {
6426 FixedArray* old_elements = FixedArray::cast(elements());
6427 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
6428 // Fill out the new array with this content and array holes.
6429 for (uint32_t i = 0; i < old_length; i++) {
6430 elems->set(i, old_elements->get(i), mode);
6431 }
6432 break;
6433 }
6434 case DICTIONARY_ELEMENTS: {
6435 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6436 for (int i = 0; i < dictionary->Capacity(); i++) {
6437 Object* key = dictionary->KeyAt(i);
6438 if (key->IsNumber()) {
6439 uint32_t entry = static_cast<uint32_t>(key->Number());
6440 elems->set(entry, dictionary->ValueAt(i), mode);
6441 }
6442 }
6443 break;
6444 }
6445 default:
6446 UNREACHABLE();
6447 break;
6448 }
Steve Block8defd9f2010-07-08 12:39:36 +01006449
6450 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00006451 set_elements(elems);
Steve Block8defd9f2010-07-08 12:39:36 +01006452
6453 if (IsJSArray()) {
6454 JSArray::cast(this)->set_length(Smi::FromInt(length));
6455 }
6456
6457 return this;
Steve Blocka7e24c12009-10-30 11:49:00 +00006458}
6459
6460
John Reck59135872010-11-02 12:39:01 -07006461MaybeObject* JSObject::SetSlowElements(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00006462 // We should never end in here with a pixel or external array.
6463 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00006464
6465 uint32_t new_length = static_cast<uint32_t>(len->Number());
6466
6467 switch (GetElementsKind()) {
6468 case FAST_ELEMENTS: {
6469 // Make sure we never try to shrink dense arrays into sparse arrays.
6470 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
6471 new_length);
John Reck59135872010-11-02 12:39:01 -07006472 Object* obj;
6473 { MaybeObject* maybe_obj = NormalizeElements();
6474 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6475 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006476
6477 // Update length for JSArrays.
6478 if (IsJSArray()) JSArray::cast(this)->set_length(len);
6479 break;
6480 }
6481 case DICTIONARY_ELEMENTS: {
6482 if (IsJSArray()) {
6483 uint32_t old_length =
Steve Block6ded16b2010-05-10 14:33:55 +01006484 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
Steve Blocka7e24c12009-10-30 11:49:00 +00006485 element_dictionary()->RemoveNumberEntries(new_length, old_length),
6486 JSArray::cast(this)->set_length(len);
6487 }
6488 break;
6489 }
6490 default:
6491 UNREACHABLE();
6492 break;
6493 }
6494 return this;
6495}
6496
6497
John Reck59135872010-11-02 12:39:01 -07006498MaybeObject* JSArray::Initialize(int capacity) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006499 ASSERT(capacity >= 0);
Leon Clarke4515c472010-02-03 11:58:03 +00006500 set_length(Smi::FromInt(0));
Steve Blocka7e24c12009-10-30 11:49:00 +00006501 FixedArray* new_elements;
6502 if (capacity == 0) {
6503 new_elements = Heap::empty_fixed_array();
6504 } else {
John Reck59135872010-11-02 12:39:01 -07006505 Object* obj;
6506 { MaybeObject* maybe_obj = Heap::AllocateFixedArrayWithHoles(capacity);
6507 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6508 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006509 new_elements = FixedArray::cast(obj);
6510 }
6511 set_elements(new_elements);
6512 return this;
6513}
6514
6515
6516void JSArray::Expand(int required_size) {
6517 Handle<JSArray> self(this);
6518 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
6519 int old_size = old_backing->length();
Steve Blockd0582a62009-12-15 09:54:21 +00006520 int new_size = required_size > old_size ? required_size : old_size;
Steve Blocka7e24c12009-10-30 11:49:00 +00006521 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
6522 // Can't use this any more now because we may have had a GC!
6523 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
6524 self->SetContent(*new_backing);
6525}
6526
6527
John Reck59135872010-11-02 12:39:01 -07006528static Failure* ArrayLengthRangeError() {
Steve Blocka7e24c12009-10-30 11:49:00 +00006529 HandleScope scope;
6530 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
6531 HandleVector<Object>(NULL, 0)));
6532}
6533
6534
John Reck59135872010-11-02 12:39:01 -07006535MaybeObject* JSObject::SetElementsLength(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00006536 // We should never end in here with a pixel or external array.
Steve Block6ded16b2010-05-10 14:33:55 +01006537 ASSERT(AllowsSetElementsLength());
Steve Blocka7e24c12009-10-30 11:49:00 +00006538
John Reck59135872010-11-02 12:39:01 -07006539 MaybeObject* maybe_smi_length = len->ToSmi();
6540 Object* smi_length = Smi::FromInt(0);
6541 if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) {
Steve Block8defd9f2010-07-08 12:39:36 +01006542 const int value = Smi::cast(smi_length)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00006543 if (value < 0) return ArrayLengthRangeError();
6544 switch (GetElementsKind()) {
6545 case FAST_ELEMENTS: {
6546 int old_capacity = FixedArray::cast(elements())->length();
6547 if (value <= old_capacity) {
6548 if (IsJSArray()) {
John Reck59135872010-11-02 12:39:01 -07006549 Object* obj;
6550 { MaybeObject* maybe_obj = EnsureWritableFastElements();
6551 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6552 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006553 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
6554 // NOTE: We may be able to optimize this by removing the
6555 // last part of the elements backing storage array and
6556 // setting the capacity to the new size.
6557 for (int i = value; i < old_length; i++) {
6558 FixedArray::cast(elements())->set_the_hole(i);
6559 }
Leon Clarke4515c472010-02-03 11:58:03 +00006560 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006561 }
6562 return this;
6563 }
6564 int min = NewElementsCapacity(old_capacity);
6565 int new_capacity = value > min ? value : min;
6566 if (new_capacity <= kMaxFastElementsLength ||
6567 !ShouldConvertToSlowElements(new_capacity)) {
John Reck59135872010-11-02 12:39:01 -07006568 Object* obj;
6569 { MaybeObject* maybe_obj =
6570 SetFastElementsCapacityAndLength(new_capacity, value);
6571 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6572 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006573 return this;
6574 }
6575 break;
6576 }
6577 case DICTIONARY_ELEMENTS: {
6578 if (IsJSArray()) {
6579 if (value == 0) {
6580 // If the length of a slow array is reset to zero, we clear
6581 // the array and flush backing storage. This has the added
6582 // benefit that the array returns to fast mode.
John Reck59135872010-11-02 12:39:01 -07006583 Object* obj;
6584 { MaybeObject* maybe_obj = ResetElements();
6585 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6586 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006587 } else {
6588 // Remove deleted elements.
6589 uint32_t old_length =
6590 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
6591 element_dictionary()->RemoveNumberEntries(value, old_length);
6592 }
Leon Clarke4515c472010-02-03 11:58:03 +00006593 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006594 }
6595 return this;
6596 }
6597 default:
6598 UNREACHABLE();
6599 break;
6600 }
6601 }
6602
6603 // General slow case.
6604 if (len->IsNumber()) {
6605 uint32_t length;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006606 if (len->ToArrayIndex(&length)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006607 return SetSlowElements(len);
6608 } else {
6609 return ArrayLengthRangeError();
6610 }
6611 }
6612
6613 // len is not a number so make the array size one and
6614 // set only element to len.
John Reck59135872010-11-02 12:39:01 -07006615 Object* obj;
6616 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(1);
6617 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6618 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006619 FixedArray::cast(obj)->set(0, len);
Leon Clarke4515c472010-02-03 11:58:03 +00006620 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006621 set_elements(FixedArray::cast(obj));
6622 return this;
6623}
6624
6625
John Reck59135872010-11-02 12:39:01 -07006626MaybeObject* JSObject::SetPrototype(Object* value,
6627 bool skip_hidden_prototypes) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006628 // Silently ignore the change if value is not a JSObject or null.
6629 // SpiderMonkey behaves this way.
6630 if (!value->IsJSObject() && !value->IsNull()) return value;
6631
6632 // Before we can set the prototype we need to be sure
6633 // prototype cycles are prevented.
6634 // It is sufficient to validate that the receiver is not in the new prototype
6635 // chain.
6636 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
6637 if (JSObject::cast(pt) == this) {
6638 // Cycle detected.
6639 HandleScope scope;
6640 return Top::Throw(*Factory::NewError("cyclic_proto",
6641 HandleVector<Object>(NULL, 0)));
6642 }
6643 }
6644
6645 JSObject* real_receiver = this;
6646
6647 if (skip_hidden_prototypes) {
6648 // Find the first object in the chain whose prototype object is not
6649 // hidden and set the new prototype on that object.
6650 Object* current_proto = real_receiver->GetPrototype();
6651 while (current_proto->IsJSObject() &&
6652 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
6653 real_receiver = JSObject::cast(current_proto);
6654 current_proto = current_proto->GetPrototype();
6655 }
6656 }
6657
6658 // Set the new prototype of the object.
John Reck59135872010-11-02 12:39:01 -07006659 Object* new_map;
6660 { MaybeObject* maybe_new_map = real_receiver->map()->CopyDropTransitions();
6661 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
6662 }
Andrei Popescu402d9372010-02-26 13:31:12 +00006663 Map::cast(new_map)->set_prototype(value);
6664 real_receiver->set_map(Map::cast(new_map));
6665
Kristian Monsen25f61362010-05-21 11:50:48 +01006666 Heap::ClearInstanceofCache();
6667
Andrei Popescu402d9372010-02-26 13:31:12 +00006668 return value;
6669}
6670
6671
Steve Blocka7e24c12009-10-30 11:49:00 +00006672bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
6673 switch (GetElementsKind()) {
6674 case FAST_ELEMENTS: {
6675 uint32_t length = IsJSArray() ?
6676 static_cast<uint32_t>
6677 (Smi::cast(JSArray::cast(this)->length())->value()) :
6678 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6679 if ((index < length) &&
6680 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
6681 return true;
6682 }
6683 break;
6684 }
6685 case PIXEL_ELEMENTS: {
Steve Blocka7e24c12009-10-30 11:49:00 +00006686 PixelArray* pixels = PixelArray::cast(elements());
6687 if (index < static_cast<uint32_t>(pixels->length())) {
6688 return true;
6689 }
6690 break;
6691 }
Steve Block3ce2e202009-11-05 08:53:23 +00006692 case EXTERNAL_BYTE_ELEMENTS:
6693 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6694 case EXTERNAL_SHORT_ELEMENTS:
6695 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6696 case EXTERNAL_INT_ELEMENTS:
6697 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6698 case EXTERNAL_FLOAT_ELEMENTS: {
Steve Block3ce2e202009-11-05 08:53:23 +00006699 ExternalArray* array = ExternalArray::cast(elements());
6700 if (index < static_cast<uint32_t>(array->length())) {
6701 return true;
6702 }
6703 break;
6704 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006705 case DICTIONARY_ELEMENTS: {
6706 if (element_dictionary()->FindEntry(index)
6707 != NumberDictionary::kNotFound) {
6708 return true;
6709 }
6710 break;
6711 }
6712 default:
6713 UNREACHABLE();
6714 break;
6715 }
6716
6717 // Handle [] on String objects.
6718 if (this->IsStringObjectWithCharacterAt(index)) return true;
6719
6720 Object* pt = GetPrototype();
6721 if (pt == Heap::null_value()) return false;
6722 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
6723}
6724
6725
6726bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
6727 // Make sure that the top context does not change when doing
6728 // callbacks or interceptor calls.
6729 AssertNoContextChange ncc;
6730 HandleScope scope;
6731 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6732 Handle<JSObject> receiver_handle(receiver);
6733 Handle<JSObject> holder_handle(this);
6734 CustomArguments args(interceptor->data(), receiver, this);
6735 v8::AccessorInfo info(args.end());
6736 if (!interceptor->query()->IsUndefined()) {
6737 v8::IndexedPropertyQuery query =
6738 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
6739 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
Iain Merrick75681382010-08-19 15:07:18 +01006740 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00006741 {
6742 // Leaving JavaScript.
6743 VMState state(EXTERNAL);
6744 result = query(index, info);
6745 }
Iain Merrick75681382010-08-19 15:07:18 +01006746 if (!result.IsEmpty()) {
6747 ASSERT(result->IsInt32());
6748 return true; // absence of property is signaled by empty handle.
6749 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006750 } else if (!interceptor->getter()->IsUndefined()) {
6751 v8::IndexedPropertyGetter getter =
6752 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
6753 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
6754 v8::Handle<v8::Value> result;
6755 {
6756 // Leaving JavaScript.
6757 VMState state(EXTERNAL);
6758 result = getter(index, info);
6759 }
6760 if (!result.IsEmpty()) return true;
6761 }
6762 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
6763}
6764
6765
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006766JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006767 // Check access rights if needed.
6768 if (IsAccessCheckNeeded() &&
6769 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6770 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006771 return UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006772 }
6773
Steve Block1e0659c2011-05-24 12:43:12 +01006774 if (IsJSGlobalProxy()) {
6775 Object* proto = GetPrototype();
6776 if (proto->IsNull()) return UNDEFINED_ELEMENT;
6777 ASSERT(proto->IsJSGlobalObject());
6778 return JSObject::cast(proto)->HasLocalElement(index);
6779 }
6780
Steve Blocka7e24c12009-10-30 11:49:00 +00006781 // Check for lookup interceptor
6782 if (HasIndexedInterceptor()) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006783 return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
6784 : UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006785 }
6786
6787 // Handle [] on String objects.
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006788 if (this->IsStringObjectWithCharacterAt(index)) {
6789 return STRING_CHARACTER_ELEMENT;
6790 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006791
6792 switch (GetElementsKind()) {
6793 case FAST_ELEMENTS: {
6794 uint32_t length = IsJSArray() ?
6795 static_cast<uint32_t>
6796 (Smi::cast(JSArray::cast(this)->length())->value()) :
6797 static_cast<uint32_t>(FixedArray::cast(elements())->length());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006798 if ((index < length) &&
6799 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
6800 return FAST_ELEMENT;
6801 }
6802 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006803 }
6804 case PIXEL_ELEMENTS: {
6805 PixelArray* pixels = PixelArray::cast(elements());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006806 if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT;
6807 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006808 }
Steve Block3ce2e202009-11-05 08:53:23 +00006809 case EXTERNAL_BYTE_ELEMENTS:
6810 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6811 case EXTERNAL_SHORT_ELEMENTS:
6812 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6813 case EXTERNAL_INT_ELEMENTS:
6814 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6815 case EXTERNAL_FLOAT_ELEMENTS: {
6816 ExternalArray* array = ExternalArray::cast(elements());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006817 if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
6818 break;
Steve Block3ce2e202009-11-05 08:53:23 +00006819 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006820 case DICTIONARY_ELEMENTS: {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006821 if (element_dictionary()->FindEntry(index) !=
6822 NumberDictionary::kNotFound) {
6823 return DICTIONARY_ELEMENT;
6824 }
6825 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006826 }
6827 default:
6828 UNREACHABLE();
6829 break;
6830 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006831
6832 return UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006833}
6834
6835
6836bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
6837 // Check access rights if needed.
6838 if (IsAccessCheckNeeded() &&
6839 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6840 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6841 return false;
6842 }
6843
6844 // Check for lookup interceptor
6845 if (HasIndexedInterceptor()) {
6846 return HasElementWithInterceptor(receiver, index);
6847 }
6848
6849 switch (GetElementsKind()) {
6850 case FAST_ELEMENTS: {
6851 uint32_t length = IsJSArray() ?
6852 static_cast<uint32_t>
6853 (Smi::cast(JSArray::cast(this)->length())->value()) :
6854 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6855 if ((index < length) &&
6856 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
6857 break;
6858 }
6859 case PIXEL_ELEMENTS: {
6860 PixelArray* pixels = PixelArray::cast(elements());
6861 if (index < static_cast<uint32_t>(pixels->length())) {
6862 return true;
6863 }
6864 break;
6865 }
Steve Block3ce2e202009-11-05 08:53:23 +00006866 case EXTERNAL_BYTE_ELEMENTS:
6867 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6868 case EXTERNAL_SHORT_ELEMENTS:
6869 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6870 case EXTERNAL_INT_ELEMENTS:
6871 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6872 case EXTERNAL_FLOAT_ELEMENTS: {
6873 ExternalArray* array = ExternalArray::cast(elements());
6874 if (index < static_cast<uint32_t>(array->length())) {
6875 return true;
6876 }
6877 break;
6878 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006879 case DICTIONARY_ELEMENTS: {
6880 if (element_dictionary()->FindEntry(index)
6881 != NumberDictionary::kNotFound) {
6882 return true;
6883 }
6884 break;
6885 }
6886 default:
6887 UNREACHABLE();
6888 break;
6889 }
6890
6891 // Handle [] on String objects.
6892 if (this->IsStringObjectWithCharacterAt(index)) return true;
6893
6894 Object* pt = GetPrototype();
6895 if (pt == Heap::null_value()) return false;
6896 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
6897}
6898
6899
John Reck59135872010-11-02 12:39:01 -07006900MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
Steve Block9fac8402011-05-12 15:51:54 +01006901 Object* value,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01006902 StrictModeFlag strict_mode,
Steve Block9fac8402011-05-12 15:51:54 +01006903 bool check_prototype) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006904 // Make sure that the top context does not change when doing
6905 // callbacks or interceptor calls.
6906 AssertNoContextChange ncc;
6907 HandleScope scope;
6908 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6909 Handle<JSObject> this_handle(this);
6910 Handle<Object> value_handle(value);
6911 if (!interceptor->setter()->IsUndefined()) {
6912 v8::IndexedPropertySetter setter =
6913 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
6914 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
6915 CustomArguments args(interceptor->data(), this, this);
6916 v8::AccessorInfo info(args.end());
6917 v8::Handle<v8::Value> result;
6918 {
6919 // Leaving JavaScript.
6920 VMState state(EXTERNAL);
6921 result = setter(index, v8::Utils::ToLocal(value_handle), info);
6922 }
6923 RETURN_IF_SCHEDULED_EXCEPTION();
6924 if (!result.IsEmpty()) return *value_handle;
6925 }
John Reck59135872010-11-02 12:39:01 -07006926 MaybeObject* raw_result =
Steve Block9fac8402011-05-12 15:51:54 +01006927 this_handle->SetElementWithoutInterceptor(index,
6928 *value_handle,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01006929 strict_mode,
Steve Block9fac8402011-05-12 15:51:54 +01006930 check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00006931 RETURN_IF_SCHEDULED_EXCEPTION();
6932 return raw_result;
6933}
6934
6935
John Reck59135872010-11-02 12:39:01 -07006936MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
6937 Object* structure,
6938 uint32_t index,
6939 Object* holder) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006940 ASSERT(!structure->IsProxy());
6941
6942 // api style callbacks.
6943 if (structure->IsAccessorInfo()) {
6944 AccessorInfo* data = AccessorInfo::cast(structure);
6945 Object* fun_obj = data->getter();
6946 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
6947 HandleScope scope;
6948 Handle<JSObject> self(JSObject::cast(receiver));
6949 Handle<JSObject> holder_handle(JSObject::cast(holder));
6950 Handle<Object> number = Factory::NewNumberFromUint(index);
6951 Handle<String> key(Factory::NumberToString(number));
6952 LOG(ApiNamedPropertyAccess("load", *self, *key));
6953 CustomArguments args(data->data(), *self, *holder_handle);
6954 v8::AccessorInfo info(args.end());
6955 v8::Handle<v8::Value> result;
6956 {
6957 // Leaving JavaScript.
6958 VMState state(EXTERNAL);
6959 result = call_fun(v8::Utils::ToLocal(key), info);
6960 }
6961 RETURN_IF_SCHEDULED_EXCEPTION();
6962 if (result.IsEmpty()) return Heap::undefined_value();
6963 return *v8::Utils::OpenHandle(*result);
6964 }
6965
6966 // __defineGetter__ callback
6967 if (structure->IsFixedArray()) {
6968 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
6969 if (getter->IsJSFunction()) {
6970 return Object::GetPropertyWithDefinedGetter(receiver,
6971 JSFunction::cast(getter));
6972 }
6973 // Getter is not a function.
6974 return Heap::undefined_value();
6975 }
6976
6977 UNREACHABLE();
6978 return NULL;
6979}
6980
6981
John Reck59135872010-11-02 12:39:01 -07006982MaybeObject* JSObject::SetElementWithCallback(Object* structure,
6983 uint32_t index,
6984 Object* value,
6985 JSObject* holder) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006986 HandleScope scope;
6987
6988 // We should never get here to initialize a const with the hole
6989 // value since a const declaration would conflict with the setter.
6990 ASSERT(!value->IsTheHole());
6991 Handle<Object> value_handle(value);
6992
6993 // To accommodate both the old and the new api we switch on the
6994 // data structure used to store the callbacks. Eventually proxy
6995 // callbacks should be phased out.
6996 ASSERT(!structure->IsProxy());
6997
6998 if (structure->IsAccessorInfo()) {
6999 // api style callbacks
7000 AccessorInfo* data = AccessorInfo::cast(structure);
7001 Object* call_obj = data->setter();
7002 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
7003 if (call_fun == NULL) return value;
7004 Handle<Object> number = Factory::NewNumberFromUint(index);
7005 Handle<String> key(Factory::NumberToString(number));
7006 LOG(ApiNamedPropertyAccess("store", this, *key));
7007 CustomArguments args(data->data(), this, JSObject::cast(holder));
7008 v8::AccessorInfo info(args.end());
7009 {
7010 // Leaving JavaScript.
7011 VMState state(EXTERNAL);
7012 call_fun(v8::Utils::ToLocal(key),
7013 v8::Utils::ToLocal(value_handle),
7014 info);
7015 }
7016 RETURN_IF_SCHEDULED_EXCEPTION();
7017 return *value_handle;
7018 }
7019
7020 if (structure->IsFixedArray()) {
7021 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
7022 if (setter->IsJSFunction()) {
7023 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
7024 } else {
7025 Handle<Object> holder_handle(holder);
7026 Handle<Object> key(Factory::NewNumberFromUint(index));
7027 Handle<Object> args[2] = { key, holder_handle };
7028 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
7029 HandleVector(args, 2)));
7030 }
7031 }
7032
7033 UNREACHABLE();
7034 return NULL;
7035}
7036
7037
Steve Blocka7e24c12009-10-30 11:49:00 +00007038// Adding n elements in fast case is O(n*n).
7039// Note: revisit design to have dual undefined values to capture absent
7040// elements.
Steve Block9fac8402011-05-12 15:51:54 +01007041MaybeObject* JSObject::SetFastElement(uint32_t index,
7042 Object* value,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007043 StrictModeFlag strict_mode,
Steve Block9fac8402011-05-12 15:51:54 +01007044 bool check_prototype) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007045 ASSERT(HasFastElements());
7046
John Reck59135872010-11-02 12:39:01 -07007047 Object* elms_obj;
7048 { MaybeObject* maybe_elms_obj = EnsureWritableFastElements();
7049 if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
7050 }
Iain Merrick75681382010-08-19 15:07:18 +01007051 FixedArray* elms = FixedArray::cast(elms_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00007052 uint32_t elms_length = static_cast<uint32_t>(elms->length());
7053
Steve Block9fac8402011-05-12 15:51:54 +01007054 if (check_prototype &&
Steve Block1e0659c2011-05-24 12:43:12 +01007055 (index >= elms_length || elms->get(index)->IsTheHole())) {
7056 bool found;
7057 MaybeObject* result =
7058 SetElementWithCallbackSetterInPrototypes(index, value, &found);
7059 if (found) return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00007060 }
7061
Steve Block9fac8402011-05-12 15:51:54 +01007062
Steve Blocka7e24c12009-10-30 11:49:00 +00007063 // Check whether there is extra space in fixed array..
7064 if (index < elms_length) {
7065 elms->set(index, value);
7066 if (IsJSArray()) {
7067 // Update the length of the array if needed.
7068 uint32_t array_length = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007069 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00007070 if (index >= array_length) {
Leon Clarke4515c472010-02-03 11:58:03 +00007071 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +00007072 }
7073 }
7074 return value;
7075 }
7076
7077 // Allow gap in fast case.
7078 if ((index - elms_length) < kMaxGap) {
7079 // Try allocating extra space.
7080 int new_capacity = NewElementsCapacity(index+1);
7081 if (new_capacity <= kMaxFastElementsLength ||
7082 !ShouldConvertToSlowElements(new_capacity)) {
7083 ASSERT(static_cast<uint32_t>(new_capacity) > index);
John Reck59135872010-11-02 12:39:01 -07007084 Object* obj;
7085 { MaybeObject* maybe_obj =
7086 SetFastElementsCapacityAndLength(new_capacity, index + 1);
7087 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7088 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007089 FixedArray::cast(elements())->set(index, value);
7090 return value;
7091 }
7092 }
7093
7094 // Otherwise default to slow case.
John Reck59135872010-11-02 12:39:01 -07007095 Object* obj;
7096 { MaybeObject* maybe_obj = NormalizeElements();
7097 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7098 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007099 ASSERT(HasDictionaryElements());
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007100 return SetElement(index, value, strict_mode, check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007101}
7102
Iain Merrick75681382010-08-19 15:07:18 +01007103
Steve Block9fac8402011-05-12 15:51:54 +01007104MaybeObject* JSObject::SetElement(uint32_t index,
7105 Object* value,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007106 StrictModeFlag strict_mode,
Steve Block9fac8402011-05-12 15:51:54 +01007107 bool check_prototype) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007108 // Check access rights if needed.
7109 if (IsAccessCheckNeeded() &&
7110 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
Iain Merrick75681382010-08-19 15:07:18 +01007111 HandleScope scope;
7112 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00007113 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01007114 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00007115 }
7116
7117 if (IsJSGlobalProxy()) {
7118 Object* proto = GetPrototype();
7119 if (proto->IsNull()) return value;
7120 ASSERT(proto->IsJSGlobalObject());
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007121 return JSObject::cast(proto)->SetElement(index,
7122 value,
7123 strict_mode,
7124 check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007125 }
7126
7127 // Check for lookup interceptor
7128 if (HasIndexedInterceptor()) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007129 return SetElementWithInterceptor(index,
7130 value,
7131 strict_mode,
7132 check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007133 }
7134
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007135 return SetElementWithoutInterceptor(index,
7136 value,
7137 strict_mode,
7138 check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007139}
7140
7141
John Reck59135872010-11-02 12:39:01 -07007142MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
Steve Block9fac8402011-05-12 15:51:54 +01007143 Object* value,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007144 StrictModeFlag strict_mode,
Steve Block9fac8402011-05-12 15:51:54 +01007145 bool check_prototype) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007146 switch (GetElementsKind()) {
7147 case FAST_ELEMENTS:
7148 // Fast case.
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007149 return SetFastElement(index, value, strict_mode, check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007150 case PIXEL_ELEMENTS: {
7151 PixelArray* pixels = PixelArray::cast(elements());
7152 return pixels->SetValue(index, value);
7153 }
Steve Block3ce2e202009-11-05 08:53:23 +00007154 case EXTERNAL_BYTE_ELEMENTS: {
7155 ExternalByteArray* array = ExternalByteArray::cast(elements());
7156 return array->SetValue(index, value);
7157 }
7158 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7159 ExternalUnsignedByteArray* array =
7160 ExternalUnsignedByteArray::cast(elements());
7161 return array->SetValue(index, value);
7162 }
7163 case EXTERNAL_SHORT_ELEMENTS: {
7164 ExternalShortArray* array = ExternalShortArray::cast(elements());
7165 return array->SetValue(index, value);
7166 }
7167 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7168 ExternalUnsignedShortArray* array =
7169 ExternalUnsignedShortArray::cast(elements());
7170 return array->SetValue(index, value);
7171 }
7172 case EXTERNAL_INT_ELEMENTS: {
7173 ExternalIntArray* array = ExternalIntArray::cast(elements());
7174 return array->SetValue(index, value);
7175 }
7176 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7177 ExternalUnsignedIntArray* array =
7178 ExternalUnsignedIntArray::cast(elements());
7179 return array->SetValue(index, value);
7180 }
7181 case EXTERNAL_FLOAT_ELEMENTS: {
7182 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
7183 return array->SetValue(index, value);
7184 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007185 case DICTIONARY_ELEMENTS: {
7186 // Insert element in the dictionary.
7187 FixedArray* elms = FixedArray::cast(elements());
7188 NumberDictionary* dictionary = NumberDictionary::cast(elms);
7189
7190 int entry = dictionary->FindEntry(index);
7191 if (entry != NumberDictionary::kNotFound) {
7192 Object* element = dictionary->ValueAt(entry);
7193 PropertyDetails details = dictionary->DetailsAt(entry);
7194 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01007195 return SetElementWithCallback(element, index, value, this);
Steve Blocka7e24c12009-10-30 11:49:00 +00007196 } else {
7197 dictionary->UpdateMaxNumberKey(index);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007198 // If put fails instrict mode, throw exception.
7199 if (!dictionary->ValueAtPut(entry, value) &&
7200 strict_mode == kStrictMode) {
7201 Handle<Object> number(Factory::NewNumberFromUint(index));
7202 Handle<Object> holder(this);
7203 Handle<Object> args[2] = { number, holder };
7204 return Top::Throw(
7205 *Factory::NewTypeError("strict_read_only_property",
7206 HandleVector(args, 2)));
7207 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007208 }
7209 } else {
7210 // Index not already used. Look for an accessor in the prototype chain.
Steve Block1e0659c2011-05-24 12:43:12 +01007211 if (check_prototype) {
7212 bool found;
7213 MaybeObject* result =
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007214 // Strict mode not needed. No-setter case already handled.
Steve Block1e0659c2011-05-24 12:43:12 +01007215 SetElementWithCallbackSetterInPrototypes(index, value, &found);
7216 if (found) return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00007217 }
Steve Block8defd9f2010-07-08 12:39:36 +01007218 // When we set the is_extensible flag to false we always force
7219 // the element into dictionary mode (and force them to stay there).
7220 if (!map()->is_extensible()) {
Ben Murdochf87a2032010-10-22 12:50:53 +01007221 Handle<Object> number(Factory::NewNumberFromUint(index));
Steve Block8defd9f2010-07-08 12:39:36 +01007222 Handle<String> index_string(Factory::NumberToString(number));
7223 Handle<Object> args[1] = { index_string };
7224 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
7225 HandleVector(args, 1)));
7226 }
John Reck59135872010-11-02 12:39:01 -07007227 Object* result;
7228 { MaybeObject* maybe_result = dictionary->AtNumberPut(index, value);
7229 if (!maybe_result->ToObject(&result)) return maybe_result;
7230 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007231 if (elms != FixedArray::cast(result)) {
7232 set_elements(FixedArray::cast(result));
7233 }
7234 }
7235
7236 // Update the array length if this JSObject is an array.
7237 if (IsJSArray()) {
7238 JSArray* array = JSArray::cast(this);
John Reck59135872010-11-02 12:39:01 -07007239 Object* return_value;
7240 { MaybeObject* maybe_return_value =
7241 array->JSArrayUpdateLengthFromIndex(index, value);
7242 if (!maybe_return_value->ToObject(&return_value)) {
7243 return maybe_return_value;
7244 }
7245 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007246 }
7247
7248 // Attempt to put this object back in fast case.
7249 if (ShouldConvertToFastElements()) {
7250 uint32_t new_length = 0;
7251 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007252 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00007253 } else {
7254 new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
7255 }
John Reck59135872010-11-02 12:39:01 -07007256 Object* obj;
7257 { MaybeObject* maybe_obj =
7258 SetFastElementsCapacityAndLength(new_length, new_length);
7259 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7260 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007261#ifdef DEBUG
7262 if (FLAG_trace_normalization) {
7263 PrintF("Object elements are fast case again:\n");
7264 Print();
7265 }
7266#endif
7267 }
7268
7269 return value;
7270 }
7271 default:
7272 UNREACHABLE();
7273 break;
7274 }
7275 // All possible cases have been handled above. Add a return to avoid the
7276 // complaints from the compiler.
7277 UNREACHABLE();
7278 return Heap::null_value();
7279}
7280
7281
John Reck59135872010-11-02 12:39:01 -07007282MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
7283 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007284 uint32_t old_len = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007285 CHECK(length()->ToArrayIndex(&old_len));
Steve Blocka7e24c12009-10-30 11:49:00 +00007286 // Check to see if we need to update the length. For now, we make
7287 // sure that the length stays within 32-bits (unsigned).
7288 if (index >= old_len && index != 0xffffffff) {
John Reck59135872010-11-02 12:39:01 -07007289 Object* len;
7290 { MaybeObject* maybe_len =
7291 Heap::NumberFromDouble(static_cast<double>(index) + 1);
7292 if (!maybe_len->ToObject(&len)) return maybe_len;
7293 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007294 set_length(len);
7295 }
7296 return value;
7297}
7298
7299
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007300MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver,
John Reck59135872010-11-02 12:39:01 -07007301 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007302 // Get element works for both JSObject and JSArray since
7303 // JSArray::length cannot change.
7304 switch (GetElementsKind()) {
7305 case FAST_ELEMENTS: {
7306 FixedArray* elms = FixedArray::cast(elements());
7307 if (index < static_cast<uint32_t>(elms->length())) {
7308 Object* value = elms->get(index);
7309 if (!value->IsTheHole()) return value;
7310 }
7311 break;
7312 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007313 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00007314 case EXTERNAL_BYTE_ELEMENTS:
7315 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7316 case EXTERNAL_SHORT_ELEMENTS:
7317 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7318 case EXTERNAL_INT_ELEMENTS:
7319 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7320 case EXTERNAL_FLOAT_ELEMENTS: {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007321 MaybeObject* maybe_value = GetExternalElement(index);
7322 Object* value;
7323 if (!maybe_value->ToObject(&value)) return maybe_value;
7324 if (!value->IsUndefined()) return value;
Steve Block3ce2e202009-11-05 08:53:23 +00007325 break;
7326 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007327 case DICTIONARY_ELEMENTS: {
7328 NumberDictionary* dictionary = element_dictionary();
7329 int entry = dictionary->FindEntry(index);
7330 if (entry != NumberDictionary::kNotFound) {
7331 Object* element = dictionary->ValueAt(entry);
7332 PropertyDetails details = dictionary->DetailsAt(entry);
7333 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01007334 return GetElementWithCallback(receiver,
7335 element,
7336 index,
7337 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00007338 }
7339 return element;
7340 }
7341 break;
7342 }
7343 default:
7344 UNREACHABLE();
7345 break;
7346 }
7347
7348 // Continue searching via the prototype chain.
7349 Object* pt = GetPrototype();
7350 if (pt == Heap::null_value()) return Heap::undefined_value();
7351 return pt->GetElementWithReceiver(receiver, index);
7352}
7353
7354
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007355MaybeObject* JSObject::GetElementWithInterceptor(Object* receiver,
John Reck59135872010-11-02 12:39:01 -07007356 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007357 // Make sure that the top context does not change when doing
7358 // callbacks or interceptor calls.
7359 AssertNoContextChange ncc;
7360 HandleScope scope;
7361 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007362 Handle<Object> this_handle(receiver);
Steve Blocka7e24c12009-10-30 11:49:00 +00007363 Handle<JSObject> holder_handle(this);
7364
7365 if (!interceptor->getter()->IsUndefined()) {
7366 v8::IndexedPropertyGetter getter =
7367 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
7368 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
7369 CustomArguments args(interceptor->data(), receiver, this);
7370 v8::AccessorInfo info(args.end());
7371 v8::Handle<v8::Value> result;
7372 {
7373 // Leaving JavaScript.
7374 VMState state(EXTERNAL);
7375 result = getter(index, info);
7376 }
7377 RETURN_IF_SCHEDULED_EXCEPTION();
7378 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
7379 }
7380
John Reck59135872010-11-02 12:39:01 -07007381 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00007382 holder_handle->GetElementPostInterceptor(*this_handle, index);
7383 RETURN_IF_SCHEDULED_EXCEPTION();
7384 return raw_result;
7385}
7386
7387
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007388MaybeObject* JSObject::GetElementWithReceiver(Object* receiver,
John Reck59135872010-11-02 12:39:01 -07007389 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007390 // Check access rights if needed.
7391 if (IsAccessCheckNeeded() &&
7392 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
7393 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
7394 return Heap::undefined_value();
7395 }
7396
7397 if (HasIndexedInterceptor()) {
7398 return GetElementWithInterceptor(receiver, index);
7399 }
7400
7401 // Get element works for both JSObject and JSArray since
7402 // JSArray::length cannot change.
7403 switch (GetElementsKind()) {
7404 case FAST_ELEMENTS: {
7405 FixedArray* elms = FixedArray::cast(elements());
7406 if (index < static_cast<uint32_t>(elms->length())) {
7407 Object* value = elms->get(index);
7408 if (!value->IsTheHole()) return value;
7409 }
7410 break;
7411 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007412 case PIXEL_ELEMENTS:
7413 case EXTERNAL_BYTE_ELEMENTS:
7414 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7415 case EXTERNAL_SHORT_ELEMENTS:
7416 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7417 case EXTERNAL_INT_ELEMENTS:
7418 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7419 case EXTERNAL_FLOAT_ELEMENTS: {
7420 MaybeObject* maybe_value = GetExternalElement(index);
7421 Object* value;
7422 if (!maybe_value->ToObject(&value)) return maybe_value;
7423 if (!value->IsUndefined()) return value;
7424 break;
7425 }
7426 case DICTIONARY_ELEMENTS: {
7427 NumberDictionary* dictionary = element_dictionary();
7428 int entry = dictionary->FindEntry(index);
7429 if (entry != NumberDictionary::kNotFound) {
7430 Object* element = dictionary->ValueAt(entry);
7431 PropertyDetails details = dictionary->DetailsAt(entry);
7432 if (details.type() == CALLBACKS) {
7433 return GetElementWithCallback(receiver,
7434 element,
7435 index,
7436 this);
7437 }
7438 return element;
7439 }
7440 break;
7441 }
7442 }
7443
7444 Object* pt = GetPrototype();
7445 if (pt == Heap::null_value()) return Heap::undefined_value();
7446 return pt->GetElementWithReceiver(receiver, index);
7447}
7448
7449
7450MaybeObject* JSObject::GetExternalElement(uint32_t index) {
7451 // Get element works for both JSObject and JSArray since
7452 // JSArray::length cannot change.
7453 switch (GetElementsKind()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007454 case PIXEL_ELEMENTS: {
7455 PixelArray* pixels = PixelArray::cast(elements());
7456 if (index < static_cast<uint32_t>(pixels->length())) {
7457 uint8_t value = pixels->get(index);
7458 return Smi::FromInt(value);
7459 }
7460 break;
7461 }
Steve Block3ce2e202009-11-05 08:53:23 +00007462 case EXTERNAL_BYTE_ELEMENTS: {
7463 ExternalByteArray* array = ExternalByteArray::cast(elements());
7464 if (index < static_cast<uint32_t>(array->length())) {
7465 int8_t value = array->get(index);
7466 return Smi::FromInt(value);
7467 }
7468 break;
7469 }
7470 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7471 ExternalUnsignedByteArray* array =
7472 ExternalUnsignedByteArray::cast(elements());
7473 if (index < static_cast<uint32_t>(array->length())) {
7474 uint8_t value = array->get(index);
7475 return Smi::FromInt(value);
7476 }
7477 break;
7478 }
7479 case EXTERNAL_SHORT_ELEMENTS: {
7480 ExternalShortArray* array = ExternalShortArray::cast(elements());
7481 if (index < static_cast<uint32_t>(array->length())) {
7482 int16_t value = array->get(index);
7483 return Smi::FromInt(value);
7484 }
7485 break;
7486 }
7487 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7488 ExternalUnsignedShortArray* array =
7489 ExternalUnsignedShortArray::cast(elements());
7490 if (index < static_cast<uint32_t>(array->length())) {
7491 uint16_t value = array->get(index);
7492 return Smi::FromInt(value);
7493 }
7494 break;
7495 }
7496 case EXTERNAL_INT_ELEMENTS: {
7497 ExternalIntArray* array = ExternalIntArray::cast(elements());
7498 if (index < static_cast<uint32_t>(array->length())) {
7499 int32_t value = array->get(index);
7500 return Heap::NumberFromInt32(value);
7501 }
7502 break;
7503 }
7504 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7505 ExternalUnsignedIntArray* array =
7506 ExternalUnsignedIntArray::cast(elements());
7507 if (index < static_cast<uint32_t>(array->length())) {
7508 uint32_t value = array->get(index);
7509 return Heap::NumberFromUint32(value);
7510 }
7511 break;
7512 }
7513 case EXTERNAL_FLOAT_ELEMENTS: {
7514 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
7515 if (index < static_cast<uint32_t>(array->length())) {
7516 float value = array->get(index);
7517 return Heap::AllocateHeapNumber(value);
7518 }
7519 break;
7520 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007521 case FAST_ELEMENTS:
7522 case DICTIONARY_ELEMENTS:
7523 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +00007524 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00007525 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007526 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00007527}
7528
7529
7530bool JSObject::HasDenseElements() {
7531 int capacity = 0;
7532 int number_of_elements = 0;
7533
7534 switch (GetElementsKind()) {
7535 case FAST_ELEMENTS: {
7536 FixedArray* elms = FixedArray::cast(elements());
7537 capacity = elms->length();
7538 for (int i = 0; i < capacity; i++) {
7539 if (!elms->get(i)->IsTheHole()) number_of_elements++;
7540 }
7541 break;
7542 }
Steve Block3ce2e202009-11-05 08:53:23 +00007543 case PIXEL_ELEMENTS:
7544 case EXTERNAL_BYTE_ELEMENTS:
7545 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7546 case EXTERNAL_SHORT_ELEMENTS:
7547 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7548 case EXTERNAL_INT_ELEMENTS:
7549 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7550 case EXTERNAL_FLOAT_ELEMENTS: {
Steve Blocka7e24c12009-10-30 11:49:00 +00007551 return true;
7552 }
7553 case DICTIONARY_ELEMENTS: {
7554 NumberDictionary* dictionary = NumberDictionary::cast(elements());
7555 capacity = dictionary->Capacity();
7556 number_of_elements = dictionary->NumberOfElements();
7557 break;
7558 }
7559 default:
7560 UNREACHABLE();
7561 break;
7562 }
7563
7564 if (capacity == 0) return true;
7565 return (number_of_elements > (capacity / 2));
7566}
7567
7568
7569bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
7570 ASSERT(HasFastElements());
7571 // Keep the array in fast case if the current backing storage is
7572 // almost filled and if the new capacity is no more than twice the
7573 // old capacity.
7574 int elements_length = FixedArray::cast(elements())->length();
7575 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
7576}
7577
7578
7579bool JSObject::ShouldConvertToFastElements() {
7580 ASSERT(HasDictionaryElements());
7581 NumberDictionary* dictionary = NumberDictionary::cast(elements());
7582 // If the elements are sparse, we should not go back to fast case.
7583 if (!HasDenseElements()) return false;
7584 // If an element has been added at a very high index in the elements
7585 // dictionary, we cannot go back to fast case.
7586 if (dictionary->requires_slow_elements()) return false;
7587 // An object requiring access checks is never allowed to have fast
7588 // elements. If it had fast elements we would skip security checks.
7589 if (IsAccessCheckNeeded()) return false;
7590 // If the dictionary backing storage takes up roughly half as much
7591 // space as a fast-case backing storage would the array should have
7592 // fast elements.
7593 uint32_t length = 0;
7594 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007595 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
Steve Blocka7e24c12009-10-30 11:49:00 +00007596 } else {
7597 length = dictionary->max_number_key();
7598 }
7599 return static_cast<uint32_t>(dictionary->Capacity()) >=
7600 (length / (2 * NumberDictionary::kEntrySize));
7601}
7602
7603
7604// Certain compilers request function template instantiation when they
7605// see the definition of the other template functions in the
7606// class. This requires us to have the template functions put
7607// together, so even though this function belongs in objects-debug.cc,
7608// we keep it here instead to satisfy certain compilers.
Ben Murdochb0fe1622011-05-05 13:52:32 +01007609#ifdef OBJECT_PRINT
Steve Blocka7e24c12009-10-30 11:49:00 +00007610template<typename Shape, typename Key>
Ben Murdochb0fe1622011-05-05 13:52:32 +01007611void Dictionary<Shape, Key>::Print(FILE* out) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007612 int capacity = HashTable<Shape, Key>::Capacity();
7613 for (int i = 0; i < capacity; i++) {
7614 Object* k = HashTable<Shape, Key>::KeyAt(i);
7615 if (HashTable<Shape, Key>::IsKey(k)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01007616 PrintF(out, " ");
Steve Blocka7e24c12009-10-30 11:49:00 +00007617 if (k->IsString()) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01007618 String::cast(k)->StringPrint(out);
Steve Blocka7e24c12009-10-30 11:49:00 +00007619 } else {
Ben Murdochb0fe1622011-05-05 13:52:32 +01007620 k->ShortPrint(out);
Steve Blocka7e24c12009-10-30 11:49:00 +00007621 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01007622 PrintF(out, ": ");
7623 ValueAt(i)->ShortPrint(out);
7624 PrintF(out, "\n");
Steve Blocka7e24c12009-10-30 11:49:00 +00007625 }
7626 }
7627}
7628#endif
7629
7630
7631template<typename Shape, typename Key>
7632void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
7633 int pos = 0;
7634 int capacity = HashTable<Shape, Key>::Capacity();
Leon Clarke4515c472010-02-03 11:58:03 +00007635 AssertNoAllocation no_gc;
7636 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007637 for (int i = 0; i < capacity; i++) {
7638 Object* k = Dictionary<Shape, Key>::KeyAt(i);
7639 if (Dictionary<Shape, Key>::IsKey(k)) {
7640 elements->set(pos++, ValueAt(i), mode);
7641 }
7642 }
7643 ASSERT(pos == elements->length());
7644}
7645
7646
7647InterceptorInfo* JSObject::GetNamedInterceptor() {
7648 ASSERT(map()->has_named_interceptor());
7649 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01007650 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00007651 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01007652 constructor->shared()->get_api_func_data()->named_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00007653 return InterceptorInfo::cast(result);
7654}
7655
7656
7657InterceptorInfo* JSObject::GetIndexedInterceptor() {
7658 ASSERT(map()->has_indexed_interceptor());
7659 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01007660 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00007661 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01007662 constructor->shared()->get_api_func_data()->indexed_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00007663 return InterceptorInfo::cast(result);
7664}
7665
7666
John Reck59135872010-11-02 12:39:01 -07007667MaybeObject* JSObject::GetPropertyPostInterceptor(
7668 JSObject* receiver,
7669 String* name,
7670 PropertyAttributes* attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007671 // Check local property in holder, ignore interceptor.
7672 LookupResult result;
7673 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007674 if (result.IsProperty()) {
7675 return GetProperty(receiver, &result, name, attributes);
7676 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007677 // Continue searching via the prototype chain.
7678 Object* pt = GetPrototype();
7679 *attributes = ABSENT;
7680 if (pt == Heap::null_value()) return Heap::undefined_value();
7681 return pt->GetPropertyWithReceiver(receiver, name, attributes);
7682}
7683
7684
John Reck59135872010-11-02 12:39:01 -07007685MaybeObject* JSObject::GetLocalPropertyPostInterceptor(
Steve Blockd0582a62009-12-15 09:54:21 +00007686 JSObject* receiver,
7687 String* name,
7688 PropertyAttributes* attributes) {
7689 // Check local property in holder, ignore interceptor.
7690 LookupResult result;
7691 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007692 if (result.IsProperty()) {
7693 return GetProperty(receiver, &result, name, attributes);
7694 }
7695 return Heap::undefined_value();
Steve Blockd0582a62009-12-15 09:54:21 +00007696}
7697
7698
John Reck59135872010-11-02 12:39:01 -07007699MaybeObject* JSObject::GetPropertyWithInterceptor(
Steve Blocka7e24c12009-10-30 11:49:00 +00007700 JSObject* receiver,
7701 String* name,
7702 PropertyAttributes* attributes) {
7703 InterceptorInfo* interceptor = GetNamedInterceptor();
7704 HandleScope scope;
7705 Handle<JSObject> receiver_handle(receiver);
7706 Handle<JSObject> holder_handle(this);
7707 Handle<String> name_handle(name);
7708
7709 if (!interceptor->getter()->IsUndefined()) {
7710 v8::NamedPropertyGetter getter =
7711 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
7712 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
7713 CustomArguments args(interceptor->data(), receiver, this);
7714 v8::AccessorInfo info(args.end());
7715 v8::Handle<v8::Value> result;
7716 {
7717 // Leaving JavaScript.
7718 VMState state(EXTERNAL);
7719 result = getter(v8::Utils::ToLocal(name_handle), info);
7720 }
7721 RETURN_IF_SCHEDULED_EXCEPTION();
7722 if (!result.IsEmpty()) {
7723 *attributes = NONE;
7724 return *v8::Utils::OpenHandle(*result);
7725 }
7726 }
7727
John Reck59135872010-11-02 12:39:01 -07007728 MaybeObject* result = holder_handle->GetPropertyPostInterceptor(
Steve Blocka7e24c12009-10-30 11:49:00 +00007729 *receiver_handle,
7730 *name_handle,
7731 attributes);
7732 RETURN_IF_SCHEDULED_EXCEPTION();
7733 return result;
7734}
7735
7736
7737bool JSObject::HasRealNamedProperty(String* key) {
7738 // Check access rights if needed.
7739 if (IsAccessCheckNeeded() &&
7740 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
7741 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7742 return false;
7743 }
7744
7745 LookupResult result;
7746 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007747 return result.IsProperty() && (result.type() != INTERCEPTOR);
Steve Blocka7e24c12009-10-30 11:49:00 +00007748}
7749
7750
7751bool JSObject::HasRealElementProperty(uint32_t index) {
7752 // Check access rights if needed.
7753 if (IsAccessCheckNeeded() &&
7754 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
7755 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7756 return false;
7757 }
7758
7759 // Handle [] on String objects.
7760 if (this->IsStringObjectWithCharacterAt(index)) return true;
7761
7762 switch (GetElementsKind()) {
7763 case FAST_ELEMENTS: {
7764 uint32_t length = IsJSArray() ?
7765 static_cast<uint32_t>(
7766 Smi::cast(JSArray::cast(this)->length())->value()) :
7767 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7768 return (index < length) &&
7769 !FixedArray::cast(elements())->get(index)->IsTheHole();
7770 }
7771 case PIXEL_ELEMENTS: {
7772 PixelArray* pixels = PixelArray::cast(elements());
7773 return index < static_cast<uint32_t>(pixels->length());
7774 }
Steve Block3ce2e202009-11-05 08:53:23 +00007775 case EXTERNAL_BYTE_ELEMENTS:
7776 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7777 case EXTERNAL_SHORT_ELEMENTS:
7778 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7779 case EXTERNAL_INT_ELEMENTS:
7780 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7781 case EXTERNAL_FLOAT_ELEMENTS: {
7782 ExternalArray* array = ExternalArray::cast(elements());
7783 return index < static_cast<uint32_t>(array->length());
7784 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007785 case DICTIONARY_ELEMENTS: {
7786 return element_dictionary()->FindEntry(index)
7787 != NumberDictionary::kNotFound;
7788 }
7789 default:
7790 UNREACHABLE();
7791 break;
7792 }
7793 // All possibilities have been handled above already.
7794 UNREACHABLE();
7795 return Heap::null_value();
7796}
7797
7798
7799bool JSObject::HasRealNamedCallbackProperty(String* key) {
7800 // Check access rights if needed.
7801 if (IsAccessCheckNeeded() &&
7802 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
7803 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7804 return false;
7805 }
7806
7807 LookupResult result;
7808 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007809 return result.IsProperty() && (result.type() == CALLBACKS);
Steve Blocka7e24c12009-10-30 11:49:00 +00007810}
7811
7812
7813int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
7814 if (HasFastProperties()) {
7815 DescriptorArray* descs = map()->instance_descriptors();
7816 int result = 0;
7817 for (int i = 0; i < descs->number_of_descriptors(); i++) {
7818 PropertyDetails details = descs->GetDetails(i);
7819 if (details.IsProperty() && (details.attributes() & filter) == 0) {
7820 result++;
7821 }
7822 }
7823 return result;
7824 } else {
7825 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
7826 }
7827}
7828
7829
7830int JSObject::NumberOfEnumProperties() {
7831 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
7832}
7833
7834
7835void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
7836 Object* temp = get(i);
7837 set(i, get(j));
7838 set(j, temp);
7839 if (this != numbers) {
7840 temp = numbers->get(i);
7841 numbers->set(i, numbers->get(j));
7842 numbers->set(j, temp);
7843 }
7844}
7845
7846
7847static void InsertionSortPairs(FixedArray* content,
7848 FixedArray* numbers,
7849 int len) {
7850 for (int i = 1; i < len; i++) {
7851 int j = i;
7852 while (j > 0 &&
7853 (NumberToUint32(numbers->get(j - 1)) >
7854 NumberToUint32(numbers->get(j)))) {
7855 content->SwapPairs(numbers, j - 1, j);
7856 j--;
7857 }
7858 }
7859}
7860
7861
7862void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
7863 // In-place heap sort.
7864 ASSERT(content->length() == numbers->length());
7865
7866 // Bottom-up max-heap construction.
7867 for (int i = 1; i < len; ++i) {
7868 int child_index = i;
7869 while (child_index > 0) {
7870 int parent_index = ((child_index + 1) >> 1) - 1;
7871 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
7872 uint32_t child_value = NumberToUint32(numbers->get(child_index));
7873 if (parent_value < child_value) {
7874 content->SwapPairs(numbers, parent_index, child_index);
7875 } else {
7876 break;
7877 }
7878 child_index = parent_index;
7879 }
7880 }
7881
7882 // Extract elements and create sorted array.
7883 for (int i = len - 1; i > 0; --i) {
7884 // Put max element at the back of the array.
7885 content->SwapPairs(numbers, 0, i);
7886 // Sift down the new top element.
7887 int parent_index = 0;
7888 while (true) {
7889 int child_index = ((parent_index + 1) << 1) - 1;
7890 if (child_index >= i) break;
7891 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
7892 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
7893 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
7894 if (child_index + 1 >= i || child1_value > child2_value) {
7895 if (parent_value > child1_value) break;
7896 content->SwapPairs(numbers, parent_index, child_index);
7897 parent_index = child_index;
7898 } else {
7899 if (parent_value > child2_value) break;
7900 content->SwapPairs(numbers, parent_index, child_index + 1);
7901 parent_index = child_index + 1;
7902 }
7903 }
7904 }
7905}
7906
7907
7908// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
7909void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
7910 ASSERT(this->length() == numbers->length());
7911 // For small arrays, simply use insertion sort.
7912 if (len <= 10) {
7913 InsertionSortPairs(this, numbers, len);
7914 return;
7915 }
7916 // Check the range of indices.
7917 uint32_t min_index = NumberToUint32(numbers->get(0));
7918 uint32_t max_index = min_index;
7919 uint32_t i;
7920 for (i = 1; i < len; i++) {
7921 if (NumberToUint32(numbers->get(i)) < min_index) {
7922 min_index = NumberToUint32(numbers->get(i));
7923 } else if (NumberToUint32(numbers->get(i)) > max_index) {
7924 max_index = NumberToUint32(numbers->get(i));
7925 }
7926 }
7927 if (max_index - min_index + 1 == len) {
7928 // Indices form a contiguous range, unless there are duplicates.
7929 // Do an in-place linear time sort assuming distinct numbers, but
7930 // avoid hanging in case they are not.
7931 for (i = 0; i < len; i++) {
7932 uint32_t p;
7933 uint32_t j = 0;
7934 // While the current element at i is not at its correct position p,
7935 // swap the elements at these two positions.
7936 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
7937 j++ < len) {
7938 SwapPairs(numbers, i, p);
7939 }
7940 }
7941 } else {
7942 HeapSortPairs(this, numbers, len);
7943 return;
7944 }
7945}
7946
7947
7948// Fill in the names of local properties into the supplied storage. The main
7949// purpose of this function is to provide reflection information for the object
7950// mirrors.
7951void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
7952 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
7953 if (HasFastProperties()) {
7954 DescriptorArray* descs = map()->instance_descriptors();
7955 for (int i = 0; i < descs->number_of_descriptors(); i++) {
7956 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
7957 }
7958 ASSERT(storage->length() >= index);
7959 } else {
7960 property_dictionary()->CopyKeysTo(storage);
7961 }
7962}
7963
7964
7965int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
7966 return GetLocalElementKeys(NULL, filter);
7967}
7968
7969
7970int JSObject::NumberOfEnumElements() {
Steve Blockd0582a62009-12-15 09:54:21 +00007971 // Fast case for objects with no elements.
7972 if (!IsJSValue() && HasFastElements()) {
7973 uint32_t length = IsJSArray() ?
7974 static_cast<uint32_t>(
7975 Smi::cast(JSArray::cast(this)->length())->value()) :
7976 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7977 if (length == 0) return 0;
7978 }
7979 // Compute the number of enumerable elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00007980 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
7981}
7982
7983
7984int JSObject::GetLocalElementKeys(FixedArray* storage,
7985 PropertyAttributes filter) {
7986 int counter = 0;
7987 switch (GetElementsKind()) {
7988 case FAST_ELEMENTS: {
7989 int length = IsJSArray() ?
7990 Smi::cast(JSArray::cast(this)->length())->value() :
7991 FixedArray::cast(elements())->length();
7992 for (int i = 0; i < length; i++) {
7993 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
7994 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007995 storage->set(counter, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00007996 }
7997 counter++;
7998 }
7999 }
8000 ASSERT(!storage || storage->length() >= counter);
8001 break;
8002 }
8003 case PIXEL_ELEMENTS: {
8004 int length = PixelArray::cast(elements())->length();
8005 while (counter < length) {
8006 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00008007 storage->set(counter, Smi::FromInt(counter));
Steve Blocka7e24c12009-10-30 11:49:00 +00008008 }
8009 counter++;
8010 }
8011 ASSERT(!storage || storage->length() >= counter);
8012 break;
8013 }
Steve Block3ce2e202009-11-05 08:53:23 +00008014 case EXTERNAL_BYTE_ELEMENTS:
8015 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
8016 case EXTERNAL_SHORT_ELEMENTS:
8017 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
8018 case EXTERNAL_INT_ELEMENTS:
8019 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
8020 case EXTERNAL_FLOAT_ELEMENTS: {
8021 int length = ExternalArray::cast(elements())->length();
8022 while (counter < length) {
8023 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00008024 storage->set(counter, Smi::FromInt(counter));
Steve Block3ce2e202009-11-05 08:53:23 +00008025 }
8026 counter++;
8027 }
8028 ASSERT(!storage || storage->length() >= counter);
8029 break;
8030 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008031 case DICTIONARY_ELEMENTS: {
8032 if (storage != NULL) {
8033 element_dictionary()->CopyKeysTo(storage, filter);
8034 }
8035 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
8036 break;
8037 }
8038 default:
8039 UNREACHABLE();
8040 break;
8041 }
8042
8043 if (this->IsJSValue()) {
8044 Object* val = JSValue::cast(this)->value();
8045 if (val->IsString()) {
8046 String* str = String::cast(val);
8047 if (storage) {
8048 for (int i = 0; i < str->length(); i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00008049 storage->set(counter + i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00008050 }
8051 }
8052 counter += str->length();
8053 }
8054 }
8055 ASSERT(!storage || storage->length() == counter);
8056 return counter;
8057}
8058
8059
8060int JSObject::GetEnumElementKeys(FixedArray* storage) {
8061 return GetLocalElementKeys(storage,
8062 static_cast<PropertyAttributes>(DONT_ENUM));
8063}
8064
8065
8066bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
8067 ASSERT(other->IsNumber());
8068 return key == static_cast<uint32_t>(other->Number());
8069}
8070
8071
8072uint32_t NumberDictionaryShape::Hash(uint32_t key) {
8073 return ComputeIntegerHash(key);
8074}
8075
8076
8077uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) {
8078 ASSERT(other->IsNumber());
8079 return ComputeIntegerHash(static_cast<uint32_t>(other->Number()));
8080}
8081
8082
John Reck59135872010-11-02 12:39:01 -07008083MaybeObject* NumberDictionaryShape::AsObject(uint32_t key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008084 return Heap::NumberFromUint32(key);
8085}
8086
8087
8088bool StringDictionaryShape::IsMatch(String* key, Object* other) {
8089 // We know that all entries in a hash table had their hash keys created.
8090 // Use that knowledge to have fast failure.
8091 if (key->Hash() != String::cast(other)->Hash()) return false;
8092 return key->Equals(String::cast(other));
8093}
8094
8095
8096uint32_t StringDictionaryShape::Hash(String* key) {
8097 return key->Hash();
8098}
8099
8100
8101uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) {
8102 return String::cast(other)->Hash();
8103}
8104
8105
John Reck59135872010-11-02 12:39:01 -07008106MaybeObject* StringDictionaryShape::AsObject(String* key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008107 return key;
8108}
8109
8110
8111// StringKey simply carries a string object as key.
8112class StringKey : public HashTableKey {
8113 public:
8114 explicit StringKey(String* string) :
8115 string_(string),
8116 hash_(HashForObject(string)) { }
8117
8118 bool IsMatch(Object* string) {
8119 // We know that all entries in a hash table had their hash keys created.
8120 // Use that knowledge to have fast failure.
8121 if (hash_ != HashForObject(string)) {
8122 return false;
8123 }
8124 return string_->Equals(String::cast(string));
8125 }
8126
8127 uint32_t Hash() { return hash_; }
8128
8129 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
8130
8131 Object* AsObject() { return string_; }
8132
8133 String* string_;
8134 uint32_t hash_;
8135};
8136
8137
8138// StringSharedKeys are used as keys in the eval cache.
8139class StringSharedKey : public HashTableKey {
8140 public:
Steve Block1e0659c2011-05-24 12:43:12 +01008141 StringSharedKey(String* source,
8142 SharedFunctionInfo* shared,
8143 StrictModeFlag strict_mode)
8144 : source_(source),
8145 shared_(shared),
8146 strict_mode_(strict_mode) { }
Steve Blocka7e24c12009-10-30 11:49:00 +00008147
8148 bool IsMatch(Object* other) {
8149 if (!other->IsFixedArray()) return false;
8150 FixedArray* pair = FixedArray::cast(other);
8151 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
8152 if (shared != shared_) return false;
Steve Block1e0659c2011-05-24 12:43:12 +01008153 StrictModeFlag strict_mode = static_cast<StrictModeFlag>(
8154 Smi::cast(pair->get(2))->value());
8155 if (strict_mode != strict_mode_) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00008156 String* source = String::cast(pair->get(1));
8157 return source->Equals(source_);
8158 }
8159
8160 static uint32_t StringSharedHashHelper(String* source,
Steve Block1e0659c2011-05-24 12:43:12 +01008161 SharedFunctionInfo* shared,
8162 StrictModeFlag strict_mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008163 uint32_t hash = source->Hash();
8164 if (shared->HasSourceCode()) {
8165 // Instead of using the SharedFunctionInfo pointer in the hash
8166 // code computation, we use a combination of the hash of the
8167 // script source code and the start and end positions. We do
8168 // this to ensure that the cache entries can survive garbage
8169 // collection.
8170 Script* script = Script::cast(shared->script());
8171 hash ^= String::cast(script->source())->Hash();
Steve Block1e0659c2011-05-24 12:43:12 +01008172 if (strict_mode == kStrictMode) hash ^= 0x8000;
Steve Blocka7e24c12009-10-30 11:49:00 +00008173 hash += shared->start_position();
8174 }
8175 return hash;
8176 }
8177
8178 uint32_t Hash() {
Steve Block1e0659c2011-05-24 12:43:12 +01008179 return StringSharedHashHelper(source_, shared_, strict_mode_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008180 }
8181
8182 uint32_t HashForObject(Object* obj) {
8183 FixedArray* pair = FixedArray::cast(obj);
8184 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
8185 String* source = String::cast(pair->get(1));
Steve Block1e0659c2011-05-24 12:43:12 +01008186 StrictModeFlag strict_mode = static_cast<StrictModeFlag>(
8187 Smi::cast(pair->get(2))->value());
8188 return StringSharedHashHelper(source, shared, strict_mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00008189 }
8190
John Reck59135872010-11-02 12:39:01 -07008191 MUST_USE_RESULT MaybeObject* AsObject() {
8192 Object* obj;
Steve Block1e0659c2011-05-24 12:43:12 +01008193 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(3);
John Reck59135872010-11-02 12:39:01 -07008194 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8195 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008196 FixedArray* pair = FixedArray::cast(obj);
8197 pair->set(0, shared_);
8198 pair->set(1, source_);
Steve Block1e0659c2011-05-24 12:43:12 +01008199 pair->set(2, Smi::FromInt(strict_mode_));
Steve Blocka7e24c12009-10-30 11:49:00 +00008200 return pair;
8201 }
8202
8203 private:
8204 String* source_;
8205 SharedFunctionInfo* shared_;
Steve Block1e0659c2011-05-24 12:43:12 +01008206 StrictModeFlag strict_mode_;
Steve Blocka7e24c12009-10-30 11:49:00 +00008207};
8208
8209
8210// RegExpKey carries the source and flags of a regular expression as key.
8211class RegExpKey : public HashTableKey {
8212 public:
8213 RegExpKey(String* string, JSRegExp::Flags flags)
8214 : string_(string),
8215 flags_(Smi::FromInt(flags.value())) { }
8216
Steve Block3ce2e202009-11-05 08:53:23 +00008217 // Rather than storing the key in the hash table, a pointer to the
8218 // stored value is stored where the key should be. IsMatch then
8219 // compares the search key to the found object, rather than comparing
8220 // a key to a key.
Steve Blocka7e24c12009-10-30 11:49:00 +00008221 bool IsMatch(Object* obj) {
8222 FixedArray* val = FixedArray::cast(obj);
8223 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
8224 && (flags_ == val->get(JSRegExp::kFlagsIndex));
8225 }
8226
8227 uint32_t Hash() { return RegExpHash(string_, flags_); }
8228
8229 Object* AsObject() {
8230 // Plain hash maps, which is where regexp keys are used, don't
8231 // use this function.
8232 UNREACHABLE();
8233 return NULL;
8234 }
8235
8236 uint32_t HashForObject(Object* obj) {
8237 FixedArray* val = FixedArray::cast(obj);
8238 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
8239 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
8240 }
8241
8242 static uint32_t RegExpHash(String* string, Smi* flags) {
8243 return string->Hash() + flags->value();
8244 }
8245
8246 String* string_;
8247 Smi* flags_;
8248};
8249
8250// Utf8SymbolKey carries a vector of chars as key.
8251class Utf8SymbolKey : public HashTableKey {
8252 public:
8253 explicit Utf8SymbolKey(Vector<const char> string)
Steve Blockd0582a62009-12-15 09:54:21 +00008254 : string_(string), hash_field_(0) { }
Steve Blocka7e24c12009-10-30 11:49:00 +00008255
8256 bool IsMatch(Object* string) {
8257 return String::cast(string)->IsEqualTo(string_);
8258 }
8259
8260 uint32_t Hash() {
Steve Blockd0582a62009-12-15 09:54:21 +00008261 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00008262 unibrow::Utf8InputBuffer<> buffer(string_.start(),
8263 static_cast<unsigned>(string_.length()));
8264 chars_ = buffer.Length();
Steve Blockd0582a62009-12-15 09:54:21 +00008265 hash_field_ = String::ComputeHashField(&buffer, chars_);
8266 uint32_t result = hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00008267 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
8268 return result;
8269 }
8270
8271 uint32_t HashForObject(Object* other) {
8272 return String::cast(other)->Hash();
8273 }
8274
John Reck59135872010-11-02 12:39:01 -07008275 MaybeObject* AsObject() {
Steve Blockd0582a62009-12-15 09:54:21 +00008276 if (hash_field_ == 0) Hash();
8277 return Heap::AllocateSymbol(string_, chars_, hash_field_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008278 }
8279
8280 Vector<const char> string_;
Steve Blockd0582a62009-12-15 09:54:21 +00008281 uint32_t hash_field_;
Steve Blocka7e24c12009-10-30 11:49:00 +00008282 int chars_; // Caches the number of characters when computing the hash code.
8283};
8284
8285
Steve Block9fac8402011-05-12 15:51:54 +01008286template <typename Char>
8287class SequentialSymbolKey : public HashTableKey {
8288 public:
8289 explicit SequentialSymbolKey(Vector<const Char> string)
8290 : string_(string), hash_field_(0) { }
8291
8292 uint32_t Hash() {
8293 StringHasher hasher(string_.length());
8294
8295 // Very long strings have a trivial hash that doesn't inspect the
8296 // string contents.
8297 if (hasher.has_trivial_hash()) {
8298 hash_field_ = hasher.GetHashField();
8299 } else {
8300 int i = 0;
8301 // Do the iterative array index computation as long as there is a
8302 // chance this is an array index.
8303 while (i < string_.length() && hasher.is_array_index()) {
8304 hasher.AddCharacter(static_cast<uc32>(string_[i]));
8305 i++;
8306 }
8307
8308 // Process the remaining characters without updating the array
8309 // index.
8310 while (i < string_.length()) {
8311 hasher.AddCharacterNoIndex(static_cast<uc32>(string_[i]));
8312 i++;
8313 }
8314 hash_field_ = hasher.GetHashField();
8315 }
8316
8317 uint32_t result = hash_field_ >> String::kHashShift;
8318 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
8319 return result;
8320 }
8321
8322
8323 uint32_t HashForObject(Object* other) {
8324 return String::cast(other)->Hash();
8325 }
8326
8327 Vector<const Char> string_;
8328 uint32_t hash_field_;
8329};
8330
8331
8332
8333class AsciiSymbolKey : public SequentialSymbolKey<char> {
8334 public:
8335 explicit AsciiSymbolKey(Vector<const char> str)
8336 : SequentialSymbolKey<char>(str) { }
8337
8338 bool IsMatch(Object* string) {
8339 return String::cast(string)->IsAsciiEqualTo(string_);
8340 }
8341
8342 MaybeObject* AsObject() {
8343 if (hash_field_ == 0) Hash();
8344 return Heap::AllocateAsciiSymbol(string_, hash_field_);
8345 }
8346};
8347
8348
8349class TwoByteSymbolKey : public SequentialSymbolKey<uc16> {
8350 public:
8351 explicit TwoByteSymbolKey(Vector<const uc16> str)
8352 : SequentialSymbolKey<uc16>(str) { }
8353
8354 bool IsMatch(Object* string) {
8355 return String::cast(string)->IsTwoByteEqualTo(string_);
8356 }
8357
8358 MaybeObject* AsObject() {
8359 if (hash_field_ == 0) Hash();
8360 return Heap::AllocateTwoByteSymbol(string_, hash_field_);
8361 }
8362};
8363
8364
Steve Blocka7e24c12009-10-30 11:49:00 +00008365// SymbolKey carries a string/symbol object as key.
8366class SymbolKey : public HashTableKey {
8367 public:
8368 explicit SymbolKey(String* string) : string_(string) { }
8369
8370 bool IsMatch(Object* string) {
8371 return String::cast(string)->Equals(string_);
8372 }
8373
8374 uint32_t Hash() { return string_->Hash(); }
8375
8376 uint32_t HashForObject(Object* other) {
8377 return String::cast(other)->Hash();
8378 }
8379
John Reck59135872010-11-02 12:39:01 -07008380 MaybeObject* AsObject() {
Leon Clarkef7060e22010-06-03 12:02:55 +01008381 // Attempt to flatten the string, so that symbols will most often
8382 // be flat strings.
8383 string_ = string_->TryFlattenGetString();
Steve Blocka7e24c12009-10-30 11:49:00 +00008384 // Transform string to symbol if possible.
8385 Map* map = Heap::SymbolMapForString(string_);
8386 if (map != NULL) {
8387 string_->set_map(map);
8388 ASSERT(string_->IsSymbol());
8389 return string_;
8390 }
8391 // Otherwise allocate a new symbol.
8392 StringInputBuffer buffer(string_);
8393 return Heap::AllocateInternalSymbol(&buffer,
8394 string_->length(),
Steve Blockd0582a62009-12-15 09:54:21 +00008395 string_->hash_field());
Steve Blocka7e24c12009-10-30 11:49:00 +00008396 }
8397
8398 static uint32_t StringHash(Object* obj) {
8399 return String::cast(obj)->Hash();
8400 }
8401
8402 String* string_;
8403};
8404
8405
8406template<typename Shape, typename Key>
8407void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
8408 IteratePointers(v, 0, kElementsStartOffset);
8409}
8410
8411
8412template<typename Shape, typename Key>
8413void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
8414 IteratePointers(v,
8415 kElementsStartOffset,
8416 kHeaderSize + length() * kPointerSize);
8417}
8418
8419
8420template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008421MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
8422 PretenureFlag pretenure) {
Steve Block6ded16b2010-05-10 14:33:55 +01008423 const int kMinCapacity = 32;
8424 int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
8425 if (capacity < kMinCapacity) {
8426 capacity = kMinCapacity; // Guarantee min capacity.
Leon Clarkee46be812010-01-19 14:06:41 +00008427 } else if (capacity > HashTable::kMaxCapacity) {
8428 return Failure::OutOfMemoryException();
8429 }
8430
John Reck59135872010-11-02 12:39:01 -07008431 Object* obj;
8432 { MaybeObject* maybe_obj =
8433 Heap::AllocateHashTable(EntryToIndex(capacity), pretenure);
8434 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00008435 }
John Reck59135872010-11-02 12:39:01 -07008436 HashTable::cast(obj)->SetNumberOfElements(0);
8437 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
8438 HashTable::cast(obj)->SetCapacity(capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00008439 return obj;
8440}
8441
8442
Leon Clarkee46be812010-01-19 14:06:41 +00008443// Find entry for key otherwise return kNotFound.
Steve Blocka7e24c12009-10-30 11:49:00 +00008444template<typename Shape, typename Key>
8445int HashTable<Shape, Key>::FindEntry(Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008446 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00008447 uint32_t entry = FirstProbe(Shape::Hash(key), capacity);
8448 uint32_t count = 1;
8449 // EnsureCapacity will guarantee the hash table is never full.
8450 while (true) {
8451 Object* element = KeyAt(entry);
8452 if (element->IsUndefined()) break; // Empty entry.
8453 if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
8454 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00008455 }
8456 return kNotFound;
8457}
8458
8459
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008460// Find entry for key otherwise return kNotFound.
8461int StringDictionary::FindEntry(String* key) {
8462 if (!key->IsSymbol()) {
8463 return HashTable<StringDictionaryShape, String*>::FindEntry(key);
8464 }
8465
8466 // Optimized for symbol key. Knowledge of the key type allows:
8467 // 1. Move the check if the key is a symbol out of the loop.
8468 // 2. Avoid comparing hash codes in symbol to symbol comparision.
8469 // 3. Detect a case when a dictionary key is not a symbol but the key is.
8470 // In case of positive result the dictionary key may be replaced by
8471 // the symbol with minimal performance penalty. It gives a chance to
8472 // perform further lookups in code stubs (and significant performance boost
8473 // a certain style of code).
8474
8475 // EnsureCapacity will guarantee the hash table is never full.
8476 uint32_t capacity = Capacity();
8477 uint32_t entry = FirstProbe(key->Hash(), capacity);
8478 uint32_t count = 1;
8479
8480 while (true) {
8481 int index = EntryToIndex(entry);
8482 Object* element = get(index);
8483 if (element->IsUndefined()) break; // Empty entry.
8484 if (key == element) return entry;
8485 if (!element->IsSymbol() &&
8486 !element->IsNull() &&
8487 String::cast(element)->Equals(key)) {
8488 // Replace a non-symbol key by the equivalent symbol for faster further
8489 // lookups.
8490 set(index, key);
8491 return entry;
8492 }
8493 ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
8494 entry = NextProbe(entry, count++, capacity);
8495 }
8496 return kNotFound;
8497}
8498
8499
Steve Blocka7e24c12009-10-30 11:49:00 +00008500template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008501MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008502 int capacity = Capacity();
8503 int nof = NumberOfElements() + n;
Leon Clarkee46be812010-01-19 14:06:41 +00008504 int nod = NumberOfDeletedElements();
8505 // Return if:
8506 // 50% is still free after adding n elements and
8507 // at most 50% of the free elements are deleted elements.
Steve Block6ded16b2010-05-10 14:33:55 +01008508 if (nod <= (capacity - nof) >> 1) {
8509 int needed_free = nof >> 1;
8510 if (nof + needed_free <= capacity) return this;
8511 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008512
Steve Block6ded16b2010-05-10 14:33:55 +01008513 const int kMinCapacityForPretenure = 256;
8514 bool pretenure =
8515 (capacity > kMinCapacityForPretenure) && !Heap::InNewSpace(this);
John Reck59135872010-11-02 12:39:01 -07008516 Object* obj;
8517 { MaybeObject* maybe_obj =
8518 Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
8519 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8520 }
Leon Clarke4515c472010-02-03 11:58:03 +00008521
8522 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00008523 HashTable* table = HashTable::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00008524 WriteBarrierMode mode = table->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00008525
8526 // Copy prefix to new array.
8527 for (int i = kPrefixStartIndex;
8528 i < kPrefixStartIndex + Shape::kPrefixSize;
8529 i++) {
8530 table->set(i, get(i), mode);
8531 }
8532 // Rehash the elements.
8533 for (int i = 0; i < capacity; i++) {
8534 uint32_t from_index = EntryToIndex(i);
8535 Object* k = get(from_index);
8536 if (IsKey(k)) {
8537 uint32_t hash = Shape::HashForObject(key, k);
8538 uint32_t insertion_index =
8539 EntryToIndex(table->FindInsertionEntry(hash));
8540 for (int j = 0; j < Shape::kEntrySize; j++) {
8541 table->set(insertion_index + j, get(from_index + j), mode);
8542 }
8543 }
8544 }
8545 table->SetNumberOfElements(NumberOfElements());
Leon Clarkee46be812010-01-19 14:06:41 +00008546 table->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008547 return table;
8548}
8549
8550
8551template<typename Shape, typename Key>
8552uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
8553 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00008554 uint32_t entry = FirstProbe(hash, capacity);
8555 uint32_t count = 1;
8556 // EnsureCapacity will guarantee the hash table is never full.
8557 while (true) {
8558 Object* element = KeyAt(entry);
8559 if (element->IsUndefined() || element->IsNull()) break;
8560 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00008561 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008562 return entry;
8563}
8564
8565// Force instantiation of template instances class.
8566// Please note this list is compiler dependent.
8567
8568template class HashTable<SymbolTableShape, HashTableKey*>;
8569
8570template class HashTable<CompilationCacheShape, HashTableKey*>;
8571
8572template class HashTable<MapCacheShape, HashTableKey*>;
8573
8574template class Dictionary<StringDictionaryShape, String*>;
8575
8576template class Dictionary<NumberDictionaryShape, uint32_t>;
8577
John Reck59135872010-11-02 12:39:01 -07008578template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
Steve Blocka7e24c12009-10-30 11:49:00 +00008579 int);
8580
John Reck59135872010-11-02 12:39:01 -07008581template MaybeObject* Dictionary<StringDictionaryShape, String*>::Allocate(
Steve Blocka7e24c12009-10-30 11:49:00 +00008582 int);
8583
John Reck59135872010-11-02 12:39:01 -07008584template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
Steve Blocka7e24c12009-10-30 11:49:00 +00008585 uint32_t, Object*);
8586
8587template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
8588 Object*);
8589
8590template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
8591 Object*);
8592
8593template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
8594 FixedArray*, PropertyAttributes);
8595
8596template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
8597 int, JSObject::DeleteMode);
8598
8599template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
8600 int, JSObject::DeleteMode);
8601
8602template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
8603 FixedArray*);
8604
8605template int
8606Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
8607 PropertyAttributes);
8608
John Reck59135872010-11-02 12:39:01 -07008609template MaybeObject* Dictionary<StringDictionaryShape, String*>::Add(
Steve Blocka7e24c12009-10-30 11:49:00 +00008610 String*, Object*, PropertyDetails);
8611
John Reck59135872010-11-02 12:39:01 -07008612template MaybeObject*
Steve Blocka7e24c12009-10-30 11:49:00 +00008613Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
8614
8615template int
8616Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
8617 PropertyAttributes);
8618
John Reck59135872010-11-02 12:39:01 -07008619template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Add(
Steve Blocka7e24c12009-10-30 11:49:00 +00008620 uint32_t, Object*, PropertyDetails);
8621
John Reck59135872010-11-02 12:39:01 -07008622template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::
8623 EnsureCapacity(int, uint32_t);
Steve Blocka7e24c12009-10-30 11:49:00 +00008624
John Reck59135872010-11-02 12:39:01 -07008625template MaybeObject* Dictionary<StringDictionaryShape, String*>::
8626 EnsureCapacity(int, String*);
Steve Blocka7e24c12009-10-30 11:49:00 +00008627
John Reck59135872010-11-02 12:39:01 -07008628template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
Steve Blocka7e24c12009-10-30 11:49:00 +00008629 uint32_t, Object*, PropertyDetails, uint32_t);
8630
John Reck59135872010-11-02 12:39:01 -07008631template MaybeObject* Dictionary<StringDictionaryShape, String*>::AddEntry(
Steve Blocka7e24c12009-10-30 11:49:00 +00008632 String*, Object*, PropertyDetails, uint32_t);
8633
8634template
8635int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
8636
8637template
8638int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
8639
Leon Clarkee46be812010-01-19 14:06:41 +00008640template
8641int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
8642
8643
Steve Blocka7e24c12009-10-30 11:49:00 +00008644// Collates undefined and unexisting elements below limit from position
8645// zero of the elements. The object stays in Dictionary mode.
John Reck59135872010-11-02 12:39:01 -07008646MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008647 ASSERT(HasDictionaryElements());
8648 // Must stay in dictionary mode, either because of requires_slow_elements,
8649 // or because we are not going to sort (and therefore compact) all of the
8650 // elements.
8651 NumberDictionary* dict = element_dictionary();
8652 HeapNumber* result_double = NULL;
8653 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
8654 // Allocate space for result before we start mutating the object.
John Reck59135872010-11-02 12:39:01 -07008655 Object* new_double;
8656 { MaybeObject* maybe_new_double = Heap::AllocateHeapNumber(0.0);
8657 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
8658 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008659 result_double = HeapNumber::cast(new_double);
8660 }
8661
John Reck59135872010-11-02 12:39:01 -07008662 Object* obj;
8663 { MaybeObject* maybe_obj =
8664 NumberDictionary::Allocate(dict->NumberOfElements());
8665 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8666 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008667 NumberDictionary* new_dict = NumberDictionary::cast(obj);
8668
8669 AssertNoAllocation no_alloc;
8670
8671 uint32_t pos = 0;
8672 uint32_t undefs = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01008673 int capacity = dict->Capacity();
Steve Blocka7e24c12009-10-30 11:49:00 +00008674 for (int i = 0; i < capacity; i++) {
8675 Object* k = dict->KeyAt(i);
8676 if (dict->IsKey(k)) {
8677 ASSERT(k->IsNumber());
8678 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
8679 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
8680 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
8681 Object* value = dict->ValueAt(i);
8682 PropertyDetails details = dict->DetailsAt(i);
8683 if (details.type() == CALLBACKS) {
8684 // Bail out and do the sorting of undefineds and array holes in JS.
8685 return Smi::FromInt(-1);
8686 }
8687 uint32_t key = NumberToUint32(k);
John Reck59135872010-11-02 12:39:01 -07008688 // In the following we assert that adding the entry to the new dictionary
8689 // does not cause GC. This is the case because we made sure to allocate
8690 // the dictionary big enough above, so it need not grow.
Steve Blocka7e24c12009-10-30 11:49:00 +00008691 if (key < limit) {
8692 if (value->IsUndefined()) {
8693 undefs++;
8694 } else {
Steve Block1e0659c2011-05-24 12:43:12 +01008695 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
8696 // Adding an entry with the key beyond smi-range requires
8697 // allocation. Bailout.
8698 return Smi::FromInt(-1);
8699 }
John Reck59135872010-11-02 12:39:01 -07008700 new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008701 pos++;
8702 }
8703 } else {
Steve Block1e0659c2011-05-24 12:43:12 +01008704 if (key > static_cast<uint32_t>(Smi::kMaxValue)) {
8705 // Adding an entry with the key beyond smi-range requires
8706 // allocation. Bailout.
8707 return Smi::FromInt(-1);
8708 }
John Reck59135872010-11-02 12:39:01 -07008709 new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008710 }
8711 }
8712 }
8713
8714 uint32_t result = pos;
8715 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
8716 while (undefs > 0) {
Steve Block1e0659c2011-05-24 12:43:12 +01008717 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
8718 // Adding an entry with the key beyond smi-range requires
8719 // allocation. Bailout.
8720 return Smi::FromInt(-1);
8721 }
John Reck59135872010-11-02 12:39:01 -07008722 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details)->
8723 ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008724 pos++;
8725 undefs--;
8726 }
8727
8728 set_elements(new_dict);
8729
8730 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
8731 return Smi::FromInt(static_cast<int>(result));
8732 }
8733
8734 ASSERT_NE(NULL, result_double);
8735 result_double->set_value(static_cast<double>(result));
8736 return result_double;
8737}
8738
8739
8740// Collects all defined (non-hole) and non-undefined (array) elements at
8741// the start of the elements array.
8742// If the object is in dictionary mode, it is converted to fast elements
8743// mode.
John Reck59135872010-11-02 12:39:01 -07008744MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
Steve Block3ce2e202009-11-05 08:53:23 +00008745 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00008746
8747 if (HasDictionaryElements()) {
8748 // Convert to fast elements containing only the existing properties.
8749 // Ordering is irrelevant, since we are going to sort anyway.
8750 NumberDictionary* dict = element_dictionary();
8751 if (IsJSArray() || dict->requires_slow_elements() ||
8752 dict->max_number_key() >= limit) {
8753 return PrepareSlowElementsForSort(limit);
8754 }
8755 // Convert to fast elements.
8756
John Reck59135872010-11-02 12:39:01 -07008757 Object* obj;
8758 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
8759 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8760 }
Steve Block8defd9f2010-07-08 12:39:36 +01008761 Map* new_map = Map::cast(obj);
8762
Steve Blocka7e24c12009-10-30 11:49:00 +00008763 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
John Reck59135872010-11-02 12:39:01 -07008764 Object* new_array;
8765 { MaybeObject* maybe_new_array =
8766 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
8767 if (!maybe_new_array->ToObject(&new_array)) return maybe_new_array;
8768 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008769 FixedArray* fast_elements = FixedArray::cast(new_array);
8770 dict->CopyValuesTo(fast_elements);
Steve Block8defd9f2010-07-08 12:39:36 +01008771
8772 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00008773 set_elements(fast_elements);
Iain Merrick75681382010-08-19 15:07:18 +01008774 } else {
John Reck59135872010-11-02 12:39:01 -07008775 Object* obj;
8776 { MaybeObject* maybe_obj = EnsureWritableFastElements();
8777 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8778 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008779 }
8780 ASSERT(HasFastElements());
8781
8782 // Collect holes at the end, undefined before that and the rest at the
8783 // start, and return the number of non-hole, non-undefined values.
8784
8785 FixedArray* elements = FixedArray::cast(this->elements());
8786 uint32_t elements_length = static_cast<uint32_t>(elements->length());
8787 if (limit > elements_length) {
8788 limit = elements_length ;
8789 }
8790 if (limit == 0) {
8791 return Smi::FromInt(0);
8792 }
8793
8794 HeapNumber* result_double = NULL;
8795 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
8796 // Pessimistically allocate space for return value before
8797 // we start mutating the array.
John Reck59135872010-11-02 12:39:01 -07008798 Object* new_double;
8799 { MaybeObject* maybe_new_double = Heap::AllocateHeapNumber(0.0);
8800 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
8801 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008802 result_double = HeapNumber::cast(new_double);
8803 }
8804
8805 AssertNoAllocation no_alloc;
8806
8807 // Split elements into defined, undefined and the_hole, in that order.
8808 // Only count locations for undefined and the hole, and fill them afterwards.
Leon Clarke4515c472010-02-03 11:58:03 +00008809 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
Steve Blocka7e24c12009-10-30 11:49:00 +00008810 unsigned int undefs = limit;
8811 unsigned int holes = limit;
8812 // Assume most arrays contain no holes and undefined values, so minimize the
8813 // number of stores of non-undefined, non-the-hole values.
8814 for (unsigned int i = 0; i < undefs; i++) {
8815 Object* current = elements->get(i);
8816 if (current->IsTheHole()) {
8817 holes--;
8818 undefs--;
8819 } else if (current->IsUndefined()) {
8820 undefs--;
8821 } else {
8822 continue;
8823 }
8824 // Position i needs to be filled.
8825 while (undefs > i) {
8826 current = elements->get(undefs);
8827 if (current->IsTheHole()) {
8828 holes--;
8829 undefs--;
8830 } else if (current->IsUndefined()) {
8831 undefs--;
8832 } else {
8833 elements->set(i, current, write_barrier);
8834 break;
8835 }
8836 }
8837 }
8838 uint32_t result = undefs;
8839 while (undefs < holes) {
8840 elements->set_undefined(undefs);
8841 undefs++;
8842 }
8843 while (holes < limit) {
8844 elements->set_the_hole(holes);
8845 holes++;
8846 }
8847
8848 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
8849 return Smi::FromInt(static_cast<int>(result));
8850 }
8851 ASSERT_NE(NULL, result_double);
8852 result_double->set_value(static_cast<double>(result));
8853 return result_double;
8854}
8855
8856
8857Object* PixelArray::SetValue(uint32_t index, Object* value) {
8858 uint8_t clamped_value = 0;
8859 if (index < static_cast<uint32_t>(length())) {
8860 if (value->IsSmi()) {
8861 int int_value = Smi::cast(value)->value();
8862 if (int_value < 0) {
8863 clamped_value = 0;
8864 } else if (int_value > 255) {
8865 clamped_value = 255;
8866 } else {
8867 clamped_value = static_cast<uint8_t>(int_value);
8868 }
8869 } else if (value->IsHeapNumber()) {
8870 double double_value = HeapNumber::cast(value)->value();
8871 if (!(double_value > 0)) {
8872 // NaN and less than zero clamp to zero.
8873 clamped_value = 0;
8874 } else if (double_value > 255) {
8875 // Greater than 255 clamp to 255.
8876 clamped_value = 255;
8877 } else {
8878 // Other doubles are rounded to the nearest integer.
8879 clamped_value = static_cast<uint8_t>(double_value + 0.5);
8880 }
8881 } else {
8882 // Clamp undefined to zero (default). All other types have been
8883 // converted to a number type further up in the call chain.
8884 ASSERT(value->IsUndefined());
8885 }
8886 set(index, clamped_value);
8887 }
8888 return Smi::FromInt(clamped_value);
8889}
8890
8891
Steve Block3ce2e202009-11-05 08:53:23 +00008892template<typename ExternalArrayClass, typename ValueType>
John Reck59135872010-11-02 12:39:01 -07008893static MaybeObject* ExternalArrayIntSetter(ExternalArrayClass* receiver,
8894 uint32_t index,
8895 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008896 ValueType cast_value = 0;
8897 if (index < static_cast<uint32_t>(receiver->length())) {
8898 if (value->IsSmi()) {
8899 int int_value = Smi::cast(value)->value();
8900 cast_value = static_cast<ValueType>(int_value);
8901 } else if (value->IsHeapNumber()) {
8902 double double_value = HeapNumber::cast(value)->value();
8903 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
8904 } else {
8905 // Clamp undefined to zero (default). All other types have been
8906 // converted to a number type further up in the call chain.
8907 ASSERT(value->IsUndefined());
8908 }
8909 receiver->set(index, cast_value);
8910 }
8911 return Heap::NumberFromInt32(cast_value);
8912}
8913
8914
John Reck59135872010-11-02 12:39:01 -07008915MaybeObject* ExternalByteArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008916 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
8917 (this, index, value);
8918}
8919
8920
John Reck59135872010-11-02 12:39:01 -07008921MaybeObject* ExternalUnsignedByteArray::SetValue(uint32_t index,
8922 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008923 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
8924 (this, index, value);
8925}
8926
8927
John Reck59135872010-11-02 12:39:01 -07008928MaybeObject* ExternalShortArray::SetValue(uint32_t index,
8929 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008930 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
8931 (this, index, value);
8932}
8933
8934
John Reck59135872010-11-02 12:39:01 -07008935MaybeObject* ExternalUnsignedShortArray::SetValue(uint32_t index,
8936 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008937 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
8938 (this, index, value);
8939}
8940
8941
John Reck59135872010-11-02 12:39:01 -07008942MaybeObject* ExternalIntArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008943 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
8944 (this, index, value);
8945}
8946
8947
John Reck59135872010-11-02 12:39:01 -07008948MaybeObject* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008949 uint32_t cast_value = 0;
8950 if (index < static_cast<uint32_t>(length())) {
8951 if (value->IsSmi()) {
8952 int int_value = Smi::cast(value)->value();
8953 cast_value = static_cast<uint32_t>(int_value);
8954 } else if (value->IsHeapNumber()) {
8955 double double_value = HeapNumber::cast(value)->value();
8956 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
8957 } else {
8958 // Clamp undefined to zero (default). All other types have been
8959 // converted to a number type further up in the call chain.
8960 ASSERT(value->IsUndefined());
8961 }
8962 set(index, cast_value);
8963 }
8964 return Heap::NumberFromUint32(cast_value);
8965}
8966
8967
John Reck59135872010-11-02 12:39:01 -07008968MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008969 float cast_value = 0;
8970 if (index < static_cast<uint32_t>(length())) {
8971 if (value->IsSmi()) {
8972 int int_value = Smi::cast(value)->value();
8973 cast_value = static_cast<float>(int_value);
8974 } else if (value->IsHeapNumber()) {
8975 double double_value = HeapNumber::cast(value)->value();
8976 cast_value = static_cast<float>(double_value);
8977 } else {
8978 // Clamp undefined to zero (default). All other types have been
8979 // converted to a number type further up in the call chain.
8980 ASSERT(value->IsUndefined());
8981 }
8982 set(index, cast_value);
8983 }
8984 return Heap::AllocateHeapNumber(cast_value);
8985}
8986
8987
Ben Murdochb0fe1622011-05-05 13:52:32 +01008988JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008989 ASSERT(!HasFastProperties());
8990 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
Ben Murdochb0fe1622011-05-05 13:52:32 +01008991 return JSGlobalPropertyCell::cast(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00008992}
8993
8994
John Reck59135872010-11-02 12:39:01 -07008995MaybeObject* GlobalObject::EnsurePropertyCell(String* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008996 ASSERT(!HasFastProperties());
8997 int entry = property_dictionary()->FindEntry(name);
8998 if (entry == StringDictionary::kNotFound) {
John Reck59135872010-11-02 12:39:01 -07008999 Object* cell;
9000 { MaybeObject* maybe_cell =
9001 Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
9002 if (!maybe_cell->ToObject(&cell)) return maybe_cell;
9003 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009004 PropertyDetails details(NONE, NORMAL);
9005 details = details.AsDeleted();
John Reck59135872010-11-02 12:39:01 -07009006 Object* dictionary;
9007 { MaybeObject* maybe_dictionary =
9008 property_dictionary()->Add(name, cell, details);
9009 if (!maybe_dictionary->ToObject(&dictionary)) return maybe_dictionary;
9010 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009011 set_properties(StringDictionary::cast(dictionary));
9012 return cell;
9013 } else {
9014 Object* value = property_dictionary()->ValueAt(entry);
9015 ASSERT(value->IsJSGlobalPropertyCell());
9016 return value;
9017 }
9018}
9019
9020
John Reck59135872010-11-02 12:39:01 -07009021MaybeObject* SymbolTable::LookupString(String* string, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009022 SymbolKey key(string);
9023 return LookupKey(&key, s);
9024}
9025
9026
Steve Blockd0582a62009-12-15 09:54:21 +00009027// This class is used for looking up two character strings in the symbol table.
9028// If we don't have a hit we don't want to waste much time so we unroll the
9029// string hash calculation loop here for speed. Doesn't work if the two
9030// characters form a decimal integer, since such strings have a different hash
9031// algorithm.
9032class TwoCharHashTableKey : public HashTableKey {
9033 public:
9034 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
9035 : c1_(c1), c2_(c2) {
9036 // Char 1.
9037 uint32_t hash = c1 + (c1 << 10);
9038 hash ^= hash >> 6;
9039 // Char 2.
9040 hash += c2;
9041 hash += hash << 10;
9042 hash ^= hash >> 6;
9043 // GetHash.
9044 hash += hash << 3;
9045 hash ^= hash >> 11;
9046 hash += hash << 15;
9047 if (hash == 0) hash = 27;
9048#ifdef DEBUG
9049 StringHasher hasher(2);
9050 hasher.AddCharacter(c1);
9051 hasher.AddCharacter(c2);
9052 // If this assert fails then we failed to reproduce the two-character
9053 // version of the string hashing algorithm above. One reason could be
9054 // that we were passed two digits as characters, since the hash
9055 // algorithm is different in that case.
9056 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
9057#endif
9058 hash_ = hash;
9059 }
9060
9061 bool IsMatch(Object* o) {
9062 if (!o->IsString()) return false;
9063 String* other = String::cast(o);
9064 if (other->length() != 2) return false;
9065 if (other->Get(0) != c1_) return false;
9066 return other->Get(1) == c2_;
9067 }
9068
9069 uint32_t Hash() { return hash_; }
9070 uint32_t HashForObject(Object* key) {
9071 if (!key->IsString()) return 0;
9072 return String::cast(key)->Hash();
9073 }
9074
9075 Object* AsObject() {
9076 // The TwoCharHashTableKey is only used for looking in the symbol
9077 // table, not for adding to it.
9078 UNREACHABLE();
9079 return NULL;
9080 }
9081 private:
9082 uint32_t c1_;
9083 uint32_t c2_;
9084 uint32_t hash_;
9085};
9086
9087
Steve Blocka7e24c12009-10-30 11:49:00 +00009088bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
9089 SymbolKey key(string);
9090 int entry = FindEntry(&key);
9091 if (entry == kNotFound) {
9092 return false;
9093 } else {
9094 String* result = String::cast(KeyAt(entry));
9095 ASSERT(StringShape(result).IsSymbol());
9096 *symbol = result;
9097 return true;
9098 }
9099}
9100
9101
Steve Blockd0582a62009-12-15 09:54:21 +00009102bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
9103 uint32_t c2,
9104 String** symbol) {
9105 TwoCharHashTableKey key(c1, c2);
9106 int entry = FindEntry(&key);
9107 if (entry == kNotFound) {
9108 return false;
9109 } else {
9110 String* result = String::cast(KeyAt(entry));
9111 ASSERT(StringShape(result).IsSymbol());
9112 *symbol = result;
9113 return true;
9114 }
9115}
9116
9117
John Reck59135872010-11-02 12:39:01 -07009118MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009119 Utf8SymbolKey key(str);
9120 return LookupKey(&key, s);
9121}
9122
9123
Steve Block9fac8402011-05-12 15:51:54 +01009124MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str,
9125 Object** s) {
9126 AsciiSymbolKey key(str);
9127 return LookupKey(&key, s);
9128}
9129
9130
9131MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str,
9132 Object** s) {
9133 TwoByteSymbolKey key(str);
9134 return LookupKey(&key, s);
9135}
9136
John Reck59135872010-11-02 12:39:01 -07009137MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009138 int entry = FindEntry(key);
9139
9140 // Symbol already in table.
9141 if (entry != kNotFound) {
9142 *s = KeyAt(entry);
9143 return this;
9144 }
9145
9146 // Adding new symbol. Grow table if needed.
John Reck59135872010-11-02 12:39:01 -07009147 Object* obj;
9148 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
9149 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9150 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009151
9152 // Create symbol object.
John Reck59135872010-11-02 12:39:01 -07009153 Object* symbol;
9154 { MaybeObject* maybe_symbol = key->AsObject();
9155 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
9156 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009157
9158 // If the symbol table grew as part of EnsureCapacity, obj is not
9159 // the current symbol table and therefore we cannot use
9160 // SymbolTable::cast here.
9161 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
9162
9163 // Add the new symbol and return it along with the symbol table.
9164 entry = table->FindInsertionEntry(key->Hash());
9165 table->set(EntryToIndex(entry), symbol);
9166 table->ElementAdded();
9167 *s = symbol;
9168 return table;
9169}
9170
9171
9172Object* CompilationCacheTable::Lookup(String* src) {
9173 StringKey key(src);
9174 int entry = FindEntry(&key);
9175 if (entry == kNotFound) return Heap::undefined_value();
9176 return get(EntryToIndex(entry) + 1);
9177}
9178
9179
Steve Block1e0659c2011-05-24 12:43:12 +01009180Object* CompilationCacheTable::LookupEval(String* src,
9181 Context* context,
9182 StrictModeFlag strict_mode) {
9183 StringSharedKey key(src, context->closure()->shared(), strict_mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00009184 int entry = FindEntry(&key);
9185 if (entry == kNotFound) return Heap::undefined_value();
9186 return get(EntryToIndex(entry) + 1);
9187}
9188
9189
9190Object* CompilationCacheTable::LookupRegExp(String* src,
9191 JSRegExp::Flags flags) {
9192 RegExpKey key(src, flags);
9193 int entry = FindEntry(&key);
9194 if (entry == kNotFound) return Heap::undefined_value();
9195 return get(EntryToIndex(entry) + 1);
9196}
9197
9198
John Reck59135872010-11-02 12:39:01 -07009199MaybeObject* CompilationCacheTable::Put(String* src, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009200 StringKey key(src);
John Reck59135872010-11-02 12:39:01 -07009201 Object* obj;
9202 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
9203 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9204 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009205
9206 CompilationCacheTable* cache =
9207 reinterpret_cast<CompilationCacheTable*>(obj);
9208 int entry = cache->FindInsertionEntry(key.Hash());
9209 cache->set(EntryToIndex(entry), src);
9210 cache->set(EntryToIndex(entry) + 1, value);
9211 cache->ElementAdded();
9212 return cache;
9213}
9214
9215
John Reck59135872010-11-02 12:39:01 -07009216MaybeObject* CompilationCacheTable::PutEval(String* src,
9217 Context* context,
Steve Block1e0659c2011-05-24 12:43:12 +01009218 SharedFunctionInfo* value) {
9219 StringSharedKey key(src,
9220 context->closure()->shared(),
9221 value->strict_mode() ? kStrictMode : kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -07009222 Object* obj;
9223 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
9224 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9225 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009226
9227 CompilationCacheTable* cache =
9228 reinterpret_cast<CompilationCacheTable*>(obj);
9229 int entry = cache->FindInsertionEntry(key.Hash());
9230
John Reck59135872010-11-02 12:39:01 -07009231 Object* k;
9232 { MaybeObject* maybe_k = key.AsObject();
9233 if (!maybe_k->ToObject(&k)) return maybe_k;
9234 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009235
9236 cache->set(EntryToIndex(entry), k);
9237 cache->set(EntryToIndex(entry) + 1, value);
9238 cache->ElementAdded();
9239 return cache;
9240}
9241
9242
John Reck59135872010-11-02 12:39:01 -07009243MaybeObject* CompilationCacheTable::PutRegExp(String* src,
9244 JSRegExp::Flags flags,
9245 FixedArray* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009246 RegExpKey key(src, flags);
John Reck59135872010-11-02 12:39:01 -07009247 Object* obj;
9248 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
9249 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9250 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009251
9252 CompilationCacheTable* cache =
9253 reinterpret_cast<CompilationCacheTable*>(obj);
9254 int entry = cache->FindInsertionEntry(key.Hash());
Steve Block3ce2e202009-11-05 08:53:23 +00009255 // We store the value in the key slot, and compare the search key
9256 // to the stored value with a custon IsMatch function during lookups.
Steve Blocka7e24c12009-10-30 11:49:00 +00009257 cache->set(EntryToIndex(entry), value);
9258 cache->set(EntryToIndex(entry) + 1, value);
9259 cache->ElementAdded();
9260 return cache;
9261}
9262
9263
Ben Murdochb0fe1622011-05-05 13:52:32 +01009264void CompilationCacheTable::Remove(Object* value) {
9265 for (int entry = 0, size = Capacity(); entry < size; entry++) {
9266 int entry_index = EntryToIndex(entry);
9267 int value_index = entry_index + 1;
9268 if (get(value_index) == value) {
9269 fast_set(this, entry_index, Heap::null_value());
9270 fast_set(this, value_index, Heap::null_value());
9271 ElementRemoved();
9272 }
9273 }
9274 return;
9275}
9276
9277
Steve Blocka7e24c12009-10-30 11:49:00 +00009278// SymbolsKey used for HashTable where key is array of symbols.
9279class SymbolsKey : public HashTableKey {
9280 public:
9281 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
9282
9283 bool IsMatch(Object* symbols) {
9284 FixedArray* o = FixedArray::cast(symbols);
9285 int len = symbols_->length();
9286 if (o->length() != len) return false;
9287 for (int i = 0; i < len; i++) {
9288 if (o->get(i) != symbols_->get(i)) return false;
9289 }
9290 return true;
9291 }
9292
9293 uint32_t Hash() { return HashForObject(symbols_); }
9294
9295 uint32_t HashForObject(Object* obj) {
9296 FixedArray* symbols = FixedArray::cast(obj);
9297 int len = symbols->length();
9298 uint32_t hash = 0;
9299 for (int i = 0; i < len; i++) {
9300 hash ^= String::cast(symbols->get(i))->Hash();
9301 }
9302 return hash;
9303 }
9304
9305 Object* AsObject() { return symbols_; }
9306
9307 private:
9308 FixedArray* symbols_;
9309};
9310
9311
9312Object* MapCache::Lookup(FixedArray* array) {
9313 SymbolsKey key(array);
9314 int entry = FindEntry(&key);
9315 if (entry == kNotFound) return Heap::undefined_value();
9316 return get(EntryToIndex(entry) + 1);
9317}
9318
9319
John Reck59135872010-11-02 12:39:01 -07009320MaybeObject* MapCache::Put(FixedArray* array, Map* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009321 SymbolsKey key(array);
John Reck59135872010-11-02 12:39:01 -07009322 Object* obj;
9323 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
9324 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9325 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009326
9327 MapCache* cache = reinterpret_cast<MapCache*>(obj);
9328 int entry = cache->FindInsertionEntry(key.Hash());
9329 cache->set(EntryToIndex(entry), array);
9330 cache->set(EntryToIndex(entry) + 1, value);
9331 cache->ElementAdded();
9332 return cache;
9333}
9334
9335
9336template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009337MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
9338 Object* obj;
9339 { MaybeObject* maybe_obj =
9340 HashTable<Shape, Key>::Allocate(at_least_space_for);
9341 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00009342 }
John Reck59135872010-11-02 12:39:01 -07009343 // Initialize the next enumeration index.
9344 Dictionary<Shape, Key>::cast(obj)->
9345 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
Steve Blocka7e24c12009-10-30 11:49:00 +00009346 return obj;
9347}
9348
9349
9350template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009351MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
Steve Blocka7e24c12009-10-30 11:49:00 +00009352 int length = HashTable<Shape, Key>::NumberOfElements();
9353
9354 // Allocate and initialize iteration order array.
John Reck59135872010-11-02 12:39:01 -07009355 Object* obj;
9356 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(length);
9357 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9358 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009359 FixedArray* iteration_order = FixedArray::cast(obj);
9360 for (int i = 0; i < length; i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00009361 iteration_order->set(i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00009362 }
9363
9364 // Allocate array with enumeration order.
John Reck59135872010-11-02 12:39:01 -07009365 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(length);
9366 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9367 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009368 FixedArray* enumeration_order = FixedArray::cast(obj);
9369
9370 // Fill the enumeration order array with property details.
9371 int capacity = HashTable<Shape, Key>::Capacity();
9372 int pos = 0;
9373 for (int i = 0; i < capacity; i++) {
9374 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
Leon Clarke4515c472010-02-03 11:58:03 +00009375 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00009376 }
9377 }
9378
9379 // Sort the arrays wrt. enumeration order.
9380 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
9381
9382 // Overwrite the enumeration_order with the enumeration indices.
9383 for (int i = 0; i < length; i++) {
9384 int index = Smi::cast(iteration_order->get(i))->value();
9385 int enum_index = PropertyDetails::kInitialIndex + i;
Leon Clarke4515c472010-02-03 11:58:03 +00009386 enumeration_order->set(index, Smi::FromInt(enum_index));
Steve Blocka7e24c12009-10-30 11:49:00 +00009387 }
9388
9389 // Update the dictionary with new indices.
9390 capacity = HashTable<Shape, Key>::Capacity();
9391 pos = 0;
9392 for (int i = 0; i < capacity; i++) {
9393 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
9394 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
9395 PropertyDetails details = DetailsAt(i);
9396 PropertyDetails new_details =
9397 PropertyDetails(details.attributes(), details.type(), enum_index);
9398 DetailsAtPut(i, new_details);
9399 }
9400 }
9401
9402 // Set the next enumeration index.
9403 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
9404 return this;
9405}
9406
9407template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009408MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009409 // Check whether there are enough enumeration indices to add n elements.
9410 if (Shape::kIsEnumerable &&
9411 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
9412 // If not, we generate new indices for the properties.
John Reck59135872010-11-02 12:39:01 -07009413 Object* result;
9414 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
9415 if (!maybe_result->ToObject(&result)) return maybe_result;
9416 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009417 }
9418 return HashTable<Shape, Key>::EnsureCapacity(n, key);
9419}
9420
9421
9422void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
9423 // Do nothing if the interval [from, to) is empty.
9424 if (from >= to) return;
9425
9426 int removed_entries = 0;
9427 Object* sentinel = Heap::null_value();
9428 int capacity = Capacity();
9429 for (int i = 0; i < capacity; i++) {
9430 Object* key = KeyAt(i);
9431 if (key->IsNumber()) {
9432 uint32_t number = static_cast<uint32_t>(key->Number());
9433 if (from <= number && number < to) {
9434 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
9435 removed_entries++;
9436 }
9437 }
9438 }
9439
9440 // Update the number of elements.
Leon Clarkee46be812010-01-19 14:06:41 +00009441 ElementsRemoved(removed_entries);
Steve Blocka7e24c12009-10-30 11:49:00 +00009442}
9443
9444
9445template<typename Shape, typename Key>
9446Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
9447 JSObject::DeleteMode mode) {
9448 PropertyDetails details = DetailsAt(entry);
9449 // Ignore attributes if forcing a deletion.
Ben Murdoche0cee9b2011-05-25 10:26:03 +01009450 if (details.IsDontDelete() && mode != JSObject::FORCE_DELETION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009451 return Heap::false_value();
9452 }
9453 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
9454 HashTable<Shape, Key>::ElementRemoved();
9455 return Heap::true_value();
9456}
9457
9458
9459template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009460MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009461 int entry = this->FindEntry(key);
Steve Blocka7e24c12009-10-30 11:49:00 +00009462
9463 // If the entry is present set the value;
9464 if (entry != Dictionary<Shape, Key>::kNotFound) {
9465 ValueAtPut(entry, value);
9466 return this;
9467 }
9468
9469 // Check whether the dictionary should be extended.
John Reck59135872010-11-02 12:39:01 -07009470 Object* obj;
9471 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
9472 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9473 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009474
John Reck59135872010-11-02 12:39:01 -07009475 Object* k;
9476 { MaybeObject* maybe_k = Shape::AsObject(key);
9477 if (!maybe_k->ToObject(&k)) return maybe_k;
9478 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009479 PropertyDetails details = PropertyDetails(NONE, NORMAL);
9480 return Dictionary<Shape, Key>::cast(obj)->
9481 AddEntry(key, value, details, Shape::Hash(key));
9482}
9483
9484
9485template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009486MaybeObject* Dictionary<Shape, Key>::Add(Key key,
9487 Object* value,
9488 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009489 // Valdate key is absent.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009490 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
Steve Blocka7e24c12009-10-30 11:49:00 +00009491 // Check whether the dictionary should be extended.
John Reck59135872010-11-02 12:39:01 -07009492 Object* obj;
9493 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
9494 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9495 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009496 return Dictionary<Shape, Key>::cast(obj)->
9497 AddEntry(key, value, details, Shape::Hash(key));
9498}
9499
9500
9501// Add a key, value pair to the dictionary.
9502template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009503MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key,
9504 Object* value,
9505 PropertyDetails details,
9506 uint32_t hash) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009507 // Compute the key object.
John Reck59135872010-11-02 12:39:01 -07009508 Object* k;
9509 { MaybeObject* maybe_k = Shape::AsObject(key);
9510 if (!maybe_k->ToObject(&k)) return maybe_k;
9511 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009512
9513 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
9514 // Insert element at empty or deleted entry
9515 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
9516 // Assign an enumeration index to the property and update
9517 // SetNextEnumerationIndex.
9518 int index = NextEnumerationIndex();
9519 details = PropertyDetails(details.attributes(), details.type(), index);
9520 SetNextEnumerationIndex(index + 1);
9521 }
9522 SetEntry(entry, k, value, details);
9523 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
9524 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
9525 HashTable<Shape, Key>::ElementAdded();
9526 return this;
9527}
9528
9529
9530void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
9531 // If the dictionary requires slow elements an element has already
9532 // been added at a high index.
9533 if (requires_slow_elements()) return;
9534 // Check if this index is high enough that we should require slow
9535 // elements.
9536 if (key > kRequiresSlowElementsLimit) {
9537 set_requires_slow_elements();
9538 return;
9539 }
9540 // Update max key value.
9541 Object* max_index_object = get(kMaxNumberKeyIndex);
9542 if (!max_index_object->IsSmi() || max_number_key() < key) {
9543 FixedArray::set(kMaxNumberKeyIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00009544 Smi::FromInt(key << kRequiresSlowElementsTagSize));
Steve Blocka7e24c12009-10-30 11:49:00 +00009545 }
9546}
9547
9548
John Reck59135872010-11-02 12:39:01 -07009549MaybeObject* NumberDictionary::AddNumberEntry(uint32_t key,
9550 Object* value,
9551 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009552 UpdateMaxNumberKey(key);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009553 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
Steve Blocka7e24c12009-10-30 11:49:00 +00009554 return Add(key, value, details);
9555}
9556
9557
John Reck59135872010-11-02 12:39:01 -07009558MaybeObject* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009559 UpdateMaxNumberKey(key);
9560 return AtPut(key, value);
9561}
9562
9563
John Reck59135872010-11-02 12:39:01 -07009564MaybeObject* NumberDictionary::Set(uint32_t key,
9565 Object* value,
9566 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009567 int entry = FindEntry(key);
9568 if (entry == kNotFound) return AddNumberEntry(key, value, details);
9569 // Preserve enumeration index.
9570 details = PropertyDetails(details.attributes(),
9571 details.type(),
9572 DetailsAt(entry).index());
John Reck59135872010-11-02 12:39:01 -07009573 MaybeObject* maybe_object_key = NumberDictionaryShape::AsObject(key);
9574 Object* object_key;
9575 if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
Ben Murdochf87a2032010-10-22 12:50:53 +01009576 SetEntry(entry, object_key, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00009577 return this;
9578}
9579
9580
9581
9582template<typename Shape, typename Key>
9583int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
9584 PropertyAttributes filter) {
9585 int capacity = HashTable<Shape, Key>::Capacity();
9586 int result = 0;
9587 for (int i = 0; i < capacity; i++) {
9588 Object* k = HashTable<Shape, Key>::KeyAt(i);
9589 if (HashTable<Shape, Key>::IsKey(k)) {
9590 PropertyDetails details = DetailsAt(i);
9591 if (details.IsDeleted()) continue;
9592 PropertyAttributes attr = details.attributes();
9593 if ((attr & filter) == 0) result++;
9594 }
9595 }
9596 return result;
9597}
9598
9599
9600template<typename Shape, typename Key>
9601int Dictionary<Shape, Key>::NumberOfEnumElements() {
9602 return NumberOfElementsFilterAttributes(
9603 static_cast<PropertyAttributes>(DONT_ENUM));
9604}
9605
9606
9607template<typename Shape, typename Key>
9608void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
9609 PropertyAttributes filter) {
9610 ASSERT(storage->length() >= NumberOfEnumElements());
9611 int capacity = HashTable<Shape, Key>::Capacity();
9612 int index = 0;
9613 for (int i = 0; i < capacity; i++) {
9614 Object* k = HashTable<Shape, Key>::KeyAt(i);
9615 if (HashTable<Shape, Key>::IsKey(k)) {
9616 PropertyDetails details = DetailsAt(i);
9617 if (details.IsDeleted()) continue;
9618 PropertyAttributes attr = details.attributes();
9619 if ((attr & filter) == 0) storage->set(index++, k);
9620 }
9621 }
9622 storage->SortPairs(storage, index);
9623 ASSERT(storage->length() >= index);
9624}
9625
9626
9627void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
9628 FixedArray* sort_array) {
9629 ASSERT(storage->length() >= NumberOfEnumElements());
9630 int capacity = Capacity();
9631 int index = 0;
9632 for (int i = 0; i < capacity; i++) {
9633 Object* k = KeyAt(i);
9634 if (IsKey(k)) {
9635 PropertyDetails details = DetailsAt(i);
9636 if (details.IsDeleted() || details.IsDontEnum()) continue;
9637 storage->set(index, k);
Leon Clarke4515c472010-02-03 11:58:03 +00009638 sort_array->set(index, Smi::FromInt(details.index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00009639 index++;
9640 }
9641 }
9642 storage->SortPairs(sort_array, sort_array->length());
9643 ASSERT(storage->length() >= index);
9644}
9645
9646
9647template<typename Shape, typename Key>
9648void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
9649 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
9650 static_cast<PropertyAttributes>(NONE)));
9651 int capacity = HashTable<Shape, Key>::Capacity();
9652 int index = 0;
9653 for (int i = 0; i < capacity; i++) {
9654 Object* k = HashTable<Shape, Key>::KeyAt(i);
9655 if (HashTable<Shape, Key>::IsKey(k)) {
9656 PropertyDetails details = DetailsAt(i);
9657 if (details.IsDeleted()) continue;
9658 storage->set(index++, k);
9659 }
9660 }
9661 ASSERT(storage->length() >= index);
9662}
9663
9664
9665// Backwards lookup (slow).
9666template<typename Shape, typename Key>
9667Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
9668 int capacity = HashTable<Shape, Key>::Capacity();
9669 for (int i = 0; i < capacity; i++) {
9670 Object* k = HashTable<Shape, Key>::KeyAt(i);
9671 if (Dictionary<Shape, Key>::IsKey(k)) {
9672 Object* e = ValueAt(i);
9673 if (e->IsJSGlobalPropertyCell()) {
9674 e = JSGlobalPropertyCell::cast(e)->value();
9675 }
9676 if (e == value) return k;
9677 }
9678 }
9679 return Heap::undefined_value();
9680}
9681
9682
John Reck59135872010-11-02 12:39:01 -07009683MaybeObject* StringDictionary::TransformPropertiesToFastFor(
Steve Blocka7e24c12009-10-30 11:49:00 +00009684 JSObject* obj, int unused_property_fields) {
9685 // Make sure we preserve dictionary representation if there are too many
9686 // descriptors.
9687 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
9688
9689 // Figure out if it is necessary to generate new enumeration indices.
9690 int max_enumeration_index =
9691 NextEnumerationIndex() +
9692 (DescriptorArray::kMaxNumberOfDescriptors -
9693 NumberOfElements());
9694 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
John Reck59135872010-11-02 12:39:01 -07009695 Object* result;
9696 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
9697 if (!maybe_result->ToObject(&result)) return maybe_result;
9698 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009699 }
9700
9701 int instance_descriptor_length = 0;
9702 int number_of_fields = 0;
9703
9704 // Compute the length of the instance descriptor.
9705 int capacity = Capacity();
9706 for (int i = 0; i < capacity; i++) {
9707 Object* k = KeyAt(i);
9708 if (IsKey(k)) {
9709 Object* value = ValueAt(i);
9710 PropertyType type = DetailsAt(i).type();
9711 ASSERT(type != FIELD);
9712 instance_descriptor_length++;
Leon Clarkee46be812010-01-19 14:06:41 +00009713 if (type == NORMAL &&
9714 (!value->IsJSFunction() || Heap::InNewSpace(value))) {
9715 number_of_fields += 1;
9716 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009717 }
9718 }
9719
9720 // Allocate the instance descriptor.
John Reck59135872010-11-02 12:39:01 -07009721 Object* descriptors_unchecked;
9722 { MaybeObject* maybe_descriptors_unchecked =
9723 DescriptorArray::Allocate(instance_descriptor_length);
9724 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
9725 return maybe_descriptors_unchecked;
9726 }
9727 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009728 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
9729
9730 int inobject_props = obj->map()->inobject_properties();
9731 int number_of_allocated_fields =
9732 number_of_fields + unused_property_fields - inobject_props;
Ben Murdochf87a2032010-10-22 12:50:53 +01009733 if (number_of_allocated_fields < 0) {
9734 // There is enough inobject space for all fields (including unused).
9735 number_of_allocated_fields = 0;
9736 unused_property_fields = inobject_props - number_of_fields;
9737 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009738
9739 // Allocate the fixed array for the fields.
John Reck59135872010-11-02 12:39:01 -07009740 Object* fields;
9741 { MaybeObject* maybe_fields =
9742 Heap::AllocateFixedArray(number_of_allocated_fields);
9743 if (!maybe_fields->ToObject(&fields)) return maybe_fields;
9744 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009745
9746 // Fill in the instance descriptor and the fields.
9747 int next_descriptor = 0;
9748 int current_offset = 0;
9749 for (int i = 0; i < capacity; i++) {
9750 Object* k = KeyAt(i);
9751 if (IsKey(k)) {
9752 Object* value = ValueAt(i);
9753 // Ensure the key is a symbol before writing into the instance descriptor.
John Reck59135872010-11-02 12:39:01 -07009754 Object* key;
9755 { MaybeObject* maybe_key = Heap::LookupSymbol(String::cast(k));
9756 if (!maybe_key->ToObject(&key)) return maybe_key;
9757 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009758 PropertyDetails details = DetailsAt(i);
9759 PropertyType type = details.type();
9760
Leon Clarkee46be812010-01-19 14:06:41 +00009761 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009762 ConstantFunctionDescriptor d(String::cast(key),
9763 JSFunction::cast(value),
9764 details.attributes(),
9765 details.index());
9766 descriptors->Set(next_descriptor++, &d);
9767 } else if (type == NORMAL) {
9768 if (current_offset < inobject_props) {
9769 obj->InObjectPropertyAtPut(current_offset,
9770 value,
9771 UPDATE_WRITE_BARRIER);
9772 } else {
9773 int offset = current_offset - inobject_props;
9774 FixedArray::cast(fields)->set(offset, value);
9775 }
9776 FieldDescriptor d(String::cast(key),
9777 current_offset++,
9778 details.attributes(),
9779 details.index());
9780 descriptors->Set(next_descriptor++, &d);
9781 } else if (type == CALLBACKS) {
9782 CallbacksDescriptor d(String::cast(key),
9783 value,
9784 details.attributes(),
9785 details.index());
9786 descriptors->Set(next_descriptor++, &d);
9787 } else {
9788 UNREACHABLE();
9789 }
9790 }
9791 }
9792 ASSERT(current_offset == number_of_fields);
9793
9794 descriptors->Sort();
9795 // Allocate new map.
John Reck59135872010-11-02 12:39:01 -07009796 Object* new_map;
9797 { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
9798 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
9799 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009800
9801 // Transform the object.
9802 obj->set_map(Map::cast(new_map));
9803 obj->map()->set_instance_descriptors(descriptors);
9804 obj->map()->set_unused_property_fields(unused_property_fields);
9805
9806 obj->set_properties(FixedArray::cast(fields));
9807 ASSERT(obj->IsJSObject());
9808
9809 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
9810 // Check that it really works.
9811 ASSERT(obj->HasFastProperties());
9812
9813 return obj;
9814}
9815
9816
9817#ifdef ENABLE_DEBUGGER_SUPPORT
9818// Check if there is a break point at this code position.
9819bool DebugInfo::HasBreakPoint(int code_position) {
9820 // Get the break point info object for this code position.
9821 Object* break_point_info = GetBreakPointInfo(code_position);
9822
9823 // If there is no break point info object or no break points in the break
9824 // point info object there is no break point at this code position.
9825 if (break_point_info->IsUndefined()) return false;
9826 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
9827}
9828
9829
9830// Get the break point info object for this code position.
9831Object* DebugInfo::GetBreakPointInfo(int code_position) {
9832 // Find the index of the break point info object for this code position.
9833 int index = GetBreakPointInfoIndex(code_position);
9834
9835 // Return the break point info object if any.
9836 if (index == kNoBreakPointInfo) return Heap::undefined_value();
9837 return BreakPointInfo::cast(break_points()->get(index));
9838}
9839
9840
9841// Clear a break point at the specified code position.
9842void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
9843 int code_position,
9844 Handle<Object> break_point_object) {
9845 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
9846 if (break_point_info->IsUndefined()) return;
9847 BreakPointInfo::ClearBreakPoint(
9848 Handle<BreakPointInfo>::cast(break_point_info),
9849 break_point_object);
9850}
9851
9852
9853void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
9854 int code_position,
9855 int source_position,
9856 int statement_position,
9857 Handle<Object> break_point_object) {
9858 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
9859 if (!break_point_info->IsUndefined()) {
9860 BreakPointInfo::SetBreakPoint(
9861 Handle<BreakPointInfo>::cast(break_point_info),
9862 break_point_object);
9863 return;
9864 }
9865
9866 // Adding a new break point for a code position which did not have any
9867 // break points before. Try to find a free slot.
9868 int index = kNoBreakPointInfo;
9869 for (int i = 0; i < debug_info->break_points()->length(); i++) {
9870 if (debug_info->break_points()->get(i)->IsUndefined()) {
9871 index = i;
9872 break;
9873 }
9874 }
9875 if (index == kNoBreakPointInfo) {
9876 // No free slot - extend break point info array.
9877 Handle<FixedArray> old_break_points =
9878 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
Steve Blocka7e24c12009-10-30 11:49:00 +00009879 Handle<FixedArray> new_break_points =
Kristian Monsen0d5e1162010-09-30 15:31:59 +01009880 Factory::NewFixedArray(old_break_points->length() +
9881 Debug::kEstimatedNofBreakPointsInFunction);
9882
9883 debug_info->set_break_points(*new_break_points);
Steve Blocka7e24c12009-10-30 11:49:00 +00009884 for (int i = 0; i < old_break_points->length(); i++) {
9885 new_break_points->set(i, old_break_points->get(i));
9886 }
9887 index = old_break_points->length();
9888 }
9889 ASSERT(index != kNoBreakPointInfo);
9890
9891 // Allocate new BreakPointInfo object and set the break point.
9892 Handle<BreakPointInfo> new_break_point_info =
9893 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
9894 new_break_point_info->set_code_position(Smi::FromInt(code_position));
9895 new_break_point_info->set_source_position(Smi::FromInt(source_position));
9896 new_break_point_info->
9897 set_statement_position(Smi::FromInt(statement_position));
9898 new_break_point_info->set_break_point_objects(Heap::undefined_value());
9899 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
9900 debug_info->break_points()->set(index, *new_break_point_info);
9901}
9902
9903
9904// Get the break point objects for a code position.
9905Object* DebugInfo::GetBreakPointObjects(int code_position) {
9906 Object* break_point_info = GetBreakPointInfo(code_position);
9907 if (break_point_info->IsUndefined()) {
9908 return Heap::undefined_value();
9909 }
9910 return BreakPointInfo::cast(break_point_info)->break_point_objects();
9911}
9912
9913
9914// Get the total number of break points.
9915int DebugInfo::GetBreakPointCount() {
9916 if (break_points()->IsUndefined()) return 0;
9917 int count = 0;
9918 for (int i = 0; i < break_points()->length(); i++) {
9919 if (!break_points()->get(i)->IsUndefined()) {
9920 BreakPointInfo* break_point_info =
9921 BreakPointInfo::cast(break_points()->get(i));
9922 count += break_point_info->GetBreakPointCount();
9923 }
9924 }
9925 return count;
9926}
9927
9928
9929Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
9930 Handle<Object> break_point_object) {
9931 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
9932 for (int i = 0; i < debug_info->break_points()->length(); i++) {
9933 if (!debug_info->break_points()->get(i)->IsUndefined()) {
9934 Handle<BreakPointInfo> break_point_info =
9935 Handle<BreakPointInfo>(BreakPointInfo::cast(
9936 debug_info->break_points()->get(i)));
9937 if (BreakPointInfo::HasBreakPointObject(break_point_info,
9938 break_point_object)) {
9939 return *break_point_info;
9940 }
9941 }
9942 }
9943 return Heap::undefined_value();
9944}
9945
9946
9947// Find the index of the break point info object for the specified code
9948// position.
9949int DebugInfo::GetBreakPointInfoIndex(int code_position) {
9950 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
9951 for (int i = 0; i < break_points()->length(); i++) {
9952 if (!break_points()->get(i)->IsUndefined()) {
9953 BreakPointInfo* break_point_info =
9954 BreakPointInfo::cast(break_points()->get(i));
9955 if (break_point_info->code_position()->value() == code_position) {
9956 return i;
9957 }
9958 }
9959 }
9960 return kNoBreakPointInfo;
9961}
9962
9963
9964// Remove the specified break point object.
9965void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
9966 Handle<Object> break_point_object) {
9967 // If there are no break points just ignore.
9968 if (break_point_info->break_point_objects()->IsUndefined()) return;
9969 // If there is a single break point clear it if it is the same.
9970 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9971 if (break_point_info->break_point_objects() == *break_point_object) {
9972 break_point_info->set_break_point_objects(Heap::undefined_value());
9973 }
9974 return;
9975 }
9976 // If there are multiple break points shrink the array
9977 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
9978 Handle<FixedArray> old_array =
9979 Handle<FixedArray>(
9980 FixedArray::cast(break_point_info->break_point_objects()));
9981 Handle<FixedArray> new_array =
9982 Factory::NewFixedArray(old_array->length() - 1);
9983 int found_count = 0;
9984 for (int i = 0; i < old_array->length(); i++) {
9985 if (old_array->get(i) == *break_point_object) {
9986 ASSERT(found_count == 0);
9987 found_count++;
9988 } else {
9989 new_array->set(i - found_count, old_array->get(i));
9990 }
9991 }
9992 // If the break point was found in the list change it.
9993 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
9994}
9995
9996
9997// Add the specified break point object.
9998void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
9999 Handle<Object> break_point_object) {
10000 // If there was no break point objects before just set it.
10001 if (break_point_info->break_point_objects()->IsUndefined()) {
10002 break_point_info->set_break_point_objects(*break_point_object);
10003 return;
10004 }
10005 // If the break point object is the same as before just ignore.
10006 if (break_point_info->break_point_objects() == *break_point_object) return;
10007 // If there was one break point object before replace with array.
10008 if (!break_point_info->break_point_objects()->IsFixedArray()) {
10009 Handle<FixedArray> array = Factory::NewFixedArray(2);
10010 array->set(0, break_point_info->break_point_objects());
10011 array->set(1, *break_point_object);
10012 break_point_info->set_break_point_objects(*array);
10013 return;
10014 }
10015 // If there was more than one break point before extend array.
10016 Handle<FixedArray> old_array =
10017 Handle<FixedArray>(
10018 FixedArray::cast(break_point_info->break_point_objects()));
10019 Handle<FixedArray> new_array =
10020 Factory::NewFixedArray(old_array->length() + 1);
10021 for (int i = 0; i < old_array->length(); i++) {
10022 // If the break point was there before just ignore.
10023 if (old_array->get(i) == *break_point_object) return;
10024 new_array->set(i, old_array->get(i));
10025 }
10026 // Add the new break point.
10027 new_array->set(old_array->length(), *break_point_object);
10028 break_point_info->set_break_point_objects(*new_array);
10029}
10030
10031
10032bool BreakPointInfo::HasBreakPointObject(
10033 Handle<BreakPointInfo> break_point_info,
10034 Handle<Object> break_point_object) {
10035 // No break point.
10036 if (break_point_info->break_point_objects()->IsUndefined()) return false;
10037 // Single beak point.
10038 if (!break_point_info->break_point_objects()->IsFixedArray()) {
10039 return break_point_info->break_point_objects() == *break_point_object;
10040 }
10041 // Multiple break points.
10042 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
10043 for (int i = 0; i < array->length(); i++) {
10044 if (array->get(i) == *break_point_object) {
10045 return true;
10046 }
10047 }
10048 return false;
10049}
10050
10051
10052// Get the number of break points.
10053int BreakPointInfo::GetBreakPointCount() {
10054 // No break point.
10055 if (break_point_objects()->IsUndefined()) return 0;
10056 // Single beak point.
10057 if (!break_point_objects()->IsFixedArray()) return 1;
10058 // Multiple break points.
10059 return FixedArray::cast(break_point_objects())->length();
10060}
10061#endif
10062
10063
10064} } // namespace v8::internal