blob: 927194f70daf2a14fb3d7617514f6326a293aacb [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) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000534 // Non-JS objects do not have integer indexed properties.
535 if (!IsJSObject()) return Heap::undefined_value();
536 return JSObject::cast(this)->GetElementWithReceiver(JSObject::cast(receiver),
537 index);
538}
539
540
541Object* Object::GetPrototype() {
542 // The object is either a number, a string, a boolean, or a real JS object.
543 if (IsJSObject()) return JSObject::cast(this)->map()->prototype();
544 Context* context = Top::context()->global_context();
545
546 if (IsNumber()) return context->number_function()->instance_prototype();
547 if (IsString()) return context->string_function()->instance_prototype();
548 if (IsBoolean()) {
549 return context->boolean_function()->instance_prototype();
550 } else {
551 return Heap::null_value();
552 }
553}
554
555
Ben Murdochb0fe1622011-05-05 13:52:32 +0100556void Object::ShortPrint(FILE* out) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000557 HeapStringAllocator allocator;
558 StringStream accumulator(&allocator);
559 ShortPrint(&accumulator);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100560 accumulator.OutputToFile(out);
Steve Blocka7e24c12009-10-30 11:49:00 +0000561}
562
563
564void Object::ShortPrint(StringStream* accumulator) {
565 if (IsSmi()) {
566 Smi::cast(this)->SmiPrint(accumulator);
567 } else if (IsFailure()) {
568 Failure::cast(this)->FailurePrint(accumulator);
569 } else {
570 HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
571 }
572}
573
574
Ben Murdochb0fe1622011-05-05 13:52:32 +0100575void Smi::SmiPrint(FILE* out) {
576 PrintF(out, "%d", value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000577}
578
579
580void Smi::SmiPrint(StringStream* accumulator) {
581 accumulator->Add("%d", value());
582}
583
584
585void Failure::FailurePrint(StringStream* accumulator) {
Steve Block3ce2e202009-11-05 08:53:23 +0000586 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000587}
588
589
Ben Murdochb0fe1622011-05-05 13:52:32 +0100590void Failure::FailurePrint(FILE* out) {
591 PrintF(out, "Failure(%p)", reinterpret_cast<void*>(value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000592}
593
594
Steve Blocka7e24c12009-10-30 11:49:00 +0000595// Should a word be prefixed by 'a' or 'an' in order to read naturally in
596// English? Returns false for non-ASCII or words that don't start with
597// a capital letter. The a/an rule follows pronunciation in English.
598// We don't use the BBC's overcorrect "an historic occasion" though if
599// you speak a dialect you may well say "an 'istoric occasion".
600static bool AnWord(String* str) {
601 if (str->length() == 0) return false; // A nothing.
602 int c0 = str->Get(0);
603 int c1 = str->length() > 1 ? str->Get(1) : 0;
604 if (c0 == 'U') {
605 if (c1 > 'Z') {
606 return true; // An Umpire, but a UTF8String, a U.
607 }
608 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
609 return true; // An Ape, an ABCBook.
610 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
611 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
612 c0 == 'S' || c0 == 'X')) {
613 return true; // An MP3File, an M.
614 }
615 return false;
616}
617
618
John Reck59135872010-11-02 12:39:01 -0700619MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000620#ifdef DEBUG
621 // Do not attempt to flatten in debug mode when allocation is not
622 // allowed. This is to avoid an assertion failure when allocating.
623 // Flattening strings is the only case where we always allow
624 // allocation because no GC is performed if the allocation fails.
625 if (!Heap::IsAllocationAllowed()) return this;
626#endif
627
628 switch (StringShape(this).representation_tag()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000629 case kConsStringTag: {
630 ConsString* cs = ConsString::cast(this);
631 if (cs->second()->length() == 0) {
Leon Clarkef7060e22010-06-03 12:02:55 +0100632 return cs->first();
Steve Blocka7e24c12009-10-30 11:49:00 +0000633 }
634 // There's little point in putting the flat string in new space if the
635 // cons string is in old space. It can never get GCed until there is
636 // an old space GC.
Steve Block6ded16b2010-05-10 14:33:55 +0100637 PretenureFlag tenure = Heap::InNewSpace(this) ? pretenure : TENURED;
Steve Blocka7e24c12009-10-30 11:49:00 +0000638 int len = length();
639 Object* object;
640 String* result;
641 if (IsAsciiRepresentation()) {
John Reck59135872010-11-02 12:39:01 -0700642 { MaybeObject* maybe_object = Heap::AllocateRawAsciiString(len, tenure);
643 if (!maybe_object->ToObject(&object)) return maybe_object;
644 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000645 result = String::cast(object);
646 String* first = cs->first();
647 int first_length = first->length();
648 char* dest = SeqAsciiString::cast(result)->GetChars();
649 WriteToFlat(first, dest, 0, first_length);
650 String* second = cs->second();
651 WriteToFlat(second,
652 dest + first_length,
653 0,
654 len - first_length);
655 } else {
John Reck59135872010-11-02 12:39:01 -0700656 { MaybeObject* maybe_object =
657 Heap::AllocateRawTwoByteString(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 uc16* dest = SeqTwoByteString::cast(result)->GetChars();
662 String* first = cs->first();
663 int first_length = first->length();
664 WriteToFlat(first, dest, 0, first_length);
665 String* second = cs->second();
666 WriteToFlat(second,
667 dest + first_length,
668 0,
669 len - first_length);
670 }
671 cs->set_first(result);
672 cs->set_second(Heap::empty_string());
Leon Clarkef7060e22010-06-03 12:02:55 +0100673 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +0000674 }
675 default:
676 return this;
677 }
678}
679
680
681bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
Steve Block8defd9f2010-07-08 12:39:36 +0100682 // Externalizing twice leaks the external resource, so it's
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100683 // prohibited by the API.
684 ASSERT(!this->IsExternalString());
Steve Blocka7e24c12009-10-30 11:49:00 +0000685#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000686 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000687 // Assert that the resource and the string are equivalent.
688 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100689 ScopedVector<uc16> smart_chars(this->length());
690 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
691 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000692 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100693 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000694 }
695#endif // DEBUG
696
697 int size = this->Size(); // Byte size of the original string.
698 if (size < ExternalString::kSize) {
699 // The string is too small to fit an external String in its place. This can
700 // only happen for zero length strings.
701 return false;
702 }
703 ASSERT(size >= ExternalString::kSize);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100704 bool is_ascii = this->IsAsciiRepresentation();
Steve Blocka7e24c12009-10-30 11:49:00 +0000705 bool is_symbol = this->IsSymbol();
706 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000707 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000708
709 // Morph the object to an external string by adjusting the map and
710 // reinitializing the fields.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100711 this->set_map(is_ascii ?
712 Heap::external_string_with_ascii_data_map() :
713 Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000714 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
715 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000716 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000717 self->set_resource(resource);
718 // Additionally make the object into an external symbol if the original string
719 // was a symbol to start with.
720 if (is_symbol) {
721 self->Hash(); // Force regeneration of the hash value.
722 // Now morph this external string into a external symbol.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100723 this->set_map(is_ascii ?
724 Heap::external_symbol_with_ascii_data_map() :
725 Heap::external_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000726 }
727
728 // Fill the remainder of the string with dead wood.
729 int new_size = this->Size(); // Byte size of the external String object.
730 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
731 return true;
732}
733
734
735bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
736#ifdef DEBUG
Steve Block3ce2e202009-11-05 08:53:23 +0000737 if (FLAG_enable_slow_asserts) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000738 // Assert that the resource and the string are equivalent.
739 ASSERT(static_cast<size_t>(this->length()) == resource->length());
Kristian Monsen25f61362010-05-21 11:50:48 +0100740 ScopedVector<char> smart_chars(this->length());
741 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
742 ASSERT(memcmp(smart_chars.start(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000743 resource->data(),
Kristian Monsen25f61362010-05-21 11:50:48 +0100744 resource->length() * sizeof(smart_chars[0])) == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000745 }
746#endif // DEBUG
747
748 int size = this->Size(); // Byte size of the original string.
749 if (size < ExternalString::kSize) {
750 // The string is too small to fit an external String in its place. This can
751 // only happen for zero length strings.
752 return false;
753 }
754 ASSERT(size >= ExternalString::kSize);
755 bool is_symbol = this->IsSymbol();
756 int length = this->length();
Steve Blockd0582a62009-12-15 09:54:21 +0000757 int hash_field = this->hash_field();
Steve Blocka7e24c12009-10-30 11:49:00 +0000758
759 // Morph the object to an external string by adjusting the map and
760 // reinitializing the fields.
Steve Blockd0582a62009-12-15 09:54:21 +0000761 this->set_map(Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000762 ExternalAsciiString* self = ExternalAsciiString::cast(this);
763 self->set_length(length);
Steve Blockd0582a62009-12-15 09:54:21 +0000764 self->set_hash_field(hash_field);
Steve Blocka7e24c12009-10-30 11:49:00 +0000765 self->set_resource(resource);
766 // Additionally make the object into an external symbol if the original string
767 // was a symbol to start with.
768 if (is_symbol) {
769 self->Hash(); // Force regeneration of the hash value.
770 // Now morph this external string into a external symbol.
Steve Blockd0582a62009-12-15 09:54:21 +0000771 this->set_map(Heap::external_ascii_symbol_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000772 }
773
774 // Fill the remainder of the string with dead wood.
775 int new_size = this->Size(); // Byte size of the external String object.
776 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
777 return true;
778}
779
780
781void String::StringShortPrint(StringStream* accumulator) {
782 int len = length();
Steve Blockd0582a62009-12-15 09:54:21 +0000783 if (len > kMaxShortPrintLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000784 accumulator->Add("<Very long string[%u]>", len);
785 return;
786 }
787
788 if (!LooksValid()) {
789 accumulator->Add("<Invalid String>");
790 return;
791 }
792
793 StringInputBuffer buf(this);
794
795 bool truncated = false;
796 if (len > kMaxShortPrintLength) {
797 len = kMaxShortPrintLength;
798 truncated = true;
799 }
800 bool ascii = true;
801 for (int i = 0; i < len; i++) {
802 int c = buf.GetNext();
803
804 if (c < 32 || c >= 127) {
805 ascii = false;
806 }
807 }
808 buf.Reset(this);
809 if (ascii) {
810 accumulator->Add("<String[%u]: ", length());
811 for (int i = 0; i < len; i++) {
812 accumulator->Put(buf.GetNext());
813 }
814 accumulator->Put('>');
815 } else {
816 // Backslash indicates that the string contains control
817 // characters and that backslashes are therefore escaped.
818 accumulator->Add("<String[%u]\\: ", length());
819 for (int i = 0; i < len; i++) {
820 int c = buf.GetNext();
821 if (c == '\n') {
822 accumulator->Add("\\n");
823 } else if (c == '\r') {
824 accumulator->Add("\\r");
825 } else if (c == '\\') {
826 accumulator->Add("\\\\");
827 } else if (c < 32 || c > 126) {
828 accumulator->Add("\\x%02x", c);
829 } else {
830 accumulator->Put(c);
831 }
832 }
833 if (truncated) {
834 accumulator->Put('.');
835 accumulator->Put('.');
836 accumulator->Put('.');
837 }
838 accumulator->Put('>');
839 }
840 return;
841}
842
843
844void JSObject::JSObjectShortPrint(StringStream* accumulator) {
845 switch (map()->instance_type()) {
846 case JS_ARRAY_TYPE: {
847 double length = JSArray::cast(this)->length()->Number();
848 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
849 break;
850 }
851 case JS_REGEXP_TYPE: {
852 accumulator->Add("<JS RegExp>");
853 break;
854 }
855 case JS_FUNCTION_TYPE: {
856 Object* fun_name = JSFunction::cast(this)->shared()->name();
857 bool printed = false;
858 if (fun_name->IsString()) {
859 String* str = String::cast(fun_name);
860 if (str->length() > 0) {
861 accumulator->Add("<JS Function ");
862 accumulator->Put(str);
863 accumulator->Put('>');
864 printed = true;
865 }
866 }
867 if (!printed) {
868 accumulator->Add("<JS Function>");
869 }
870 break;
871 }
872 // All other JSObjects are rather similar to each other (JSObject,
873 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
874 default: {
875 Object* constructor = map()->constructor();
876 bool printed = false;
877 if (constructor->IsHeapObject() &&
878 !Heap::Contains(HeapObject::cast(constructor))) {
879 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
880 } else {
881 bool global_object = IsJSGlobalProxy();
882 if (constructor->IsJSFunction()) {
883 if (!Heap::Contains(JSFunction::cast(constructor)->shared())) {
884 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
885 } else {
886 Object* constructor_name =
887 JSFunction::cast(constructor)->shared()->name();
888 if (constructor_name->IsString()) {
889 String* str = String::cast(constructor_name);
890 if (str->length() > 0) {
891 bool vowel = AnWord(str);
892 accumulator->Add("<%sa%s ",
893 global_object ? "Global Object: " : "",
894 vowel ? "n" : "");
895 accumulator->Put(str);
896 accumulator->Put('>');
897 printed = true;
898 }
899 }
900 }
901 }
902 if (!printed) {
903 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
904 }
905 }
906 if (IsJSValue()) {
907 accumulator->Add(" value = ");
908 JSValue::cast(this)->value()->ShortPrint(accumulator);
909 }
910 accumulator->Put('>');
911 break;
912 }
913 }
914}
915
916
917void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
918 // if (!Heap::InNewSpace(this)) PrintF("*", this);
919 if (!Heap::Contains(this)) {
920 accumulator->Add("!!!INVALID POINTER!!!");
921 return;
922 }
923 if (!Heap::Contains(map())) {
924 accumulator->Add("!!!INVALID MAP!!!");
925 return;
926 }
927
928 accumulator->Add("%p ", this);
929
930 if (IsString()) {
931 String::cast(this)->StringShortPrint(accumulator);
932 return;
933 }
934 if (IsJSObject()) {
935 JSObject::cast(this)->JSObjectShortPrint(accumulator);
936 return;
937 }
938 switch (map()->instance_type()) {
939 case MAP_TYPE:
940 accumulator->Add("<Map>");
941 break;
942 case FIXED_ARRAY_TYPE:
943 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
944 break;
945 case BYTE_ARRAY_TYPE:
946 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
947 break;
948 case PIXEL_ARRAY_TYPE:
949 accumulator->Add("<PixelArray[%u]>", PixelArray::cast(this)->length());
950 break;
Steve Block3ce2e202009-11-05 08:53:23 +0000951 case EXTERNAL_BYTE_ARRAY_TYPE:
952 accumulator->Add("<ExternalByteArray[%u]>",
953 ExternalByteArray::cast(this)->length());
954 break;
955 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
956 accumulator->Add("<ExternalUnsignedByteArray[%u]>",
957 ExternalUnsignedByteArray::cast(this)->length());
958 break;
959 case EXTERNAL_SHORT_ARRAY_TYPE:
960 accumulator->Add("<ExternalShortArray[%u]>",
961 ExternalShortArray::cast(this)->length());
962 break;
963 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
964 accumulator->Add("<ExternalUnsignedShortArray[%u]>",
965 ExternalUnsignedShortArray::cast(this)->length());
966 break;
967 case EXTERNAL_INT_ARRAY_TYPE:
968 accumulator->Add("<ExternalIntArray[%u]>",
969 ExternalIntArray::cast(this)->length());
970 break;
971 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
972 accumulator->Add("<ExternalUnsignedIntArray[%u]>",
973 ExternalUnsignedIntArray::cast(this)->length());
974 break;
975 case EXTERNAL_FLOAT_ARRAY_TYPE:
976 accumulator->Add("<ExternalFloatArray[%u]>",
977 ExternalFloatArray::cast(this)->length());
978 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000979 case SHARED_FUNCTION_INFO_TYPE:
980 accumulator->Add("<SharedFunctionInfo>");
981 break;
982#define MAKE_STRUCT_CASE(NAME, Name, name) \
983 case NAME##_TYPE: \
984 accumulator->Put('<'); \
985 accumulator->Add(#Name); \
986 accumulator->Put('>'); \
987 break;
988 STRUCT_LIST(MAKE_STRUCT_CASE)
989#undef MAKE_STRUCT_CASE
990 case CODE_TYPE:
991 accumulator->Add("<Code>");
992 break;
993 case ODDBALL_TYPE: {
994 if (IsUndefined())
995 accumulator->Add("<undefined>");
996 else if (IsTheHole())
997 accumulator->Add("<the hole>");
998 else if (IsNull())
999 accumulator->Add("<null>");
1000 else if (IsTrue())
1001 accumulator->Add("<true>");
1002 else if (IsFalse())
1003 accumulator->Add("<false>");
1004 else
1005 accumulator->Add("<Odd Oddball>");
1006 break;
1007 }
1008 case HEAP_NUMBER_TYPE:
1009 accumulator->Add("<Number: ");
1010 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
1011 accumulator->Put('>');
1012 break;
1013 case PROXY_TYPE:
1014 accumulator->Add("<Proxy>");
1015 break;
1016 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1017 accumulator->Add("Cell for ");
1018 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator);
1019 break;
1020 default:
1021 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
1022 break;
1023 }
1024}
1025
1026
Steve Blocka7e24c12009-10-30 11:49:00 +00001027void HeapObject::Iterate(ObjectVisitor* v) {
1028 // Handle header
1029 IteratePointer(v, kMapOffset);
1030 // Handle object body
1031 Map* m = map();
1032 IterateBody(m->instance_type(), SizeFromMap(m), v);
1033}
1034
1035
1036void HeapObject::IterateBody(InstanceType type, int object_size,
1037 ObjectVisitor* v) {
1038 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
1039 // During GC, the map pointer field is encoded.
1040 if (type < FIRST_NONSTRING_TYPE) {
1041 switch (type & kStringRepresentationMask) {
1042 case kSeqStringTag:
1043 break;
1044 case kConsStringTag:
Iain Merrick75681382010-08-19 15:07:18 +01001045 ConsString::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001046 break;
Steve Blockd0582a62009-12-15 09:54:21 +00001047 case kExternalStringTag:
1048 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1049 reinterpret_cast<ExternalAsciiString*>(this)->
1050 ExternalAsciiStringIterateBody(v);
1051 } else {
1052 reinterpret_cast<ExternalTwoByteString*>(this)->
1053 ExternalTwoByteStringIterateBody(v);
1054 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001055 break;
1056 }
1057 return;
1058 }
1059
1060 switch (type) {
1061 case FIXED_ARRAY_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001062 FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001063 break;
1064 case JS_OBJECT_TYPE:
1065 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
1066 case JS_VALUE_TYPE:
1067 case JS_ARRAY_TYPE:
1068 case JS_REGEXP_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001069 case JS_GLOBAL_PROXY_TYPE:
1070 case JS_GLOBAL_OBJECT_TYPE:
1071 case JS_BUILTINS_OBJECT_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001072 JSObject::BodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001073 break;
Steve Block791712a2010-08-27 10:21:07 +01001074 case JS_FUNCTION_TYPE:
1075 reinterpret_cast<JSFunction*>(this)
1076 ->JSFunctionIterateBody(object_size, v);
1077 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001078 case ODDBALL_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001079 Oddball::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001080 break;
1081 case PROXY_TYPE:
1082 reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
1083 break;
1084 case MAP_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001085 Map::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001086 break;
1087 case CODE_TYPE:
1088 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1089 break;
1090 case JS_GLOBAL_PROPERTY_CELL_TYPE:
Iain Merrick75681382010-08-19 15:07:18 +01001091 JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001092 break;
1093 case HEAP_NUMBER_TYPE:
1094 case FILLER_TYPE:
1095 case BYTE_ARRAY_TYPE:
1096 case PIXEL_ARRAY_TYPE:
Steve Block3ce2e202009-11-05 08:53:23 +00001097 case EXTERNAL_BYTE_ARRAY_TYPE:
1098 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1099 case EXTERNAL_SHORT_ARRAY_TYPE:
1100 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1101 case EXTERNAL_INT_ARRAY_TYPE:
1102 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1103 case EXTERNAL_FLOAT_ARRAY_TYPE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001104 break;
Iain Merrick75681382010-08-19 15:07:18 +01001105 case SHARED_FUNCTION_INFO_TYPE:
1106 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001107 break;
Iain Merrick75681382010-08-19 15:07:18 +01001108
Steve Blocka7e24c12009-10-30 11:49:00 +00001109#define MAKE_STRUCT_CASE(NAME, Name, name) \
1110 case NAME##_TYPE:
1111 STRUCT_LIST(MAKE_STRUCT_CASE)
1112#undef MAKE_STRUCT_CASE
Iain Merrick75681382010-08-19 15:07:18 +01001113 StructBodyDescriptor::IterateBody(this, object_size, v);
Steve Blocka7e24c12009-10-30 11:49:00 +00001114 break;
1115 default:
1116 PrintF("Unknown type: %d\n", type);
1117 UNREACHABLE();
1118 }
1119}
1120
1121
Steve Blocka7e24c12009-10-30 11:49:00 +00001122Object* HeapNumber::HeapNumberToBoolean() {
1123 // NaN, +0, and -0 should return the false object
Iain Merrick75681382010-08-19 15:07:18 +01001124#if __BYTE_ORDER == __LITTLE_ENDIAN
1125 union IeeeDoubleLittleEndianArchType u;
1126#elif __BYTE_ORDER == __BIG_ENDIAN
1127 union IeeeDoubleBigEndianArchType u;
1128#endif
1129 u.d = value();
1130 if (u.bits.exp == 2047) {
1131 // Detect NaN for IEEE double precision floating point.
1132 if ((u.bits.man_low | u.bits.man_high) != 0)
1133 return Heap::false_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001134 }
Iain Merrick75681382010-08-19 15:07:18 +01001135 if (u.bits.exp == 0) {
1136 // Detect +0, and -0 for IEEE double precision floating point.
1137 if ((u.bits.man_low | u.bits.man_high) == 0)
1138 return Heap::false_value();
1139 }
1140 return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001141}
1142
1143
Ben Murdochb0fe1622011-05-05 13:52:32 +01001144void HeapNumber::HeapNumberPrint(FILE* out) {
1145 PrintF(out, "%.16g", Number());
Steve Blocka7e24c12009-10-30 11:49:00 +00001146}
1147
1148
1149void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1150 // The Windows version of vsnprintf can allocate when printing a %g string
1151 // into a buffer that may not be big enough. We don't want random memory
1152 // allocation when producing post-crash stack traces, so we print into a
1153 // buffer that is plenty big enough for any floating point number, then
1154 // print that using vsnprintf (which may truncate but never allocate if
1155 // there is no more space in the buffer).
1156 EmbeddedVector<char, 100> buffer;
1157 OS::SNPrintF(buffer, "%.16g", Number());
1158 accumulator->Add("%s", buffer.start());
1159}
1160
1161
1162String* JSObject::class_name() {
1163 if (IsJSFunction()) {
1164 return Heap::function_class_symbol();
1165 }
1166 if (map()->constructor()->IsJSFunction()) {
1167 JSFunction* constructor = JSFunction::cast(map()->constructor());
1168 return String::cast(constructor->shared()->instance_class_name());
1169 }
1170 // If the constructor is not present, return "Object".
1171 return Heap::Object_symbol();
1172}
1173
1174
1175String* JSObject::constructor_name() {
Steve Blocka7e24c12009-10-30 11:49:00 +00001176 if (map()->constructor()->IsJSFunction()) {
1177 JSFunction* constructor = JSFunction::cast(map()->constructor());
1178 String* name = String::cast(constructor->shared()->name());
Ben Murdochf87a2032010-10-22 12:50:53 +01001179 if (name->length() > 0) return name;
1180 String* inferred_name = constructor->shared()->inferred_name();
1181 if (inferred_name->length() > 0) return inferred_name;
1182 Object* proto = GetPrototype();
1183 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
Steve Blocka7e24c12009-10-30 11:49:00 +00001184 }
1185 // If the constructor is not present, return "Object".
1186 return Heap::Object_symbol();
1187}
1188
1189
John Reck59135872010-11-02 12:39:01 -07001190MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
1191 String* name,
1192 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001193 int index = new_map->PropertyIndexFor(name);
1194 if (map()->unused_property_fields() == 0) {
1195 ASSERT(map()->unused_property_fields() == 0);
1196 int new_unused = new_map->unused_property_fields();
John Reck59135872010-11-02 12:39:01 -07001197 Object* values;
1198 { MaybeObject* maybe_values =
1199 properties()->CopySize(properties()->length() + new_unused + 1);
1200 if (!maybe_values->ToObject(&values)) return maybe_values;
1201 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001202 set_properties(FixedArray::cast(values));
1203 }
1204 set_map(new_map);
1205 return FastPropertyAtPut(index, value);
1206}
1207
1208
John Reck59135872010-11-02 12:39:01 -07001209MaybeObject* JSObject::AddFastProperty(String* name,
1210 Object* value,
1211 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001212 // Normalize the object if the name is an actual string (not the
1213 // hidden symbols) and is not a real identifier.
1214 StringInputBuffer buffer(name);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001215 if (!ScannerConstants::IsIdentifier(&buffer)
1216 && name != Heap::hidden_symbol()) {
John Reck59135872010-11-02 12:39:01 -07001217 Object* obj;
1218 { MaybeObject* maybe_obj =
1219 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1220 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1221 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001222 return AddSlowProperty(name, value, attributes);
1223 }
1224
1225 DescriptorArray* old_descriptors = map()->instance_descriptors();
1226 // Compute the new index for new field.
1227 int index = map()->NextFreePropertyIndex();
1228
1229 // Allocate new instance descriptors with (name, index) added
1230 FieldDescriptor new_field(name, index, attributes);
John Reck59135872010-11-02 12:39:01 -07001231 Object* new_descriptors;
1232 { MaybeObject* maybe_new_descriptors =
1233 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1234 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1235 return maybe_new_descriptors;
1236 }
1237 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001238
1239 // Only allow map transition if the object's map is NOT equal to the
1240 // global object_function's map and there is not a transition for name.
1241 bool allow_map_transition =
1242 !old_descriptors->Contains(name) &&
1243 (Top::context()->global_context()->object_function()->map() != map());
1244
1245 ASSERT(index < map()->inobject_properties() ||
1246 (index - map()->inobject_properties()) < properties()->length() ||
1247 map()->unused_property_fields() == 0);
1248 // Allocate a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001249 Object* r;
1250 { MaybeObject* maybe_r = map()->CopyDropDescriptors();
1251 if (!maybe_r->ToObject(&r)) return maybe_r;
1252 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001253 Map* new_map = Map::cast(r);
1254 if (allow_map_transition) {
1255 // Allocate new instance descriptors for the old map with map transition.
1256 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
John Reck59135872010-11-02 12:39:01 -07001257 Object* r;
1258 { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1259 if (!maybe_r->ToObject(&r)) return maybe_r;
1260 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001261 old_descriptors = DescriptorArray::cast(r);
1262 }
1263
1264 if (map()->unused_property_fields() == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01001265 if (properties()->length() > MaxFastProperties()) {
John Reck59135872010-11-02 12:39:01 -07001266 Object* obj;
1267 { MaybeObject* maybe_obj =
1268 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1269 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1270 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001271 return AddSlowProperty(name, value, attributes);
1272 }
1273 // Make room for the new value
John Reck59135872010-11-02 12:39:01 -07001274 Object* values;
1275 { MaybeObject* maybe_values =
1276 properties()->CopySize(properties()->length() + kFieldsAdded);
1277 if (!maybe_values->ToObject(&values)) return maybe_values;
1278 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001279 set_properties(FixedArray::cast(values));
1280 new_map->set_unused_property_fields(kFieldsAdded - 1);
1281 } else {
1282 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
1283 }
1284 // We have now allocated all the necessary objects.
1285 // All the changes can be applied at once, so they are atomic.
1286 map()->set_instance_descriptors(old_descriptors);
1287 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1288 set_map(new_map);
1289 return FastPropertyAtPut(index, value);
1290}
1291
1292
John Reck59135872010-11-02 12:39:01 -07001293MaybeObject* JSObject::AddConstantFunctionProperty(
1294 String* name,
1295 JSFunction* function,
1296 PropertyAttributes attributes) {
Leon Clarkee46be812010-01-19 14:06:41 +00001297 ASSERT(!Heap::InNewSpace(function));
1298
Steve Blocka7e24c12009-10-30 11:49:00 +00001299 // Allocate new instance descriptors with (name, function) added
1300 ConstantFunctionDescriptor d(name, function, attributes);
John Reck59135872010-11-02 12:39:01 -07001301 Object* new_descriptors;
1302 { MaybeObject* maybe_new_descriptors =
1303 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1304 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1305 return maybe_new_descriptors;
1306 }
1307 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001308
1309 // Allocate a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001310 Object* new_map;
1311 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
1312 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
1313 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001314
1315 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1316 Map::cast(new_map)->set_instance_descriptors(descriptors);
1317 Map* old_map = map();
1318 set_map(Map::cast(new_map));
1319
1320 // If the old map is the global object map (from new Object()),
1321 // then transitions are not added to it, so we are done.
1322 if (old_map == Top::context()->global_context()->object_function()->map()) {
1323 return function;
1324 }
1325
1326 // Do not add CONSTANT_TRANSITIONS to global objects
1327 if (IsGlobalObject()) {
1328 return function;
1329 }
1330
1331 // Add a CONSTANT_TRANSITION descriptor to the old map,
1332 // so future assignments to this property on other objects
1333 // of the same type will create a normal field, not a constant function.
1334 // Don't do this for special properties, with non-trival attributes.
1335 if (attributes != NONE) {
1336 return function;
1337 }
Iain Merrick75681382010-08-19 15:07:18 +01001338 ConstTransitionDescriptor mark(name, Map::cast(new_map));
John Reck59135872010-11-02 12:39:01 -07001339 { MaybeObject* maybe_new_descriptors =
1340 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1341 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1342 // We have accomplished the main goal, so return success.
1343 return function;
1344 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001345 }
1346 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1347
1348 return function;
1349}
1350
1351
1352// Add property in slow mode
John Reck59135872010-11-02 12:39:01 -07001353MaybeObject* JSObject::AddSlowProperty(String* name,
1354 Object* value,
1355 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001356 ASSERT(!HasFastProperties());
1357 StringDictionary* dict = property_dictionary();
1358 Object* store_value = value;
1359 if (IsGlobalObject()) {
1360 // In case name is an orphaned property reuse the cell.
1361 int entry = dict->FindEntry(name);
1362 if (entry != StringDictionary::kNotFound) {
1363 store_value = dict->ValueAt(entry);
1364 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1365 // Assign an enumeration index to the property and update
1366 // SetNextEnumerationIndex.
1367 int index = dict->NextEnumerationIndex();
1368 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1369 dict->SetNextEnumerationIndex(index + 1);
1370 dict->SetEntry(entry, name, store_value, details);
1371 return value;
1372 }
John Reck59135872010-11-02 12:39:01 -07001373 { MaybeObject* maybe_store_value =
1374 Heap::AllocateJSGlobalPropertyCell(value);
1375 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
1376 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001377 JSGlobalPropertyCell::cast(store_value)->set_value(value);
1378 }
1379 PropertyDetails details = PropertyDetails(attributes, NORMAL);
John Reck59135872010-11-02 12:39:01 -07001380 Object* result;
1381 { MaybeObject* maybe_result = dict->Add(name, store_value, details);
1382 if (!maybe_result->ToObject(&result)) return maybe_result;
1383 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001384 if (dict != result) set_properties(StringDictionary::cast(result));
1385 return value;
1386}
1387
1388
John Reck59135872010-11-02 12:39:01 -07001389MaybeObject* JSObject::AddProperty(String* name,
1390 Object* value,
1391 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001392 ASSERT(!IsJSGlobalProxy());
Steve Block8defd9f2010-07-08 12:39:36 +01001393 if (!map()->is_extensible()) {
1394 Handle<Object> args[1] = {Handle<String>(name)};
1395 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
1396 HandleVector(args, 1)));
1397 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001398 if (HasFastProperties()) {
1399 // Ensure the descriptor array does not get too big.
1400 if (map()->instance_descriptors()->number_of_descriptors() <
1401 DescriptorArray::kMaxNumberOfDescriptors) {
Leon Clarkee46be812010-01-19 14:06:41 +00001402 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001403 return AddConstantFunctionProperty(name,
1404 JSFunction::cast(value),
1405 attributes);
1406 } else {
1407 return AddFastProperty(name, value, attributes);
1408 }
1409 } else {
1410 // Normalize the object to prevent very large instance descriptors.
1411 // This eliminates unwanted N^2 allocation and lookup behavior.
John Reck59135872010-11-02 12:39:01 -07001412 Object* obj;
1413 { MaybeObject* maybe_obj =
1414 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1415 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1416 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001417 }
1418 }
1419 return AddSlowProperty(name, value, attributes);
1420}
1421
1422
John Reck59135872010-11-02 12:39:01 -07001423MaybeObject* JSObject::SetPropertyPostInterceptor(
1424 String* name,
1425 Object* value,
1426 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001427 // Check local property, ignore interceptor.
1428 LookupResult result;
1429 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001430 if (result.IsFound()) {
1431 // An existing property, a map transition or a null descriptor was
1432 // found. Use set property to handle all these cases.
1433 return SetProperty(&result, name, value, attributes);
1434 }
1435 // Add a new real property.
Steve Blocka7e24c12009-10-30 11:49:00 +00001436 return AddProperty(name, value, attributes);
1437}
1438
1439
John Reck59135872010-11-02 12:39:01 -07001440MaybeObject* JSObject::ReplaceSlowProperty(String* name,
1441 Object* value,
1442 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001443 StringDictionary* dictionary = property_dictionary();
1444 int old_index = dictionary->FindEntry(name);
1445 int new_enumeration_index = 0; // 0 means "Use the next available index."
1446 if (old_index != -1) {
1447 // All calls to ReplaceSlowProperty have had all transitions removed.
1448 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1449 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1450 }
1451
1452 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
1453 return SetNormalizedProperty(name, value, new_details);
1454}
1455
Steve Blockd0582a62009-12-15 09:54:21 +00001456
John Reck59135872010-11-02 12:39:01 -07001457MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
Steve Blocka7e24c12009-10-30 11:49:00 +00001458 String* name,
1459 Object* new_value,
1460 PropertyAttributes attributes) {
1461 Map* old_map = map();
John Reck59135872010-11-02 12:39:01 -07001462 Object* result;
1463 { MaybeObject* maybe_result =
1464 ConvertDescriptorToField(name, new_value, attributes);
1465 if (!maybe_result->ToObject(&result)) return maybe_result;
1466 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001467 // If we get to this point we have succeeded - do not return failure
1468 // after this point. Later stuff is optional.
1469 if (!HasFastProperties()) {
1470 return result;
1471 }
1472 // Do not add transitions to the map of "new Object()".
1473 if (map() == Top::context()->global_context()->object_function()->map()) {
1474 return result;
1475 }
1476
1477 MapTransitionDescriptor transition(name,
1478 map(),
1479 attributes);
John Reck59135872010-11-02 12:39:01 -07001480 Object* new_descriptors;
1481 { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
1482 CopyInsert(&transition, KEEP_TRANSITIONS);
1483 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1484 return result; // Yes, return _result_.
1485 }
1486 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001487 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1488 return result;
1489}
1490
1491
John Reck59135872010-11-02 12:39:01 -07001492MaybeObject* JSObject::ConvertDescriptorToField(String* name,
1493 Object* new_value,
1494 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001495 if (map()->unused_property_fields() == 0 &&
Steve Block8defd9f2010-07-08 12:39:36 +01001496 properties()->length() > MaxFastProperties()) {
John Reck59135872010-11-02 12:39:01 -07001497 Object* obj;
1498 { MaybeObject* maybe_obj =
1499 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1500 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1501 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001502 return ReplaceSlowProperty(name, new_value, attributes);
1503 }
1504
1505 int index = map()->NextFreePropertyIndex();
1506 FieldDescriptor new_field(name, index, attributes);
1507 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
John Reck59135872010-11-02 12:39:01 -07001508 Object* descriptors_unchecked;
1509 { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
1510 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1511 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
1512 return maybe_descriptors_unchecked;
1513 }
1514 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001515 DescriptorArray* new_descriptors =
1516 DescriptorArray::cast(descriptors_unchecked);
1517
1518 // Make a new map for the object.
John Reck59135872010-11-02 12:39:01 -07001519 Object* new_map_unchecked;
1520 { MaybeObject* maybe_new_map_unchecked = map()->CopyDropDescriptors();
1521 if (!maybe_new_map_unchecked->ToObject(&new_map_unchecked)) {
1522 return maybe_new_map_unchecked;
1523 }
1524 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001525 Map* new_map = Map::cast(new_map_unchecked);
1526 new_map->set_instance_descriptors(new_descriptors);
1527
1528 // Make new properties array if necessary.
1529 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1530 int new_unused_property_fields = map()->unused_property_fields() - 1;
1531 if (map()->unused_property_fields() == 0) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001532 new_unused_property_fields = kFieldsAdded - 1;
John Reck59135872010-11-02 12:39:01 -07001533 Object* new_properties_object;
1534 { MaybeObject* maybe_new_properties_object =
1535 properties()->CopySize(properties()->length() + kFieldsAdded);
1536 if (!maybe_new_properties_object->ToObject(&new_properties_object)) {
1537 return maybe_new_properties_object;
1538 }
1539 }
1540 new_properties = FixedArray::cast(new_properties_object);
Steve Blocka7e24c12009-10-30 11:49:00 +00001541 }
1542
1543 // Update pointers to commit changes.
1544 // Object points to the new map.
1545 new_map->set_unused_property_fields(new_unused_property_fields);
1546 set_map(new_map);
1547 if (new_properties) {
1548 set_properties(FixedArray::cast(new_properties));
1549 }
1550 return FastPropertyAtPut(index, new_value);
1551}
1552
1553
1554
John Reck59135872010-11-02 12:39:01 -07001555MaybeObject* JSObject::SetPropertyWithInterceptor(
1556 String* name,
1557 Object* value,
1558 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001559 HandleScope scope;
1560 Handle<JSObject> this_handle(this);
1561 Handle<String> name_handle(name);
1562 Handle<Object> value_handle(value);
1563 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1564 if (!interceptor->setter()->IsUndefined()) {
1565 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name));
1566 CustomArguments args(interceptor->data(), this, this);
1567 v8::AccessorInfo info(args.end());
1568 v8::NamedPropertySetter setter =
1569 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1570 v8::Handle<v8::Value> result;
1571 {
1572 // Leaving JavaScript.
1573 VMState state(EXTERNAL);
1574 Handle<Object> value_unhole(value->IsTheHole() ?
1575 Heap::undefined_value() :
1576 value);
1577 result = setter(v8::Utils::ToLocal(name_handle),
1578 v8::Utils::ToLocal(value_unhole),
1579 info);
1580 }
1581 RETURN_IF_SCHEDULED_EXCEPTION();
1582 if (!result.IsEmpty()) return *value_handle;
1583 }
John Reck59135872010-11-02 12:39:01 -07001584 MaybeObject* raw_result =
1585 this_handle->SetPropertyPostInterceptor(*name_handle,
1586 *value_handle,
1587 attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00001588 RETURN_IF_SCHEDULED_EXCEPTION();
1589 return raw_result;
1590}
1591
1592
John Reck59135872010-11-02 12:39:01 -07001593MaybeObject* JSObject::SetProperty(String* name,
1594 Object* value,
1595 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001596 LookupResult result;
1597 LocalLookup(name, &result);
1598 return SetProperty(&result, name, value, attributes);
1599}
1600
1601
John Reck59135872010-11-02 12:39:01 -07001602MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
1603 String* name,
1604 Object* value,
1605 JSObject* holder) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001606 HandleScope scope;
1607
1608 // We should never get here to initialize a const with the hole
1609 // value since a const declaration would conflict with the setter.
1610 ASSERT(!value->IsTheHole());
1611 Handle<Object> value_handle(value);
1612
1613 // To accommodate both the old and the new api we switch on the
1614 // data structure used to store the callbacks. Eventually proxy
1615 // callbacks should be phased out.
1616 if (structure->IsProxy()) {
1617 AccessorDescriptor* callback =
1618 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
John Reck59135872010-11-02 12:39:01 -07001619 MaybeObject* obj = (callback->setter)(this, value, callback->data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001620 RETURN_IF_SCHEDULED_EXCEPTION();
1621 if (obj->IsFailure()) return obj;
1622 return *value_handle;
1623 }
1624
1625 if (structure->IsAccessorInfo()) {
1626 // api style callbacks
1627 AccessorInfo* data = AccessorInfo::cast(structure);
1628 Object* call_obj = data->setter();
1629 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1630 if (call_fun == NULL) return value;
1631 Handle<String> key(name);
1632 LOG(ApiNamedPropertyAccess("store", this, name));
1633 CustomArguments args(data->data(), this, JSObject::cast(holder));
1634 v8::AccessorInfo info(args.end());
1635 {
1636 // Leaving JavaScript.
1637 VMState state(EXTERNAL);
1638 call_fun(v8::Utils::ToLocal(key),
1639 v8::Utils::ToLocal(value_handle),
1640 info);
1641 }
1642 RETURN_IF_SCHEDULED_EXCEPTION();
1643 return *value_handle;
1644 }
1645
1646 if (structure->IsFixedArray()) {
1647 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1648 if (setter->IsJSFunction()) {
1649 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
1650 } else {
1651 Handle<String> key(name);
1652 Handle<Object> holder_handle(holder);
1653 Handle<Object> args[2] = { key, holder_handle };
1654 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
1655 HandleVector(args, 2)));
1656 }
1657 }
1658
1659 UNREACHABLE();
Leon Clarkef7060e22010-06-03 12:02:55 +01001660 return NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001661}
1662
1663
John Reck59135872010-11-02 12:39:01 -07001664MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1665 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001666 Handle<Object> value_handle(value);
1667 Handle<JSFunction> fun(JSFunction::cast(setter));
1668 Handle<JSObject> self(this);
1669#ifdef ENABLE_DEBUGGER_SUPPORT
1670 // Handle stepping into a setter if step into is active.
1671 if (Debug::StepInActive()) {
1672 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
1673 }
1674#endif
1675 bool has_pending_exception;
1676 Object** argv[] = { value_handle.location() };
1677 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1678 // Check for pending exception and return the result.
1679 if (has_pending_exception) return Failure::Exception();
1680 return *value_handle;
1681}
1682
1683
1684void JSObject::LookupCallbackSetterInPrototypes(String* name,
1685 LookupResult* result) {
1686 for (Object* pt = GetPrototype();
1687 pt != Heap::null_value();
1688 pt = pt->GetPrototype()) {
1689 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001690 if (result->IsProperty()) {
1691 if (result->IsReadOnly()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001692 result->NotFound();
1693 return;
1694 }
1695 if (result->type() == CALLBACKS) {
1696 return;
1697 }
1698 }
1699 }
1700 result->NotFound();
1701}
1702
1703
Leon Clarkef7060e22010-06-03 12:02:55 +01001704bool JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
1705 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001706 for (Object* pt = GetPrototype();
1707 pt != Heap::null_value();
1708 pt = pt->GetPrototype()) {
1709 if (!JSObject::cast(pt)->HasDictionaryElements()) {
1710 continue;
1711 }
1712 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1713 int entry = dictionary->FindEntry(index);
1714 if (entry != NumberDictionary::kNotFound) {
1715 Object* element = dictionary->ValueAt(entry);
1716 PropertyDetails details = dictionary->DetailsAt(entry);
1717 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01001718 SetElementWithCallback(element, index, value, JSObject::cast(pt));
1719 return true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001720 }
1721 }
1722 }
Leon Clarkef7060e22010-06-03 12:02:55 +01001723 return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001724}
1725
1726
1727void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1728 DescriptorArray* descriptors = map()->instance_descriptors();
Iain Merrick75681382010-08-19 15:07:18 +01001729 int number = descriptors->SearchWithCache(name);
Steve Blocka7e24c12009-10-30 11:49:00 +00001730 if (number != DescriptorArray::kNotFound) {
1731 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1732 } else {
1733 result->NotFound();
1734 }
1735}
1736
1737
Ben Murdochb0fe1622011-05-05 13:52:32 +01001738void Map::LookupInDescriptors(JSObject* holder,
1739 String* name,
1740 LookupResult* result) {
1741 DescriptorArray* descriptors = instance_descriptors();
1742 int number = DescriptorLookupCache::Lookup(descriptors, name);
1743 if (number == DescriptorLookupCache::kAbsent) {
1744 number = descriptors->Search(name);
1745 DescriptorLookupCache::Update(descriptors, name, number);
1746 }
1747 if (number != DescriptorArray::kNotFound) {
1748 result->DescriptorResult(holder, descriptors->GetDetails(number), number);
1749 } else {
1750 result->NotFound();
1751 }
1752}
1753
1754
Steve Blocka7e24c12009-10-30 11:49:00 +00001755void JSObject::LocalLookupRealNamedProperty(String* name,
1756 LookupResult* result) {
1757 if (IsJSGlobalProxy()) {
1758 Object* proto = GetPrototype();
1759 if (proto->IsNull()) return result->NotFound();
1760 ASSERT(proto->IsJSGlobalObject());
1761 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
1762 }
1763
1764 if (HasFastProperties()) {
1765 LookupInDescriptor(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001766 if (result->IsFound()) {
1767 // A property, a map transition or a null descriptor was found.
1768 // We return all of these result types because
1769 // LocalLookupRealNamedProperty is used when setting properties
1770 // where map transitions and null descriptors are handled.
Steve Blocka7e24c12009-10-30 11:49:00 +00001771 ASSERT(result->holder() == this && result->type() != NORMAL);
1772 // Disallow caching for uninitialized constants. These can only
1773 // occur as fields.
1774 if (result->IsReadOnly() && result->type() == FIELD &&
1775 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
1776 result->DisallowCaching();
1777 }
1778 return;
1779 }
1780 } else {
1781 int entry = property_dictionary()->FindEntry(name);
1782 if (entry != StringDictionary::kNotFound) {
1783 Object* value = property_dictionary()->ValueAt(entry);
1784 if (IsGlobalObject()) {
1785 PropertyDetails d = property_dictionary()->DetailsAt(entry);
1786 if (d.IsDeleted()) {
1787 result->NotFound();
1788 return;
1789 }
1790 value = JSGlobalPropertyCell::cast(value)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00001791 }
1792 // Make sure to disallow caching for uninitialized constants
1793 // found in the dictionary-mode objects.
1794 if (value->IsTheHole()) result->DisallowCaching();
1795 result->DictionaryResult(this, entry);
1796 return;
1797 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001798 }
1799 result->NotFound();
1800}
1801
1802
1803void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
1804 LocalLookupRealNamedProperty(name, result);
1805 if (result->IsProperty()) return;
1806
1807 LookupRealNamedPropertyInPrototypes(name, result);
1808}
1809
1810
1811void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
1812 LookupResult* result) {
1813 for (Object* pt = GetPrototype();
1814 pt != Heap::null_value();
1815 pt = JSObject::cast(pt)->GetPrototype()) {
1816 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001817 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001818 }
1819 result->NotFound();
1820}
1821
1822
1823// We only need to deal with CALLBACKS and INTERCEPTORS
John Reck59135872010-11-02 12:39:01 -07001824MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
1825 String* name,
1826 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001827 if (!result->IsProperty()) {
1828 LookupCallbackSetterInPrototypes(name, result);
1829 }
1830
1831 if (result->IsProperty()) {
1832 if (!result->IsReadOnly()) {
1833 switch (result->type()) {
1834 case CALLBACKS: {
1835 Object* obj = result->GetCallbackObject();
1836 if (obj->IsAccessorInfo()) {
1837 AccessorInfo* info = AccessorInfo::cast(obj);
1838 if (info->all_can_write()) {
1839 return SetPropertyWithCallback(result->GetCallbackObject(),
1840 name,
1841 value,
1842 result->holder());
1843 }
1844 }
1845 break;
1846 }
1847 case INTERCEPTOR: {
1848 // Try lookup real named properties. Note that only property can be
1849 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
1850 LookupResult r;
1851 LookupRealNamedProperty(name, &r);
1852 if (r.IsProperty()) {
1853 return SetPropertyWithFailedAccessCheck(&r, name, value);
1854 }
1855 break;
1856 }
1857 default: {
1858 break;
1859 }
1860 }
1861 }
1862 }
1863
Iain Merrick75681382010-08-19 15:07:18 +01001864 HandleScope scope;
1865 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001866 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01001867 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00001868}
1869
1870
John Reck59135872010-11-02 12:39:01 -07001871MaybeObject* JSObject::SetProperty(LookupResult* result,
1872 String* name,
1873 Object* value,
1874 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001875 // Make sure that the top context does not change when doing callbacks or
1876 // interceptor calls.
1877 AssertNoContextChange ncc;
1878
Steve Blockd0582a62009-12-15 09:54:21 +00001879 // Optimization for 2-byte strings often used as keys in a decompression
1880 // dictionary. We make these short keys into symbols to avoid constantly
1881 // reallocating them.
1882 if (!name->IsSymbol() && name->length() <= 2) {
John Reck59135872010-11-02 12:39:01 -07001883 Object* symbol_version;
1884 { MaybeObject* maybe_symbol_version = Heap::LookupSymbol(name);
1885 if (maybe_symbol_version->ToObject(&symbol_version)) {
1886 name = String::cast(symbol_version);
1887 }
1888 }
Steve Blockd0582a62009-12-15 09:54:21 +00001889 }
1890
Steve Blocka7e24c12009-10-30 11:49:00 +00001891 // Check access rights if needed.
1892 if (IsAccessCheckNeeded()
1893 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1894 return SetPropertyWithFailedAccessCheck(result, name, value);
1895 }
1896
1897 if (IsJSGlobalProxy()) {
1898 Object* proto = GetPrototype();
1899 if (proto->IsNull()) return value;
1900 ASSERT(proto->IsJSGlobalObject());
1901 return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
1902 }
1903
1904 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
1905 // We could not find a local property so let's check whether there is an
1906 // accessor that wants to handle the property.
1907 LookupResult accessor_result;
1908 LookupCallbackSetterInPrototypes(name, &accessor_result);
Andrei Popescu402d9372010-02-26 13:31:12 +00001909 if (accessor_result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001910 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
1911 name,
1912 value,
1913 accessor_result.holder());
1914 }
1915 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001916 if (!result->IsFound()) {
1917 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00001918 return AddProperty(name, value, attributes);
1919 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001920 if (result->IsReadOnly() && result->IsProperty()) return value;
1921 // This is a real property that is not read-only, or it is a
1922 // transition or null descriptor and there are no setters in the prototypes.
1923 switch (result->type()) {
1924 case NORMAL:
1925 return SetNormalizedProperty(result, value);
1926 case FIELD:
1927 return FastPropertyAtPut(result->GetFieldIndex(), value);
1928 case MAP_TRANSITION:
1929 if (attributes == result->GetAttributes()) {
1930 // Only use map transition if the attributes match.
1931 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1932 name,
1933 value);
1934 }
1935 return ConvertDescriptorToField(name, value, attributes);
1936 case CONSTANT_FUNCTION:
1937 // Only replace the function if necessary.
1938 if (value == result->GetConstantFunction()) return value;
1939 // Preserve the attributes of this existing property.
1940 attributes = result->GetAttributes();
1941 return ConvertDescriptorToField(name, value, attributes);
1942 case CALLBACKS:
1943 return SetPropertyWithCallback(result->GetCallbackObject(),
1944 name,
1945 value,
1946 result->holder());
1947 case INTERCEPTOR:
1948 return SetPropertyWithInterceptor(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01001949 case CONSTANT_TRANSITION: {
1950 // If the same constant function is being added we can simply
1951 // transition to the target map.
1952 Map* target_map = result->GetTransitionMap();
1953 DescriptorArray* target_descriptors = target_map->instance_descriptors();
1954 int number = target_descriptors->SearchWithCache(name);
1955 ASSERT(number != DescriptorArray::kNotFound);
1956 ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
1957 JSFunction* function =
1958 JSFunction::cast(target_descriptors->GetValue(number));
1959 ASSERT(!Heap::InNewSpace(function));
1960 if (value == function) {
1961 set_map(target_map);
1962 return value;
1963 }
1964 // Otherwise, replace with a MAP_TRANSITION to a new map with a
1965 // FIELD, even if the value is a constant function.
Steve Blocka7e24c12009-10-30 11:49:00 +00001966 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
Iain Merrick75681382010-08-19 15:07:18 +01001967 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001968 case NULL_DESCRIPTOR:
1969 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
1970 default:
1971 UNREACHABLE();
1972 }
1973 UNREACHABLE();
1974 return value;
1975}
1976
1977
1978// Set a real local property, even if it is READ_ONLY. If the property is not
1979// present, add it with attributes NONE. This code is an exact clone of
1980// SetProperty, with the check for IsReadOnly and the check for a
1981// callback setter removed. The two lines looking up the LookupResult
1982// result are also added. If one of the functions is changed, the other
1983// should be.
John Reck59135872010-11-02 12:39:01 -07001984MaybeObject* JSObject::IgnoreAttributesAndSetLocalProperty(
Steve Blocka7e24c12009-10-30 11:49:00 +00001985 String* name,
1986 Object* value,
1987 PropertyAttributes attributes) {
1988 // Make sure that the top context does not change when doing callbacks or
1989 // interceptor calls.
1990 AssertNoContextChange ncc;
Andrei Popescu402d9372010-02-26 13:31:12 +00001991 LookupResult result;
1992 LocalLookup(name, &result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001993 // Check access rights if needed.
1994 if (IsAccessCheckNeeded()
Andrei Popescu402d9372010-02-26 13:31:12 +00001995 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1996 return SetPropertyWithFailedAccessCheck(&result, name, value);
Steve Blocka7e24c12009-10-30 11:49:00 +00001997 }
1998
1999 if (IsJSGlobalProxy()) {
2000 Object* proto = GetPrototype();
2001 if (proto->IsNull()) return value;
2002 ASSERT(proto->IsJSGlobalObject());
2003 return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
2004 name,
2005 value,
2006 attributes);
2007 }
2008
2009 // Check for accessor in prototype chain removed here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00002010 if (!result.IsFound()) {
2011 // Neither properties nor transitions found.
Steve Blocka7e24c12009-10-30 11:49:00 +00002012 return AddProperty(name, value, attributes);
2013 }
Steve Block6ded16b2010-05-10 14:33:55 +01002014
Andrei Popescu402d9372010-02-26 13:31:12 +00002015 PropertyDetails details = PropertyDetails(attributes, NORMAL);
2016
Steve Blocka7e24c12009-10-30 11:49:00 +00002017 // Check of IsReadOnly removed from here in clone.
Andrei Popescu402d9372010-02-26 13:31:12 +00002018 switch (result.type()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002019 case NORMAL:
Andrei Popescu402d9372010-02-26 13:31:12 +00002020 return SetNormalizedProperty(name, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00002021 case FIELD:
Andrei Popescu402d9372010-02-26 13:31:12 +00002022 return FastPropertyAtPut(result.GetFieldIndex(), value);
Steve Blocka7e24c12009-10-30 11:49:00 +00002023 case MAP_TRANSITION:
Andrei Popescu402d9372010-02-26 13:31:12 +00002024 if (attributes == result.GetAttributes()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002025 // Only use map transition if the attributes match.
Andrei Popescu402d9372010-02-26 13:31:12 +00002026 return AddFastPropertyUsingMap(result.GetTransitionMap(),
Steve Blocka7e24c12009-10-30 11:49:00 +00002027 name,
2028 value);
2029 }
2030 return ConvertDescriptorToField(name, value, attributes);
2031 case CONSTANT_FUNCTION:
2032 // Only replace the function if necessary.
Andrei Popescu402d9372010-02-26 13:31:12 +00002033 if (value == result.GetConstantFunction()) return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00002034 // Preserve the attributes of this existing property.
Andrei Popescu402d9372010-02-26 13:31:12 +00002035 attributes = result.GetAttributes();
Steve Blocka7e24c12009-10-30 11:49:00 +00002036 return ConvertDescriptorToField(name, value, attributes);
2037 case CALLBACKS:
2038 case INTERCEPTOR:
2039 // Override callback in clone
2040 return ConvertDescriptorToField(name, value, attributes);
2041 case CONSTANT_TRANSITION:
2042 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
2043 // if the value is a function.
2044 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2045 case NULL_DESCRIPTOR:
2046 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
2047 default:
2048 UNREACHABLE();
2049 }
2050 UNREACHABLE();
2051 return value;
2052}
2053
2054
2055PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
2056 JSObject* receiver,
2057 String* name,
2058 bool continue_search) {
2059 // Check local property, ignore interceptor.
2060 LookupResult result;
2061 LocalLookupRealNamedProperty(name, &result);
2062 if (result.IsProperty()) return result.GetAttributes();
2063
2064 if (continue_search) {
2065 // Continue searching via the prototype chain.
2066 Object* pt = GetPrototype();
2067 if (pt != Heap::null_value()) {
2068 return JSObject::cast(pt)->
2069 GetPropertyAttributeWithReceiver(receiver, name);
2070 }
2071 }
2072 return ABSENT;
2073}
2074
2075
2076PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
2077 JSObject* receiver,
2078 String* name,
2079 bool continue_search) {
2080 // Make sure that the top context does not change when doing
2081 // callbacks or interceptor calls.
2082 AssertNoContextChange ncc;
2083
2084 HandleScope scope;
2085 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2086 Handle<JSObject> receiver_handle(receiver);
2087 Handle<JSObject> holder_handle(this);
2088 Handle<String> name_handle(name);
2089 CustomArguments args(interceptor->data(), receiver, this);
2090 v8::AccessorInfo info(args.end());
2091 if (!interceptor->query()->IsUndefined()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002092 v8::NamedPropertyQuery query =
2093 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
Steve Blocka7e24c12009-10-30 11:49:00 +00002094 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002095 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00002096 {
2097 // Leaving JavaScript.
2098 VMState state(EXTERNAL);
2099 result = query(v8::Utils::ToLocal(name_handle), info);
2100 }
2101 if (!result.IsEmpty()) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01002102 ASSERT(result->IsInt32());
2103 return static_cast<PropertyAttributes>(result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002104 }
2105 } else if (!interceptor->getter()->IsUndefined()) {
2106 v8::NamedPropertyGetter getter =
2107 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
2108 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
2109 v8::Handle<v8::Value> result;
2110 {
2111 // Leaving JavaScript.
2112 VMState state(EXTERNAL);
2113 result = getter(v8::Utils::ToLocal(name_handle), info);
2114 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002115 if (!result.IsEmpty()) return DONT_ENUM;
Steve Blocka7e24c12009-10-30 11:49:00 +00002116 }
2117 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2118 *name_handle,
2119 continue_search);
2120}
2121
2122
2123PropertyAttributes JSObject::GetPropertyAttributeWithReceiver(
2124 JSObject* receiver,
2125 String* key) {
2126 uint32_t index = 0;
2127 if (key->AsArrayIndex(&index)) {
2128 if (HasElementWithReceiver(receiver, index)) return NONE;
2129 return ABSENT;
2130 }
2131 // Named property.
2132 LookupResult result;
2133 Lookup(key, &result);
2134 return GetPropertyAttribute(receiver, &result, key, true);
2135}
2136
2137
2138PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
2139 LookupResult* result,
2140 String* name,
2141 bool continue_search) {
2142 // Check access rights if needed.
2143 if (IsAccessCheckNeeded() &&
2144 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2145 return GetPropertyAttributeWithFailedAccessCheck(receiver,
2146 result,
2147 name,
2148 continue_search);
2149 }
Andrei Popescu402d9372010-02-26 13:31:12 +00002150 if (result->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002151 switch (result->type()) {
2152 case NORMAL: // fall through
2153 case FIELD:
2154 case CONSTANT_FUNCTION:
2155 case CALLBACKS:
2156 return result->GetAttributes();
2157 case INTERCEPTOR:
2158 return result->holder()->
2159 GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
Steve Blocka7e24c12009-10-30 11:49:00 +00002160 default:
2161 UNREACHABLE();
Steve Blocka7e24c12009-10-30 11:49:00 +00002162 }
2163 }
2164 return ABSENT;
2165}
2166
2167
2168PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) {
2169 // Check whether the name is an array index.
2170 uint32_t index = 0;
2171 if (name->AsArrayIndex(&index)) {
2172 if (HasLocalElement(index)) return NONE;
2173 return ABSENT;
2174 }
2175 // Named property.
2176 LookupResult result;
2177 LocalLookup(name, &result);
2178 return GetPropertyAttribute(this, &result, name, false);
2179}
2180
2181
John Reck59135872010-11-02 12:39:01 -07002182MaybeObject* NormalizedMapCache::Get(JSObject* obj,
2183 PropertyNormalizationMode mode) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002184 Map* fast = obj->map();
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002185 int index = Hash(fast) % kEntries;
2186 Object* result = get(index);
2187 if (result->IsMap() && CheckHit(Map::cast(result), fast, mode)) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002188#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002189 if (FLAG_enable_slow_asserts) {
2190 // The cached map should match newly created normalized map bit-by-bit.
John Reck59135872010-11-02 12:39:01 -07002191 Object* fresh;
2192 { MaybeObject* maybe_fresh =
2193 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2194 if (maybe_fresh->ToObject(&fresh)) {
2195 ASSERT(memcmp(Map::cast(fresh)->address(),
2196 Map::cast(result)->address(),
2197 Map::kSize) == 0);
2198 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002199 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002200 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002201#endif
2202 return result;
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002203 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002204
John Reck59135872010-11-02 12:39:01 -07002205 { MaybeObject* maybe_result =
2206 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2207 if (!maybe_result->ToObject(&result)) return maybe_result;
2208 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002209 set(index, result);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002210 Counters::normalized_maps.Increment();
2211
2212 return result;
2213}
2214
2215
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002216void NormalizedMapCache::Clear() {
2217 int entries = length();
2218 for (int i = 0; i != entries; i++) {
2219 set_undefined(i);
2220 }
2221}
2222
2223
2224int NormalizedMapCache::Hash(Map* fast) {
2225 // For performance reasons we only hash the 3 most variable fields of a map:
2226 // constructor, prototype and bit_field2.
2227
2228 // Shift away the tag.
2229 int hash = (static_cast<uint32_t>(
2230 reinterpret_cast<uintptr_t>(fast->constructor())) >> 2);
2231
2232 // XOR-ing the prototype and constructor directly yields too many zero bits
2233 // when the two pointers are close (which is fairly common).
2234 // To avoid this we shift the prototype 4 bits relatively to the constructor.
2235 hash ^= (static_cast<uint32_t>(
2236 reinterpret_cast<uintptr_t>(fast->prototype())) << 2);
2237
2238 return hash ^ (hash >> 16) ^ fast->bit_field2();
2239}
2240
2241
2242bool NormalizedMapCache::CheckHit(Map* slow,
2243 Map* fast,
2244 PropertyNormalizationMode mode) {
2245#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002246 slow->SharedMapVerify();
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002247#endif
2248 return
2249 slow->constructor() == fast->constructor() &&
2250 slow->prototype() == fast->prototype() &&
2251 slow->inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ?
2252 0 :
2253 fast->inobject_properties()) &&
2254 slow->instance_type() == fast->instance_type() &&
2255 slow->bit_field() == fast->bit_field() &&
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002256 (slow->bit_field2() & ~(1<<Map::kIsShared)) == fast->bit_field2();
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002257}
2258
2259
John Reck59135872010-11-02 12:39:01 -07002260MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002261 if (map()->is_shared()) {
2262 // Fast case maps are never marked as shared.
2263 ASSERT(!HasFastProperties());
2264 // Replace the map with an identical copy that can be safely modified.
John Reck59135872010-11-02 12:39:01 -07002265 Object* obj;
2266 { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES,
2267 UNIQUE_NORMALIZED_MAP);
2268 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2269 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002270 Counters::normalized_maps.Increment();
2271
2272 set_map(Map::cast(obj));
2273 }
2274 return map()->UpdateCodeCache(name, code);
2275}
2276
2277
John Reck59135872010-11-02 12:39:01 -07002278MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2279 int expected_additional_properties) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002280 if (!HasFastProperties()) return this;
2281
2282 // The global object is always normalized.
2283 ASSERT(!IsGlobalObject());
2284
2285 // Allocate new content.
2286 int property_count = map()->NumberOfDescribedProperties();
2287 if (expected_additional_properties > 0) {
2288 property_count += expected_additional_properties;
2289 } else {
2290 property_count += 2; // Make space for two more properties.
2291 }
John Reck59135872010-11-02 12:39:01 -07002292 Object* obj;
2293 { MaybeObject* maybe_obj =
2294 StringDictionary::Allocate(property_count);
2295 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2296 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002297 StringDictionary* dictionary = StringDictionary::cast(obj);
2298
2299 DescriptorArray* descs = map()->instance_descriptors();
2300 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2301 PropertyDetails details = descs->GetDetails(i);
2302 switch (details.type()) {
2303 case CONSTANT_FUNCTION: {
2304 PropertyDetails d =
2305 PropertyDetails(details.attributes(), NORMAL, details.index());
2306 Object* value = descs->GetConstantFunction(i);
John Reck59135872010-11-02 12:39:01 -07002307 Object* result;
2308 { MaybeObject* maybe_result =
2309 dictionary->Add(descs->GetKey(i), value, d);
2310 if (!maybe_result->ToObject(&result)) return maybe_result;
2311 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002312 dictionary = StringDictionary::cast(result);
2313 break;
2314 }
2315 case FIELD: {
2316 PropertyDetails d =
2317 PropertyDetails(details.attributes(), NORMAL, details.index());
2318 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
John Reck59135872010-11-02 12:39:01 -07002319 Object* result;
2320 { MaybeObject* maybe_result =
2321 dictionary->Add(descs->GetKey(i), value, d);
2322 if (!maybe_result->ToObject(&result)) return maybe_result;
2323 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002324 dictionary = StringDictionary::cast(result);
2325 break;
2326 }
2327 case CALLBACKS: {
2328 PropertyDetails d =
2329 PropertyDetails(details.attributes(), CALLBACKS, details.index());
2330 Object* value = descs->GetCallbacksObject(i);
John Reck59135872010-11-02 12:39:01 -07002331 Object* result;
2332 { MaybeObject* maybe_result =
2333 dictionary->Add(descs->GetKey(i), value, d);
2334 if (!maybe_result->ToObject(&result)) return maybe_result;
2335 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002336 dictionary = StringDictionary::cast(result);
2337 break;
2338 }
2339 case MAP_TRANSITION:
2340 case CONSTANT_TRANSITION:
2341 case NULL_DESCRIPTOR:
2342 case INTERCEPTOR:
2343 break;
2344 default:
2345 UNREACHABLE();
2346 }
2347 }
2348
2349 // Copy the next enumeration index from instance descriptor.
2350 int index = map()->instance_descriptors()->NextEnumerationIndex();
2351 dictionary->SetNextEnumerationIndex(index);
2352
John Reck59135872010-11-02 12:39:01 -07002353 { MaybeObject* maybe_obj = Top::context()->global_context()->
2354 normalized_map_cache()->Get(this, mode);
2355 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2356 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002357 Map* new_map = Map::cast(obj);
2358
Steve Blocka7e24c12009-10-30 11:49:00 +00002359 // We have now successfully allocated all the necessary objects.
2360 // Changes can now be made with the guarantee that all of them take effect.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01002361
2362 // Resize the object in the heap if necessary.
2363 int new_instance_size = new_map->instance_size();
2364 int instance_size_delta = map()->instance_size() - new_instance_size;
2365 ASSERT(instance_size_delta >= 0);
2366 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2367 instance_size_delta);
2368
Steve Blocka7e24c12009-10-30 11:49:00 +00002369 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002370
2371 set_properties(dictionary);
2372
2373 Counters::props_to_dictionary.Increment();
2374
2375#ifdef DEBUG
2376 if (FLAG_trace_normalization) {
2377 PrintF("Object properties have been normalized:\n");
2378 Print();
2379 }
2380#endif
2381 return this;
2382}
2383
2384
John Reck59135872010-11-02 12:39:01 -07002385MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002386 if (HasFastProperties()) return this;
2387 ASSERT(!IsGlobalObject());
2388 return property_dictionary()->
2389 TransformPropertiesToFastFor(this, unused_property_fields);
2390}
2391
2392
John Reck59135872010-11-02 12:39:01 -07002393MaybeObject* JSObject::NormalizeElements() {
Steve Block3ce2e202009-11-05 08:53:23 +00002394 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002395 if (HasDictionaryElements()) return this;
Steve Block8defd9f2010-07-08 12:39:36 +01002396 ASSERT(map()->has_fast_elements());
2397
John Reck59135872010-11-02 12:39:01 -07002398 Object* obj;
2399 { MaybeObject* maybe_obj = map()->GetSlowElementsMap();
2400 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2401 }
Steve Block8defd9f2010-07-08 12:39:36 +01002402 Map* new_map = Map::cast(obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00002403
2404 // Get number of entries.
2405 FixedArray* array = FixedArray::cast(elements());
2406
2407 // Compute the effective length.
2408 int length = IsJSArray() ?
2409 Smi::cast(JSArray::cast(this)->length())->value() :
2410 array->length();
John Reck59135872010-11-02 12:39:01 -07002411 { MaybeObject* maybe_obj = NumberDictionary::Allocate(length);
2412 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2413 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002414 NumberDictionary* dictionary = NumberDictionary::cast(obj);
2415 // Copy entries.
2416 for (int i = 0; i < length; i++) {
2417 Object* value = array->get(i);
2418 if (!value->IsTheHole()) {
2419 PropertyDetails details = PropertyDetails(NONE, NORMAL);
John Reck59135872010-11-02 12:39:01 -07002420 Object* result;
2421 { MaybeObject* maybe_result =
2422 dictionary->AddNumberEntry(i, array->get(i), details);
2423 if (!maybe_result->ToObject(&result)) return maybe_result;
2424 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002425 dictionary = NumberDictionary::cast(result);
2426 }
2427 }
Steve Block8defd9f2010-07-08 12:39:36 +01002428 // Switch to using the dictionary as the backing storage for
2429 // elements. Set the new map first to satify the elements type
2430 // assert in set_elements().
2431 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00002432 set_elements(dictionary);
2433
2434 Counters::elements_to_dictionary.Increment();
2435
2436#ifdef DEBUG
2437 if (FLAG_trace_normalization) {
2438 PrintF("Object elements have been normalized:\n");
2439 Print();
2440 }
2441#endif
2442
2443 return this;
2444}
2445
2446
John Reck59135872010-11-02 12:39:01 -07002447MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
2448 DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002449 // Check local property, ignore interceptor.
2450 LookupResult result;
2451 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002452 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002453
2454 // Normalize object if needed.
John Reck59135872010-11-02 12:39:01 -07002455 Object* obj;
2456 { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2457 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2458 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002459
2460 return DeleteNormalizedProperty(name, mode);
2461}
2462
2463
John Reck59135872010-11-02 12:39:01 -07002464MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002465 HandleScope scope;
2466 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2467 Handle<String> name_handle(name);
2468 Handle<JSObject> this_handle(this);
2469 if (!interceptor->deleter()->IsUndefined()) {
2470 v8::NamedPropertyDeleter deleter =
2471 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2472 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2473 CustomArguments args(interceptor->data(), this, this);
2474 v8::AccessorInfo info(args.end());
2475 v8::Handle<v8::Boolean> result;
2476 {
2477 // Leaving JavaScript.
2478 VMState state(EXTERNAL);
2479 result = deleter(v8::Utils::ToLocal(name_handle), info);
2480 }
2481 RETURN_IF_SCHEDULED_EXCEPTION();
2482 if (!result.IsEmpty()) {
2483 ASSERT(result->IsBoolean());
2484 return *v8::Utils::OpenHandle(*result);
2485 }
2486 }
John Reck59135872010-11-02 12:39:01 -07002487 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00002488 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
2489 RETURN_IF_SCHEDULED_EXCEPTION();
2490 return raw_result;
2491}
2492
2493
John Reck59135872010-11-02 12:39:01 -07002494MaybeObject* JSObject::DeleteElementPostInterceptor(uint32_t index,
2495 DeleteMode mode) {
Steve Block3ce2e202009-11-05 08:53:23 +00002496 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00002497 switch (GetElementsKind()) {
2498 case FAST_ELEMENTS: {
John Reck59135872010-11-02 12:39:01 -07002499 Object* obj;
2500 { MaybeObject* maybe_obj = EnsureWritableFastElements();
2501 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2502 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002503 uint32_t length = IsJSArray() ?
2504 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2505 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2506 if (index < length) {
2507 FixedArray::cast(elements())->set_the_hole(index);
2508 }
2509 break;
2510 }
2511 case DICTIONARY_ELEMENTS: {
2512 NumberDictionary* dictionary = element_dictionary();
2513 int entry = dictionary->FindEntry(index);
2514 if (entry != NumberDictionary::kNotFound) {
2515 return dictionary->DeleteProperty(entry, mode);
2516 }
2517 break;
2518 }
2519 default:
2520 UNREACHABLE();
2521 break;
2522 }
2523 return Heap::true_value();
2524}
2525
2526
John Reck59135872010-11-02 12:39:01 -07002527MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002528 // Make sure that the top context does not change when doing
2529 // callbacks or interceptor calls.
2530 AssertNoContextChange ncc;
2531 HandleScope scope;
2532 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2533 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2534 v8::IndexedPropertyDeleter deleter =
2535 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2536 Handle<JSObject> this_handle(this);
2537 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2538 CustomArguments args(interceptor->data(), this, this);
2539 v8::AccessorInfo info(args.end());
2540 v8::Handle<v8::Boolean> result;
2541 {
2542 // Leaving JavaScript.
2543 VMState state(EXTERNAL);
2544 result = deleter(index, info);
2545 }
2546 RETURN_IF_SCHEDULED_EXCEPTION();
2547 if (!result.IsEmpty()) {
2548 ASSERT(result->IsBoolean());
2549 return *v8::Utils::OpenHandle(*result);
2550 }
John Reck59135872010-11-02 12:39:01 -07002551 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00002552 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
2553 RETURN_IF_SCHEDULED_EXCEPTION();
2554 return raw_result;
2555}
2556
2557
John Reck59135872010-11-02 12:39:01 -07002558MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002559 // Check access rights if needed.
2560 if (IsAccessCheckNeeded() &&
2561 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
2562 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2563 return Heap::false_value();
2564 }
2565
2566 if (IsJSGlobalProxy()) {
2567 Object* proto = GetPrototype();
2568 if (proto->IsNull()) return Heap::false_value();
2569 ASSERT(proto->IsJSGlobalObject());
2570 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
2571 }
2572
2573 if (HasIndexedInterceptor()) {
2574 // Skip interceptor if forcing deletion.
2575 if (mode == FORCE_DELETION) {
2576 return DeleteElementPostInterceptor(index, mode);
2577 }
2578 return DeleteElementWithInterceptor(index);
2579 }
2580
2581 switch (GetElementsKind()) {
2582 case FAST_ELEMENTS: {
John Reck59135872010-11-02 12:39:01 -07002583 Object* obj;
2584 { MaybeObject* maybe_obj = EnsureWritableFastElements();
2585 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2586 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002587 uint32_t length = IsJSArray() ?
2588 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2589 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2590 if (index < length) {
2591 FixedArray::cast(elements())->set_the_hole(index);
2592 }
2593 break;
2594 }
Steve Block3ce2e202009-11-05 08:53:23 +00002595 case PIXEL_ELEMENTS:
2596 case EXTERNAL_BYTE_ELEMENTS:
2597 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2598 case EXTERNAL_SHORT_ELEMENTS:
2599 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2600 case EXTERNAL_INT_ELEMENTS:
2601 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2602 case EXTERNAL_FLOAT_ELEMENTS:
2603 // Pixel and external array elements cannot be deleted. Just
2604 // silently ignore here.
Steve Blocka7e24c12009-10-30 11:49:00 +00002605 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00002606 case DICTIONARY_ELEMENTS: {
2607 NumberDictionary* dictionary = element_dictionary();
2608 int entry = dictionary->FindEntry(index);
2609 if (entry != NumberDictionary::kNotFound) {
2610 return dictionary->DeleteProperty(entry, mode);
2611 }
2612 break;
2613 }
2614 default:
2615 UNREACHABLE();
2616 break;
2617 }
2618 return Heap::true_value();
2619}
2620
2621
John Reck59135872010-11-02 12:39:01 -07002622MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002623 // ECMA-262, 3rd, 8.6.2.5
2624 ASSERT(name->IsString());
2625
2626 // Check access rights if needed.
2627 if (IsAccessCheckNeeded() &&
2628 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2629 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2630 return Heap::false_value();
2631 }
2632
2633 if (IsJSGlobalProxy()) {
2634 Object* proto = GetPrototype();
2635 if (proto->IsNull()) return Heap::false_value();
2636 ASSERT(proto->IsJSGlobalObject());
2637 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
2638 }
2639
2640 uint32_t index = 0;
2641 if (name->AsArrayIndex(&index)) {
2642 return DeleteElement(index, mode);
2643 } else {
2644 LookupResult result;
2645 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002646 if (!result.IsProperty()) return Heap::true_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002647 // Ignore attributes if forcing a deletion.
2648 if (result.IsDontDelete() && mode != FORCE_DELETION) {
2649 return Heap::false_value();
2650 }
2651 // Check for interceptor.
2652 if (result.type() == INTERCEPTOR) {
2653 // Skip interceptor if forcing a deletion.
2654 if (mode == FORCE_DELETION) {
2655 return DeletePropertyPostInterceptor(name, mode);
2656 }
2657 return DeletePropertyWithInterceptor(name);
2658 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002659 // Normalize object if needed.
John Reck59135872010-11-02 12:39:01 -07002660 Object* obj;
2661 { MaybeObject* maybe_obj =
2662 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2663 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2664 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002665 // Make sure the properties are normalized before removing the entry.
2666 return DeleteNormalizedProperty(name, mode);
2667 }
2668}
2669
2670
2671// Check whether this object references another object.
2672bool JSObject::ReferencesObject(Object* obj) {
2673 AssertNoAllocation no_alloc;
2674
2675 // Is the object the constructor for this object?
2676 if (map()->constructor() == obj) {
2677 return true;
2678 }
2679
2680 // Is the object the prototype for this object?
2681 if (map()->prototype() == obj) {
2682 return true;
2683 }
2684
2685 // Check if the object is among the named properties.
2686 Object* key = SlowReverseLookup(obj);
2687 if (key != Heap::undefined_value()) {
2688 return true;
2689 }
2690
2691 // Check if the object is among the indexed properties.
2692 switch (GetElementsKind()) {
2693 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002694 case EXTERNAL_BYTE_ELEMENTS:
2695 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2696 case EXTERNAL_SHORT_ELEMENTS:
2697 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2698 case EXTERNAL_INT_ELEMENTS:
2699 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2700 case EXTERNAL_FLOAT_ELEMENTS:
2701 // Raw pixels and external arrays do not reference other
2702 // objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00002703 break;
2704 case FAST_ELEMENTS: {
2705 int length = IsJSArray() ?
2706 Smi::cast(JSArray::cast(this)->length())->value() :
2707 FixedArray::cast(elements())->length();
2708 for (int i = 0; i < length; i++) {
2709 Object* element = FixedArray::cast(elements())->get(i);
2710 if (!element->IsTheHole() && element == obj) {
2711 return true;
2712 }
2713 }
2714 break;
2715 }
2716 case DICTIONARY_ELEMENTS: {
2717 key = element_dictionary()->SlowReverseLookup(obj);
2718 if (key != Heap::undefined_value()) {
2719 return true;
2720 }
2721 break;
2722 }
2723 default:
2724 UNREACHABLE();
2725 break;
2726 }
2727
Steve Block6ded16b2010-05-10 14:33:55 +01002728 // For functions check the context.
2729 if (IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002730 // Get the constructor function for arguments array.
2731 JSObject* arguments_boilerplate =
2732 Top::context()->global_context()->arguments_boilerplate();
2733 JSFunction* arguments_function =
2734 JSFunction::cast(arguments_boilerplate->map()->constructor());
2735
2736 // Get the context and don't check if it is the global context.
2737 JSFunction* f = JSFunction::cast(this);
2738 Context* context = f->context();
2739 if (context->IsGlobalContext()) {
2740 return false;
2741 }
2742
2743 // Check the non-special context slots.
2744 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2745 // Only check JS objects.
2746 if (context->get(i)->IsJSObject()) {
2747 JSObject* ctxobj = JSObject::cast(context->get(i));
2748 // If it is an arguments array check the content.
2749 if (ctxobj->map()->constructor() == arguments_function) {
2750 if (ctxobj->ReferencesObject(obj)) {
2751 return true;
2752 }
2753 } else if (ctxobj == obj) {
2754 return true;
2755 }
2756 }
2757 }
2758
2759 // Check the context extension if any.
2760 if (context->has_extension()) {
2761 return context->extension()->ReferencesObject(obj);
2762 }
2763 }
2764
2765 // No references to object.
2766 return false;
2767}
2768
2769
John Reck59135872010-11-02 12:39:01 -07002770MaybeObject* JSObject::PreventExtensions() {
Steve Block8defd9f2010-07-08 12:39:36 +01002771 // If there are fast elements we normalize.
2772 if (HasFastElements()) {
John Reck59135872010-11-02 12:39:01 -07002773 Object* ok;
2774 { MaybeObject* maybe_ok = NormalizeElements();
2775 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
2776 }
Steve Block8defd9f2010-07-08 12:39:36 +01002777 }
2778 // Make sure that we never go back to fast case.
2779 element_dictionary()->set_requires_slow_elements();
2780
2781 // Do a map transition, other objects with this map may still
2782 // be extensible.
John Reck59135872010-11-02 12:39:01 -07002783 Object* new_map;
2784 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
2785 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
2786 }
Steve Block8defd9f2010-07-08 12:39:36 +01002787 Map::cast(new_map)->set_is_extensible(false);
2788 set_map(Map::cast(new_map));
2789 ASSERT(!map()->is_extensible());
2790 return new_map;
2791}
2792
2793
Steve Blocka7e24c12009-10-30 11:49:00 +00002794// Tests for the fast common case for property enumeration:
Steve Blockd0582a62009-12-15 09:54:21 +00002795// - This object and all prototypes has an enum cache (which means that it has
2796// no interceptors and needs no access checks).
2797// - This object has no elements.
2798// - No prototype has enumerable properties/elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002799bool JSObject::IsSimpleEnum() {
Steve Blocka7e24c12009-10-30 11:49:00 +00002800 for (Object* o = this;
2801 o != Heap::null_value();
2802 o = JSObject::cast(o)->GetPrototype()) {
2803 JSObject* curr = JSObject::cast(o);
Steve Blocka7e24c12009-10-30 11:49:00 +00002804 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00002805 ASSERT(!curr->HasNamedInterceptor());
2806 ASSERT(!curr->HasIndexedInterceptor());
2807 ASSERT(!curr->IsAccessCheckNeeded());
Steve Blocka7e24c12009-10-30 11:49:00 +00002808 if (curr->NumberOfEnumElements() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002809 if (curr != this) {
2810 FixedArray* curr_fixed_array =
2811 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
Steve Blockd0582a62009-12-15 09:54:21 +00002812 if (curr_fixed_array->length() > 0) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00002813 }
2814 }
2815 return true;
2816}
2817
2818
2819int Map::NumberOfDescribedProperties() {
2820 int result = 0;
2821 DescriptorArray* descs = instance_descriptors();
2822 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2823 if (descs->IsProperty(i)) result++;
2824 }
2825 return result;
2826}
2827
2828
2829int Map::PropertyIndexFor(String* name) {
2830 DescriptorArray* descs = instance_descriptors();
2831 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2832 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
2833 return descs->GetFieldIndex(i);
2834 }
2835 }
2836 return -1;
2837}
2838
2839
2840int Map::NextFreePropertyIndex() {
2841 int max_index = -1;
2842 DescriptorArray* descs = instance_descriptors();
2843 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2844 if (descs->GetType(i) == FIELD) {
2845 int current_index = descs->GetFieldIndex(i);
2846 if (current_index > max_index) max_index = current_index;
2847 }
2848 }
2849 return max_index + 1;
2850}
2851
2852
2853AccessorDescriptor* Map::FindAccessor(String* name) {
2854 DescriptorArray* descs = instance_descriptors();
2855 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2856 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
2857 return descs->GetCallbacks(i);
2858 }
2859 }
2860 return NULL;
2861}
2862
2863
2864void JSObject::LocalLookup(String* name, LookupResult* result) {
2865 ASSERT(name->IsString());
2866
2867 if (IsJSGlobalProxy()) {
2868 Object* proto = GetPrototype();
2869 if (proto->IsNull()) return result->NotFound();
2870 ASSERT(proto->IsJSGlobalObject());
2871 return JSObject::cast(proto)->LocalLookup(name, result);
2872 }
2873
2874 // Do not use inline caching if the object is a non-global object
2875 // that requires access checks.
2876 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
2877 result->DisallowCaching();
2878 }
2879
2880 // Check __proto__ before interceptor.
2881 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
2882 result->ConstantResult(this);
2883 return;
2884 }
2885
2886 // Check for lookup interceptor except when bootstrapping.
2887 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2888 result->InterceptorResult(this);
2889 return;
2890 }
2891
2892 LocalLookupRealNamedProperty(name, result);
2893}
2894
2895
2896void JSObject::Lookup(String* name, LookupResult* result) {
2897 // Ecma-262 3rd 8.6.2.4
2898 for (Object* current = this;
2899 current != Heap::null_value();
2900 current = JSObject::cast(current)->GetPrototype()) {
2901 JSObject::cast(current)->LocalLookup(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002902 if (result->IsProperty()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002903 }
2904 result->NotFound();
2905}
2906
2907
2908// Search object and it's prototype chain for callback properties.
2909void JSObject::LookupCallback(String* name, LookupResult* result) {
2910 for (Object* current = this;
2911 current != Heap::null_value();
2912 current = JSObject::cast(current)->GetPrototype()) {
2913 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002914 if (result->IsProperty() && result->type() == CALLBACKS) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002915 }
2916 result->NotFound();
2917}
2918
2919
John Reck59135872010-11-02 12:39:01 -07002920MaybeObject* JSObject::DefineGetterSetter(String* name,
2921 PropertyAttributes attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002922 // Make sure that the top context does not change when doing callbacks or
2923 // interceptor calls.
2924 AssertNoContextChange ncc;
2925
Steve Blocka7e24c12009-10-30 11:49:00 +00002926 // Try to flatten before operating on the string.
Steve Block6ded16b2010-05-10 14:33:55 +01002927 name->TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00002928
Leon Clarkef7060e22010-06-03 12:02:55 +01002929 if (!CanSetCallback(name)) {
2930 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00002931 }
2932
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01002933 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00002934 bool is_element = name->AsArrayIndex(&index);
Steve Blocka7e24c12009-10-30 11:49:00 +00002935
2936 if (is_element) {
2937 switch (GetElementsKind()) {
2938 case FAST_ELEMENTS:
2939 break;
2940 case PIXEL_ELEMENTS:
Steve Block3ce2e202009-11-05 08:53:23 +00002941 case EXTERNAL_BYTE_ELEMENTS:
2942 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
2943 case EXTERNAL_SHORT_ELEMENTS:
2944 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
2945 case EXTERNAL_INT_ELEMENTS:
2946 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
2947 case EXTERNAL_FLOAT_ELEMENTS:
2948 // Ignore getters and setters on pixel and external array
2949 // elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00002950 return Heap::undefined_value();
2951 case DICTIONARY_ELEMENTS: {
2952 // Lookup the index.
2953 NumberDictionary* dictionary = element_dictionary();
2954 int entry = dictionary->FindEntry(index);
2955 if (entry != NumberDictionary::kNotFound) {
2956 Object* result = dictionary->ValueAt(entry);
2957 PropertyDetails details = dictionary->DetailsAt(entry);
2958 if (details.IsReadOnly()) return Heap::undefined_value();
2959 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002960 if (result->IsFixedArray()) {
2961 return result;
2962 }
2963 // Otherwise allow to override it.
Steve Blocka7e24c12009-10-30 11:49:00 +00002964 }
2965 }
2966 break;
2967 }
2968 default:
2969 UNREACHABLE();
2970 break;
2971 }
2972 } else {
2973 // Lookup the name.
2974 LookupResult result;
2975 LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00002976 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00002977 if (result.IsReadOnly()) return Heap::undefined_value();
2978 if (result.type() == CALLBACKS) {
2979 Object* obj = result.GetCallbackObject();
Leon Clarkef7060e22010-06-03 12:02:55 +01002980 // Need to preserve old getters/setters.
Leon Clarked91b9f72010-01-27 17:25:45 +00002981 if (obj->IsFixedArray()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002982 // Use set to update attributes.
2983 return SetPropertyCallback(name, obj, attributes);
Leon Clarked91b9f72010-01-27 17:25:45 +00002984 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002985 }
2986 }
2987 }
2988
2989 // Allocate the fixed array to hold getter and setter.
John Reck59135872010-11-02 12:39:01 -07002990 Object* structure;
2991 { MaybeObject* maybe_structure = Heap::AllocateFixedArray(2, TENURED);
2992 if (!maybe_structure->ToObject(&structure)) return maybe_structure;
2993 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002994
2995 if (is_element) {
Leon Clarkef7060e22010-06-03 12:02:55 +01002996 return SetElementCallback(index, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002997 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01002998 return SetPropertyCallback(name, structure, attributes);
Steve Blocka7e24c12009-10-30 11:49:00 +00002999 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003000}
3001
3002
3003bool JSObject::CanSetCallback(String* name) {
3004 ASSERT(!IsAccessCheckNeeded()
3005 || Top::MayNamedAccess(this, name, v8::ACCESS_SET));
3006
3007 // Check if there is an API defined callback object which prohibits
3008 // callback overwriting in this object or it's prototype chain.
3009 // This mechanism is needed for instance in a browser setting, where
3010 // certain accessors such as window.location should not be allowed
3011 // to be overwritten because allowing overwriting could potentially
3012 // cause security problems.
3013 LookupResult callback_result;
3014 LookupCallback(name, &callback_result);
3015 if (callback_result.IsProperty()) {
3016 Object* obj = callback_result.GetCallbackObject();
3017 if (obj->IsAccessorInfo() &&
3018 AccessorInfo::cast(obj)->prohibits_overwriting()) {
3019 return false;
3020 }
3021 }
3022
3023 return true;
3024}
3025
3026
John Reck59135872010-11-02 12:39:01 -07003027MaybeObject* JSObject::SetElementCallback(uint32_t index,
3028 Object* structure,
3029 PropertyAttributes attributes) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003030 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3031
3032 // Normalize elements to make this operation simple.
John Reck59135872010-11-02 12:39:01 -07003033 Object* ok;
3034 { MaybeObject* maybe_ok = NormalizeElements();
3035 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3036 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003037
3038 // Update the dictionary with the new CALLBACKS property.
John Reck59135872010-11-02 12:39:01 -07003039 Object* dict;
3040 { MaybeObject* maybe_dict =
3041 element_dictionary()->Set(index, structure, details);
3042 if (!maybe_dict->ToObject(&dict)) return maybe_dict;
3043 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003044
3045 NumberDictionary* elements = NumberDictionary::cast(dict);
3046 elements->set_requires_slow_elements();
3047 // Set the potential new dictionary on the object.
3048 set_elements(elements);
Steve Blocka7e24c12009-10-30 11:49:00 +00003049
3050 return structure;
3051}
3052
3053
John Reck59135872010-11-02 12:39:01 -07003054MaybeObject* JSObject::SetPropertyCallback(String* name,
3055 Object* structure,
3056 PropertyAttributes attributes) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003057 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3058
3059 bool convert_back_to_fast = HasFastProperties() &&
3060 (map()->instance_descriptors()->number_of_descriptors()
3061 < DescriptorArray::kMaxNumberOfDescriptors);
3062
3063 // Normalize object to make this operation simple.
John Reck59135872010-11-02 12:39:01 -07003064 Object* ok;
3065 { MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3066 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3067 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003068
3069 // For the global object allocate a new map to invalidate the global inline
3070 // caches which have a global property cell reference directly in the code.
3071 if (IsGlobalObject()) {
John Reck59135872010-11-02 12:39:01 -07003072 Object* new_map;
3073 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
3074 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3075 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003076 set_map(Map::cast(new_map));
Ben Murdochb0fe1622011-05-05 13:52:32 +01003077 // When running crankshaft, changing the map is not enough. We
3078 // need to deoptimize all functions that rely on this global
3079 // object.
3080 Deoptimizer::DeoptimizeGlobalObject(this);
Leon Clarkef7060e22010-06-03 12:02:55 +01003081 }
3082
3083 // Update the dictionary with the new CALLBACKS property.
John Reck59135872010-11-02 12:39:01 -07003084 Object* result;
3085 { MaybeObject* maybe_result = SetNormalizedProperty(name, structure, details);
3086 if (!maybe_result->ToObject(&result)) return maybe_result;
3087 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003088
3089 if (convert_back_to_fast) {
John Reck59135872010-11-02 12:39:01 -07003090 { MaybeObject* maybe_ok = TransformToFastProperties(0);
3091 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3092 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003093 }
3094 return result;
3095}
3096
John Reck59135872010-11-02 12:39:01 -07003097MaybeObject* JSObject::DefineAccessor(String* name,
3098 bool is_getter,
Ben Murdochb0fe1622011-05-05 13:52:32 +01003099 Object* fun,
John Reck59135872010-11-02 12:39:01 -07003100 PropertyAttributes attributes) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01003101 ASSERT(fun->IsJSFunction() || fun->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00003102 // Check access rights if needed.
3103 if (IsAccessCheckNeeded() &&
Leon Clarkef7060e22010-06-03 12:02:55 +01003104 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
3105 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Steve Blocka7e24c12009-10-30 11:49:00 +00003106 return Heap::undefined_value();
3107 }
3108
3109 if (IsJSGlobalProxy()) {
3110 Object* proto = GetPrototype();
3111 if (proto->IsNull()) return this;
3112 ASSERT(proto->IsJSGlobalObject());
3113 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
3114 fun, attributes);
3115 }
3116
John Reck59135872010-11-02 12:39:01 -07003117 Object* array;
3118 { MaybeObject* maybe_array = DefineGetterSetter(name, attributes);
3119 if (!maybe_array->ToObject(&array)) return maybe_array;
3120 }
3121 if (array->IsUndefined()) return array;
Steve Blocka7e24c12009-10-30 11:49:00 +00003122 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
3123 return this;
3124}
3125
3126
John Reck59135872010-11-02 12:39:01 -07003127MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003128 String* name = String::cast(info->name());
3129 // Check access rights if needed.
3130 if (IsAccessCheckNeeded() &&
3131 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
3132 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
3133 return Heap::undefined_value();
3134 }
3135
3136 if (IsJSGlobalProxy()) {
3137 Object* proto = GetPrototype();
3138 if (proto->IsNull()) return this;
3139 ASSERT(proto->IsJSGlobalObject());
3140 return JSObject::cast(proto)->DefineAccessor(info);
3141 }
3142
3143 // Make sure that the top context does not change when doing callbacks or
3144 // interceptor calls.
3145 AssertNoContextChange ncc;
3146
3147 // Try to flatten before operating on the string.
3148 name->TryFlatten();
3149
3150 if (!CanSetCallback(name)) {
3151 return Heap::undefined_value();
3152 }
3153
3154 uint32_t index = 0;
3155 bool is_element = name->AsArrayIndex(&index);
3156
3157 if (is_element) {
3158 if (IsJSArray()) return Heap::undefined_value();
3159
3160 // Accessors overwrite previous callbacks (cf. with getters/setters).
3161 switch (GetElementsKind()) {
3162 case FAST_ELEMENTS:
3163 break;
3164 case PIXEL_ELEMENTS:
3165 case EXTERNAL_BYTE_ELEMENTS:
3166 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3167 case EXTERNAL_SHORT_ELEMENTS:
3168 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3169 case EXTERNAL_INT_ELEMENTS:
3170 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3171 case EXTERNAL_FLOAT_ELEMENTS:
3172 // Ignore getters and setters on pixel and external array
3173 // elements.
3174 return Heap::undefined_value();
3175 case DICTIONARY_ELEMENTS:
3176 break;
3177 default:
3178 UNREACHABLE();
3179 break;
3180 }
3181
John Reck59135872010-11-02 12:39:01 -07003182 Object* ok;
3183 { MaybeObject* maybe_ok =
3184 SetElementCallback(index, info, info->property_attributes());
3185 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3186 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003187 } else {
3188 // Lookup the name.
3189 LookupResult result;
3190 LocalLookup(name, &result);
3191 // ES5 forbids turning a property into an accessor if it's not
3192 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
3193 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
3194 return Heap::undefined_value();
3195 }
John Reck59135872010-11-02 12:39:01 -07003196 Object* ok;
3197 { MaybeObject* maybe_ok =
3198 SetPropertyCallback(name, info, info->property_attributes());
3199 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3200 }
Leon Clarkef7060e22010-06-03 12:02:55 +01003201 }
3202
3203 return this;
3204}
3205
3206
Steve Blocka7e24c12009-10-30 11:49:00 +00003207Object* JSObject::LookupAccessor(String* name, bool is_getter) {
3208 // Make sure that the top context does not change when doing callbacks or
3209 // interceptor calls.
3210 AssertNoContextChange ncc;
3211
3212 // Check access rights if needed.
3213 if (IsAccessCheckNeeded() &&
3214 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
3215 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
3216 return Heap::undefined_value();
3217 }
3218
3219 // Make the lookup and include prototypes.
3220 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01003221 uint32_t index = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00003222 if (name->AsArrayIndex(&index)) {
3223 for (Object* obj = this;
3224 obj != Heap::null_value();
3225 obj = JSObject::cast(obj)->GetPrototype()) {
3226 JSObject* js_object = JSObject::cast(obj);
3227 if (js_object->HasDictionaryElements()) {
3228 NumberDictionary* dictionary = js_object->element_dictionary();
3229 int entry = dictionary->FindEntry(index);
3230 if (entry != NumberDictionary::kNotFound) {
3231 Object* element = dictionary->ValueAt(entry);
3232 PropertyDetails details = dictionary->DetailsAt(entry);
3233 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01003234 if (element->IsFixedArray()) {
3235 return FixedArray::cast(element)->get(accessor_index);
3236 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003237 }
3238 }
3239 }
3240 }
3241 } else {
3242 for (Object* obj = this;
3243 obj != Heap::null_value();
3244 obj = JSObject::cast(obj)->GetPrototype()) {
3245 LookupResult result;
3246 JSObject::cast(obj)->LocalLookup(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00003247 if (result.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003248 if (result.IsReadOnly()) return Heap::undefined_value();
3249 if (result.type() == CALLBACKS) {
3250 Object* obj = result.GetCallbackObject();
3251 if (obj->IsFixedArray()) {
3252 return FixedArray::cast(obj)->get(accessor_index);
3253 }
3254 }
3255 }
3256 }
3257 }
3258 return Heap::undefined_value();
3259}
3260
3261
3262Object* JSObject::SlowReverseLookup(Object* value) {
3263 if (HasFastProperties()) {
3264 DescriptorArray* descs = map()->instance_descriptors();
3265 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3266 if (descs->GetType(i) == FIELD) {
3267 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3268 return descs->GetKey(i);
3269 }
3270 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3271 if (descs->GetConstantFunction(i) == value) {
3272 return descs->GetKey(i);
3273 }
3274 }
3275 }
3276 return Heap::undefined_value();
3277 } else {
3278 return property_dictionary()->SlowReverseLookup(value);
3279 }
3280}
3281
3282
John Reck59135872010-11-02 12:39:01 -07003283MaybeObject* Map::CopyDropDescriptors() {
3284 Object* result;
3285 { MaybeObject* maybe_result =
3286 Heap::AllocateMap(instance_type(), instance_size());
3287 if (!maybe_result->ToObject(&result)) return maybe_result;
3288 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003289 Map::cast(result)->set_prototype(prototype());
3290 Map::cast(result)->set_constructor(constructor());
3291 // Don't copy descriptors, so map transitions always remain a forest.
3292 // If we retained the same descriptors we would have two maps
3293 // pointing to the same transition which is bad because the garbage
3294 // collector relies on being able to reverse pointers from transitions
3295 // to maps. If properties need to be retained use CopyDropTransitions.
3296 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
3297 // Please note instance_type and instance_size are set when allocated.
3298 Map::cast(result)->set_inobject_properties(inobject_properties());
3299 Map::cast(result)->set_unused_property_fields(unused_property_fields());
3300
3301 // If the map has pre-allocated properties always start out with a descriptor
3302 // array describing these properties.
3303 if (pre_allocated_property_fields() > 0) {
3304 ASSERT(constructor()->IsJSFunction());
3305 JSFunction* ctor = JSFunction::cast(constructor());
John Reck59135872010-11-02 12:39:01 -07003306 Object* descriptors;
3307 { MaybeObject* maybe_descriptors =
3308 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
3309 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
3310 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003311 Map::cast(result)->set_instance_descriptors(
3312 DescriptorArray::cast(descriptors));
3313 Map::cast(result)->set_pre_allocated_property_fields(
3314 pre_allocated_property_fields());
3315 }
3316 Map::cast(result)->set_bit_field(bit_field());
3317 Map::cast(result)->set_bit_field2(bit_field2());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003318 Map::cast(result)->set_is_shared(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00003319 Map::cast(result)->ClearCodeCache();
3320 return result;
3321}
3322
3323
John Reck59135872010-11-02 12:39:01 -07003324MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
3325 NormalizedMapSharingMode sharing) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003326 int new_instance_size = instance_size();
3327 if (mode == CLEAR_INOBJECT_PROPERTIES) {
3328 new_instance_size -= inobject_properties() * kPointerSize;
3329 }
3330
John Reck59135872010-11-02 12:39:01 -07003331 Object* result;
3332 { MaybeObject* maybe_result =
3333 Heap::AllocateMap(instance_type(), new_instance_size);
3334 if (!maybe_result->ToObject(&result)) return maybe_result;
3335 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003336
3337 if (mode != CLEAR_INOBJECT_PROPERTIES) {
3338 Map::cast(result)->set_inobject_properties(inobject_properties());
3339 }
3340
3341 Map::cast(result)->set_prototype(prototype());
3342 Map::cast(result)->set_constructor(constructor());
3343
3344 Map::cast(result)->set_bit_field(bit_field());
3345 Map::cast(result)->set_bit_field2(bit_field2());
3346
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003347 Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
3348
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003349#ifdef DEBUG
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003350 if (Map::cast(result)->is_shared()) {
3351 Map::cast(result)->SharedMapVerify();
3352 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01003353#endif
3354
3355 return result;
3356}
3357
3358
John Reck59135872010-11-02 12:39:01 -07003359MaybeObject* Map::CopyDropTransitions() {
3360 Object* new_map;
3361 { MaybeObject* maybe_new_map = CopyDropDescriptors();
3362 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3363 }
3364 Object* descriptors;
3365 { MaybeObject* maybe_descriptors =
3366 instance_descriptors()->RemoveTransitions();
3367 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
3368 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003369 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
Steve Block8defd9f2010-07-08 12:39:36 +01003370 return new_map;
Steve Blocka7e24c12009-10-30 11:49:00 +00003371}
3372
3373
John Reck59135872010-11-02 12:39:01 -07003374MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003375 // Allocate the code cache if not present.
3376 if (code_cache()->IsFixedArray()) {
John Reck59135872010-11-02 12:39:01 -07003377 Object* result;
3378 { MaybeObject* maybe_result = Heap::AllocateCodeCache();
3379 if (!maybe_result->ToObject(&result)) return maybe_result;
3380 }
Steve Block6ded16b2010-05-10 14:33:55 +01003381 set_code_cache(result);
3382 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003383
Steve Block6ded16b2010-05-10 14:33:55 +01003384 // Update the code cache.
3385 return CodeCache::cast(code_cache())->Update(name, code);
3386}
3387
3388
3389Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
3390 // Do a lookup if a code cache exists.
3391 if (!code_cache()->IsFixedArray()) {
3392 return CodeCache::cast(code_cache())->Lookup(name, flags);
3393 } else {
3394 return Heap::undefined_value();
3395 }
3396}
3397
3398
3399int Map::IndexInCodeCache(Object* name, Code* code) {
3400 // Get the internal index if a code cache exists.
3401 if (!code_cache()->IsFixedArray()) {
3402 return CodeCache::cast(code_cache())->GetIndex(name, code);
3403 }
3404 return -1;
3405}
3406
3407
3408void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
3409 // No GC is supposed to happen between a call to IndexInCodeCache and
3410 // RemoveFromCodeCache so the code cache must be there.
3411 ASSERT(!code_cache()->IsFixedArray());
3412 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
3413}
3414
3415
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003416void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
3417 Map* current = this;
3418 while (current != Heap::meta_map()) {
3419 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
3420 *RawField(current, Map::kInstanceDescriptorsOffset));
3421 if (d == Heap::empty_descriptor_array()) {
3422 Map* prev = current->map();
3423 current->set_map(Heap::meta_map());
3424 callback(current, data);
3425 current = prev;
3426 continue;
3427 }
3428
3429 FixedArray* contents = reinterpret_cast<FixedArray*>(
3430 d->get(DescriptorArray::kContentArrayIndex));
3431 Object** map_or_index_field = RawField(contents, HeapObject::kMapOffset);
3432 Object* map_or_index = *map_or_index_field;
3433 bool map_done = true;
3434 for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0;
3435 i < contents->length();
3436 i += 2) {
3437 PropertyDetails details(Smi::cast(contents->get(i + 1)));
3438 if (details.IsTransition()) {
3439 Map* next = reinterpret_cast<Map*>(contents->get(i));
3440 next->set_map(current);
3441 *map_or_index_field = Smi::FromInt(i + 2);
3442 current = next;
3443 map_done = false;
3444 break;
3445 }
3446 }
3447 if (!map_done) continue;
3448 *map_or_index_field = Heap::fixed_array_map();
3449 Map* prev = current->map();
3450 current->set_map(Heap::meta_map());
3451 callback(current, data);
3452 current = prev;
3453 }
3454}
3455
3456
John Reck59135872010-11-02 12:39:01 -07003457MaybeObject* CodeCache::Update(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003458 ASSERT(code->ic_state() == MONOMORPHIC);
3459
3460 // The number of monomorphic stubs for normal load/store/call IC's can grow to
3461 // a large number and therefore they need to go into a hash table. They are
3462 // used to load global properties from cells.
3463 if (code->type() == NORMAL) {
3464 // Make sure that a hash table is allocated for the normal load code cache.
3465 if (normal_type_cache()->IsUndefined()) {
John Reck59135872010-11-02 12:39:01 -07003466 Object* result;
3467 { MaybeObject* maybe_result =
3468 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
3469 if (!maybe_result->ToObject(&result)) return maybe_result;
3470 }
Steve Block6ded16b2010-05-10 14:33:55 +01003471 set_normal_type_cache(result);
3472 }
3473 return UpdateNormalTypeCache(name, code);
3474 } else {
3475 ASSERT(default_cache()->IsFixedArray());
3476 return UpdateDefaultCache(name, code);
3477 }
3478}
3479
3480
John Reck59135872010-11-02 12:39:01 -07003481MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003482 // When updating the default code cache we disregard the type encoded in the
Steve Blocka7e24c12009-10-30 11:49:00 +00003483 // flags. This allows call constant stubs to overwrite call field
3484 // stubs, etc.
3485 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
3486
3487 // First check whether we can update existing code cache without
3488 // extending it.
Steve Block6ded16b2010-05-10 14:33:55 +01003489 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003490 int length = cache->length();
3491 int deleted_index = -1;
Steve Block6ded16b2010-05-10 14:33:55 +01003492 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003493 Object* key = cache->get(i);
3494 if (key->IsNull()) {
3495 if (deleted_index < 0) deleted_index = i;
3496 continue;
3497 }
3498 if (key->IsUndefined()) {
3499 if (deleted_index >= 0) i = deleted_index;
Steve Block6ded16b2010-05-10 14:33:55 +01003500 cache->set(i + kCodeCacheEntryNameOffset, name);
3501 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003502 return this;
3503 }
3504 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003505 Code::Flags found =
3506 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
Steve Blocka7e24c12009-10-30 11:49:00 +00003507 if (Code::RemoveTypeFromFlags(found) == flags) {
Steve Block6ded16b2010-05-10 14:33:55 +01003508 cache->set(i + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003509 return this;
3510 }
3511 }
3512 }
3513
3514 // Reached the end of the code cache. If there were deleted
3515 // elements, reuse the space for the first of them.
3516 if (deleted_index >= 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01003517 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
3518 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
Steve Blocka7e24c12009-10-30 11:49:00 +00003519 return this;
3520 }
3521
Steve Block6ded16b2010-05-10 14:33:55 +01003522 // Extend the code cache with some new entries (at least one). Must be a
3523 // multiple of the entry size.
3524 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
3525 new_length = new_length - new_length % kCodeCacheEntrySize;
3526 ASSERT((new_length % kCodeCacheEntrySize) == 0);
John Reck59135872010-11-02 12:39:01 -07003527 Object* result;
3528 { MaybeObject* maybe_result = cache->CopySize(new_length);
3529 if (!maybe_result->ToObject(&result)) return maybe_result;
3530 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003531
3532 // Add the (name, code) pair to the new cache.
3533 cache = FixedArray::cast(result);
Steve Block6ded16b2010-05-10 14:33:55 +01003534 cache->set(length + kCodeCacheEntryNameOffset, name);
3535 cache->set(length + kCodeCacheEntryCodeOffset, code);
3536 set_default_cache(cache);
Steve Blocka7e24c12009-10-30 11:49:00 +00003537 return this;
3538}
3539
3540
John Reck59135872010-11-02 12:39:01 -07003541MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003542 // Adding a new entry can cause a new cache to be allocated.
3543 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
John Reck59135872010-11-02 12:39:01 -07003544 Object* new_cache;
3545 { MaybeObject* maybe_new_cache = cache->Put(name, code);
3546 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
3547 }
Steve Block6ded16b2010-05-10 14:33:55 +01003548 set_normal_type_cache(new_cache);
3549 return this;
3550}
3551
3552
3553Object* CodeCache::Lookup(String* name, Code::Flags flags) {
3554 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
3555 return LookupNormalTypeCache(name, flags);
3556 } else {
3557 return LookupDefaultCache(name, flags);
3558 }
3559}
3560
3561
3562Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
3563 FixedArray* cache = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003564 int length = cache->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003565 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
3566 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
Steve Blocka7e24c12009-10-30 11:49:00 +00003567 // Skip deleted elements.
3568 if (key->IsNull()) continue;
3569 if (key->IsUndefined()) return key;
3570 if (name->Equals(String::cast(key))) {
Steve Block6ded16b2010-05-10 14:33:55 +01003571 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
3572 if (code->flags() == flags) {
3573 return code;
3574 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003575 }
3576 }
3577 return Heap::undefined_value();
3578}
3579
3580
Steve Block6ded16b2010-05-10 14:33:55 +01003581Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
3582 if (!normal_type_cache()->IsUndefined()) {
3583 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3584 return cache->Lookup(name, flags);
3585 } else {
3586 return Heap::undefined_value();
3587 }
3588}
3589
3590
3591int CodeCache::GetIndex(Object* name, Code* code) {
3592 if (code->type() == NORMAL) {
3593 if (normal_type_cache()->IsUndefined()) return -1;
3594 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3595 return cache->GetIndex(String::cast(name), code->flags());
3596 }
3597
3598 FixedArray* array = default_cache();
Steve Blocka7e24c12009-10-30 11:49:00 +00003599 int len = array->length();
Steve Block6ded16b2010-05-10 14:33:55 +01003600 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
3601 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00003602 }
3603 return -1;
3604}
3605
3606
Steve Block6ded16b2010-05-10 14:33:55 +01003607void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
3608 if (code->type() == NORMAL) {
3609 ASSERT(!normal_type_cache()->IsUndefined());
3610 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
3611 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
3612 cache->RemoveByIndex(index);
3613 } else {
3614 FixedArray* array = default_cache();
3615 ASSERT(array->length() >= index && array->get(index)->IsCode());
3616 // Use null instead of undefined for deleted elements to distinguish
3617 // deleted elements from unused elements. This distinction is used
3618 // when looking up in the cache and when updating the cache.
3619 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
3620 array->set_null(index - 1); // Name.
3621 array->set_null(index); // Code.
3622 }
3623}
3624
3625
3626// The key in the code cache hash table consists of the property name and the
3627// code object. The actual match is on the name and the code flags. If a key
3628// is created using the flags and not a code object it can only be used for
3629// lookup not to create a new entry.
3630class CodeCacheHashTableKey : public HashTableKey {
3631 public:
3632 CodeCacheHashTableKey(String* name, Code::Flags flags)
3633 : name_(name), flags_(flags), code_(NULL) { }
3634
3635 CodeCacheHashTableKey(String* name, Code* code)
3636 : name_(name),
3637 flags_(code->flags()),
3638 code_(code) { }
3639
3640
3641 bool IsMatch(Object* other) {
3642 if (!other->IsFixedArray()) return false;
3643 FixedArray* pair = FixedArray::cast(other);
3644 String* name = String::cast(pair->get(0));
3645 Code::Flags flags = Code::cast(pair->get(1))->flags();
3646 if (flags != flags_) {
3647 return false;
3648 }
3649 return name_->Equals(name);
3650 }
3651
3652 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
3653 return name->Hash() ^ flags;
3654 }
3655
3656 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
3657
3658 uint32_t HashForObject(Object* obj) {
3659 FixedArray* pair = FixedArray::cast(obj);
3660 String* name = String::cast(pair->get(0));
3661 Code* code = Code::cast(pair->get(1));
3662 return NameFlagsHashHelper(name, code->flags());
3663 }
3664
John Reck59135872010-11-02 12:39:01 -07003665 MUST_USE_RESULT MaybeObject* AsObject() {
Steve Block6ded16b2010-05-10 14:33:55 +01003666 ASSERT(code_ != NULL);
John Reck59135872010-11-02 12:39:01 -07003667 Object* obj;
3668 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(2);
3669 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3670 }
Steve Block6ded16b2010-05-10 14:33:55 +01003671 FixedArray* pair = FixedArray::cast(obj);
3672 pair->set(0, name_);
3673 pair->set(1, code_);
3674 return pair;
3675 }
3676
3677 private:
3678 String* name_;
3679 Code::Flags flags_;
3680 Code* code_;
3681};
3682
3683
3684Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
3685 CodeCacheHashTableKey key(name, flags);
3686 int entry = FindEntry(&key);
3687 if (entry == kNotFound) return Heap::undefined_value();
3688 return get(EntryToIndex(entry) + 1);
3689}
3690
3691
John Reck59135872010-11-02 12:39:01 -07003692MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) {
Steve Block6ded16b2010-05-10 14:33:55 +01003693 CodeCacheHashTableKey key(name, code);
John Reck59135872010-11-02 12:39:01 -07003694 Object* obj;
3695 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
3696 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3697 }
Steve Block6ded16b2010-05-10 14:33:55 +01003698
3699 // Don't use this, as the table might have grown.
3700 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
3701
3702 int entry = cache->FindInsertionEntry(key.Hash());
John Reck59135872010-11-02 12:39:01 -07003703 Object* k;
3704 { MaybeObject* maybe_k = key.AsObject();
3705 if (!maybe_k->ToObject(&k)) return maybe_k;
3706 }
Steve Block6ded16b2010-05-10 14:33:55 +01003707
3708 cache->set(EntryToIndex(entry), k);
3709 cache->set(EntryToIndex(entry) + 1, code);
3710 cache->ElementAdded();
3711 return cache;
3712}
3713
3714
3715int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
3716 CodeCacheHashTableKey key(name, flags);
3717 int entry = FindEntry(&key);
3718 return (entry == kNotFound) ? -1 : entry;
3719}
3720
3721
3722void CodeCacheHashTable::RemoveByIndex(int index) {
3723 ASSERT(index >= 0);
3724 set(EntryToIndex(index), Heap::null_value());
3725 set(EntryToIndex(index) + 1, Heap::null_value());
3726 ElementRemoved();
Steve Blocka7e24c12009-10-30 11:49:00 +00003727}
3728
3729
Steve Blocka7e24c12009-10-30 11:49:00 +00003730static bool HasKey(FixedArray* array, Object* key) {
3731 int len0 = array->length();
3732 for (int i = 0; i < len0; i++) {
3733 Object* element = array->get(i);
3734 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
3735 if (element->IsString() &&
3736 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
3737 return true;
3738 }
3739 }
3740 return false;
3741}
3742
3743
John Reck59135872010-11-02 12:39:01 -07003744MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
Steve Block3ce2e202009-11-05 08:53:23 +00003745 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00003746 switch (array->GetElementsKind()) {
3747 case JSObject::FAST_ELEMENTS:
3748 return UnionOfKeys(FixedArray::cast(array->elements()));
3749 case JSObject::DICTIONARY_ELEMENTS: {
3750 NumberDictionary* dict = array->element_dictionary();
3751 int size = dict->NumberOfElements();
3752
3753 // Allocate a temporary fixed array.
John Reck59135872010-11-02 12:39:01 -07003754 Object* object;
3755 { MaybeObject* maybe_object = Heap::AllocateFixedArray(size);
3756 if (!maybe_object->ToObject(&object)) return maybe_object;
3757 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003758 FixedArray* key_array = FixedArray::cast(object);
3759
3760 int capacity = dict->Capacity();
3761 int pos = 0;
3762 // Copy the elements from the JSArray to the temporary fixed array.
3763 for (int i = 0; i < capacity; i++) {
3764 if (dict->IsKey(dict->KeyAt(i))) {
3765 key_array->set(pos++, dict->ValueAt(i));
3766 }
3767 }
3768 // Compute the union of this and the temporary fixed array.
3769 return UnionOfKeys(key_array);
3770 }
3771 default:
3772 UNREACHABLE();
3773 }
3774 UNREACHABLE();
3775 return Heap::null_value(); // Failure case needs to "return" a value.
3776}
3777
3778
John Reck59135872010-11-02 12:39:01 -07003779MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003780 int len0 = length();
Ben Murdochf87a2032010-10-22 12:50:53 +01003781#ifdef DEBUG
3782 if (FLAG_enable_slow_asserts) {
3783 for (int i = 0; i < len0; i++) {
3784 ASSERT(get(i)->IsString() || get(i)->IsNumber());
3785 }
3786 }
3787#endif
Steve Blocka7e24c12009-10-30 11:49:00 +00003788 int len1 = other->length();
Ben Murdochf87a2032010-10-22 12:50:53 +01003789 // Optimize if 'other' is empty.
3790 // We cannot optimize if 'this' is empty, as other may have holes
3791 // or non keys.
Steve Blocka7e24c12009-10-30 11:49:00 +00003792 if (len1 == 0) return this;
3793
3794 // Compute how many elements are not in this.
3795 int extra = 0;
3796 for (int y = 0; y < len1; y++) {
3797 Object* value = other->get(y);
3798 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
3799 }
3800
3801 if (extra == 0) return this;
3802
3803 // Allocate the result
John Reck59135872010-11-02 12:39:01 -07003804 Object* obj;
3805 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(len0 + extra);
3806 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3807 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003808 // Fill in the content
Leon Clarke4515c472010-02-03 11:58:03 +00003809 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003810 FixedArray* result = FixedArray::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00003811 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003812 for (int i = 0; i < len0; i++) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003813 Object* e = get(i);
3814 ASSERT(e->IsString() || e->IsNumber());
3815 result->set(i, e, mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00003816 }
3817 // Fill in the extra keys.
3818 int index = 0;
3819 for (int y = 0; y < len1; y++) {
3820 Object* value = other->get(y);
3821 if (!value->IsTheHole() && !HasKey(this, value)) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003822 Object* e = other->get(y);
3823 ASSERT(e->IsString() || e->IsNumber());
3824 result->set(len0 + index, e, mode);
Steve Blocka7e24c12009-10-30 11:49:00 +00003825 index++;
3826 }
3827 }
3828 ASSERT(extra == index);
3829 return result;
3830}
3831
3832
John Reck59135872010-11-02 12:39:01 -07003833MaybeObject* FixedArray::CopySize(int new_length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003834 if (new_length == 0) return Heap::empty_fixed_array();
John Reck59135872010-11-02 12:39:01 -07003835 Object* obj;
3836 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(new_length);
3837 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3838 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003839 FixedArray* result = FixedArray::cast(obj);
3840 // Copy the content
Leon Clarke4515c472010-02-03 11:58:03 +00003841 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00003842 int len = length();
3843 if (new_length < len) len = new_length;
3844 result->set_map(map());
Leon Clarke4515c472010-02-03 11:58:03 +00003845 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003846 for (int i = 0; i < len; i++) {
3847 result->set(i, get(i), mode);
3848 }
3849 return result;
3850}
3851
3852
3853void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
Leon Clarke4515c472010-02-03 11:58:03 +00003854 AssertNoAllocation no_gc;
3855 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00003856 for (int index = 0; index < len; index++) {
3857 dest->set(dest_pos+index, get(pos+index), mode);
3858 }
3859}
3860
3861
3862#ifdef DEBUG
3863bool FixedArray::IsEqualTo(FixedArray* other) {
3864 if (length() != other->length()) return false;
3865 for (int i = 0 ; i < length(); ++i) {
3866 if (get(i) != other->get(i)) return false;
3867 }
3868 return true;
3869}
3870#endif
3871
3872
John Reck59135872010-11-02 12:39:01 -07003873MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003874 if (number_of_descriptors == 0) {
3875 return Heap::empty_descriptor_array();
3876 }
3877 // Allocate the array of keys.
John Reck59135872010-11-02 12:39:01 -07003878 Object* array;
3879 { MaybeObject* maybe_array =
3880 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
3881 if (!maybe_array->ToObject(&array)) return maybe_array;
3882 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003883 // Do not use DescriptorArray::cast on incomplete object.
3884 FixedArray* result = FixedArray::cast(array);
3885
3886 // Allocate the content array and set it in the descriptor array.
John Reck59135872010-11-02 12:39:01 -07003887 { MaybeObject* maybe_array =
3888 Heap::AllocateFixedArray(number_of_descriptors << 1);
3889 if (!maybe_array->ToObject(&array)) return maybe_array;
3890 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003891 result->set(kContentArrayIndex, array);
3892 result->set(kEnumerationIndexIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00003893 Smi::FromInt(PropertyDetails::kInitialIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +00003894 return result;
3895}
3896
3897
3898void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
3899 FixedArray* new_cache) {
3900 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
3901 if (HasEnumCache()) {
3902 FixedArray::cast(get(kEnumerationIndexIndex))->
3903 set(kEnumCacheBridgeCacheIndex, new_cache);
3904 } else {
3905 if (IsEmpty()) return; // Do nothing for empty descriptor array.
3906 FixedArray::cast(bridge_storage)->
3907 set(kEnumCacheBridgeCacheIndex, new_cache);
3908 fast_set(FixedArray::cast(bridge_storage),
3909 kEnumCacheBridgeEnumIndex,
3910 get(kEnumerationIndexIndex));
3911 set(kEnumerationIndexIndex, bridge_storage);
3912 }
3913}
3914
3915
John Reck59135872010-11-02 12:39:01 -07003916MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
3917 TransitionFlag transition_flag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00003918 // Transitions are only kept when inserting another transition.
3919 // This precondition is not required by this function's implementation, but
3920 // is currently required by the semantics of maps, so we check it.
3921 // Conversely, we filter after replacing, so replacing a transition and
3922 // removing all other transitions is not supported.
3923 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
3924 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
3925 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
3926
3927 // Ensure the key is a symbol.
John Reck59135872010-11-02 12:39:01 -07003928 Object* result;
3929 { MaybeObject* maybe_result = descriptor->KeyToSymbol();
3930 if (!maybe_result->ToObject(&result)) return maybe_result;
3931 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003932
3933 int transitions = 0;
3934 int null_descriptors = 0;
3935 if (remove_transitions) {
3936 for (int i = 0; i < number_of_descriptors(); i++) {
3937 if (IsTransition(i)) transitions++;
3938 if (IsNullDescriptor(i)) null_descriptors++;
3939 }
3940 } else {
3941 for (int i = 0; i < number_of_descriptors(); i++) {
3942 if (IsNullDescriptor(i)) null_descriptors++;
3943 }
3944 }
3945 int new_size = number_of_descriptors() - transitions - null_descriptors;
3946
3947 // If key is in descriptor, we replace it in-place when filtering.
3948 // Count a null descriptor for key as inserted, not replaced.
3949 int index = Search(descriptor->GetKey());
3950 const bool inserting = (index == kNotFound);
3951 const bool replacing = !inserting;
3952 bool keep_enumeration_index = false;
3953 if (inserting) {
3954 ++new_size;
3955 }
3956 if (replacing) {
3957 // We are replacing an existing descriptor. We keep the enumeration
3958 // index of a visible property.
3959 PropertyType t = PropertyDetails(GetDetails(index)).type();
3960 if (t == CONSTANT_FUNCTION ||
3961 t == FIELD ||
3962 t == CALLBACKS ||
3963 t == INTERCEPTOR) {
3964 keep_enumeration_index = true;
3965 } else if (remove_transitions) {
3966 // Replaced descriptor has been counted as removed if it is
3967 // a transition that will be replaced. Adjust count in this case.
3968 ++new_size;
3969 }
3970 }
John Reck59135872010-11-02 12:39:01 -07003971 { MaybeObject* maybe_result = Allocate(new_size);
3972 if (!maybe_result->ToObject(&result)) return maybe_result;
3973 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003974 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3975 // Set the enumeration index in the descriptors and set the enumeration index
3976 // in the result.
3977 int enumeration_index = NextEnumerationIndex();
3978 if (!descriptor->GetDetails().IsTransition()) {
3979 if (keep_enumeration_index) {
3980 descriptor->SetEnumerationIndex(
3981 PropertyDetails(GetDetails(index)).index());
3982 } else {
3983 descriptor->SetEnumerationIndex(enumeration_index);
3984 ++enumeration_index;
3985 }
3986 }
3987 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3988
3989 // Copy the descriptors, filtering out transitions and null descriptors,
3990 // and inserting or replacing a descriptor.
3991 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
3992 int from_index = 0;
3993 int to_index = 0;
3994
3995 for (; from_index < number_of_descriptors(); from_index++) {
3996 String* key = GetKey(from_index);
3997 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
3998 break;
3999 }
4000 if (IsNullDescriptor(from_index)) continue;
4001 if (remove_transitions && IsTransition(from_index)) continue;
4002 new_descriptors->CopyFrom(to_index++, this, from_index);
4003 }
4004
4005 new_descriptors->Set(to_index++, descriptor);
4006 if (replacing) from_index++;
4007
4008 for (; from_index < number_of_descriptors(); from_index++) {
4009 if (IsNullDescriptor(from_index)) continue;
4010 if (remove_transitions && IsTransition(from_index)) continue;
4011 new_descriptors->CopyFrom(to_index++, this, from_index);
4012 }
4013
4014 ASSERT(to_index == new_descriptors->number_of_descriptors());
4015 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
4016
4017 return new_descriptors;
4018}
4019
4020
John Reck59135872010-11-02 12:39:01 -07004021MaybeObject* DescriptorArray::RemoveTransitions() {
Steve Blocka7e24c12009-10-30 11:49:00 +00004022 // Remove all transitions and null descriptors. Return a copy of the array
4023 // with all transitions removed, or a Failure object if the new array could
4024 // not be allocated.
4025
4026 // Compute the size of the map transition entries to be removed.
4027 int num_removed = 0;
4028 for (int i = 0; i < number_of_descriptors(); i++) {
4029 if (!IsProperty(i)) num_removed++;
4030 }
4031
4032 // Allocate the new descriptor array.
John Reck59135872010-11-02 12:39:01 -07004033 Object* result;
4034 { MaybeObject* maybe_result = Allocate(number_of_descriptors() - num_removed);
4035 if (!maybe_result->ToObject(&result)) return maybe_result;
4036 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004037 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
4038
4039 // Copy the content.
4040 int next_descriptor = 0;
4041 for (int i = 0; i < number_of_descriptors(); i++) {
4042 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
4043 }
4044 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
4045
4046 return new_descriptors;
4047}
4048
4049
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004050void DescriptorArray::SortUnchecked() {
Steve Blocka7e24c12009-10-30 11:49:00 +00004051 // In-place heap sort.
4052 int len = number_of_descriptors();
4053
4054 // Bottom-up max-heap construction.
Steve Block6ded16b2010-05-10 14:33:55 +01004055 // Index of the last node with children
4056 const int max_parent_index = (len / 2) - 1;
4057 for (int i = max_parent_index; i >= 0; --i) {
4058 int parent_index = i;
4059 const uint32_t parent_hash = GetKey(i)->Hash();
4060 while (parent_index <= max_parent_index) {
4061 int child_index = 2 * parent_index + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00004062 uint32_t child_hash = GetKey(child_index)->Hash();
Steve Block6ded16b2010-05-10 14:33:55 +01004063 if (child_index + 1 < len) {
4064 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4065 if (right_child_hash > child_hash) {
4066 child_index++;
4067 child_hash = right_child_hash;
4068 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004069 }
Steve Block6ded16b2010-05-10 14:33:55 +01004070 if (child_hash <= parent_hash) break;
4071 Swap(parent_index, child_index);
4072 // Now element at child_index could be < its children.
4073 parent_index = child_index; // parent_hash remains correct.
Steve Blocka7e24c12009-10-30 11:49:00 +00004074 }
4075 }
4076
4077 // Extract elements and create sorted array.
4078 for (int i = len - 1; i > 0; --i) {
4079 // Put max element at the back of the array.
4080 Swap(0, i);
4081 // Sift down the new top element.
4082 int parent_index = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01004083 const uint32_t parent_hash = GetKey(parent_index)->Hash();
4084 const int max_parent_index = (i / 2) - 1;
4085 while (parent_index <= max_parent_index) {
4086 int child_index = parent_index * 2 + 1;
4087 uint32_t child_hash = GetKey(child_index)->Hash();
4088 if (child_index + 1 < i) {
4089 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4090 if (right_child_hash > child_hash) {
4091 child_index++;
4092 child_hash = right_child_hash;
4093 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004094 }
Steve Block6ded16b2010-05-10 14:33:55 +01004095 if (child_hash <= parent_hash) break;
4096 Swap(parent_index, child_index);
4097 parent_index = child_index;
Steve Blocka7e24c12009-10-30 11:49:00 +00004098 }
4099 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004100}
Steve Blocka7e24c12009-10-30 11:49:00 +00004101
Kristian Monsen0d5e1162010-09-30 15:31:59 +01004102
4103void DescriptorArray::Sort() {
4104 SortUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00004105 SLOW_ASSERT(IsSortedNoDuplicates());
4106}
4107
4108
4109int DescriptorArray::BinarySearch(String* name, int low, int high) {
4110 uint32_t hash = name->Hash();
4111
4112 while (low <= high) {
4113 int mid = (low + high) / 2;
4114 String* mid_name = GetKey(mid);
4115 uint32_t mid_hash = mid_name->Hash();
4116
4117 if (mid_hash > hash) {
4118 high = mid - 1;
4119 continue;
4120 }
4121 if (mid_hash < hash) {
4122 low = mid + 1;
4123 continue;
4124 }
4125 // Found an element with the same hash-code.
4126 ASSERT(hash == mid_hash);
4127 // There might be more, so we find the first one and
4128 // check them all to see if we have a match.
4129 if (name == mid_name && !is_null_descriptor(mid)) return mid;
4130 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
4131 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
4132 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
4133 }
4134 break;
4135 }
4136 return kNotFound;
4137}
4138
4139
4140int DescriptorArray::LinearSearch(String* name, int len) {
4141 uint32_t hash = name->Hash();
4142 for (int number = 0; number < len; number++) {
4143 String* entry = GetKey(number);
4144 if ((entry->Hash() == hash) &&
4145 name->Equals(entry) &&
4146 !is_null_descriptor(number)) {
4147 return number;
4148 }
4149 }
4150 return kNotFound;
4151}
4152
4153
Ben Murdochb0fe1622011-05-05 13:52:32 +01004154MaybeObject* DeoptimizationInputData::Allocate(int deopt_entry_count,
4155 PretenureFlag pretenure) {
4156 ASSERT(deopt_entry_count > 0);
4157 return Heap::AllocateFixedArray(LengthFor(deopt_entry_count),
4158 pretenure);
4159}
4160
4161
4162MaybeObject* DeoptimizationOutputData::Allocate(int number_of_deopt_points,
4163 PretenureFlag pretenure) {
4164 if (number_of_deopt_points == 0) return Heap::empty_fixed_array();
4165 return Heap::AllocateFixedArray(LengthOfFixedArray(number_of_deopt_points),
4166 pretenure);
4167}
4168
4169
Steve Blocka7e24c12009-10-30 11:49:00 +00004170#ifdef DEBUG
4171bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
4172 if (IsEmpty()) return other->IsEmpty();
4173 if (other->IsEmpty()) return false;
4174 if (length() != other->length()) return false;
4175 for (int i = 0; i < length(); ++i) {
4176 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
4177 }
4178 return GetContentArray()->IsEqualTo(other->GetContentArray());
4179}
4180#endif
4181
4182
4183static StaticResource<StringInputBuffer> string_input_buffer;
4184
4185
4186bool String::LooksValid() {
4187 if (!Heap::Contains(this)) return false;
4188 return true;
4189}
4190
4191
4192int String::Utf8Length() {
4193 if (IsAsciiRepresentation()) return length();
4194 // Attempt to flatten before accessing the string. It probably
4195 // doesn't make Utf8Length faster, but it is very likely that
4196 // the string will be accessed later (for example by WriteUtf8)
4197 // so it's still a good idea.
Steve Block6ded16b2010-05-10 14:33:55 +01004198 TryFlatten();
Steve Blocka7e24c12009-10-30 11:49:00 +00004199 Access<StringInputBuffer> buffer(&string_input_buffer);
4200 buffer->Reset(0, this);
4201 int result = 0;
4202 while (buffer->has_more())
4203 result += unibrow::Utf8::Length(buffer->GetNext());
4204 return result;
4205}
4206
4207
4208Vector<const char> String::ToAsciiVector() {
4209 ASSERT(IsAsciiRepresentation());
4210 ASSERT(IsFlat());
4211
4212 int offset = 0;
4213 int length = this->length();
4214 StringRepresentationTag string_tag = StringShape(this).representation_tag();
4215 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00004216 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004217 ConsString* cons = ConsString::cast(string);
4218 ASSERT(cons->second()->length() == 0);
4219 string = cons->first();
4220 string_tag = StringShape(string).representation_tag();
4221 }
4222 if (string_tag == kSeqStringTag) {
4223 SeqAsciiString* seq = SeqAsciiString::cast(string);
4224 char* start = seq->GetChars();
4225 return Vector<const char>(start + offset, length);
4226 }
4227 ASSERT(string_tag == kExternalStringTag);
4228 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
4229 const char* start = ext->resource()->data();
4230 return Vector<const char>(start + offset, length);
4231}
4232
4233
4234Vector<const uc16> String::ToUC16Vector() {
4235 ASSERT(IsTwoByteRepresentation());
4236 ASSERT(IsFlat());
4237
4238 int offset = 0;
4239 int length = this->length();
4240 StringRepresentationTag string_tag = StringShape(this).representation_tag();
4241 String* string = this;
Steve Blockd0582a62009-12-15 09:54:21 +00004242 if (string_tag == kConsStringTag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00004243 ConsString* cons = ConsString::cast(string);
4244 ASSERT(cons->second()->length() == 0);
4245 string = cons->first();
4246 string_tag = StringShape(string).representation_tag();
4247 }
4248 if (string_tag == kSeqStringTag) {
4249 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
4250 return Vector<const uc16>(seq->GetChars() + offset, length);
4251 }
4252 ASSERT(string_tag == kExternalStringTag);
4253 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
4254 const uc16* start =
4255 reinterpret_cast<const uc16*>(ext->resource()->data());
4256 return Vector<const uc16>(start + offset, length);
4257}
4258
4259
4260SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
4261 RobustnessFlag robust_flag,
4262 int offset,
4263 int length,
4264 int* length_return) {
4265 ASSERT(NativeAllocationChecker::allocation_allowed());
4266 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
4267 return SmartPointer<char>(NULL);
4268 }
4269
4270 // Negative length means the to the end of the string.
4271 if (length < 0) length = kMaxInt - offset;
4272
4273 // Compute the size of the UTF-8 string. Start at the specified offset.
4274 Access<StringInputBuffer> buffer(&string_input_buffer);
4275 buffer->Reset(offset, this);
4276 int character_position = offset;
4277 int utf8_bytes = 0;
4278 while (buffer->has_more()) {
4279 uint16_t character = buffer->GetNext();
4280 if (character_position < offset + length) {
4281 utf8_bytes += unibrow::Utf8::Length(character);
4282 }
4283 character_position++;
4284 }
4285
4286 if (length_return) {
4287 *length_return = utf8_bytes;
4288 }
4289
4290 char* result = NewArray<char>(utf8_bytes + 1);
4291
4292 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
4293 buffer->Rewind();
4294 buffer->Seek(offset);
4295 character_position = offset;
4296 int utf8_byte_position = 0;
4297 while (buffer->has_more()) {
4298 uint16_t character = buffer->GetNext();
4299 if (character_position < offset + length) {
4300 if (allow_nulls == DISALLOW_NULLS && character == 0) {
4301 character = ' ';
4302 }
4303 utf8_byte_position +=
4304 unibrow::Utf8::Encode(result + utf8_byte_position, character);
4305 }
4306 character_position++;
4307 }
4308 result[utf8_byte_position] = 0;
4309 return SmartPointer<char>(result);
4310}
4311
4312
4313SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
4314 RobustnessFlag robust_flag,
4315 int* length_return) {
4316 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
4317}
4318
4319
4320const uc16* String::GetTwoByteData() {
4321 return GetTwoByteData(0);
4322}
4323
4324
4325const uc16* String::GetTwoByteData(unsigned start) {
4326 ASSERT(!IsAsciiRepresentation());
4327 switch (StringShape(this).representation_tag()) {
4328 case kSeqStringTag:
4329 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
4330 case kExternalStringTag:
4331 return ExternalTwoByteString::cast(this)->
4332 ExternalTwoByteStringGetData(start);
Steve Blocka7e24c12009-10-30 11:49:00 +00004333 case kConsStringTag:
4334 UNREACHABLE();
4335 return NULL;
4336 }
4337 UNREACHABLE();
4338 return NULL;
4339}
4340
4341
4342SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
4343 ASSERT(NativeAllocationChecker::allocation_allowed());
4344
4345 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
4346 return SmartPointer<uc16>();
4347 }
4348
4349 Access<StringInputBuffer> buffer(&string_input_buffer);
4350 buffer->Reset(this);
4351
4352 uc16* result = NewArray<uc16>(length() + 1);
4353
4354 int i = 0;
4355 while (buffer->has_more()) {
4356 uint16_t character = buffer->GetNext();
4357 result[i++] = character;
4358 }
4359 result[i] = 0;
4360 return SmartPointer<uc16>(result);
4361}
4362
4363
4364const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
4365 return reinterpret_cast<uc16*>(
4366 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
4367}
4368
4369
4370void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4371 unsigned* offset_ptr,
4372 unsigned max_chars) {
4373 unsigned chars_read = 0;
4374 unsigned offset = *offset_ptr;
4375 while (chars_read < max_chars) {
4376 uint16_t c = *reinterpret_cast<uint16_t*>(
4377 reinterpret_cast<char*>(this) -
4378 kHeapObjectTag + kHeaderSize + offset * kShortSize);
4379 if (c <= kMaxAsciiCharCode) {
4380 // Fast case for ASCII characters. Cursor is an input output argument.
4381 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4382 rbb->util_buffer,
4383 rbb->capacity,
4384 rbb->cursor)) {
4385 break;
4386 }
4387 } else {
4388 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4389 rbb->util_buffer,
4390 rbb->capacity,
4391 rbb->cursor)) {
4392 break;
4393 }
4394 }
4395 offset++;
4396 chars_read++;
4397 }
4398 *offset_ptr = offset;
4399 rbb->remaining += chars_read;
4400}
4401
4402
4403const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
4404 unsigned* remaining,
4405 unsigned* offset_ptr,
4406 unsigned max_chars) {
4407 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
4408 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
4409 *remaining = max_chars;
4410 *offset_ptr += max_chars;
4411 return b;
4412}
4413
4414
4415// This will iterate unless the block of string data spans two 'halves' of
4416// a ConsString, in which case it will recurse. Since the block of string
4417// data to be read has a maximum size this limits the maximum recursion
4418// depth to something sane. Since C++ does not have tail call recursion
4419// elimination, the iteration must be explicit. Since this is not an
4420// -IntoBuffer method it can delegate to one of the efficient
4421// *AsciiStringReadBlock routines.
4422const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
4423 unsigned* offset_ptr,
4424 unsigned max_chars) {
4425 ConsString* current = this;
4426 unsigned offset = *offset_ptr;
4427 int offset_correction = 0;
4428
4429 while (true) {
4430 String* left = current->first();
4431 unsigned left_length = (unsigned)left->length();
4432 if (left_length > offset &&
4433 (max_chars <= left_length - offset ||
4434 (rbb->capacity <= left_length - offset &&
4435 (max_chars = left_length - offset, true)))) { // comma operator!
4436 // Left hand side only - iterate unless we have reached the bottom of
4437 // the cons tree. The assignment on the left of the comma operator is
4438 // in order to make use of the fact that the -IntoBuffer routines can
4439 // produce at most 'capacity' characters. This enables us to postpone
4440 // the point where we switch to the -IntoBuffer routines (below) in order
4441 // to maximize the chances of delegating a big chunk of work to the
4442 // efficient *AsciiStringReadBlock routines.
4443 if (StringShape(left).IsCons()) {
4444 current = ConsString::cast(left);
4445 continue;
4446 } else {
4447 const unibrow::byte* answer =
4448 String::ReadBlock(left, rbb, &offset, max_chars);
4449 *offset_ptr = offset + offset_correction;
4450 return answer;
4451 }
4452 } else if (left_length <= offset) {
4453 // Right hand side only - iterate unless we have reached the bottom of
4454 // the cons tree.
4455 String* right = current->second();
4456 offset -= left_length;
4457 offset_correction += left_length;
4458 if (StringShape(right).IsCons()) {
4459 current = ConsString::cast(right);
4460 continue;
4461 } else {
4462 const unibrow::byte* answer =
4463 String::ReadBlock(right, rbb, &offset, max_chars);
4464 *offset_ptr = offset + offset_correction;
4465 return answer;
4466 }
4467 } else {
4468 // The block to be read spans two sides of the ConsString, so we call the
4469 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
4470 // are able to assemble data from several part strings because they use
4471 // the util_buffer to store their data and never return direct pointers
4472 // to their storage. We don't try to read more than the buffer capacity
4473 // here or we can get too much recursion.
4474 ASSERT(rbb->remaining == 0);
4475 ASSERT(rbb->cursor == 0);
4476 current->ConsStringReadBlockIntoBuffer(
4477 rbb,
4478 &offset,
4479 max_chars > rbb->capacity ? rbb->capacity : max_chars);
4480 *offset_ptr = offset + offset_correction;
4481 return rbb->util_buffer;
4482 }
4483 }
4484}
4485
4486
Steve Blocka7e24c12009-10-30 11:49:00 +00004487uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
4488 ASSERT(index >= 0 && index < length());
4489 return resource()->data()[index];
4490}
4491
4492
4493const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
4494 unsigned* remaining,
4495 unsigned* offset_ptr,
4496 unsigned max_chars) {
4497 // Cast const char* to unibrow::byte* (signedness difference).
4498 const unibrow::byte* b =
4499 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
4500 *remaining = max_chars;
4501 *offset_ptr += max_chars;
4502 return b;
4503}
4504
4505
4506const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
4507 unsigned start) {
4508 return resource()->data() + start;
4509}
4510
4511
4512uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
4513 ASSERT(index >= 0 && index < length());
4514 return resource()->data()[index];
4515}
4516
4517
4518void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
4519 ReadBlockBuffer* rbb,
4520 unsigned* offset_ptr,
4521 unsigned max_chars) {
4522 unsigned chars_read = 0;
4523 unsigned offset = *offset_ptr;
4524 const uint16_t* data = resource()->data();
4525 while (chars_read < max_chars) {
4526 uint16_t c = data[offset];
4527 if (c <= kMaxAsciiCharCode) {
4528 // Fast case for ASCII characters. Cursor is an input output argument.
4529 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
4530 rbb->util_buffer,
4531 rbb->capacity,
4532 rbb->cursor))
4533 break;
4534 } else {
4535 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
4536 rbb->util_buffer,
4537 rbb->capacity,
4538 rbb->cursor))
4539 break;
4540 }
4541 offset++;
4542 chars_read++;
4543 }
4544 *offset_ptr = offset;
4545 rbb->remaining += chars_read;
4546}
4547
4548
4549void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4550 unsigned* offset_ptr,
4551 unsigned max_chars) {
4552 unsigned capacity = rbb->capacity - rbb->cursor;
4553 if (max_chars > capacity) max_chars = capacity;
4554 memcpy(rbb->util_buffer + rbb->cursor,
4555 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
4556 *offset_ptr * kCharSize,
4557 max_chars);
4558 rbb->remaining += max_chars;
4559 *offset_ptr += max_chars;
4560 rbb->cursor += max_chars;
4561}
4562
4563
4564void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
4565 ReadBlockBuffer* rbb,
4566 unsigned* offset_ptr,
4567 unsigned max_chars) {
4568 unsigned capacity = rbb->capacity - rbb->cursor;
4569 if (max_chars > capacity) max_chars = capacity;
4570 memcpy(rbb->util_buffer + rbb->cursor,
4571 resource()->data() + *offset_ptr,
4572 max_chars);
4573 rbb->remaining += max_chars;
4574 *offset_ptr += max_chars;
4575 rbb->cursor += max_chars;
4576}
4577
4578
4579// This method determines the type of string involved and then copies
4580// a whole chunk of characters into a buffer, or returns a pointer to a buffer
4581// where they can be found. The pointer is not necessarily valid across a GC
4582// (see AsciiStringReadBlock).
4583const unibrow::byte* String::ReadBlock(String* input,
4584 ReadBlockBuffer* rbb,
4585 unsigned* offset_ptr,
4586 unsigned max_chars) {
4587 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
4588 if (max_chars == 0) {
4589 rbb->remaining = 0;
4590 return NULL;
4591 }
4592 switch (StringShape(input).representation_tag()) {
4593 case kSeqStringTag:
4594 if (input->IsAsciiRepresentation()) {
4595 SeqAsciiString* str = SeqAsciiString::cast(input);
4596 return str->SeqAsciiStringReadBlock(&rbb->remaining,
4597 offset_ptr,
4598 max_chars);
4599 } else {
4600 SeqTwoByteString* str = SeqTwoByteString::cast(input);
4601 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4602 offset_ptr,
4603 max_chars);
4604 return rbb->util_buffer;
4605 }
4606 case kConsStringTag:
4607 return ConsString::cast(input)->ConsStringReadBlock(rbb,
4608 offset_ptr,
4609 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004610 case kExternalStringTag:
4611 if (input->IsAsciiRepresentation()) {
4612 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
4613 &rbb->remaining,
4614 offset_ptr,
4615 max_chars);
4616 } else {
4617 ExternalTwoByteString::cast(input)->
4618 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4619 offset_ptr,
4620 max_chars);
4621 return rbb->util_buffer;
4622 }
4623 default:
4624 break;
4625 }
4626
4627 UNREACHABLE();
4628 return 0;
4629}
4630
4631
4632Relocatable* Relocatable::top_ = NULL;
4633
4634
4635void Relocatable::PostGarbageCollectionProcessing() {
4636 Relocatable* current = top_;
4637 while (current != NULL) {
4638 current->PostGarbageCollection();
4639 current = current->prev_;
4640 }
4641}
4642
4643
4644// Reserve space for statics needing saving and restoring.
4645int Relocatable::ArchiveSpacePerThread() {
4646 return sizeof(top_);
4647}
4648
4649
4650// Archive statics that are thread local.
4651char* Relocatable::ArchiveState(char* to) {
4652 *reinterpret_cast<Relocatable**>(to) = top_;
4653 top_ = NULL;
4654 return to + ArchiveSpacePerThread();
4655}
4656
4657
4658// Restore statics that are thread local.
4659char* Relocatable::RestoreState(char* from) {
4660 top_ = *reinterpret_cast<Relocatable**>(from);
4661 return from + ArchiveSpacePerThread();
4662}
4663
4664
4665char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
4666 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
4667 Iterate(v, top);
4668 return thread_storage + ArchiveSpacePerThread();
4669}
4670
4671
4672void Relocatable::Iterate(ObjectVisitor* v) {
4673 Iterate(v, top_);
4674}
4675
4676
4677void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
4678 Relocatable* current = top;
4679 while (current != NULL) {
4680 current->IterateInstance(v);
4681 current = current->prev_;
4682 }
4683}
4684
4685
4686FlatStringReader::FlatStringReader(Handle<String> str)
4687 : str_(str.location()),
4688 length_(str->length()) {
4689 PostGarbageCollection();
4690}
4691
4692
4693FlatStringReader::FlatStringReader(Vector<const char> input)
4694 : str_(0),
4695 is_ascii_(true),
4696 length_(input.length()),
4697 start_(input.start()) { }
4698
4699
4700void FlatStringReader::PostGarbageCollection() {
4701 if (str_ == NULL) return;
4702 Handle<String> str(str_);
4703 ASSERT(str->IsFlat());
4704 is_ascii_ = str->IsAsciiRepresentation();
4705 if (is_ascii_) {
4706 start_ = str->ToAsciiVector().start();
4707 } else {
4708 start_ = str->ToUC16Vector().start();
4709 }
4710}
4711
4712
4713void StringInputBuffer::Seek(unsigned pos) {
4714 Reset(pos, input_);
4715}
4716
4717
4718void SafeStringInputBuffer::Seek(unsigned pos) {
4719 Reset(pos, input_);
4720}
4721
4722
4723// This method determines the type of string involved and then copies
4724// a whole chunk of characters into a buffer. It can be used with strings
4725// that have been glued together to form a ConsString and which must cooperate
4726// to fill up a buffer.
4727void String::ReadBlockIntoBuffer(String* input,
4728 ReadBlockBuffer* rbb,
4729 unsigned* offset_ptr,
4730 unsigned max_chars) {
4731 ASSERT(*offset_ptr <= (unsigned)input->length());
4732 if (max_chars == 0) return;
4733
4734 switch (StringShape(input).representation_tag()) {
4735 case kSeqStringTag:
4736 if (input->IsAsciiRepresentation()) {
4737 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
4738 offset_ptr,
4739 max_chars);
4740 return;
4741 } else {
4742 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
4743 offset_ptr,
4744 max_chars);
4745 return;
4746 }
4747 case kConsStringTag:
4748 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
4749 offset_ptr,
4750 max_chars);
4751 return;
Steve Blocka7e24c12009-10-30 11:49:00 +00004752 case kExternalStringTag:
4753 if (input->IsAsciiRepresentation()) {
Steve Blockd0582a62009-12-15 09:54:21 +00004754 ExternalAsciiString::cast(input)->
4755 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
4756 } else {
4757 ExternalTwoByteString::cast(input)->
4758 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
4759 offset_ptr,
4760 max_chars);
Steve Blocka7e24c12009-10-30 11:49:00 +00004761 }
4762 return;
4763 default:
4764 break;
4765 }
4766
4767 UNREACHABLE();
4768 return;
4769}
4770
4771
4772const unibrow::byte* String::ReadBlock(String* input,
4773 unibrow::byte* util_buffer,
4774 unsigned capacity,
4775 unsigned* remaining,
4776 unsigned* offset_ptr) {
4777 ASSERT(*offset_ptr <= (unsigned)input->length());
4778 unsigned chars = input->length() - *offset_ptr;
4779 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4780 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
4781 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4782 *remaining = rbb.remaining;
4783 return answer;
4784}
4785
4786
4787const unibrow::byte* String::ReadBlock(String** raw_input,
4788 unibrow::byte* util_buffer,
4789 unsigned capacity,
4790 unsigned* remaining,
4791 unsigned* offset_ptr) {
4792 Handle<String> input(raw_input);
4793 ASSERT(*offset_ptr <= (unsigned)input->length());
4794 unsigned chars = input->length() - *offset_ptr;
4795 if (chars > capacity) chars = capacity;
4796 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
4797 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
4798 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
4799 *remaining = rbb.remaining;
4800 return rbb.util_buffer;
4801}
4802
4803
4804// This will iterate unless the block of string data spans two 'halves' of
4805// a ConsString, in which case it will recurse. Since the block of string
4806// data to be read has a maximum size this limits the maximum recursion
4807// depth to something sane. Since C++ does not have tail call recursion
4808// elimination, the iteration must be explicit.
4809void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
4810 unsigned* offset_ptr,
4811 unsigned max_chars) {
4812 ConsString* current = this;
4813 unsigned offset = *offset_ptr;
4814 int offset_correction = 0;
4815
4816 while (true) {
4817 String* left = current->first();
4818 unsigned left_length = (unsigned)left->length();
4819 if (left_length > offset &&
4820 max_chars <= left_length - offset) {
4821 // Left hand side only - iterate unless we have reached the bottom of
4822 // the cons tree.
4823 if (StringShape(left).IsCons()) {
4824 current = ConsString::cast(left);
4825 continue;
4826 } else {
4827 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
4828 *offset_ptr = offset + offset_correction;
4829 return;
4830 }
4831 } else if (left_length <= offset) {
4832 // Right hand side only - iterate unless we have reached the bottom of
4833 // the cons tree.
4834 offset -= left_length;
4835 offset_correction += left_length;
4836 String* right = current->second();
4837 if (StringShape(right).IsCons()) {
4838 current = ConsString::cast(right);
4839 continue;
4840 } else {
4841 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4842 *offset_ptr = offset + offset_correction;
4843 return;
4844 }
4845 } else {
4846 // The block to be read spans two sides of the ConsString, so we recurse.
4847 // First recurse on the left.
4848 max_chars -= left_length - offset;
4849 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
4850 // We may have reached the max or there may not have been enough space
4851 // in the buffer for the characters in the left hand side.
4852 if (offset == left_length) {
4853 // Recurse on the right.
4854 String* right = String::cast(current->second());
4855 offset -= left_length;
4856 offset_correction += left_length;
4857 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
4858 }
4859 *offset_ptr = offset + offset_correction;
4860 return;
4861 }
4862 }
4863}
4864
4865
Steve Blocka7e24c12009-10-30 11:49:00 +00004866uint16_t ConsString::ConsStringGet(int index) {
4867 ASSERT(index >= 0 && index < this->length());
4868
4869 // Check for a flattened cons string
4870 if (second()->length() == 0) {
4871 String* left = first();
4872 return left->Get(index);
4873 }
4874
4875 String* string = String::cast(this);
4876
4877 while (true) {
4878 if (StringShape(string).IsCons()) {
4879 ConsString* cons_string = ConsString::cast(string);
4880 String* left = cons_string->first();
4881 if (left->length() > index) {
4882 string = left;
4883 } else {
4884 index -= left->length();
4885 string = cons_string->second();
4886 }
4887 } else {
4888 return string->Get(index);
4889 }
4890 }
4891
4892 UNREACHABLE();
4893 return 0;
4894}
4895
4896
4897template <typename sinkchar>
4898void String::WriteToFlat(String* src,
4899 sinkchar* sink,
4900 int f,
4901 int t) {
4902 String* source = src;
4903 int from = f;
4904 int to = t;
4905 while (true) {
4906 ASSERT(0 <= from && from <= to && to <= source->length());
4907 switch (StringShape(source).full_representation_tag()) {
4908 case kAsciiStringTag | kExternalStringTag: {
4909 CopyChars(sink,
4910 ExternalAsciiString::cast(source)->resource()->data() + from,
4911 to - from);
4912 return;
4913 }
4914 case kTwoByteStringTag | kExternalStringTag: {
4915 const uc16* data =
4916 ExternalTwoByteString::cast(source)->resource()->data();
4917 CopyChars(sink,
4918 data + from,
4919 to - from);
4920 return;
4921 }
4922 case kAsciiStringTag | kSeqStringTag: {
4923 CopyChars(sink,
4924 SeqAsciiString::cast(source)->GetChars() + from,
4925 to - from);
4926 return;
4927 }
4928 case kTwoByteStringTag | kSeqStringTag: {
4929 CopyChars(sink,
4930 SeqTwoByteString::cast(source)->GetChars() + from,
4931 to - from);
4932 return;
4933 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004934 case kAsciiStringTag | kConsStringTag:
4935 case kTwoByteStringTag | kConsStringTag: {
4936 ConsString* cons_string = ConsString::cast(source);
4937 String* first = cons_string->first();
4938 int boundary = first->length();
4939 if (to - boundary >= boundary - from) {
4940 // Right hand side is longer. Recurse over left.
4941 if (from < boundary) {
4942 WriteToFlat(first, sink, from, boundary);
4943 sink += boundary - from;
4944 from = 0;
4945 } else {
4946 from -= boundary;
4947 }
4948 to -= boundary;
4949 source = cons_string->second();
4950 } else {
4951 // Left hand side is longer. Recurse over right.
4952 if (to > boundary) {
4953 String* second = cons_string->second();
4954 WriteToFlat(second,
4955 sink + boundary - from,
4956 0,
4957 to - boundary);
4958 to = boundary;
4959 }
4960 source = first;
4961 }
4962 break;
4963 }
4964 }
4965 }
4966}
4967
4968
Steve Blocka7e24c12009-10-30 11:49:00 +00004969template <typename IteratorA, typename IteratorB>
4970static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4971 // General slow case check. We know that the ia and ib iterators
4972 // have the same length.
4973 while (ia->has_more()) {
4974 uc32 ca = ia->GetNext();
4975 uc32 cb = ib->GetNext();
4976 if (ca != cb)
4977 return false;
4978 }
4979 return true;
4980}
4981
4982
4983// Compares the contents of two strings by reading and comparing
4984// int-sized blocks of characters.
4985template <typename Char>
4986static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4987 int length = a.length();
4988 ASSERT_EQ(length, b.length());
4989 const Char* pa = a.start();
4990 const Char* pb = b.start();
4991 int i = 0;
4992#ifndef V8_HOST_CAN_READ_UNALIGNED
4993 // If this architecture isn't comfortable reading unaligned ints
4994 // then we have to check that the strings are aligned before
4995 // comparing them blockwise.
4996 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4997 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4998 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
4999 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
5000#endif
5001 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
5002 int endpoint = length - kStepSize;
5003 // Compare blocks until we reach near the end of the string.
5004 for (; i <= endpoint; i += kStepSize) {
5005 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
5006 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
5007 if (wa != wb) {
5008 return false;
5009 }
5010 }
5011#ifndef V8_HOST_CAN_READ_UNALIGNED
5012 }
5013#endif
5014 // Compare the remaining characters that didn't fit into a block.
5015 for (; i < length; i++) {
5016 if (a[i] != b[i]) {
5017 return false;
5018 }
5019 }
5020 return true;
5021}
5022
5023
5024static StringInputBuffer string_compare_buffer_b;
5025
5026
5027template <typename IteratorA>
5028static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
5029 if (b->IsFlat()) {
5030 if (b->IsAsciiRepresentation()) {
5031 VectorIterator<char> ib(b->ToAsciiVector());
5032 return CompareStringContents(ia, &ib);
5033 } else {
5034 VectorIterator<uc16> ib(b->ToUC16Vector());
5035 return CompareStringContents(ia, &ib);
5036 }
5037 } else {
5038 string_compare_buffer_b.Reset(0, b);
5039 return CompareStringContents(ia, &string_compare_buffer_b);
5040 }
5041}
5042
5043
5044static StringInputBuffer string_compare_buffer_a;
5045
5046
5047bool String::SlowEquals(String* other) {
5048 // Fast check: negative check with lengths.
5049 int len = length();
5050 if (len != other->length()) return false;
5051 if (len == 0) return true;
5052
5053 // Fast check: if hash code is computed for both strings
5054 // a fast negative check can be performed.
5055 if (HasHashCode() && other->HasHashCode()) {
5056 if (Hash() != other->Hash()) return false;
5057 }
5058
Leon Clarkef7060e22010-06-03 12:02:55 +01005059 // We know the strings are both non-empty. Compare the first chars
5060 // before we try to flatten the strings.
5061 if (this->Get(0) != other->Get(0)) return false;
5062
5063 String* lhs = this->TryFlattenGetString();
5064 String* rhs = other->TryFlattenGetString();
5065
5066 if (StringShape(lhs).IsSequentialAscii() &&
5067 StringShape(rhs).IsSequentialAscii()) {
5068 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
5069 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
Steve Blocka7e24c12009-10-30 11:49:00 +00005070 return CompareRawStringContents(Vector<const char>(str1, len),
5071 Vector<const char>(str2, len));
5072 }
5073
Leon Clarkef7060e22010-06-03 12:02:55 +01005074 if (lhs->IsFlat()) {
Ben Murdochbb769b22010-08-11 14:56:33 +01005075 if (lhs->IsAsciiRepresentation()) {
Leon Clarkef7060e22010-06-03 12:02:55 +01005076 Vector<const char> vec1 = lhs->ToAsciiVector();
5077 if (rhs->IsFlat()) {
5078 if (rhs->IsAsciiRepresentation()) {
5079 Vector<const char> vec2 = rhs->ToAsciiVector();
Steve Blocka7e24c12009-10-30 11:49:00 +00005080 return CompareRawStringContents(vec1, vec2);
5081 } else {
5082 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005083 VectorIterator<uc16> ib(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005084 return CompareStringContents(&buf1, &ib);
5085 }
5086 } else {
5087 VectorIterator<char> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005088 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005089 return CompareStringContents(&buf1, &string_compare_buffer_b);
5090 }
5091 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005092 Vector<const uc16> vec1 = lhs->ToUC16Vector();
5093 if (rhs->IsFlat()) {
5094 if (rhs->IsAsciiRepresentation()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005095 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005096 VectorIterator<char> ib(rhs->ToAsciiVector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005097 return CompareStringContents(&buf1, &ib);
5098 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005099 Vector<const uc16> vec2(rhs->ToUC16Vector());
Steve Blocka7e24c12009-10-30 11:49:00 +00005100 return CompareRawStringContents(vec1, vec2);
5101 }
5102 } else {
5103 VectorIterator<uc16> buf1(vec1);
Leon Clarkef7060e22010-06-03 12:02:55 +01005104 string_compare_buffer_b.Reset(0, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005105 return CompareStringContents(&buf1, &string_compare_buffer_b);
5106 }
5107 }
5108 } else {
Leon Clarkef7060e22010-06-03 12:02:55 +01005109 string_compare_buffer_a.Reset(0, lhs);
5110 return CompareStringContentsPartial(&string_compare_buffer_a, rhs);
Steve Blocka7e24c12009-10-30 11:49:00 +00005111 }
5112}
5113
5114
5115bool String::MarkAsUndetectable() {
5116 if (StringShape(this).IsSymbol()) return false;
5117
5118 Map* map = this->map();
Steve Blockd0582a62009-12-15 09:54:21 +00005119 if (map == Heap::string_map()) {
5120 this->set_map(Heap::undetectable_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00005121 return true;
Steve Blockd0582a62009-12-15 09:54:21 +00005122 } else if (map == Heap::ascii_string_map()) {
5123 this->set_map(Heap::undetectable_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00005124 return true;
5125 }
5126 // Rest cannot be marked as undetectable
5127 return false;
5128}
5129
5130
5131bool String::IsEqualTo(Vector<const char> str) {
5132 int slen = length();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08005133 Access<ScannerConstants::Utf8Decoder>
5134 decoder(ScannerConstants::utf8_decoder());
Steve Blocka7e24c12009-10-30 11:49:00 +00005135 decoder->Reset(str.start(), str.length());
5136 int i;
5137 for (i = 0; i < slen && decoder->has_more(); i++) {
5138 uc32 r = decoder->GetNext();
5139 if (Get(i) != r) return false;
5140 }
5141 return i == slen && !decoder->has_more();
5142}
5143
5144
Steve Block9fac8402011-05-12 15:51:54 +01005145bool String::IsAsciiEqualTo(Vector<const char> str) {
5146 int slen = length();
5147 if (str.length() != slen) return false;
5148 for (int i = 0; i < slen; i++) {
5149 if (Get(i) != static_cast<uint16_t>(str[i])) return false;
5150 }
5151 return true;
5152}
5153
5154
5155bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
5156 int slen = length();
5157 if (str.length() != slen) return false;
5158 for (int i = 0; i < slen; i++) {
5159 if (Get(i) != str[i]) return false;
5160 }
5161 return true;
5162}
5163
5164
Steve Block6ded16b2010-05-10 14:33:55 +01005165template <typename schar>
5166static inline uint32_t HashSequentialString(const schar* chars, int length) {
5167 StringHasher hasher(length);
5168 if (!hasher.has_trivial_hash()) {
5169 int i;
5170 for (i = 0; hasher.is_array_index() && (i < length); i++) {
5171 hasher.AddCharacter(chars[i]);
5172 }
5173 for (; i < length; i++) {
5174 hasher.AddCharacterNoIndex(chars[i]);
5175 }
5176 }
5177 return hasher.GetHashField();
5178}
5179
5180
Steve Blocka7e24c12009-10-30 11:49:00 +00005181uint32_t String::ComputeAndSetHash() {
5182 // Should only be called if hash code has not yet been computed.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005183 ASSERT(!HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00005184
Steve Block6ded16b2010-05-10 14:33:55 +01005185 const int len = length();
5186
Steve Blocka7e24c12009-10-30 11:49:00 +00005187 // Compute the hash code.
Steve Block6ded16b2010-05-10 14:33:55 +01005188 uint32_t field = 0;
5189 if (StringShape(this).IsSequentialAscii()) {
5190 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
5191 } else if (StringShape(this).IsSequentialTwoByte()) {
5192 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
5193 } else {
5194 StringInputBuffer buffer(this);
5195 field = ComputeHashField(&buffer, len);
5196 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005197
5198 // Store the hash code in the object.
Steve Blockd0582a62009-12-15 09:54:21 +00005199 set_hash_field(field);
Steve Blocka7e24c12009-10-30 11:49:00 +00005200
5201 // Check the hash code is there.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005202 ASSERT(HasHashCode());
Steve Blocka7e24c12009-10-30 11:49:00 +00005203 uint32_t result = field >> kHashShift;
5204 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
5205 return result;
5206}
5207
5208
5209bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
5210 uint32_t* index,
5211 int length) {
5212 if (length == 0 || length > kMaxArrayIndexSize) return false;
5213 uc32 ch = buffer->GetNext();
5214
5215 // If the string begins with a '0' character, it must only consist
5216 // of it to be a legal array index.
5217 if (ch == '0') {
5218 *index = 0;
5219 return length == 1;
5220 }
5221
5222 // Convert string to uint32 array index; character by character.
5223 int d = ch - '0';
5224 if (d < 0 || d > 9) return false;
5225 uint32_t result = d;
5226 while (buffer->has_more()) {
5227 d = buffer->GetNext() - '0';
5228 if (d < 0 || d > 9) return false;
5229 // Check that the new result is below the 32 bit limit.
5230 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
5231 result = (result * 10) + d;
5232 }
5233
5234 *index = result;
5235 return true;
5236}
5237
5238
5239bool String::SlowAsArrayIndex(uint32_t* index) {
5240 if (length() <= kMaxCachedArrayIndexLength) {
5241 Hash(); // force computation of hash code
Steve Blockd0582a62009-12-15 09:54:21 +00005242 uint32_t field = hash_field();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005243 if ((field & kIsNotArrayIndexMask) != 0) return false;
Steve Blockd0582a62009-12-15 09:54:21 +00005244 // Isolate the array index form the full hash field.
5245 *index = (kArrayIndexHashMask & field) >> kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00005246 return true;
5247 } else {
5248 StringInputBuffer buffer(this);
5249 return ComputeArrayIndex(&buffer, index, length());
5250 }
5251}
5252
5253
Iain Merrick9ac36c92010-09-13 15:29:50 +01005254uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005255 // For array indexes mix the length into the hash as an array index could
5256 // be zero.
5257 ASSERT(length > 0);
5258 ASSERT(length <= String::kMaxArrayIndexSize);
5259 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
5260 (1 << String::kArrayIndexValueBits));
Iain Merrick9ac36c92010-09-13 15:29:50 +01005261
5262 value <<= String::kHashShift;
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005263 value |= length << String::kArrayIndexHashLengthShift;
Iain Merrick9ac36c92010-09-13 15:29:50 +01005264
5265 ASSERT((value & String::kIsNotArrayIndexMask) == 0);
5266 ASSERT((length > String::kMaxCachedArrayIndexLength) ||
5267 (value & String::kContainsCachedArrayIndexMask) == 0);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005268 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00005269}
5270
5271
5272uint32_t StringHasher::GetHashField() {
5273 ASSERT(is_valid());
Steve Blockd0582a62009-12-15 09:54:21 +00005274 if (length_ <= String::kMaxHashCalcLength) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005275 if (is_array_index()) {
Iain Merrick9ac36c92010-09-13 15:29:50 +01005276 return MakeArrayIndexHash(array_index(), length_);
Steve Blocka7e24c12009-10-30 11:49:00 +00005277 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005278 return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask;
Steve Blocka7e24c12009-10-30 11:49:00 +00005279 } else {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01005280 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
Steve Blocka7e24c12009-10-30 11:49:00 +00005281 }
5282}
5283
5284
Steve Blockd0582a62009-12-15 09:54:21 +00005285uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
5286 int length) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005287 StringHasher hasher(length);
5288
5289 // Very long strings have a trivial hash that doesn't inspect the
5290 // string contents.
5291 if (hasher.has_trivial_hash()) {
5292 return hasher.GetHashField();
5293 }
5294
5295 // Do the iterative array index computation as long as there is a
5296 // chance this is an array index.
5297 while (buffer->has_more() && hasher.is_array_index()) {
5298 hasher.AddCharacter(buffer->GetNext());
5299 }
5300
5301 // Process the remaining characters without updating the array
5302 // index.
5303 while (buffer->has_more()) {
5304 hasher.AddCharacterNoIndex(buffer->GetNext());
5305 }
5306
5307 return hasher.GetHashField();
5308}
5309
5310
John Reck59135872010-11-02 12:39:01 -07005311MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005312 if (start == 0 && end == length()) return this;
John Reck59135872010-11-02 12:39:01 -07005313 MaybeObject* result = Heap::AllocateSubString(this, start, end, pretenure);
Steve Blockd0582a62009-12-15 09:54:21 +00005314 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +00005315}
5316
5317
5318void String::PrintOn(FILE* file) {
5319 int length = this->length();
5320 for (int i = 0; i < length; i++) {
5321 fprintf(file, "%c", Get(i));
5322 }
5323}
5324
5325
5326void Map::CreateBackPointers() {
5327 DescriptorArray* descriptors = instance_descriptors();
5328 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
Iain Merrick75681382010-08-19 15:07:18 +01005329 if (descriptors->GetType(i) == MAP_TRANSITION ||
5330 descriptors->GetType(i) == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005331 // Get target.
5332 Map* target = Map::cast(descriptors->GetValue(i));
5333#ifdef DEBUG
5334 // Verify target.
5335 Object* source_prototype = prototype();
5336 Object* target_prototype = target->prototype();
5337 ASSERT(source_prototype->IsJSObject() ||
5338 source_prototype->IsMap() ||
5339 source_prototype->IsNull());
5340 ASSERT(target_prototype->IsJSObject() ||
5341 target_prototype->IsNull());
5342 ASSERT(source_prototype->IsMap() ||
5343 source_prototype == target_prototype);
5344#endif
5345 // Point target back to source. set_prototype() will not let us set
5346 // the prototype to a map, as we do here.
5347 *RawField(target, kPrototypeOffset) = this;
5348 }
5349 }
5350}
5351
5352
5353void Map::ClearNonLiveTransitions(Object* real_prototype) {
5354 // Live DescriptorArray objects will be marked, so we must use
5355 // low-level accessors to get and modify their data.
5356 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
5357 *RawField(this, Map::kInstanceDescriptorsOffset));
5358 if (d == Heap::raw_unchecked_empty_descriptor_array()) return;
5359 Smi* NullDescriptorDetails =
5360 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
5361 FixedArray* contents = reinterpret_cast<FixedArray*>(
5362 d->get(DescriptorArray::kContentArrayIndex));
5363 ASSERT(contents->length() >= 2);
5364 for (int i = 0; i < contents->length(); i += 2) {
5365 // If the pair (value, details) is a map transition,
5366 // check if the target is live. If not, null the descriptor.
5367 // Also drop the back pointer for that map transition, so that this
5368 // map is not reached again by following a back pointer from a
5369 // non-live object.
5370 PropertyDetails details(Smi::cast(contents->get(i + 1)));
Iain Merrick75681382010-08-19 15:07:18 +01005371 if (details.type() == MAP_TRANSITION ||
5372 details.type() == CONSTANT_TRANSITION) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005373 Map* target = reinterpret_cast<Map*>(contents->get(i));
5374 ASSERT(target->IsHeapObject());
5375 if (!target->IsMarked()) {
5376 ASSERT(target->IsMap());
Iain Merrick75681382010-08-19 15:07:18 +01005377 contents->set_unchecked(i + 1, NullDescriptorDetails);
5378 contents->set_null_unchecked(i);
Steve Blocka7e24c12009-10-30 11:49:00 +00005379 ASSERT(target->prototype() == this ||
5380 target->prototype() == real_prototype);
5381 // Getter prototype() is read-only, set_prototype() has side effects.
5382 *RawField(target, Map::kPrototypeOffset) = real_prototype;
5383 }
5384 }
5385 }
5386}
5387
5388
Steve Block791712a2010-08-27 10:21:07 +01005389void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
5390 // Iterate over all fields in the body but take care in dealing with
5391 // the code entry.
5392 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
5393 v->VisitCodeEntry(this->address() + kCodeEntryOffset);
5394 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
5395}
5396
5397
Ben Murdochb0fe1622011-05-05 13:52:32 +01005398void JSFunction::MarkForLazyRecompilation() {
5399 ASSERT(is_compiled() && !IsOptimized());
5400 ASSERT(shared()->allows_lazy_compilation());
5401 ReplaceCode(Builtins::builtin(Builtins::LazyRecompile));
5402}
5403
5404
5405uint32_t JSFunction::SourceHash() {
5406 uint32_t hash = 0;
5407 Object* script = shared()->script();
5408 if (!script->IsUndefined()) {
5409 Object* source = Script::cast(script)->source();
5410 if (source->IsUndefined()) hash = String::cast(source)->Hash();
5411 }
5412 hash ^= ComputeIntegerHash(shared()->start_position_and_type());
5413 hash += ComputeIntegerHash(shared()->end_position());
5414 return hash;
5415}
5416
5417
5418bool JSFunction::IsInlineable() {
5419 if (IsBuiltin()) return false;
5420 // Check that the function has a script associated with it.
5421 if (!shared()->script()->IsScript()) return false;
5422 Code* code = shared()->code();
5423 if (code->kind() == Code::OPTIMIZED_FUNCTION) return true;
5424 // If we never ran this (unlikely) then lets try to optimize it.
5425 if (code->kind() != Code::FUNCTION) return true;
5426 return code->optimizable();
5427}
5428
5429
Steve Blocka7e24c12009-10-30 11:49:00 +00005430Object* JSFunction::SetInstancePrototype(Object* value) {
5431 ASSERT(value->IsJSObject());
5432
5433 if (has_initial_map()) {
5434 initial_map()->set_prototype(value);
5435 } else {
5436 // Put the value in the initial map field until an initial map is
5437 // needed. At that point, a new initial map is created and the
5438 // prototype is put into the initial map where it belongs.
5439 set_prototype_or_initial_map(value);
5440 }
Kristian Monsen25f61362010-05-21 11:50:48 +01005441 Heap::ClearInstanceofCache();
Steve Blocka7e24c12009-10-30 11:49:00 +00005442 return value;
5443}
5444
5445
John Reck59135872010-11-02 12:39:01 -07005446MaybeObject* JSFunction::SetPrototype(Object* value) {
Steve Block6ded16b2010-05-10 14:33:55 +01005447 ASSERT(should_have_prototype());
Steve Blocka7e24c12009-10-30 11:49:00 +00005448 Object* construct_prototype = value;
5449
5450 // If the value is not a JSObject, store the value in the map's
5451 // constructor field so it can be accessed. Also, set the prototype
5452 // used for constructing objects to the original object prototype.
5453 // See ECMA-262 13.2.2.
5454 if (!value->IsJSObject()) {
5455 // Copy the map so this does not affect unrelated functions.
5456 // Remove map transitions because they point to maps with a
5457 // different prototype.
John Reck59135872010-11-02 12:39:01 -07005458 Object* new_map;
5459 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
5460 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
5461 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005462 set_map(Map::cast(new_map));
5463 map()->set_constructor(value);
5464 map()->set_non_instance_prototype(true);
5465 construct_prototype =
5466 Top::context()->global_context()->initial_object_prototype();
5467 } else {
5468 map()->set_non_instance_prototype(false);
5469 }
5470
5471 return SetInstancePrototype(construct_prototype);
5472}
5473
5474
Steve Block6ded16b2010-05-10 14:33:55 +01005475Object* JSFunction::RemovePrototype() {
5476 ASSERT(map() == context()->global_context()->function_map());
5477 set_map(context()->global_context()->function_without_prototype_map());
5478 set_prototype_or_initial_map(Heap::the_hole_value());
5479 return this;
5480}
5481
5482
Steve Blocka7e24c12009-10-30 11:49:00 +00005483Object* JSFunction::SetInstanceClassName(String* name) {
5484 shared()->set_instance_class_name(name);
5485 return this;
5486}
5487
5488
Ben Murdochb0fe1622011-05-05 13:52:32 +01005489void JSFunction::PrintName(FILE* out) {
5490 SmartPointer<char> name = shared()->DebugName()->ToCString();
5491 PrintF(out, "%s", *name);
5492}
5493
5494
Steve Blocka7e24c12009-10-30 11:49:00 +00005495Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
5496 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
5497}
5498
5499
John Reck59135872010-11-02 12:39:01 -07005500MaybeObject* Oddball::Initialize(const char* to_string, Object* to_number) {
5501 Object* symbol;
5502 { MaybeObject* maybe_symbol = Heap::LookupAsciiSymbol(to_string);
5503 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
5504 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005505 set_to_string(String::cast(symbol));
5506 set_to_number(to_number);
5507 return this;
5508}
5509
5510
Ben Murdochf87a2032010-10-22 12:50:53 +01005511String* SharedFunctionInfo::DebugName() {
5512 Object* n = name();
5513 if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
5514 return String::cast(n);
5515}
5516
5517
Steve Blocka7e24c12009-10-30 11:49:00 +00005518bool SharedFunctionInfo::HasSourceCode() {
5519 return !script()->IsUndefined() &&
Iain Merrick75681382010-08-19 15:07:18 +01005520 !reinterpret_cast<Script*>(script())->source()->IsUndefined();
Steve Blocka7e24c12009-10-30 11:49:00 +00005521}
5522
5523
5524Object* SharedFunctionInfo::GetSourceCode() {
Ben Murdochb0fe1622011-05-05 13:52:32 +01005525 if (!HasSourceCode()) return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +00005526 HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +00005527 Object* source = Script::cast(script())->source();
Steve Blocka7e24c12009-10-30 11:49:00 +00005528 return *SubString(Handle<String>(String::cast(source)),
5529 start_position(), end_position());
5530}
5531
5532
Ben Murdochb0fe1622011-05-05 13:52:32 +01005533int SharedFunctionInfo::SourceSize() {
5534 return end_position() - start_position();
5535}
5536
5537
Steve Blocka7e24c12009-10-30 11:49:00 +00005538int SharedFunctionInfo::CalculateInstanceSize() {
5539 int instance_size =
5540 JSObject::kHeaderSize +
5541 expected_nof_properties() * kPointerSize;
5542 if (instance_size > JSObject::kMaxInstanceSize) {
5543 instance_size = JSObject::kMaxInstanceSize;
5544 }
5545 return instance_size;
5546}
5547
5548
5549int SharedFunctionInfo::CalculateInObjectProperties() {
5550 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
5551}
5552
5553
Andrei Popescu402d9372010-02-26 13:31:12 +00005554bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
5555 // Check the basic conditions for generating inline constructor code.
5556 if (!FLAG_inline_new
5557 || !has_only_simple_this_property_assignments()
5558 || this_property_assignments_count() == 0) {
5559 return false;
5560 }
5561
5562 // If the prototype is null inline constructors cause no problems.
5563 if (!prototype->IsJSObject()) {
5564 ASSERT(prototype->IsNull());
5565 return true;
5566 }
5567
5568 // Traverse the proposed prototype chain looking for setters for properties of
5569 // the same names as are set by the inline constructor.
5570 for (Object* obj = prototype;
5571 obj != Heap::null_value();
5572 obj = obj->GetPrototype()) {
5573 JSObject* js_object = JSObject::cast(obj);
5574 for (int i = 0; i < this_property_assignments_count(); i++) {
5575 LookupResult result;
5576 String* name = GetThisPropertyAssignmentName(i);
5577 js_object->LocalLookupRealNamedProperty(name, &result);
5578 if (result.IsProperty() && result.type() == CALLBACKS) {
5579 return false;
5580 }
5581 }
5582 }
5583
5584 return true;
5585}
5586
5587
Kristian Monsen0d5e1162010-09-30 15:31:59 +01005588void SharedFunctionInfo::ForbidInlineConstructor() {
5589 set_compiler_hints(BooleanBit::set(compiler_hints(),
5590 kHasOnlySimpleThisPropertyAssignments,
5591 false));
5592}
5593
5594
Steve Blocka7e24c12009-10-30 11:49:00 +00005595void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +00005596 bool only_simple_this_property_assignments,
5597 FixedArray* assignments) {
5598 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005599 kHasOnlySimpleThisPropertyAssignments,
5600 only_simple_this_property_assignments));
5601 set_this_property_assignments(assignments);
5602 set_this_property_assignments_count(assignments->length() / 3);
5603}
5604
5605
5606void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
5607 set_compiler_hints(BooleanBit::set(compiler_hints(),
Steve Blocka7e24c12009-10-30 11:49:00 +00005608 kHasOnlySimpleThisPropertyAssignments,
5609 false));
5610 set_this_property_assignments(Heap::undefined_value());
5611 set_this_property_assignments_count(0);
5612}
5613
5614
5615String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
5616 Object* obj = this_property_assignments();
5617 ASSERT(obj->IsFixedArray());
5618 ASSERT(index < this_property_assignments_count());
5619 obj = FixedArray::cast(obj)->get(index * 3);
5620 ASSERT(obj->IsString());
5621 return String::cast(obj);
5622}
5623
5624
5625bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
5626 Object* obj = this_property_assignments();
5627 ASSERT(obj->IsFixedArray());
5628 ASSERT(index < this_property_assignments_count());
5629 obj = FixedArray::cast(obj)->get(index * 3 + 1);
5630 return Smi::cast(obj)->value() != -1;
5631}
5632
5633
5634int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
5635 ASSERT(IsThisPropertyAssignmentArgument(index));
5636 Object* obj =
5637 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
5638 return Smi::cast(obj)->value();
5639}
5640
5641
5642Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
5643 ASSERT(!IsThisPropertyAssignmentArgument(index));
5644 Object* obj =
5645 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
5646 return obj;
5647}
5648
5649
Steve Blocka7e24c12009-10-30 11:49:00 +00005650// Support function for printing the source code to a StringStream
5651// without any allocation in the heap.
5652void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
5653 int max_length) {
5654 // For some native functions there is no source.
Ben Murdochb0fe1622011-05-05 13:52:32 +01005655 if (!HasSourceCode()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005656 accumulator->Add("<No Source>");
5657 return;
5658 }
5659
Steve Blockd0582a62009-12-15 09:54:21 +00005660 // Get the source for the script which this function came from.
Steve Blocka7e24c12009-10-30 11:49:00 +00005661 // Don't use String::cast because we don't want more assertion errors while
5662 // we are already creating a stack dump.
5663 String* script_source =
5664 reinterpret_cast<String*>(Script::cast(script())->source());
5665
5666 if (!script_source->LooksValid()) {
5667 accumulator->Add("<Invalid Source>");
5668 return;
5669 }
5670
5671 if (!is_toplevel()) {
5672 accumulator->Add("function ");
5673 Object* name = this->name();
5674 if (name->IsString() && String::cast(name)->length() > 0) {
5675 accumulator->PrintName(name);
5676 }
5677 }
5678
5679 int len = end_position() - start_position();
Ben Murdochb0fe1622011-05-05 13:52:32 +01005680 if (len <= max_length || max_length < 0) {
5681 accumulator->Put(script_source, start_position(), end_position());
5682 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +00005683 accumulator->Put(script_source,
5684 start_position(),
5685 start_position() + max_length);
5686 accumulator->Add("...\n");
Steve Blocka7e24c12009-10-30 11:49:00 +00005687 }
5688}
5689
5690
Ben Murdochb0fe1622011-05-05 13:52:32 +01005691static bool IsCodeEquivalent(Code* code, Code* recompiled) {
5692 if (code->instruction_size() != recompiled->instruction_size()) return false;
5693 ByteArray* code_relocation = code->relocation_info();
5694 ByteArray* recompiled_relocation = recompiled->relocation_info();
5695 int length = code_relocation->length();
5696 if (length != recompiled_relocation->length()) return false;
5697 int compare = memcmp(code_relocation->GetDataStartAddress(),
5698 recompiled_relocation->GetDataStartAddress(),
5699 length);
5700 return compare == 0;
5701}
5702
5703
5704void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
5705 ASSERT(!has_deoptimization_support());
5706 AssertNoAllocation no_allocation;
5707 Code* code = this->code();
5708 if (IsCodeEquivalent(code, recompiled)) {
5709 // Copy the deoptimization data from the recompiled code.
5710 code->set_deoptimization_data(recompiled->deoptimization_data());
5711 code->set_has_deoptimization_support(true);
5712 } else {
5713 // TODO(3025757): In case the recompiled isn't equivalent to the
5714 // old code, we have to replace it. We should try to avoid this
5715 // altogether because it flushes valuable type feedback by
5716 // effectively resetting all IC state.
5717 set_code(recompiled);
5718 }
5719 ASSERT(has_deoptimization_support());
5720}
5721
5722
5723bool SharedFunctionInfo::VerifyBailoutId(int id) {
5724 // TODO(srdjan): debugging ARM crashes in hydrogen. OK to disable while
5725 // we are always bailing out on ARM.
5726
5727 ASSERT(id != AstNode::kNoNumber);
5728 Code* unoptimized = code();
5729 DeoptimizationOutputData* data =
5730 DeoptimizationOutputData::cast(unoptimized->deoptimization_data());
5731 unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this);
5732 USE(ignore);
5733 return true; // Return true if there was no ASSERT.
5734}
5735
5736
Kristian Monsen0d5e1162010-09-30 15:31:59 +01005737void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) {
5738 ASSERT(!IsInobjectSlackTrackingInProgress());
5739
5740 // Only initiate the tracking the first time.
5741 if (live_objects_may_exist()) return;
5742 set_live_objects_may_exist(true);
5743
5744 // No tracking during the snapshot construction phase.
5745 if (Serializer::enabled()) return;
5746
5747 if (map->unused_property_fields() == 0) return;
5748
5749 // Nonzero counter is a leftover from the previous attempt interrupted
5750 // by GC, keep it.
5751 if (construction_count() == 0) {
5752 set_construction_count(kGenerousAllocationCount);
5753 }
5754 set_initial_map(map);
5755 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubGeneric),
5756 construct_stub());
5757 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubCountdown));
5758}
5759
5760
5761// Called from GC, hence reinterpret_cast and unchecked accessors.
5762void SharedFunctionInfo::DetachInitialMap() {
5763 Map* map = reinterpret_cast<Map*>(initial_map());
5764
5765 // Make the map remember to restore the link if it survives the GC.
5766 map->set_bit_field2(
5767 map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo));
5768
5769 // Undo state changes made by StartInobjectTracking (except the
5770 // construction_count). This way if the initial map does not survive the GC
5771 // then StartInobjectTracking will be called again the next time the
5772 // constructor is called. The countdown will continue and (possibly after
5773 // several more GCs) CompleteInobjectSlackTracking will eventually be called.
5774 set_initial_map(Heap::raw_unchecked_undefined_value());
5775 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubCountdown),
5776 *RawField(this, kConstructStubOffset));
5777 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubGeneric));
5778 // It is safe to clear the flag: it will be set again if the map is live.
5779 set_live_objects_may_exist(false);
5780}
5781
5782
5783// Called from GC, hence reinterpret_cast and unchecked accessors.
5784void SharedFunctionInfo::AttachInitialMap(Map* map) {
5785 map->set_bit_field2(
5786 map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo));
5787
5788 // Resume inobject slack tracking.
5789 set_initial_map(map);
5790 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubGeneric),
5791 *RawField(this, kConstructStubOffset));
5792 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubCountdown));
5793 // The map survived the gc, so there may be objects referencing it.
5794 set_live_objects_may_exist(true);
5795}
5796
5797
5798static void GetMinInobjectSlack(Map* map, void* data) {
5799 int slack = map->unused_property_fields();
5800 if (*reinterpret_cast<int*>(data) > slack) {
5801 *reinterpret_cast<int*>(data) = slack;
5802 }
5803}
5804
5805
5806static void ShrinkInstanceSize(Map* map, void* data) {
5807 int slack = *reinterpret_cast<int*>(data);
5808 map->set_inobject_properties(map->inobject_properties() - slack);
5809 map->set_unused_property_fields(map->unused_property_fields() - slack);
5810 map->set_instance_size(map->instance_size() - slack * kPointerSize);
5811
5812 // Visitor id might depend on the instance size, recalculate it.
5813 map->set_visitor_id(StaticVisitorBase::GetVisitorId(map));
5814}
5815
5816
5817void SharedFunctionInfo::CompleteInobjectSlackTracking() {
5818 ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress());
5819 Map* map = Map::cast(initial_map());
5820
5821 set_initial_map(Heap::undefined_value());
5822 ASSERT_EQ(Builtins::builtin(Builtins::JSConstructStubCountdown),
5823 construct_stub());
5824 set_construct_stub(Builtins::builtin(Builtins::JSConstructStubGeneric));
5825
5826 int slack = map->unused_property_fields();
5827 map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
5828 if (slack != 0) {
5829 // Resize the initial map and all maps in its transition tree.
5830 map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
5831 // Give the correct expected_nof_properties to initial maps created later.
5832 ASSERT(expected_nof_properties() >= slack);
5833 set_expected_nof_properties(expected_nof_properties() - slack);
5834 }
5835}
5836
5837
Steve Blocka7e24c12009-10-30 11:49:00 +00005838void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
5839 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
5840 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
5841 Object* old_target = target;
5842 VisitPointer(&target);
5843 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5844}
5845
5846
Steve Block791712a2010-08-27 10:21:07 +01005847void ObjectVisitor::VisitCodeEntry(Address entry_address) {
5848 Object* code = Code::GetObjectFromEntryAddress(entry_address);
5849 Object* old_code = code;
5850 VisitPointer(&code);
5851 if (code != old_code) {
5852 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry();
5853 }
5854}
5855
5856
Ben Murdochb0fe1622011-05-05 13:52:32 +01005857void ObjectVisitor::VisitGlobalPropertyCell(RelocInfo* rinfo) {
5858 ASSERT(rinfo->rmode() == RelocInfo::GLOBAL_PROPERTY_CELL);
5859 Object* cell = rinfo->target_cell();
5860 Object* old_cell = cell;
5861 VisitPointer(&cell);
5862 if (cell != old_cell) {
5863 rinfo->set_target_cell(reinterpret_cast<JSGlobalPropertyCell*>(cell));
5864 }
5865}
5866
5867
Steve Blocka7e24c12009-10-30 11:49:00 +00005868void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01005869 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
5870 rinfo->IsPatchedReturnSequence()) ||
5871 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
5872 rinfo->IsPatchedDebugBreakSlotSequence()));
Steve Blocka7e24c12009-10-30 11:49:00 +00005873 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
5874 Object* old_target = target;
5875 VisitPointer(&target);
5876 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
5877}
5878
5879
Ben Murdochb0fe1622011-05-05 13:52:32 +01005880void Code::InvalidateRelocation() {
5881 HandleScope scope;
5882 set_relocation_info(Heap::empty_byte_array());
5883}
5884
5885
Steve Blockd0582a62009-12-15 09:54:21 +00005886void Code::Relocate(intptr_t delta) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005887 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
5888 it.rinfo()->apply(delta);
5889 }
5890 CPU::FlushICache(instruction_start(), instruction_size());
5891}
5892
5893
5894void Code::CopyFrom(const CodeDesc& desc) {
5895 // copy code
5896 memmove(instruction_start(), desc.buffer, desc.instr_size);
5897
Steve Blocka7e24c12009-10-30 11:49:00 +00005898 // copy reloc info
5899 memmove(relocation_start(),
5900 desc.buffer + desc.buffer_size - desc.reloc_size,
5901 desc.reloc_size);
5902
5903 // unbox handles and relocate
Steve Block3ce2e202009-11-05 08:53:23 +00005904 intptr_t delta = instruction_start() - desc.buffer;
Steve Blocka7e24c12009-10-30 11:49:00 +00005905 int mode_mask = RelocInfo::kCodeTargetMask |
5906 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
Ben Murdochb0fe1622011-05-05 13:52:32 +01005907 RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) |
Steve Blocka7e24c12009-10-30 11:49:00 +00005908 RelocInfo::kApplyMask;
Steve Block3ce2e202009-11-05 08:53:23 +00005909 Assembler* origin = desc.origin; // Needed to find target_object on X64.
Steve Blocka7e24c12009-10-30 11:49:00 +00005910 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
5911 RelocInfo::Mode mode = it.rinfo()->rmode();
5912 if (mode == RelocInfo::EMBEDDED_OBJECT) {
Steve Block3ce2e202009-11-05 08:53:23 +00005913 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005914 it.rinfo()->set_target_object(*p);
Ben Murdochb0fe1622011-05-05 13:52:32 +01005915 } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
5916 Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle();
5917 it.rinfo()->set_target_cell(*cell);
Steve Blocka7e24c12009-10-30 11:49:00 +00005918 } else if (RelocInfo::IsCodeTarget(mode)) {
5919 // rewrite code handles in inline cache targets to direct
5920 // pointers to the first instruction in the code object
Steve Block3ce2e202009-11-05 08:53:23 +00005921 Handle<Object> p = it.rinfo()->target_object_handle(origin);
Steve Blocka7e24c12009-10-30 11:49:00 +00005922 Code* code = Code::cast(*p);
5923 it.rinfo()->set_target_address(code->instruction_start());
5924 } else {
5925 it.rinfo()->apply(delta);
5926 }
5927 }
5928 CPU::FlushICache(instruction_start(), instruction_size());
5929}
5930
5931
5932// Locate the source position which is closest to the address in the code. This
5933// is using the source position information embedded in the relocation info.
5934// The position returned is relative to the beginning of the script where the
5935// source for this function is found.
5936int Code::SourcePosition(Address pc) {
5937 int distance = kMaxInt;
5938 int position = RelocInfo::kNoPosition; // Initially no position found.
5939 // Run through all the relocation info to find the best matching source
5940 // position. All the code needs to be considered as the sequence of the
5941 // instructions in the code does not necessarily follow the same order as the
5942 // source.
5943 RelocIterator it(this, RelocInfo::kPositionMask);
5944 while (!it.done()) {
5945 // Only look at positions after the current pc.
5946 if (it.rinfo()->pc() < pc) {
5947 // Get position and distance.
Steve Blockd0582a62009-12-15 09:54:21 +00005948
5949 int dist = static_cast<int>(pc - it.rinfo()->pc());
5950 int pos = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005951 // If this position is closer than the current candidate or if it has the
5952 // same distance as the current candidate and the position is higher then
5953 // this position is the new candidate.
5954 if ((dist < distance) ||
5955 (dist == distance && pos > position)) {
5956 position = pos;
5957 distance = dist;
5958 }
5959 }
5960 it.next();
5961 }
5962 return position;
5963}
5964
5965
5966// Same as Code::SourcePosition above except it only looks for statement
5967// positions.
5968int Code::SourceStatementPosition(Address pc) {
5969 // First find the position as close as possible using all position
5970 // information.
5971 int position = SourcePosition(pc);
5972 // Now find the closest statement position before the position.
5973 int statement_position = 0;
5974 RelocIterator it(this, RelocInfo::kPositionMask);
5975 while (!it.done()) {
5976 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
Steve Blockd0582a62009-12-15 09:54:21 +00005977 int p = static_cast<int>(it.rinfo()->data());
Steve Blocka7e24c12009-10-30 11:49:00 +00005978 if (statement_position < p && p <= position) {
5979 statement_position = p;
5980 }
5981 }
5982 it.next();
5983 }
5984 return statement_position;
5985}
5986
5987
Ben Murdochb0fe1622011-05-05 13:52:32 +01005988uint8_t* Code::GetSafepointEntry(Address pc) {
5989 SafepointTable table(this);
5990 unsigned pc_offset = static_cast<unsigned>(pc - instruction_start());
5991 for (unsigned i = 0; i < table.length(); i++) {
5992 // TODO(kasperl): Replace the linear search with binary search.
5993 if (table.GetPcOffset(i) == pc_offset) return table.GetEntry(i);
5994 }
5995 return NULL;
5996}
5997
5998
5999void Code::SetNoStackCheckTable() {
6000 // Indicate the absence of a stack-check table by a table start after the
6001 // end of the instructions. Table start must be aligned, so round up.
6002 set_stack_check_table_start(RoundUp(instruction_size(), kIntSize));
6003}
6004
6005
6006Map* Code::FindFirstMap() {
6007 ASSERT(is_inline_cache_stub());
6008 AssertNoAllocation no_allocation;
6009 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
6010 for (RelocIterator it(this, mask); !it.done(); it.next()) {
6011 RelocInfo* info = it.rinfo();
6012 Object* object = info->target_object();
6013 if (object->IsMap()) return Map::cast(object);
6014 }
6015 return NULL;
6016}
6017
6018
Steve Blocka7e24c12009-10-30 11:49:00 +00006019#ifdef ENABLE_DISASSEMBLER
Ben Murdochb0fe1622011-05-05 13:52:32 +01006020
6021#ifdef OBJECT_PRINT
6022
6023void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
6024 disasm::NameConverter converter;
6025 int deopt_count = DeoptCount();
6026 PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count);
6027 if (0 == deopt_count) return;
6028
6029 PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc", "commands");
6030 for (int i = 0; i < deopt_count; i++) {
6031 int command_count = 0;
6032 PrintF(out, "%6d %6d %6d",
6033 i, AstId(i)->value(), ArgumentsStackHeight(i)->value());
6034 int translation_index = TranslationIndex(i)->value();
6035 TranslationIterator iterator(TranslationByteArray(), translation_index);
6036 Translation::Opcode opcode =
6037 static_cast<Translation::Opcode>(iterator.Next());
6038 ASSERT(Translation::BEGIN == opcode);
6039 int frame_count = iterator.Next();
6040 if (FLAG_print_code_verbose) {
6041 PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode),
6042 frame_count);
6043 }
6044
6045 for (int i = 0; i < frame_count; ++i) {
6046 opcode = static_cast<Translation::Opcode>(iterator.Next());
6047 ASSERT(Translation::FRAME == opcode);
6048 int ast_id = iterator.Next();
6049 int function_id = iterator.Next();
6050 JSFunction* function =
6051 JSFunction::cast(LiteralArray()->get(function_id));
6052 unsigned height = iterator.Next();
6053 if (FLAG_print_code_verbose) {
6054 PrintF(out, "%24s %s {ast_id=%d, function=",
6055 "", Translation::StringFor(opcode), ast_id);
6056 function->PrintName(out);
6057 PrintF(out, ", height=%u}\n", height);
6058 }
6059
6060 // Size of translation is height plus all incoming arguments including
6061 // receiver.
6062 int size = height + function->shared()->formal_parameter_count() + 1;
6063 command_count += size;
6064 for (int j = 0; j < size; ++j) {
6065 opcode = static_cast<Translation::Opcode>(iterator.Next());
6066 if (FLAG_print_code_verbose) {
6067 PrintF(out, "%24s %s ", "", Translation::StringFor(opcode));
6068 }
6069
6070 if (opcode == Translation::DUPLICATE) {
6071 opcode = static_cast<Translation::Opcode>(iterator.Next());
6072 if (FLAG_print_code_verbose) {
6073 PrintF(out, "%s ", Translation::StringFor(opcode));
6074 }
6075 --j; // Two commands share the same frame index.
6076 }
6077
6078 switch (opcode) {
6079 case Translation::BEGIN:
6080 case Translation::FRAME:
6081 case Translation::DUPLICATE:
6082 UNREACHABLE();
6083 break;
6084
6085 case Translation::REGISTER: {
6086 int reg_code = iterator.Next();
6087 if (FLAG_print_code_verbose) {
6088 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
6089 }
6090 break;
6091 }
6092
6093 case Translation::INT32_REGISTER: {
6094 int reg_code = iterator.Next();
6095 if (FLAG_print_code_verbose) {
6096 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
6097 }
6098 break;
6099 }
6100
6101 case Translation::DOUBLE_REGISTER: {
6102 int reg_code = iterator.Next();
6103 if (FLAG_print_code_verbose) {
6104 PrintF(out, "{input=%s}",
6105 DoubleRegister::AllocationIndexToString(reg_code));
6106 }
6107 break;
6108 }
6109
6110 case Translation::STACK_SLOT: {
6111 int input_slot_index = iterator.Next();
6112 if (FLAG_print_code_verbose) {
6113 PrintF(out, "{input=%d}", input_slot_index);
6114 }
6115 break;
6116 }
6117
6118 case Translation::INT32_STACK_SLOT: {
6119 int input_slot_index = iterator.Next();
6120 if (FLAG_print_code_verbose) {
6121 PrintF(out, "{input=%d}", input_slot_index);
6122 }
6123 break;
6124 }
6125
6126 case Translation::DOUBLE_STACK_SLOT: {
6127 int input_slot_index = iterator.Next();
6128 if (FLAG_print_code_verbose) {
6129 PrintF(out, "{input=%d}", input_slot_index);
6130 }
6131 break;
6132 }
6133
6134 case Translation::LITERAL: {
6135 unsigned literal_index = iterator.Next();
6136 if (FLAG_print_code_verbose) {
6137 PrintF(out, "{literal_id=%u}", literal_index);
6138 }
6139 break;
6140 }
6141
6142 case Translation::ARGUMENTS_OBJECT:
6143 break;
6144 }
6145 if (FLAG_print_code_verbose) PrintF(out, "\n");
6146 }
6147 }
6148 if (!FLAG_print_code_verbose) PrintF(out, " %12d\n", command_count);
6149 }
6150}
6151
6152
6153void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) {
6154 PrintF(out, "Deoptimization Output Data (deopt points = %d)\n",
6155 this->DeoptPoints());
6156 if (this->DeoptPoints() == 0) return;
6157
6158 PrintF("%6s %8s %s\n", "ast id", "pc", "state");
6159 for (int i = 0; i < this->DeoptPoints(); i++) {
6160 int pc_and_state = this->PcAndState(i)->value();
6161 PrintF("%6d %8d %s\n",
6162 this->AstId(i)->value(),
6163 FullCodeGenerator::PcField::decode(pc_and_state),
6164 FullCodeGenerator::State2String(
6165 FullCodeGenerator::StateField::decode(pc_and_state)));
6166 }
6167}
6168
6169#endif
6170
6171
Steve Blocka7e24c12009-10-30 11:49:00 +00006172// Identify kind of code.
6173const char* Code::Kind2String(Kind kind) {
6174 switch (kind) {
6175 case FUNCTION: return "FUNCTION";
Ben Murdochb0fe1622011-05-05 13:52:32 +01006176 case OPTIMIZED_FUNCTION: return "OPTIMIZED_FUNCTION";
Steve Blocka7e24c12009-10-30 11:49:00 +00006177 case STUB: return "STUB";
6178 case BUILTIN: return "BUILTIN";
6179 case LOAD_IC: return "LOAD_IC";
6180 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
6181 case STORE_IC: return "STORE_IC";
6182 case KEYED_STORE_IC: return "KEYED_STORE_IC";
6183 case CALL_IC: return "CALL_IC";
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006184 case KEYED_CALL_IC: return "KEYED_CALL_IC";
Steve Block6ded16b2010-05-10 14:33:55 +01006185 case BINARY_OP_IC: return "BINARY_OP_IC";
Ben Murdochb0fe1622011-05-05 13:52:32 +01006186 case TYPE_RECORDING_BINARY_OP_IC: return "TYPE_RECORDING_BINARY_OP_IC";
6187 case COMPARE_IC: return "COMPARE_IC";
Steve Blocka7e24c12009-10-30 11:49:00 +00006188 }
6189 UNREACHABLE();
6190 return NULL;
6191}
6192
6193
6194const char* Code::ICState2String(InlineCacheState state) {
6195 switch (state) {
6196 case UNINITIALIZED: return "UNINITIALIZED";
6197 case PREMONOMORPHIC: return "PREMONOMORPHIC";
6198 case MONOMORPHIC: return "MONOMORPHIC";
6199 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
6200 case MEGAMORPHIC: return "MEGAMORPHIC";
6201 case DEBUG_BREAK: return "DEBUG_BREAK";
6202 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
6203 }
6204 UNREACHABLE();
6205 return NULL;
6206}
6207
6208
6209const char* Code::PropertyType2String(PropertyType type) {
6210 switch (type) {
6211 case NORMAL: return "NORMAL";
6212 case FIELD: return "FIELD";
6213 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
6214 case CALLBACKS: return "CALLBACKS";
6215 case INTERCEPTOR: return "INTERCEPTOR";
6216 case MAP_TRANSITION: return "MAP_TRANSITION";
6217 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
6218 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
6219 }
6220 UNREACHABLE();
6221 return NULL;
6222}
6223
Ben Murdochb0fe1622011-05-05 13:52:32 +01006224
6225void Code::Disassemble(const char* name, FILE* out) {
6226 PrintF(out, "kind = %s\n", Kind2String(kind()));
Steve Blocka7e24c12009-10-30 11:49:00 +00006227 if (is_inline_cache_stub()) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01006228 PrintF(out, "ic_state = %s\n", ICState2String(ic_state()));
6229 PrintF(out, "ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
Steve Blocka7e24c12009-10-30 11:49:00 +00006230 if (ic_state() == MONOMORPHIC) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01006231 PrintF(out, "type = %s\n", PropertyType2String(type()));
Steve Blocka7e24c12009-10-30 11:49:00 +00006232 }
6233 }
6234 if ((name != NULL) && (name[0] != '\0')) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01006235 PrintF(out, "name = %s\n", name);
6236 }
6237 if (kind() == OPTIMIZED_FUNCTION) {
6238 PrintF(out, "stack_slots = %d\n", stack_slots());
Steve Blocka7e24c12009-10-30 11:49:00 +00006239 }
6240
Ben Murdochb0fe1622011-05-05 13:52:32 +01006241 PrintF(out, "Instructions (size = %d)\n", instruction_size());
6242 Disassembler::Decode(out, this);
6243 PrintF(out, "\n");
6244
6245#ifdef DEBUG
6246 if (kind() == FUNCTION) {
6247 DeoptimizationOutputData* data =
6248 DeoptimizationOutputData::cast(this->deoptimization_data());
6249 data->DeoptimizationOutputDataPrint(out);
6250 } else if (kind() == OPTIMIZED_FUNCTION) {
6251 DeoptimizationInputData* data =
6252 DeoptimizationInputData::cast(this->deoptimization_data());
6253 data->DeoptimizationInputDataPrint(out);
6254 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006255 PrintF("\n");
Ben Murdochb0fe1622011-05-05 13:52:32 +01006256#endif
6257
6258 if (kind() == OPTIMIZED_FUNCTION) {
6259 SafepointTable table(this);
6260 PrintF(out, "Safepoints (size = %u)\n", table.size());
6261 for (unsigned i = 0; i < table.length(); i++) {
6262 unsigned pc_offset = table.GetPcOffset(i);
6263 PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset);
6264 table.PrintEntry(i);
6265 PrintF(out, " (sp -> fp)");
6266 int deoptimization_index = table.GetDeoptimizationIndex(i);
6267 if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) {
6268 PrintF(out, " %6d", deoptimization_index);
6269 } else {
6270 PrintF(out, " <none>");
6271 }
6272 PrintF(out, "\n");
6273 }
6274 PrintF(out, "\n");
6275 } else if (kind() == FUNCTION) {
6276 unsigned offset = stack_check_table_start();
6277 // If there is no stack check table, the "table start" will at or after
6278 // (due to alignment) the end of the instruction stream.
6279 if (static_cast<int>(offset) < instruction_size()) {
6280 unsigned* address =
6281 reinterpret_cast<unsigned*>(instruction_start() + offset);
6282 unsigned length = address[0];
6283 PrintF(out, "Stack checks (size = %u)\n", length);
6284 PrintF(out, "ast_id pc_offset\n");
6285 for (unsigned i = 0; i < length; ++i) {
6286 unsigned index = (2 * i) + 1;
6287 PrintF(out, "%6u %9u\n", address[index], address[index + 1]);
6288 }
6289 PrintF(out, "\n");
6290 }
6291 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006292
6293 PrintF("RelocInfo (size = %d)\n", relocation_size());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006294 for (RelocIterator it(this); !it.done(); it.next()) it.rinfo()->Print(out);
6295 PrintF(out, "\n");
Steve Blocka7e24c12009-10-30 11:49:00 +00006296}
6297#endif // ENABLE_DISASSEMBLER
6298
6299
John Reck59135872010-11-02 12:39:01 -07006300MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
6301 int length) {
Steve Block3ce2e202009-11-05 08:53:23 +00006302 // We should never end in here with a pixel or external array.
6303 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Block8defd9f2010-07-08 12:39:36 +01006304
John Reck59135872010-11-02 12:39:01 -07006305 Object* obj;
6306 { MaybeObject* maybe_obj = Heap::AllocateFixedArrayWithHoles(capacity);
6307 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6308 }
Steve Block8defd9f2010-07-08 12:39:36 +01006309 FixedArray* elems = FixedArray::cast(obj);
6310
John Reck59135872010-11-02 12:39:01 -07006311 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
6312 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6313 }
Steve Block8defd9f2010-07-08 12:39:36 +01006314 Map* new_map = Map::cast(obj);
6315
Leon Clarke4515c472010-02-03 11:58:03 +00006316 AssertNoAllocation no_gc;
6317 WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00006318 switch (GetElementsKind()) {
6319 case FAST_ELEMENTS: {
6320 FixedArray* old_elements = FixedArray::cast(elements());
6321 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
6322 // Fill out the new array with this content and array holes.
6323 for (uint32_t i = 0; i < old_length; i++) {
6324 elems->set(i, old_elements->get(i), mode);
6325 }
6326 break;
6327 }
6328 case DICTIONARY_ELEMENTS: {
6329 NumberDictionary* dictionary = NumberDictionary::cast(elements());
6330 for (int i = 0; i < dictionary->Capacity(); i++) {
6331 Object* key = dictionary->KeyAt(i);
6332 if (key->IsNumber()) {
6333 uint32_t entry = static_cast<uint32_t>(key->Number());
6334 elems->set(entry, dictionary->ValueAt(i), mode);
6335 }
6336 }
6337 break;
6338 }
6339 default:
6340 UNREACHABLE();
6341 break;
6342 }
Steve Block8defd9f2010-07-08 12:39:36 +01006343
6344 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00006345 set_elements(elems);
Steve Block8defd9f2010-07-08 12:39:36 +01006346
6347 if (IsJSArray()) {
6348 JSArray::cast(this)->set_length(Smi::FromInt(length));
6349 }
6350
6351 return this;
Steve Blocka7e24c12009-10-30 11:49:00 +00006352}
6353
6354
John Reck59135872010-11-02 12:39:01 -07006355MaybeObject* JSObject::SetSlowElements(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00006356 // We should never end in here with a pixel or external array.
6357 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00006358
6359 uint32_t new_length = static_cast<uint32_t>(len->Number());
6360
6361 switch (GetElementsKind()) {
6362 case FAST_ELEMENTS: {
6363 // Make sure we never try to shrink dense arrays into sparse arrays.
6364 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
6365 new_length);
John Reck59135872010-11-02 12:39:01 -07006366 Object* obj;
6367 { MaybeObject* maybe_obj = NormalizeElements();
6368 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6369 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006370
6371 // Update length for JSArrays.
6372 if (IsJSArray()) JSArray::cast(this)->set_length(len);
6373 break;
6374 }
6375 case DICTIONARY_ELEMENTS: {
6376 if (IsJSArray()) {
6377 uint32_t old_length =
Steve Block6ded16b2010-05-10 14:33:55 +01006378 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
Steve Blocka7e24c12009-10-30 11:49:00 +00006379 element_dictionary()->RemoveNumberEntries(new_length, old_length),
6380 JSArray::cast(this)->set_length(len);
6381 }
6382 break;
6383 }
6384 default:
6385 UNREACHABLE();
6386 break;
6387 }
6388 return this;
6389}
6390
6391
John Reck59135872010-11-02 12:39:01 -07006392MaybeObject* JSArray::Initialize(int capacity) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006393 ASSERT(capacity >= 0);
Leon Clarke4515c472010-02-03 11:58:03 +00006394 set_length(Smi::FromInt(0));
Steve Blocka7e24c12009-10-30 11:49:00 +00006395 FixedArray* new_elements;
6396 if (capacity == 0) {
6397 new_elements = Heap::empty_fixed_array();
6398 } else {
John Reck59135872010-11-02 12:39:01 -07006399 Object* obj;
6400 { MaybeObject* maybe_obj = Heap::AllocateFixedArrayWithHoles(capacity);
6401 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6402 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006403 new_elements = FixedArray::cast(obj);
6404 }
6405 set_elements(new_elements);
6406 return this;
6407}
6408
6409
6410void JSArray::Expand(int required_size) {
6411 Handle<JSArray> self(this);
6412 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
6413 int old_size = old_backing->length();
Steve Blockd0582a62009-12-15 09:54:21 +00006414 int new_size = required_size > old_size ? required_size : old_size;
Steve Blocka7e24c12009-10-30 11:49:00 +00006415 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
6416 // Can't use this any more now because we may have had a GC!
6417 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
6418 self->SetContent(*new_backing);
6419}
6420
6421
6422// Computes the new capacity when expanding the elements of a JSObject.
6423static int NewElementsCapacity(int old_capacity) {
6424 // (old_capacity + 50%) + 16
6425 return old_capacity + (old_capacity >> 1) + 16;
6426}
6427
6428
John Reck59135872010-11-02 12:39:01 -07006429static Failure* ArrayLengthRangeError() {
Steve Blocka7e24c12009-10-30 11:49:00 +00006430 HandleScope scope;
6431 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
6432 HandleVector<Object>(NULL, 0)));
6433}
6434
6435
John Reck59135872010-11-02 12:39:01 -07006436MaybeObject* JSObject::SetElementsLength(Object* len) {
Steve Block3ce2e202009-11-05 08:53:23 +00006437 // We should never end in here with a pixel or external array.
Steve Block6ded16b2010-05-10 14:33:55 +01006438 ASSERT(AllowsSetElementsLength());
Steve Blocka7e24c12009-10-30 11:49:00 +00006439
John Reck59135872010-11-02 12:39:01 -07006440 MaybeObject* maybe_smi_length = len->ToSmi();
6441 Object* smi_length = Smi::FromInt(0);
6442 if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) {
Steve Block8defd9f2010-07-08 12:39:36 +01006443 const int value = Smi::cast(smi_length)->value();
Steve Blocka7e24c12009-10-30 11:49:00 +00006444 if (value < 0) return ArrayLengthRangeError();
6445 switch (GetElementsKind()) {
6446 case FAST_ELEMENTS: {
6447 int old_capacity = FixedArray::cast(elements())->length();
6448 if (value <= old_capacity) {
6449 if (IsJSArray()) {
John Reck59135872010-11-02 12:39:01 -07006450 Object* obj;
6451 { MaybeObject* maybe_obj = EnsureWritableFastElements();
6452 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6453 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006454 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
6455 // NOTE: We may be able to optimize this by removing the
6456 // last part of the elements backing storage array and
6457 // setting the capacity to the new size.
6458 for (int i = value; i < old_length; i++) {
6459 FixedArray::cast(elements())->set_the_hole(i);
6460 }
Leon Clarke4515c472010-02-03 11:58:03 +00006461 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006462 }
6463 return this;
6464 }
6465 int min = NewElementsCapacity(old_capacity);
6466 int new_capacity = value > min ? value : min;
6467 if (new_capacity <= kMaxFastElementsLength ||
6468 !ShouldConvertToSlowElements(new_capacity)) {
John Reck59135872010-11-02 12:39:01 -07006469 Object* obj;
6470 { MaybeObject* maybe_obj =
6471 SetFastElementsCapacityAndLength(new_capacity, value);
6472 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6473 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006474 return this;
6475 }
6476 break;
6477 }
6478 case DICTIONARY_ELEMENTS: {
6479 if (IsJSArray()) {
6480 if (value == 0) {
6481 // If the length of a slow array is reset to zero, we clear
6482 // the array and flush backing storage. This has the added
6483 // benefit that the array returns to fast mode.
John Reck59135872010-11-02 12:39:01 -07006484 Object* obj;
6485 { MaybeObject* maybe_obj = ResetElements();
6486 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6487 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006488 } else {
6489 // Remove deleted elements.
6490 uint32_t old_length =
6491 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
6492 element_dictionary()->RemoveNumberEntries(value, old_length);
6493 }
Leon Clarke4515c472010-02-03 11:58:03 +00006494 JSArray::cast(this)->set_length(Smi::cast(smi_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006495 }
6496 return this;
6497 }
6498 default:
6499 UNREACHABLE();
6500 break;
6501 }
6502 }
6503
6504 // General slow case.
6505 if (len->IsNumber()) {
6506 uint32_t length;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006507 if (len->ToArrayIndex(&length)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006508 return SetSlowElements(len);
6509 } else {
6510 return ArrayLengthRangeError();
6511 }
6512 }
6513
6514 // len is not a number so make the array size one and
6515 // set only element to len.
John Reck59135872010-11-02 12:39:01 -07006516 Object* obj;
6517 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(1);
6518 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6519 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006520 FixedArray::cast(obj)->set(0, len);
Leon Clarke4515c472010-02-03 11:58:03 +00006521 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006522 set_elements(FixedArray::cast(obj));
6523 return this;
6524}
6525
6526
John Reck59135872010-11-02 12:39:01 -07006527MaybeObject* JSObject::SetPrototype(Object* value,
6528 bool skip_hidden_prototypes) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006529 // Silently ignore the change if value is not a JSObject or null.
6530 // SpiderMonkey behaves this way.
6531 if (!value->IsJSObject() && !value->IsNull()) return value;
6532
6533 // Before we can set the prototype we need to be sure
6534 // prototype cycles are prevented.
6535 // It is sufficient to validate that the receiver is not in the new prototype
6536 // chain.
6537 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
6538 if (JSObject::cast(pt) == this) {
6539 // Cycle detected.
6540 HandleScope scope;
6541 return Top::Throw(*Factory::NewError("cyclic_proto",
6542 HandleVector<Object>(NULL, 0)));
6543 }
6544 }
6545
6546 JSObject* real_receiver = this;
6547
6548 if (skip_hidden_prototypes) {
6549 // Find the first object in the chain whose prototype object is not
6550 // hidden and set the new prototype on that object.
6551 Object* current_proto = real_receiver->GetPrototype();
6552 while (current_proto->IsJSObject() &&
6553 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
6554 real_receiver = JSObject::cast(current_proto);
6555 current_proto = current_proto->GetPrototype();
6556 }
6557 }
6558
6559 // Set the new prototype of the object.
John Reck59135872010-11-02 12:39:01 -07006560 Object* new_map;
6561 { MaybeObject* maybe_new_map = real_receiver->map()->CopyDropTransitions();
6562 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
6563 }
Andrei Popescu402d9372010-02-26 13:31:12 +00006564 Map::cast(new_map)->set_prototype(value);
6565 real_receiver->set_map(Map::cast(new_map));
6566
Kristian Monsen25f61362010-05-21 11:50:48 +01006567 Heap::ClearInstanceofCache();
6568
Andrei Popescu402d9372010-02-26 13:31:12 +00006569 return value;
6570}
6571
6572
Steve Blocka7e24c12009-10-30 11:49:00 +00006573bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
6574 switch (GetElementsKind()) {
6575 case FAST_ELEMENTS: {
6576 uint32_t length = IsJSArray() ?
6577 static_cast<uint32_t>
6578 (Smi::cast(JSArray::cast(this)->length())->value()) :
6579 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6580 if ((index < length) &&
6581 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
6582 return true;
6583 }
6584 break;
6585 }
6586 case PIXEL_ELEMENTS: {
6587 // TODO(iposva): Add testcase.
6588 PixelArray* pixels = PixelArray::cast(elements());
6589 if (index < static_cast<uint32_t>(pixels->length())) {
6590 return true;
6591 }
6592 break;
6593 }
Steve Block3ce2e202009-11-05 08:53:23 +00006594 case EXTERNAL_BYTE_ELEMENTS:
6595 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6596 case EXTERNAL_SHORT_ELEMENTS:
6597 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6598 case EXTERNAL_INT_ELEMENTS:
6599 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6600 case EXTERNAL_FLOAT_ELEMENTS: {
6601 // TODO(kbr): Add testcase.
6602 ExternalArray* array = ExternalArray::cast(elements());
6603 if (index < static_cast<uint32_t>(array->length())) {
6604 return true;
6605 }
6606 break;
6607 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006608 case DICTIONARY_ELEMENTS: {
6609 if (element_dictionary()->FindEntry(index)
6610 != NumberDictionary::kNotFound) {
6611 return true;
6612 }
6613 break;
6614 }
6615 default:
6616 UNREACHABLE();
6617 break;
6618 }
6619
6620 // Handle [] on String objects.
6621 if (this->IsStringObjectWithCharacterAt(index)) return true;
6622
6623 Object* pt = GetPrototype();
6624 if (pt == Heap::null_value()) return false;
6625 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
6626}
6627
6628
6629bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
6630 // Make sure that the top context does not change when doing
6631 // callbacks or interceptor calls.
6632 AssertNoContextChange ncc;
6633 HandleScope scope;
6634 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6635 Handle<JSObject> receiver_handle(receiver);
6636 Handle<JSObject> holder_handle(this);
6637 CustomArguments args(interceptor->data(), receiver, this);
6638 v8::AccessorInfo info(args.end());
6639 if (!interceptor->query()->IsUndefined()) {
6640 v8::IndexedPropertyQuery query =
6641 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
6642 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
Iain Merrick75681382010-08-19 15:07:18 +01006643 v8::Handle<v8::Integer> result;
Steve Blocka7e24c12009-10-30 11:49:00 +00006644 {
6645 // Leaving JavaScript.
6646 VMState state(EXTERNAL);
6647 result = query(index, info);
6648 }
Iain Merrick75681382010-08-19 15:07:18 +01006649 if (!result.IsEmpty()) {
6650 ASSERT(result->IsInt32());
6651 return true; // absence of property is signaled by empty handle.
6652 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006653 } else if (!interceptor->getter()->IsUndefined()) {
6654 v8::IndexedPropertyGetter getter =
6655 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
6656 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
6657 v8::Handle<v8::Value> result;
6658 {
6659 // Leaving JavaScript.
6660 VMState state(EXTERNAL);
6661 result = getter(index, info);
6662 }
6663 if (!result.IsEmpty()) return true;
6664 }
6665 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
6666}
6667
6668
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006669JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006670 // Check access rights if needed.
6671 if (IsAccessCheckNeeded() &&
6672 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6673 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006674 return UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006675 }
6676
6677 // Check for lookup interceptor
6678 if (HasIndexedInterceptor()) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006679 return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
6680 : UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006681 }
6682
6683 // Handle [] on String objects.
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006684 if (this->IsStringObjectWithCharacterAt(index)) {
6685 return STRING_CHARACTER_ELEMENT;
6686 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006687
6688 switch (GetElementsKind()) {
6689 case FAST_ELEMENTS: {
6690 uint32_t length = IsJSArray() ?
6691 static_cast<uint32_t>
6692 (Smi::cast(JSArray::cast(this)->length())->value()) :
6693 static_cast<uint32_t>(FixedArray::cast(elements())->length());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006694 if ((index < length) &&
6695 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
6696 return FAST_ELEMENT;
6697 }
6698 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006699 }
6700 case PIXEL_ELEMENTS: {
6701 PixelArray* pixels = PixelArray::cast(elements());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006702 if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT;
6703 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006704 }
Steve Block3ce2e202009-11-05 08:53:23 +00006705 case EXTERNAL_BYTE_ELEMENTS:
6706 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6707 case EXTERNAL_SHORT_ELEMENTS:
6708 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6709 case EXTERNAL_INT_ELEMENTS:
6710 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6711 case EXTERNAL_FLOAT_ELEMENTS: {
6712 ExternalArray* array = ExternalArray::cast(elements());
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006713 if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
6714 break;
Steve Block3ce2e202009-11-05 08:53:23 +00006715 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006716 case DICTIONARY_ELEMENTS: {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006717 if (element_dictionary()->FindEntry(index) !=
6718 NumberDictionary::kNotFound) {
6719 return DICTIONARY_ELEMENT;
6720 }
6721 break;
Steve Blocka7e24c12009-10-30 11:49:00 +00006722 }
6723 default:
6724 UNREACHABLE();
6725 break;
6726 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01006727
6728 return UNDEFINED_ELEMENT;
Steve Blocka7e24c12009-10-30 11:49:00 +00006729}
6730
6731
6732bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
6733 // Check access rights if needed.
6734 if (IsAccessCheckNeeded() &&
6735 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
6736 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
6737 return false;
6738 }
6739
6740 // Check for lookup interceptor
6741 if (HasIndexedInterceptor()) {
6742 return HasElementWithInterceptor(receiver, index);
6743 }
6744
6745 switch (GetElementsKind()) {
6746 case FAST_ELEMENTS: {
6747 uint32_t length = IsJSArray() ?
6748 static_cast<uint32_t>
6749 (Smi::cast(JSArray::cast(this)->length())->value()) :
6750 static_cast<uint32_t>(FixedArray::cast(elements())->length());
6751 if ((index < length) &&
6752 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
6753 break;
6754 }
6755 case PIXEL_ELEMENTS: {
6756 PixelArray* pixels = PixelArray::cast(elements());
6757 if (index < static_cast<uint32_t>(pixels->length())) {
6758 return true;
6759 }
6760 break;
6761 }
Steve Block3ce2e202009-11-05 08:53:23 +00006762 case EXTERNAL_BYTE_ELEMENTS:
6763 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
6764 case EXTERNAL_SHORT_ELEMENTS:
6765 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
6766 case EXTERNAL_INT_ELEMENTS:
6767 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
6768 case EXTERNAL_FLOAT_ELEMENTS: {
6769 ExternalArray* array = ExternalArray::cast(elements());
6770 if (index < static_cast<uint32_t>(array->length())) {
6771 return true;
6772 }
6773 break;
6774 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006775 case DICTIONARY_ELEMENTS: {
6776 if (element_dictionary()->FindEntry(index)
6777 != NumberDictionary::kNotFound) {
6778 return true;
6779 }
6780 break;
6781 }
6782 default:
6783 UNREACHABLE();
6784 break;
6785 }
6786
6787 // Handle [] on String objects.
6788 if (this->IsStringObjectWithCharacterAt(index)) return true;
6789
6790 Object* pt = GetPrototype();
6791 if (pt == Heap::null_value()) return false;
6792 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
6793}
6794
6795
John Reck59135872010-11-02 12:39:01 -07006796MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
Steve Block9fac8402011-05-12 15:51:54 +01006797 Object* value,
6798 bool check_prototype) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006799 // Make sure that the top context does not change when doing
6800 // callbacks or interceptor calls.
6801 AssertNoContextChange ncc;
6802 HandleScope scope;
6803 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
6804 Handle<JSObject> this_handle(this);
6805 Handle<Object> value_handle(value);
6806 if (!interceptor->setter()->IsUndefined()) {
6807 v8::IndexedPropertySetter setter =
6808 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
6809 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
6810 CustomArguments args(interceptor->data(), this, this);
6811 v8::AccessorInfo info(args.end());
6812 v8::Handle<v8::Value> result;
6813 {
6814 // Leaving JavaScript.
6815 VMState state(EXTERNAL);
6816 result = setter(index, v8::Utils::ToLocal(value_handle), info);
6817 }
6818 RETURN_IF_SCHEDULED_EXCEPTION();
6819 if (!result.IsEmpty()) return *value_handle;
6820 }
John Reck59135872010-11-02 12:39:01 -07006821 MaybeObject* raw_result =
Steve Block9fac8402011-05-12 15:51:54 +01006822 this_handle->SetElementWithoutInterceptor(index,
6823 *value_handle,
6824 check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00006825 RETURN_IF_SCHEDULED_EXCEPTION();
6826 return raw_result;
6827}
6828
6829
John Reck59135872010-11-02 12:39:01 -07006830MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
6831 Object* structure,
6832 uint32_t index,
6833 Object* holder) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006834 ASSERT(!structure->IsProxy());
6835
6836 // api style callbacks.
6837 if (structure->IsAccessorInfo()) {
6838 AccessorInfo* data = AccessorInfo::cast(structure);
6839 Object* fun_obj = data->getter();
6840 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
6841 HandleScope scope;
6842 Handle<JSObject> self(JSObject::cast(receiver));
6843 Handle<JSObject> holder_handle(JSObject::cast(holder));
6844 Handle<Object> number = Factory::NewNumberFromUint(index);
6845 Handle<String> key(Factory::NumberToString(number));
6846 LOG(ApiNamedPropertyAccess("load", *self, *key));
6847 CustomArguments args(data->data(), *self, *holder_handle);
6848 v8::AccessorInfo info(args.end());
6849 v8::Handle<v8::Value> result;
6850 {
6851 // Leaving JavaScript.
6852 VMState state(EXTERNAL);
6853 result = call_fun(v8::Utils::ToLocal(key), info);
6854 }
6855 RETURN_IF_SCHEDULED_EXCEPTION();
6856 if (result.IsEmpty()) return Heap::undefined_value();
6857 return *v8::Utils::OpenHandle(*result);
6858 }
6859
6860 // __defineGetter__ callback
6861 if (structure->IsFixedArray()) {
6862 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
6863 if (getter->IsJSFunction()) {
6864 return Object::GetPropertyWithDefinedGetter(receiver,
6865 JSFunction::cast(getter));
6866 }
6867 // Getter is not a function.
6868 return Heap::undefined_value();
6869 }
6870
6871 UNREACHABLE();
6872 return NULL;
6873}
6874
6875
John Reck59135872010-11-02 12:39:01 -07006876MaybeObject* JSObject::SetElementWithCallback(Object* structure,
6877 uint32_t index,
6878 Object* value,
6879 JSObject* holder) {
Leon Clarkef7060e22010-06-03 12:02:55 +01006880 HandleScope scope;
6881
6882 // We should never get here to initialize a const with the hole
6883 // value since a const declaration would conflict with the setter.
6884 ASSERT(!value->IsTheHole());
6885 Handle<Object> value_handle(value);
6886
6887 // To accommodate both the old and the new api we switch on the
6888 // data structure used to store the callbacks. Eventually proxy
6889 // callbacks should be phased out.
6890 ASSERT(!structure->IsProxy());
6891
6892 if (structure->IsAccessorInfo()) {
6893 // api style callbacks
6894 AccessorInfo* data = AccessorInfo::cast(structure);
6895 Object* call_obj = data->setter();
6896 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
6897 if (call_fun == NULL) return value;
6898 Handle<Object> number = Factory::NewNumberFromUint(index);
6899 Handle<String> key(Factory::NumberToString(number));
6900 LOG(ApiNamedPropertyAccess("store", this, *key));
6901 CustomArguments args(data->data(), this, JSObject::cast(holder));
6902 v8::AccessorInfo info(args.end());
6903 {
6904 // Leaving JavaScript.
6905 VMState state(EXTERNAL);
6906 call_fun(v8::Utils::ToLocal(key),
6907 v8::Utils::ToLocal(value_handle),
6908 info);
6909 }
6910 RETURN_IF_SCHEDULED_EXCEPTION();
6911 return *value_handle;
6912 }
6913
6914 if (structure->IsFixedArray()) {
6915 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
6916 if (setter->IsJSFunction()) {
6917 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
6918 } else {
6919 Handle<Object> holder_handle(holder);
6920 Handle<Object> key(Factory::NewNumberFromUint(index));
6921 Handle<Object> args[2] = { key, holder_handle };
6922 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
6923 HandleVector(args, 2)));
6924 }
6925 }
6926
6927 UNREACHABLE();
6928 return NULL;
6929}
6930
6931
Steve Blocka7e24c12009-10-30 11:49:00 +00006932// Adding n elements in fast case is O(n*n).
6933// Note: revisit design to have dual undefined values to capture absent
6934// elements.
Steve Block9fac8402011-05-12 15:51:54 +01006935MaybeObject* JSObject::SetFastElement(uint32_t index,
6936 Object* value,
6937 bool check_prototype) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006938 ASSERT(HasFastElements());
6939
John Reck59135872010-11-02 12:39:01 -07006940 Object* elms_obj;
6941 { MaybeObject* maybe_elms_obj = EnsureWritableFastElements();
6942 if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
6943 }
Iain Merrick75681382010-08-19 15:07:18 +01006944 FixedArray* elms = FixedArray::cast(elms_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +00006945 uint32_t elms_length = static_cast<uint32_t>(elms->length());
6946
Steve Block9fac8402011-05-12 15:51:54 +01006947 if (check_prototype &&
6948 (index >= elms_length || elms->get(index)->IsTheHole()) &&
6949 SetElementWithCallbackSetterInPrototypes(index, value)) {
6950 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00006951 }
6952
Steve Block9fac8402011-05-12 15:51:54 +01006953
Steve Blocka7e24c12009-10-30 11:49:00 +00006954 // Check whether there is extra space in fixed array..
6955 if (index < elms_length) {
6956 elms->set(index, value);
6957 if (IsJSArray()) {
6958 // Update the length of the array if needed.
6959 uint32_t array_length = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006960 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00006961 if (index >= array_length) {
Leon Clarke4515c472010-02-03 11:58:03 +00006962 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +00006963 }
6964 }
6965 return value;
6966 }
6967
6968 // Allow gap in fast case.
6969 if ((index - elms_length) < kMaxGap) {
6970 // Try allocating extra space.
6971 int new_capacity = NewElementsCapacity(index+1);
6972 if (new_capacity <= kMaxFastElementsLength ||
6973 !ShouldConvertToSlowElements(new_capacity)) {
6974 ASSERT(static_cast<uint32_t>(new_capacity) > index);
John Reck59135872010-11-02 12:39:01 -07006975 Object* obj;
6976 { MaybeObject* maybe_obj =
6977 SetFastElementsCapacityAndLength(new_capacity, index + 1);
6978 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6979 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006980 FixedArray::cast(elements())->set(index, value);
6981 return value;
6982 }
6983 }
6984
6985 // Otherwise default to slow case.
John Reck59135872010-11-02 12:39:01 -07006986 Object* obj;
6987 { MaybeObject* maybe_obj = NormalizeElements();
6988 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
6989 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006990 ASSERT(HasDictionaryElements());
Steve Block9fac8402011-05-12 15:51:54 +01006991 return SetElement(index, value, check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00006992}
6993
Iain Merrick75681382010-08-19 15:07:18 +01006994
Steve Block9fac8402011-05-12 15:51:54 +01006995MaybeObject* JSObject::SetElement(uint32_t index,
6996 Object* value,
6997 bool check_prototype) {
Steve Blocka7e24c12009-10-30 11:49:00 +00006998 // Check access rights if needed.
6999 if (IsAccessCheckNeeded() &&
7000 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
Iain Merrick75681382010-08-19 15:07:18 +01007001 HandleScope scope;
7002 Handle<Object> value_handle(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00007003 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
Iain Merrick75681382010-08-19 15:07:18 +01007004 return *value_handle;
Steve Blocka7e24c12009-10-30 11:49:00 +00007005 }
7006
7007 if (IsJSGlobalProxy()) {
7008 Object* proto = GetPrototype();
7009 if (proto->IsNull()) return value;
7010 ASSERT(proto->IsJSGlobalObject());
Steve Block9fac8402011-05-12 15:51:54 +01007011 return JSObject::cast(proto)->SetElement(index, value, check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007012 }
7013
7014 // Check for lookup interceptor
7015 if (HasIndexedInterceptor()) {
Steve Block9fac8402011-05-12 15:51:54 +01007016 return SetElementWithInterceptor(index, value, check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007017 }
7018
Steve Block9fac8402011-05-12 15:51:54 +01007019 return SetElementWithoutInterceptor(index, value, check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007020}
7021
7022
John Reck59135872010-11-02 12:39:01 -07007023MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
Steve Block9fac8402011-05-12 15:51:54 +01007024 Object* value,
7025 bool check_prototype) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007026 switch (GetElementsKind()) {
7027 case FAST_ELEMENTS:
7028 // Fast case.
Steve Block9fac8402011-05-12 15:51:54 +01007029 return SetFastElement(index, value, check_prototype);
Steve Blocka7e24c12009-10-30 11:49:00 +00007030 case PIXEL_ELEMENTS: {
7031 PixelArray* pixels = PixelArray::cast(elements());
7032 return pixels->SetValue(index, value);
7033 }
Steve Block3ce2e202009-11-05 08:53:23 +00007034 case EXTERNAL_BYTE_ELEMENTS: {
7035 ExternalByteArray* array = ExternalByteArray::cast(elements());
7036 return array->SetValue(index, value);
7037 }
7038 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7039 ExternalUnsignedByteArray* array =
7040 ExternalUnsignedByteArray::cast(elements());
7041 return array->SetValue(index, value);
7042 }
7043 case EXTERNAL_SHORT_ELEMENTS: {
7044 ExternalShortArray* array = ExternalShortArray::cast(elements());
7045 return array->SetValue(index, value);
7046 }
7047 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7048 ExternalUnsignedShortArray* array =
7049 ExternalUnsignedShortArray::cast(elements());
7050 return array->SetValue(index, value);
7051 }
7052 case EXTERNAL_INT_ELEMENTS: {
7053 ExternalIntArray* array = ExternalIntArray::cast(elements());
7054 return array->SetValue(index, value);
7055 }
7056 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7057 ExternalUnsignedIntArray* array =
7058 ExternalUnsignedIntArray::cast(elements());
7059 return array->SetValue(index, value);
7060 }
7061 case EXTERNAL_FLOAT_ELEMENTS: {
7062 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
7063 return array->SetValue(index, value);
7064 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007065 case DICTIONARY_ELEMENTS: {
7066 // Insert element in the dictionary.
7067 FixedArray* elms = FixedArray::cast(elements());
7068 NumberDictionary* dictionary = NumberDictionary::cast(elms);
7069
7070 int entry = dictionary->FindEntry(index);
7071 if (entry != NumberDictionary::kNotFound) {
7072 Object* element = dictionary->ValueAt(entry);
7073 PropertyDetails details = dictionary->DetailsAt(entry);
7074 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01007075 return SetElementWithCallback(element, index, value, this);
Steve Blocka7e24c12009-10-30 11:49:00 +00007076 } else {
7077 dictionary->UpdateMaxNumberKey(index);
7078 dictionary->ValueAtPut(entry, value);
7079 }
7080 } else {
7081 // Index not already used. Look for an accessor in the prototype chain.
Steve Block9fac8402011-05-12 15:51:54 +01007082 if (check_prototype &&
7083 SetElementWithCallbackSetterInPrototypes(index, value)) {
7084 return value;
Steve Blocka7e24c12009-10-30 11:49:00 +00007085 }
Steve Block8defd9f2010-07-08 12:39:36 +01007086 // When we set the is_extensible flag to false we always force
7087 // the element into dictionary mode (and force them to stay there).
7088 if (!map()->is_extensible()) {
Ben Murdochf87a2032010-10-22 12:50:53 +01007089 Handle<Object> number(Factory::NewNumberFromUint(index));
Steve Block8defd9f2010-07-08 12:39:36 +01007090 Handle<String> index_string(Factory::NumberToString(number));
7091 Handle<Object> args[1] = { index_string };
7092 return Top::Throw(*Factory::NewTypeError("object_not_extensible",
7093 HandleVector(args, 1)));
7094 }
John Reck59135872010-11-02 12:39:01 -07007095 Object* result;
7096 { MaybeObject* maybe_result = dictionary->AtNumberPut(index, value);
7097 if (!maybe_result->ToObject(&result)) return maybe_result;
7098 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007099 if (elms != FixedArray::cast(result)) {
7100 set_elements(FixedArray::cast(result));
7101 }
7102 }
7103
7104 // Update the array length if this JSObject is an array.
7105 if (IsJSArray()) {
7106 JSArray* array = JSArray::cast(this);
John Reck59135872010-11-02 12:39:01 -07007107 Object* return_value;
7108 { MaybeObject* maybe_return_value =
7109 array->JSArrayUpdateLengthFromIndex(index, value);
7110 if (!maybe_return_value->ToObject(&return_value)) {
7111 return maybe_return_value;
7112 }
7113 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007114 }
7115
7116 // Attempt to put this object back in fast case.
7117 if (ShouldConvertToFastElements()) {
7118 uint32_t new_length = 0;
7119 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007120 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
Steve Blocka7e24c12009-10-30 11:49:00 +00007121 } else {
7122 new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
7123 }
John Reck59135872010-11-02 12:39:01 -07007124 Object* obj;
7125 { MaybeObject* maybe_obj =
7126 SetFastElementsCapacityAndLength(new_length, new_length);
7127 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7128 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007129#ifdef DEBUG
7130 if (FLAG_trace_normalization) {
7131 PrintF("Object elements are fast case again:\n");
7132 Print();
7133 }
7134#endif
7135 }
7136
7137 return value;
7138 }
7139 default:
7140 UNREACHABLE();
7141 break;
7142 }
7143 // All possible cases have been handled above. Add a return to avoid the
7144 // complaints from the compiler.
7145 UNREACHABLE();
7146 return Heap::null_value();
7147}
7148
7149
John Reck59135872010-11-02 12:39:01 -07007150MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
7151 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007152 uint32_t old_len = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007153 CHECK(length()->ToArrayIndex(&old_len));
Steve Blocka7e24c12009-10-30 11:49:00 +00007154 // Check to see if we need to update the length. For now, we make
7155 // sure that the length stays within 32-bits (unsigned).
7156 if (index >= old_len && index != 0xffffffff) {
John Reck59135872010-11-02 12:39:01 -07007157 Object* len;
7158 { MaybeObject* maybe_len =
7159 Heap::NumberFromDouble(static_cast<double>(index) + 1);
7160 if (!maybe_len->ToObject(&len)) return maybe_len;
7161 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007162 set_length(len);
7163 }
7164 return value;
7165}
7166
7167
John Reck59135872010-11-02 12:39:01 -07007168MaybeObject* JSObject::GetElementPostInterceptor(JSObject* receiver,
7169 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007170 // Get element works for both JSObject and JSArray since
7171 // JSArray::length cannot change.
7172 switch (GetElementsKind()) {
7173 case FAST_ELEMENTS: {
7174 FixedArray* elms = FixedArray::cast(elements());
7175 if (index < static_cast<uint32_t>(elms->length())) {
7176 Object* value = elms->get(index);
7177 if (!value->IsTheHole()) return value;
7178 }
7179 break;
7180 }
7181 case PIXEL_ELEMENTS: {
7182 // TODO(iposva): Add testcase and implement.
7183 UNIMPLEMENTED();
7184 break;
7185 }
Steve Block3ce2e202009-11-05 08:53:23 +00007186 case EXTERNAL_BYTE_ELEMENTS:
7187 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7188 case EXTERNAL_SHORT_ELEMENTS:
7189 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7190 case EXTERNAL_INT_ELEMENTS:
7191 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7192 case EXTERNAL_FLOAT_ELEMENTS: {
7193 // TODO(kbr): Add testcase and implement.
7194 UNIMPLEMENTED();
7195 break;
7196 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007197 case DICTIONARY_ELEMENTS: {
7198 NumberDictionary* dictionary = element_dictionary();
7199 int entry = dictionary->FindEntry(index);
7200 if (entry != NumberDictionary::kNotFound) {
7201 Object* element = dictionary->ValueAt(entry);
7202 PropertyDetails details = dictionary->DetailsAt(entry);
7203 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01007204 return GetElementWithCallback(receiver,
7205 element,
7206 index,
7207 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00007208 }
7209 return element;
7210 }
7211 break;
7212 }
7213 default:
7214 UNREACHABLE();
7215 break;
7216 }
7217
7218 // Continue searching via the prototype chain.
7219 Object* pt = GetPrototype();
7220 if (pt == Heap::null_value()) return Heap::undefined_value();
7221 return pt->GetElementWithReceiver(receiver, index);
7222}
7223
7224
John Reck59135872010-11-02 12:39:01 -07007225MaybeObject* JSObject::GetElementWithInterceptor(JSObject* receiver,
7226 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007227 // Make sure that the top context does not change when doing
7228 // callbacks or interceptor calls.
7229 AssertNoContextChange ncc;
7230 HandleScope scope;
7231 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
7232 Handle<JSObject> this_handle(receiver);
7233 Handle<JSObject> holder_handle(this);
7234
7235 if (!interceptor->getter()->IsUndefined()) {
7236 v8::IndexedPropertyGetter getter =
7237 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
7238 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
7239 CustomArguments args(interceptor->data(), receiver, this);
7240 v8::AccessorInfo info(args.end());
7241 v8::Handle<v8::Value> result;
7242 {
7243 // Leaving JavaScript.
7244 VMState state(EXTERNAL);
7245 result = getter(index, info);
7246 }
7247 RETURN_IF_SCHEDULED_EXCEPTION();
7248 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
7249 }
7250
John Reck59135872010-11-02 12:39:01 -07007251 MaybeObject* raw_result =
Steve Blocka7e24c12009-10-30 11:49:00 +00007252 holder_handle->GetElementPostInterceptor(*this_handle, index);
7253 RETURN_IF_SCHEDULED_EXCEPTION();
7254 return raw_result;
7255}
7256
7257
John Reck59135872010-11-02 12:39:01 -07007258MaybeObject* JSObject::GetElementWithReceiver(JSObject* receiver,
7259 uint32_t index) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007260 // Check access rights if needed.
7261 if (IsAccessCheckNeeded() &&
7262 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
7263 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
7264 return Heap::undefined_value();
7265 }
7266
7267 if (HasIndexedInterceptor()) {
7268 return GetElementWithInterceptor(receiver, index);
7269 }
7270
7271 // Get element works for both JSObject and JSArray since
7272 // JSArray::length cannot change.
7273 switch (GetElementsKind()) {
7274 case FAST_ELEMENTS: {
7275 FixedArray* elms = FixedArray::cast(elements());
7276 if (index < static_cast<uint32_t>(elms->length())) {
7277 Object* value = elms->get(index);
7278 if (!value->IsTheHole()) return value;
7279 }
7280 break;
7281 }
7282 case PIXEL_ELEMENTS: {
7283 PixelArray* pixels = PixelArray::cast(elements());
7284 if (index < static_cast<uint32_t>(pixels->length())) {
7285 uint8_t value = pixels->get(index);
7286 return Smi::FromInt(value);
7287 }
7288 break;
7289 }
Steve Block3ce2e202009-11-05 08:53:23 +00007290 case EXTERNAL_BYTE_ELEMENTS: {
7291 ExternalByteArray* array = ExternalByteArray::cast(elements());
7292 if (index < static_cast<uint32_t>(array->length())) {
7293 int8_t value = array->get(index);
7294 return Smi::FromInt(value);
7295 }
7296 break;
7297 }
7298 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7299 ExternalUnsignedByteArray* array =
7300 ExternalUnsignedByteArray::cast(elements());
7301 if (index < static_cast<uint32_t>(array->length())) {
7302 uint8_t value = array->get(index);
7303 return Smi::FromInt(value);
7304 }
7305 break;
7306 }
7307 case EXTERNAL_SHORT_ELEMENTS: {
7308 ExternalShortArray* array = ExternalShortArray::cast(elements());
7309 if (index < static_cast<uint32_t>(array->length())) {
7310 int16_t value = array->get(index);
7311 return Smi::FromInt(value);
7312 }
7313 break;
7314 }
7315 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7316 ExternalUnsignedShortArray* array =
7317 ExternalUnsignedShortArray::cast(elements());
7318 if (index < static_cast<uint32_t>(array->length())) {
7319 uint16_t value = array->get(index);
7320 return Smi::FromInt(value);
7321 }
7322 break;
7323 }
7324 case EXTERNAL_INT_ELEMENTS: {
7325 ExternalIntArray* array = ExternalIntArray::cast(elements());
7326 if (index < static_cast<uint32_t>(array->length())) {
7327 int32_t value = array->get(index);
7328 return Heap::NumberFromInt32(value);
7329 }
7330 break;
7331 }
7332 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7333 ExternalUnsignedIntArray* array =
7334 ExternalUnsignedIntArray::cast(elements());
7335 if (index < static_cast<uint32_t>(array->length())) {
7336 uint32_t value = array->get(index);
7337 return Heap::NumberFromUint32(value);
7338 }
7339 break;
7340 }
7341 case EXTERNAL_FLOAT_ELEMENTS: {
7342 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
7343 if (index < static_cast<uint32_t>(array->length())) {
7344 float value = array->get(index);
7345 return Heap::AllocateHeapNumber(value);
7346 }
7347 break;
7348 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007349 case DICTIONARY_ELEMENTS: {
7350 NumberDictionary* dictionary = element_dictionary();
7351 int entry = dictionary->FindEntry(index);
7352 if (entry != NumberDictionary::kNotFound) {
7353 Object* element = dictionary->ValueAt(entry);
7354 PropertyDetails details = dictionary->DetailsAt(entry);
7355 if (details.type() == CALLBACKS) {
Leon Clarkef7060e22010-06-03 12:02:55 +01007356 return GetElementWithCallback(receiver,
7357 element,
7358 index,
7359 this);
Steve Blocka7e24c12009-10-30 11:49:00 +00007360 }
7361 return element;
7362 }
7363 break;
7364 }
7365 }
7366
7367 Object* pt = GetPrototype();
7368 if (pt == Heap::null_value()) return Heap::undefined_value();
7369 return pt->GetElementWithReceiver(receiver, index);
7370}
7371
7372
7373bool JSObject::HasDenseElements() {
7374 int capacity = 0;
7375 int number_of_elements = 0;
7376
7377 switch (GetElementsKind()) {
7378 case FAST_ELEMENTS: {
7379 FixedArray* elms = FixedArray::cast(elements());
7380 capacity = elms->length();
7381 for (int i = 0; i < capacity; i++) {
7382 if (!elms->get(i)->IsTheHole()) number_of_elements++;
7383 }
7384 break;
7385 }
Steve Block3ce2e202009-11-05 08:53:23 +00007386 case PIXEL_ELEMENTS:
7387 case EXTERNAL_BYTE_ELEMENTS:
7388 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7389 case EXTERNAL_SHORT_ELEMENTS:
7390 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7391 case EXTERNAL_INT_ELEMENTS:
7392 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7393 case EXTERNAL_FLOAT_ELEMENTS: {
Steve Blocka7e24c12009-10-30 11:49:00 +00007394 return true;
7395 }
7396 case DICTIONARY_ELEMENTS: {
7397 NumberDictionary* dictionary = NumberDictionary::cast(elements());
7398 capacity = dictionary->Capacity();
7399 number_of_elements = dictionary->NumberOfElements();
7400 break;
7401 }
7402 default:
7403 UNREACHABLE();
7404 break;
7405 }
7406
7407 if (capacity == 0) return true;
7408 return (number_of_elements > (capacity / 2));
7409}
7410
7411
7412bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
7413 ASSERT(HasFastElements());
7414 // Keep the array in fast case if the current backing storage is
7415 // almost filled and if the new capacity is no more than twice the
7416 // old capacity.
7417 int elements_length = FixedArray::cast(elements())->length();
7418 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
7419}
7420
7421
7422bool JSObject::ShouldConvertToFastElements() {
7423 ASSERT(HasDictionaryElements());
7424 NumberDictionary* dictionary = NumberDictionary::cast(elements());
7425 // If the elements are sparse, we should not go back to fast case.
7426 if (!HasDenseElements()) return false;
7427 // If an element has been added at a very high index in the elements
7428 // dictionary, we cannot go back to fast case.
7429 if (dictionary->requires_slow_elements()) return false;
7430 // An object requiring access checks is never allowed to have fast
7431 // elements. If it had fast elements we would skip security checks.
7432 if (IsAccessCheckNeeded()) return false;
7433 // If the dictionary backing storage takes up roughly half as much
7434 // space as a fast-case backing storage would the array should have
7435 // fast elements.
7436 uint32_t length = 0;
7437 if (IsJSArray()) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007438 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
Steve Blocka7e24c12009-10-30 11:49:00 +00007439 } else {
7440 length = dictionary->max_number_key();
7441 }
7442 return static_cast<uint32_t>(dictionary->Capacity()) >=
7443 (length / (2 * NumberDictionary::kEntrySize));
7444}
7445
7446
7447// Certain compilers request function template instantiation when they
7448// see the definition of the other template functions in the
7449// class. This requires us to have the template functions put
7450// together, so even though this function belongs in objects-debug.cc,
7451// we keep it here instead to satisfy certain compilers.
Ben Murdochb0fe1622011-05-05 13:52:32 +01007452#ifdef OBJECT_PRINT
Steve Blocka7e24c12009-10-30 11:49:00 +00007453template<typename Shape, typename Key>
Ben Murdochb0fe1622011-05-05 13:52:32 +01007454void Dictionary<Shape, Key>::Print(FILE* out) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007455 int capacity = HashTable<Shape, Key>::Capacity();
7456 for (int i = 0; i < capacity; i++) {
7457 Object* k = HashTable<Shape, Key>::KeyAt(i);
7458 if (HashTable<Shape, Key>::IsKey(k)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01007459 PrintF(out, " ");
Steve Blocka7e24c12009-10-30 11:49:00 +00007460 if (k->IsString()) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01007461 String::cast(k)->StringPrint(out);
Steve Blocka7e24c12009-10-30 11:49:00 +00007462 } else {
Ben Murdochb0fe1622011-05-05 13:52:32 +01007463 k->ShortPrint(out);
Steve Blocka7e24c12009-10-30 11:49:00 +00007464 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01007465 PrintF(out, ": ");
7466 ValueAt(i)->ShortPrint(out);
7467 PrintF(out, "\n");
Steve Blocka7e24c12009-10-30 11:49:00 +00007468 }
7469 }
7470}
7471#endif
7472
7473
7474template<typename Shape, typename Key>
7475void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
7476 int pos = 0;
7477 int capacity = HashTable<Shape, Key>::Capacity();
Leon Clarke4515c472010-02-03 11:58:03 +00007478 AssertNoAllocation no_gc;
7479 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00007480 for (int i = 0; i < capacity; i++) {
7481 Object* k = Dictionary<Shape, Key>::KeyAt(i);
7482 if (Dictionary<Shape, Key>::IsKey(k)) {
7483 elements->set(pos++, ValueAt(i), mode);
7484 }
7485 }
7486 ASSERT(pos == elements->length());
7487}
7488
7489
7490InterceptorInfo* JSObject::GetNamedInterceptor() {
7491 ASSERT(map()->has_named_interceptor());
7492 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01007493 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00007494 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01007495 constructor->shared()->get_api_func_data()->named_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00007496 return InterceptorInfo::cast(result);
7497}
7498
7499
7500InterceptorInfo* JSObject::GetIndexedInterceptor() {
7501 ASSERT(map()->has_indexed_interceptor());
7502 JSFunction* constructor = JSFunction::cast(map()->constructor());
Steve Block6ded16b2010-05-10 14:33:55 +01007503 ASSERT(constructor->shared()->IsApiFunction());
Steve Blocka7e24c12009-10-30 11:49:00 +00007504 Object* result =
Steve Block6ded16b2010-05-10 14:33:55 +01007505 constructor->shared()->get_api_func_data()->indexed_property_handler();
Steve Blocka7e24c12009-10-30 11:49:00 +00007506 return InterceptorInfo::cast(result);
7507}
7508
7509
John Reck59135872010-11-02 12:39:01 -07007510MaybeObject* JSObject::GetPropertyPostInterceptor(
7511 JSObject* receiver,
7512 String* name,
7513 PropertyAttributes* attributes) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007514 // Check local property in holder, ignore interceptor.
7515 LookupResult result;
7516 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007517 if (result.IsProperty()) {
7518 return GetProperty(receiver, &result, name, attributes);
7519 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007520 // Continue searching via the prototype chain.
7521 Object* pt = GetPrototype();
7522 *attributes = ABSENT;
7523 if (pt == Heap::null_value()) return Heap::undefined_value();
7524 return pt->GetPropertyWithReceiver(receiver, name, attributes);
7525}
7526
7527
John Reck59135872010-11-02 12:39:01 -07007528MaybeObject* JSObject::GetLocalPropertyPostInterceptor(
Steve Blockd0582a62009-12-15 09:54:21 +00007529 JSObject* receiver,
7530 String* name,
7531 PropertyAttributes* attributes) {
7532 // Check local property in holder, ignore interceptor.
7533 LookupResult result;
7534 LocalLookupRealNamedProperty(name, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007535 if (result.IsProperty()) {
7536 return GetProperty(receiver, &result, name, attributes);
7537 }
7538 return Heap::undefined_value();
Steve Blockd0582a62009-12-15 09:54:21 +00007539}
7540
7541
John Reck59135872010-11-02 12:39:01 -07007542MaybeObject* JSObject::GetPropertyWithInterceptor(
Steve Blocka7e24c12009-10-30 11:49:00 +00007543 JSObject* receiver,
7544 String* name,
7545 PropertyAttributes* attributes) {
7546 InterceptorInfo* interceptor = GetNamedInterceptor();
7547 HandleScope scope;
7548 Handle<JSObject> receiver_handle(receiver);
7549 Handle<JSObject> holder_handle(this);
7550 Handle<String> name_handle(name);
7551
7552 if (!interceptor->getter()->IsUndefined()) {
7553 v8::NamedPropertyGetter getter =
7554 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
7555 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
7556 CustomArguments args(interceptor->data(), receiver, this);
7557 v8::AccessorInfo info(args.end());
7558 v8::Handle<v8::Value> result;
7559 {
7560 // Leaving JavaScript.
7561 VMState state(EXTERNAL);
7562 result = getter(v8::Utils::ToLocal(name_handle), info);
7563 }
7564 RETURN_IF_SCHEDULED_EXCEPTION();
7565 if (!result.IsEmpty()) {
7566 *attributes = NONE;
7567 return *v8::Utils::OpenHandle(*result);
7568 }
7569 }
7570
John Reck59135872010-11-02 12:39:01 -07007571 MaybeObject* result = holder_handle->GetPropertyPostInterceptor(
Steve Blocka7e24c12009-10-30 11:49:00 +00007572 *receiver_handle,
7573 *name_handle,
7574 attributes);
7575 RETURN_IF_SCHEDULED_EXCEPTION();
7576 return result;
7577}
7578
7579
7580bool JSObject::HasRealNamedProperty(String* key) {
7581 // Check access rights if needed.
7582 if (IsAccessCheckNeeded() &&
7583 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
7584 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7585 return false;
7586 }
7587
7588 LookupResult result;
7589 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007590 return result.IsProperty() && (result.type() != INTERCEPTOR);
Steve Blocka7e24c12009-10-30 11:49:00 +00007591}
7592
7593
7594bool JSObject::HasRealElementProperty(uint32_t index) {
7595 // Check access rights if needed.
7596 if (IsAccessCheckNeeded() &&
7597 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
7598 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7599 return false;
7600 }
7601
7602 // Handle [] on String objects.
7603 if (this->IsStringObjectWithCharacterAt(index)) return true;
7604
7605 switch (GetElementsKind()) {
7606 case FAST_ELEMENTS: {
7607 uint32_t length = IsJSArray() ?
7608 static_cast<uint32_t>(
7609 Smi::cast(JSArray::cast(this)->length())->value()) :
7610 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7611 return (index < length) &&
7612 !FixedArray::cast(elements())->get(index)->IsTheHole();
7613 }
7614 case PIXEL_ELEMENTS: {
7615 PixelArray* pixels = PixelArray::cast(elements());
7616 return index < static_cast<uint32_t>(pixels->length());
7617 }
Steve Block3ce2e202009-11-05 08:53:23 +00007618 case EXTERNAL_BYTE_ELEMENTS:
7619 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7620 case EXTERNAL_SHORT_ELEMENTS:
7621 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7622 case EXTERNAL_INT_ELEMENTS:
7623 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7624 case EXTERNAL_FLOAT_ELEMENTS: {
7625 ExternalArray* array = ExternalArray::cast(elements());
7626 return index < static_cast<uint32_t>(array->length());
7627 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007628 case DICTIONARY_ELEMENTS: {
7629 return element_dictionary()->FindEntry(index)
7630 != NumberDictionary::kNotFound;
7631 }
7632 default:
7633 UNREACHABLE();
7634 break;
7635 }
7636 // All possibilities have been handled above already.
7637 UNREACHABLE();
7638 return Heap::null_value();
7639}
7640
7641
7642bool JSObject::HasRealNamedCallbackProperty(String* key) {
7643 // Check access rights if needed.
7644 if (IsAccessCheckNeeded() &&
7645 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
7646 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7647 return false;
7648 }
7649
7650 LookupResult result;
7651 LocalLookupRealNamedProperty(key, &result);
Andrei Popescu402d9372010-02-26 13:31:12 +00007652 return result.IsProperty() && (result.type() == CALLBACKS);
Steve Blocka7e24c12009-10-30 11:49:00 +00007653}
7654
7655
7656int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
7657 if (HasFastProperties()) {
7658 DescriptorArray* descs = map()->instance_descriptors();
7659 int result = 0;
7660 for (int i = 0; i < descs->number_of_descriptors(); i++) {
7661 PropertyDetails details = descs->GetDetails(i);
7662 if (details.IsProperty() && (details.attributes() & filter) == 0) {
7663 result++;
7664 }
7665 }
7666 return result;
7667 } else {
7668 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
7669 }
7670}
7671
7672
7673int JSObject::NumberOfEnumProperties() {
7674 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
7675}
7676
7677
7678void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
7679 Object* temp = get(i);
7680 set(i, get(j));
7681 set(j, temp);
7682 if (this != numbers) {
7683 temp = numbers->get(i);
7684 numbers->set(i, numbers->get(j));
7685 numbers->set(j, temp);
7686 }
7687}
7688
7689
7690static void InsertionSortPairs(FixedArray* content,
7691 FixedArray* numbers,
7692 int len) {
7693 for (int i = 1; i < len; i++) {
7694 int j = i;
7695 while (j > 0 &&
7696 (NumberToUint32(numbers->get(j - 1)) >
7697 NumberToUint32(numbers->get(j)))) {
7698 content->SwapPairs(numbers, j - 1, j);
7699 j--;
7700 }
7701 }
7702}
7703
7704
7705void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
7706 // In-place heap sort.
7707 ASSERT(content->length() == numbers->length());
7708
7709 // Bottom-up max-heap construction.
7710 for (int i = 1; i < len; ++i) {
7711 int child_index = i;
7712 while (child_index > 0) {
7713 int parent_index = ((child_index + 1) >> 1) - 1;
7714 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
7715 uint32_t child_value = NumberToUint32(numbers->get(child_index));
7716 if (parent_value < child_value) {
7717 content->SwapPairs(numbers, parent_index, child_index);
7718 } else {
7719 break;
7720 }
7721 child_index = parent_index;
7722 }
7723 }
7724
7725 // Extract elements and create sorted array.
7726 for (int i = len - 1; i > 0; --i) {
7727 // Put max element at the back of the array.
7728 content->SwapPairs(numbers, 0, i);
7729 // Sift down the new top element.
7730 int parent_index = 0;
7731 while (true) {
7732 int child_index = ((parent_index + 1) << 1) - 1;
7733 if (child_index >= i) break;
7734 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
7735 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
7736 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
7737 if (child_index + 1 >= i || child1_value > child2_value) {
7738 if (parent_value > child1_value) break;
7739 content->SwapPairs(numbers, parent_index, child_index);
7740 parent_index = child_index;
7741 } else {
7742 if (parent_value > child2_value) break;
7743 content->SwapPairs(numbers, parent_index, child_index + 1);
7744 parent_index = child_index + 1;
7745 }
7746 }
7747 }
7748}
7749
7750
7751// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
7752void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
7753 ASSERT(this->length() == numbers->length());
7754 // For small arrays, simply use insertion sort.
7755 if (len <= 10) {
7756 InsertionSortPairs(this, numbers, len);
7757 return;
7758 }
7759 // Check the range of indices.
7760 uint32_t min_index = NumberToUint32(numbers->get(0));
7761 uint32_t max_index = min_index;
7762 uint32_t i;
7763 for (i = 1; i < len; i++) {
7764 if (NumberToUint32(numbers->get(i)) < min_index) {
7765 min_index = NumberToUint32(numbers->get(i));
7766 } else if (NumberToUint32(numbers->get(i)) > max_index) {
7767 max_index = NumberToUint32(numbers->get(i));
7768 }
7769 }
7770 if (max_index - min_index + 1 == len) {
7771 // Indices form a contiguous range, unless there are duplicates.
7772 // Do an in-place linear time sort assuming distinct numbers, but
7773 // avoid hanging in case they are not.
7774 for (i = 0; i < len; i++) {
7775 uint32_t p;
7776 uint32_t j = 0;
7777 // While the current element at i is not at its correct position p,
7778 // swap the elements at these two positions.
7779 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
7780 j++ < len) {
7781 SwapPairs(numbers, i, p);
7782 }
7783 }
7784 } else {
7785 HeapSortPairs(this, numbers, len);
7786 return;
7787 }
7788}
7789
7790
7791// Fill in the names of local properties into the supplied storage. The main
7792// purpose of this function is to provide reflection information for the object
7793// mirrors.
7794void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
7795 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
7796 if (HasFastProperties()) {
7797 DescriptorArray* descs = map()->instance_descriptors();
7798 for (int i = 0; i < descs->number_of_descriptors(); i++) {
7799 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
7800 }
7801 ASSERT(storage->length() >= index);
7802 } else {
7803 property_dictionary()->CopyKeysTo(storage);
7804 }
7805}
7806
7807
7808int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
7809 return GetLocalElementKeys(NULL, filter);
7810}
7811
7812
7813int JSObject::NumberOfEnumElements() {
Steve Blockd0582a62009-12-15 09:54:21 +00007814 // Fast case for objects with no elements.
7815 if (!IsJSValue() && HasFastElements()) {
7816 uint32_t length = IsJSArray() ?
7817 static_cast<uint32_t>(
7818 Smi::cast(JSArray::cast(this)->length())->value()) :
7819 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7820 if (length == 0) return 0;
7821 }
7822 // Compute the number of enumerable elements.
Steve Blocka7e24c12009-10-30 11:49:00 +00007823 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
7824}
7825
7826
7827int JSObject::GetLocalElementKeys(FixedArray* storage,
7828 PropertyAttributes filter) {
7829 int counter = 0;
7830 switch (GetElementsKind()) {
7831 case FAST_ELEMENTS: {
7832 int length = IsJSArray() ?
7833 Smi::cast(JSArray::cast(this)->length())->value() :
7834 FixedArray::cast(elements())->length();
7835 for (int i = 0; i < length; i++) {
7836 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
7837 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007838 storage->set(counter, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00007839 }
7840 counter++;
7841 }
7842 }
7843 ASSERT(!storage || storage->length() >= counter);
7844 break;
7845 }
7846 case PIXEL_ELEMENTS: {
7847 int length = PixelArray::cast(elements())->length();
7848 while (counter < length) {
7849 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007850 storage->set(counter, Smi::FromInt(counter));
Steve Blocka7e24c12009-10-30 11:49:00 +00007851 }
7852 counter++;
7853 }
7854 ASSERT(!storage || storage->length() >= counter);
7855 break;
7856 }
Steve Block3ce2e202009-11-05 08:53:23 +00007857 case EXTERNAL_BYTE_ELEMENTS:
7858 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7859 case EXTERNAL_SHORT_ELEMENTS:
7860 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7861 case EXTERNAL_INT_ELEMENTS:
7862 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7863 case EXTERNAL_FLOAT_ELEMENTS: {
7864 int length = ExternalArray::cast(elements())->length();
7865 while (counter < length) {
7866 if (storage != NULL) {
Leon Clarke4515c472010-02-03 11:58:03 +00007867 storage->set(counter, Smi::FromInt(counter));
Steve Block3ce2e202009-11-05 08:53:23 +00007868 }
7869 counter++;
7870 }
7871 ASSERT(!storage || storage->length() >= counter);
7872 break;
7873 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007874 case DICTIONARY_ELEMENTS: {
7875 if (storage != NULL) {
7876 element_dictionary()->CopyKeysTo(storage, filter);
7877 }
7878 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
7879 break;
7880 }
7881 default:
7882 UNREACHABLE();
7883 break;
7884 }
7885
7886 if (this->IsJSValue()) {
7887 Object* val = JSValue::cast(this)->value();
7888 if (val->IsString()) {
7889 String* str = String::cast(val);
7890 if (storage) {
7891 for (int i = 0; i < str->length(); i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00007892 storage->set(counter + i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00007893 }
7894 }
7895 counter += str->length();
7896 }
7897 }
7898 ASSERT(!storage || storage->length() == counter);
7899 return counter;
7900}
7901
7902
7903int JSObject::GetEnumElementKeys(FixedArray* storage) {
7904 return GetLocalElementKeys(storage,
7905 static_cast<PropertyAttributes>(DONT_ENUM));
7906}
7907
7908
7909bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
7910 ASSERT(other->IsNumber());
7911 return key == static_cast<uint32_t>(other->Number());
7912}
7913
7914
7915uint32_t NumberDictionaryShape::Hash(uint32_t key) {
7916 return ComputeIntegerHash(key);
7917}
7918
7919
7920uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) {
7921 ASSERT(other->IsNumber());
7922 return ComputeIntegerHash(static_cast<uint32_t>(other->Number()));
7923}
7924
7925
John Reck59135872010-11-02 12:39:01 -07007926MaybeObject* NumberDictionaryShape::AsObject(uint32_t key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007927 return Heap::NumberFromUint32(key);
7928}
7929
7930
7931bool StringDictionaryShape::IsMatch(String* key, Object* other) {
7932 // We know that all entries in a hash table had their hash keys created.
7933 // Use that knowledge to have fast failure.
7934 if (key->Hash() != String::cast(other)->Hash()) return false;
7935 return key->Equals(String::cast(other));
7936}
7937
7938
7939uint32_t StringDictionaryShape::Hash(String* key) {
7940 return key->Hash();
7941}
7942
7943
7944uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) {
7945 return String::cast(other)->Hash();
7946}
7947
7948
John Reck59135872010-11-02 12:39:01 -07007949MaybeObject* StringDictionaryShape::AsObject(String* key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00007950 return key;
7951}
7952
7953
7954// StringKey simply carries a string object as key.
7955class StringKey : public HashTableKey {
7956 public:
7957 explicit StringKey(String* string) :
7958 string_(string),
7959 hash_(HashForObject(string)) { }
7960
7961 bool IsMatch(Object* string) {
7962 // We know that all entries in a hash table had their hash keys created.
7963 // Use that knowledge to have fast failure.
7964 if (hash_ != HashForObject(string)) {
7965 return false;
7966 }
7967 return string_->Equals(String::cast(string));
7968 }
7969
7970 uint32_t Hash() { return hash_; }
7971
7972 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
7973
7974 Object* AsObject() { return string_; }
7975
7976 String* string_;
7977 uint32_t hash_;
7978};
7979
7980
7981// StringSharedKeys are used as keys in the eval cache.
7982class StringSharedKey : public HashTableKey {
7983 public:
7984 StringSharedKey(String* source, SharedFunctionInfo* shared)
7985 : source_(source), shared_(shared) { }
7986
7987 bool IsMatch(Object* other) {
7988 if (!other->IsFixedArray()) return false;
7989 FixedArray* pair = FixedArray::cast(other);
7990 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
7991 if (shared != shared_) return false;
7992 String* source = String::cast(pair->get(1));
7993 return source->Equals(source_);
7994 }
7995
7996 static uint32_t StringSharedHashHelper(String* source,
7997 SharedFunctionInfo* shared) {
7998 uint32_t hash = source->Hash();
7999 if (shared->HasSourceCode()) {
8000 // Instead of using the SharedFunctionInfo pointer in the hash
8001 // code computation, we use a combination of the hash of the
8002 // script source code and the start and end positions. We do
8003 // this to ensure that the cache entries can survive garbage
8004 // collection.
8005 Script* script = Script::cast(shared->script());
8006 hash ^= String::cast(script->source())->Hash();
8007 hash += shared->start_position();
8008 }
8009 return hash;
8010 }
8011
8012 uint32_t Hash() {
8013 return StringSharedHashHelper(source_, shared_);
8014 }
8015
8016 uint32_t HashForObject(Object* obj) {
8017 FixedArray* pair = FixedArray::cast(obj);
8018 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
8019 String* source = String::cast(pair->get(1));
8020 return StringSharedHashHelper(source, shared);
8021 }
8022
John Reck59135872010-11-02 12:39:01 -07008023 MUST_USE_RESULT MaybeObject* AsObject() {
8024 Object* obj;
8025 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(2);
8026 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8027 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008028 FixedArray* pair = FixedArray::cast(obj);
8029 pair->set(0, shared_);
8030 pair->set(1, source_);
8031 return pair;
8032 }
8033
8034 private:
8035 String* source_;
8036 SharedFunctionInfo* shared_;
8037};
8038
8039
8040// RegExpKey carries the source and flags of a regular expression as key.
8041class RegExpKey : public HashTableKey {
8042 public:
8043 RegExpKey(String* string, JSRegExp::Flags flags)
8044 : string_(string),
8045 flags_(Smi::FromInt(flags.value())) { }
8046
Steve Block3ce2e202009-11-05 08:53:23 +00008047 // Rather than storing the key in the hash table, a pointer to the
8048 // stored value is stored where the key should be. IsMatch then
8049 // compares the search key to the found object, rather than comparing
8050 // a key to a key.
Steve Blocka7e24c12009-10-30 11:49:00 +00008051 bool IsMatch(Object* obj) {
8052 FixedArray* val = FixedArray::cast(obj);
8053 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
8054 && (flags_ == val->get(JSRegExp::kFlagsIndex));
8055 }
8056
8057 uint32_t Hash() { return RegExpHash(string_, flags_); }
8058
8059 Object* AsObject() {
8060 // Plain hash maps, which is where regexp keys are used, don't
8061 // use this function.
8062 UNREACHABLE();
8063 return NULL;
8064 }
8065
8066 uint32_t HashForObject(Object* obj) {
8067 FixedArray* val = FixedArray::cast(obj);
8068 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
8069 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
8070 }
8071
8072 static uint32_t RegExpHash(String* string, Smi* flags) {
8073 return string->Hash() + flags->value();
8074 }
8075
8076 String* string_;
8077 Smi* flags_;
8078};
8079
8080// Utf8SymbolKey carries a vector of chars as key.
8081class Utf8SymbolKey : public HashTableKey {
8082 public:
8083 explicit Utf8SymbolKey(Vector<const char> string)
Steve Blockd0582a62009-12-15 09:54:21 +00008084 : string_(string), hash_field_(0) { }
Steve Blocka7e24c12009-10-30 11:49:00 +00008085
8086 bool IsMatch(Object* string) {
8087 return String::cast(string)->IsEqualTo(string_);
8088 }
8089
8090 uint32_t Hash() {
Steve Blockd0582a62009-12-15 09:54:21 +00008091 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00008092 unibrow::Utf8InputBuffer<> buffer(string_.start(),
8093 static_cast<unsigned>(string_.length()));
8094 chars_ = buffer.Length();
Steve Blockd0582a62009-12-15 09:54:21 +00008095 hash_field_ = String::ComputeHashField(&buffer, chars_);
8096 uint32_t result = hash_field_ >> String::kHashShift;
Steve Blocka7e24c12009-10-30 11:49:00 +00008097 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
8098 return result;
8099 }
8100
8101 uint32_t HashForObject(Object* other) {
8102 return String::cast(other)->Hash();
8103 }
8104
John Reck59135872010-11-02 12:39:01 -07008105 MaybeObject* AsObject() {
Steve Blockd0582a62009-12-15 09:54:21 +00008106 if (hash_field_ == 0) Hash();
8107 return Heap::AllocateSymbol(string_, chars_, hash_field_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008108 }
8109
8110 Vector<const char> string_;
Steve Blockd0582a62009-12-15 09:54:21 +00008111 uint32_t hash_field_;
Steve Blocka7e24c12009-10-30 11:49:00 +00008112 int chars_; // Caches the number of characters when computing the hash code.
8113};
8114
8115
Steve Block9fac8402011-05-12 15:51:54 +01008116template <typename Char>
8117class SequentialSymbolKey : public HashTableKey {
8118 public:
8119 explicit SequentialSymbolKey(Vector<const Char> string)
8120 : string_(string), hash_field_(0) { }
8121
8122 uint32_t Hash() {
8123 StringHasher hasher(string_.length());
8124
8125 // Very long strings have a trivial hash that doesn't inspect the
8126 // string contents.
8127 if (hasher.has_trivial_hash()) {
8128 hash_field_ = hasher.GetHashField();
8129 } else {
8130 int i = 0;
8131 // Do the iterative array index computation as long as there is a
8132 // chance this is an array index.
8133 while (i < string_.length() && hasher.is_array_index()) {
8134 hasher.AddCharacter(static_cast<uc32>(string_[i]));
8135 i++;
8136 }
8137
8138 // Process the remaining characters without updating the array
8139 // index.
8140 while (i < string_.length()) {
8141 hasher.AddCharacterNoIndex(static_cast<uc32>(string_[i]));
8142 i++;
8143 }
8144 hash_field_ = hasher.GetHashField();
8145 }
8146
8147 uint32_t result = hash_field_ >> String::kHashShift;
8148 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
8149 return result;
8150 }
8151
8152
8153 uint32_t HashForObject(Object* other) {
8154 return String::cast(other)->Hash();
8155 }
8156
8157 Vector<const Char> string_;
8158 uint32_t hash_field_;
8159};
8160
8161
8162
8163class AsciiSymbolKey : public SequentialSymbolKey<char> {
8164 public:
8165 explicit AsciiSymbolKey(Vector<const char> str)
8166 : SequentialSymbolKey<char>(str) { }
8167
8168 bool IsMatch(Object* string) {
8169 return String::cast(string)->IsAsciiEqualTo(string_);
8170 }
8171
8172 MaybeObject* AsObject() {
8173 if (hash_field_ == 0) Hash();
8174 return Heap::AllocateAsciiSymbol(string_, hash_field_);
8175 }
8176};
8177
8178
8179class TwoByteSymbolKey : public SequentialSymbolKey<uc16> {
8180 public:
8181 explicit TwoByteSymbolKey(Vector<const uc16> str)
8182 : SequentialSymbolKey<uc16>(str) { }
8183
8184 bool IsMatch(Object* string) {
8185 return String::cast(string)->IsTwoByteEqualTo(string_);
8186 }
8187
8188 MaybeObject* AsObject() {
8189 if (hash_field_ == 0) Hash();
8190 return Heap::AllocateTwoByteSymbol(string_, hash_field_);
8191 }
8192};
8193
8194
Steve Blocka7e24c12009-10-30 11:49:00 +00008195// SymbolKey carries a string/symbol object as key.
8196class SymbolKey : public HashTableKey {
8197 public:
8198 explicit SymbolKey(String* string) : string_(string) { }
8199
8200 bool IsMatch(Object* string) {
8201 return String::cast(string)->Equals(string_);
8202 }
8203
8204 uint32_t Hash() { return string_->Hash(); }
8205
8206 uint32_t HashForObject(Object* other) {
8207 return String::cast(other)->Hash();
8208 }
8209
John Reck59135872010-11-02 12:39:01 -07008210 MaybeObject* AsObject() {
Leon Clarkef7060e22010-06-03 12:02:55 +01008211 // Attempt to flatten the string, so that symbols will most often
8212 // be flat strings.
8213 string_ = string_->TryFlattenGetString();
Steve Blocka7e24c12009-10-30 11:49:00 +00008214 // Transform string to symbol if possible.
8215 Map* map = Heap::SymbolMapForString(string_);
8216 if (map != NULL) {
8217 string_->set_map(map);
8218 ASSERT(string_->IsSymbol());
8219 return string_;
8220 }
8221 // Otherwise allocate a new symbol.
8222 StringInputBuffer buffer(string_);
8223 return Heap::AllocateInternalSymbol(&buffer,
8224 string_->length(),
Steve Blockd0582a62009-12-15 09:54:21 +00008225 string_->hash_field());
Steve Blocka7e24c12009-10-30 11:49:00 +00008226 }
8227
8228 static uint32_t StringHash(Object* obj) {
8229 return String::cast(obj)->Hash();
8230 }
8231
8232 String* string_;
8233};
8234
8235
8236template<typename Shape, typename Key>
8237void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
8238 IteratePointers(v, 0, kElementsStartOffset);
8239}
8240
8241
8242template<typename Shape, typename Key>
8243void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
8244 IteratePointers(v,
8245 kElementsStartOffset,
8246 kHeaderSize + length() * kPointerSize);
8247}
8248
8249
8250template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008251MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
8252 PretenureFlag pretenure) {
Steve Block6ded16b2010-05-10 14:33:55 +01008253 const int kMinCapacity = 32;
8254 int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
8255 if (capacity < kMinCapacity) {
8256 capacity = kMinCapacity; // Guarantee min capacity.
Leon Clarkee46be812010-01-19 14:06:41 +00008257 } else if (capacity > HashTable::kMaxCapacity) {
8258 return Failure::OutOfMemoryException();
8259 }
8260
John Reck59135872010-11-02 12:39:01 -07008261 Object* obj;
8262 { MaybeObject* maybe_obj =
8263 Heap::AllocateHashTable(EntryToIndex(capacity), pretenure);
8264 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00008265 }
John Reck59135872010-11-02 12:39:01 -07008266 HashTable::cast(obj)->SetNumberOfElements(0);
8267 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
8268 HashTable::cast(obj)->SetCapacity(capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00008269 return obj;
8270}
8271
8272
Leon Clarkee46be812010-01-19 14:06:41 +00008273// Find entry for key otherwise return kNotFound.
Steve Blocka7e24c12009-10-30 11:49:00 +00008274template<typename Shape, typename Key>
8275int HashTable<Shape, Key>::FindEntry(Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008276 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00008277 uint32_t entry = FirstProbe(Shape::Hash(key), capacity);
8278 uint32_t count = 1;
8279 // EnsureCapacity will guarantee the hash table is never full.
8280 while (true) {
8281 Object* element = KeyAt(entry);
8282 if (element->IsUndefined()) break; // Empty entry.
8283 if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
8284 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00008285 }
8286 return kNotFound;
8287}
8288
8289
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008290// Find entry for key otherwise return kNotFound.
8291int StringDictionary::FindEntry(String* key) {
8292 if (!key->IsSymbol()) {
8293 return HashTable<StringDictionaryShape, String*>::FindEntry(key);
8294 }
8295
8296 // Optimized for symbol key. Knowledge of the key type allows:
8297 // 1. Move the check if the key is a symbol out of the loop.
8298 // 2. Avoid comparing hash codes in symbol to symbol comparision.
8299 // 3. Detect a case when a dictionary key is not a symbol but the key is.
8300 // In case of positive result the dictionary key may be replaced by
8301 // the symbol with minimal performance penalty. It gives a chance to
8302 // perform further lookups in code stubs (and significant performance boost
8303 // a certain style of code).
8304
8305 // EnsureCapacity will guarantee the hash table is never full.
8306 uint32_t capacity = Capacity();
8307 uint32_t entry = FirstProbe(key->Hash(), capacity);
8308 uint32_t count = 1;
8309
8310 while (true) {
8311 int index = EntryToIndex(entry);
8312 Object* element = get(index);
8313 if (element->IsUndefined()) break; // Empty entry.
8314 if (key == element) return entry;
8315 if (!element->IsSymbol() &&
8316 !element->IsNull() &&
8317 String::cast(element)->Equals(key)) {
8318 // Replace a non-symbol key by the equivalent symbol for faster further
8319 // lookups.
8320 set(index, key);
8321 return entry;
8322 }
8323 ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
8324 entry = NextProbe(entry, count++, capacity);
8325 }
8326 return kNotFound;
8327}
8328
8329
Steve Blocka7e24c12009-10-30 11:49:00 +00008330template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07008331MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008332 int capacity = Capacity();
8333 int nof = NumberOfElements() + n;
Leon Clarkee46be812010-01-19 14:06:41 +00008334 int nod = NumberOfDeletedElements();
8335 // Return if:
8336 // 50% is still free after adding n elements and
8337 // at most 50% of the free elements are deleted elements.
Steve Block6ded16b2010-05-10 14:33:55 +01008338 if (nod <= (capacity - nof) >> 1) {
8339 int needed_free = nof >> 1;
8340 if (nof + needed_free <= capacity) return this;
8341 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008342
Steve Block6ded16b2010-05-10 14:33:55 +01008343 const int kMinCapacityForPretenure = 256;
8344 bool pretenure =
8345 (capacity > kMinCapacityForPretenure) && !Heap::InNewSpace(this);
John Reck59135872010-11-02 12:39:01 -07008346 Object* obj;
8347 { MaybeObject* maybe_obj =
8348 Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
8349 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8350 }
Leon Clarke4515c472010-02-03 11:58:03 +00008351
8352 AssertNoAllocation no_gc;
Steve Blocka7e24c12009-10-30 11:49:00 +00008353 HashTable* table = HashTable::cast(obj);
Leon Clarke4515c472010-02-03 11:58:03 +00008354 WriteBarrierMode mode = table->GetWriteBarrierMode(no_gc);
Steve Blocka7e24c12009-10-30 11:49:00 +00008355
8356 // Copy prefix to new array.
8357 for (int i = kPrefixStartIndex;
8358 i < kPrefixStartIndex + Shape::kPrefixSize;
8359 i++) {
8360 table->set(i, get(i), mode);
8361 }
8362 // Rehash the elements.
8363 for (int i = 0; i < capacity; i++) {
8364 uint32_t from_index = EntryToIndex(i);
8365 Object* k = get(from_index);
8366 if (IsKey(k)) {
8367 uint32_t hash = Shape::HashForObject(key, k);
8368 uint32_t insertion_index =
8369 EntryToIndex(table->FindInsertionEntry(hash));
8370 for (int j = 0; j < Shape::kEntrySize; j++) {
8371 table->set(insertion_index + j, get(from_index + j), mode);
8372 }
8373 }
8374 }
8375 table->SetNumberOfElements(NumberOfElements());
Leon Clarkee46be812010-01-19 14:06:41 +00008376 table->SetNumberOfDeletedElements(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008377 return table;
8378}
8379
8380
8381template<typename Shape, typename Key>
8382uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
8383 uint32_t capacity = Capacity();
Leon Clarkee46be812010-01-19 14:06:41 +00008384 uint32_t entry = FirstProbe(hash, capacity);
8385 uint32_t count = 1;
8386 // EnsureCapacity will guarantee the hash table is never full.
8387 while (true) {
8388 Object* element = KeyAt(entry);
8389 if (element->IsUndefined() || element->IsNull()) break;
8390 entry = NextProbe(entry, count++, capacity);
Steve Blocka7e24c12009-10-30 11:49:00 +00008391 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008392 return entry;
8393}
8394
8395// Force instantiation of template instances class.
8396// Please note this list is compiler dependent.
8397
8398template class HashTable<SymbolTableShape, HashTableKey*>;
8399
8400template class HashTable<CompilationCacheShape, HashTableKey*>;
8401
8402template class HashTable<MapCacheShape, HashTableKey*>;
8403
8404template class Dictionary<StringDictionaryShape, String*>;
8405
8406template class Dictionary<NumberDictionaryShape, uint32_t>;
8407
John Reck59135872010-11-02 12:39:01 -07008408template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
Steve Blocka7e24c12009-10-30 11:49:00 +00008409 int);
8410
John Reck59135872010-11-02 12:39:01 -07008411template MaybeObject* Dictionary<StringDictionaryShape, String*>::Allocate(
Steve Blocka7e24c12009-10-30 11:49:00 +00008412 int);
8413
John Reck59135872010-11-02 12:39:01 -07008414template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
Steve Blocka7e24c12009-10-30 11:49:00 +00008415 uint32_t, Object*);
8416
8417template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
8418 Object*);
8419
8420template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
8421 Object*);
8422
8423template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
8424 FixedArray*, PropertyAttributes);
8425
8426template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
8427 int, JSObject::DeleteMode);
8428
8429template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
8430 int, JSObject::DeleteMode);
8431
8432template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
8433 FixedArray*);
8434
8435template int
8436Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
8437 PropertyAttributes);
8438
John Reck59135872010-11-02 12:39:01 -07008439template MaybeObject* Dictionary<StringDictionaryShape, String*>::Add(
Steve Blocka7e24c12009-10-30 11:49:00 +00008440 String*, Object*, PropertyDetails);
8441
John Reck59135872010-11-02 12:39:01 -07008442template MaybeObject*
Steve Blocka7e24c12009-10-30 11:49:00 +00008443Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
8444
8445template int
8446Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
8447 PropertyAttributes);
8448
John Reck59135872010-11-02 12:39:01 -07008449template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Add(
Steve Blocka7e24c12009-10-30 11:49:00 +00008450 uint32_t, Object*, PropertyDetails);
8451
John Reck59135872010-11-02 12:39:01 -07008452template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::
8453 EnsureCapacity(int, uint32_t);
Steve Blocka7e24c12009-10-30 11:49:00 +00008454
John Reck59135872010-11-02 12:39:01 -07008455template MaybeObject* Dictionary<StringDictionaryShape, String*>::
8456 EnsureCapacity(int, String*);
Steve Blocka7e24c12009-10-30 11:49:00 +00008457
John Reck59135872010-11-02 12:39:01 -07008458template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
Steve Blocka7e24c12009-10-30 11:49:00 +00008459 uint32_t, Object*, PropertyDetails, uint32_t);
8460
John Reck59135872010-11-02 12:39:01 -07008461template MaybeObject* Dictionary<StringDictionaryShape, String*>::AddEntry(
Steve Blocka7e24c12009-10-30 11:49:00 +00008462 String*, Object*, PropertyDetails, uint32_t);
8463
8464template
8465int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
8466
8467template
8468int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
8469
Leon Clarkee46be812010-01-19 14:06:41 +00008470template
8471int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
8472
8473
Steve Blocka7e24c12009-10-30 11:49:00 +00008474// Collates undefined and unexisting elements below limit from position
8475// zero of the elements. The object stays in Dictionary mode.
John Reck59135872010-11-02 12:39:01 -07008476MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008477 ASSERT(HasDictionaryElements());
8478 // Must stay in dictionary mode, either because of requires_slow_elements,
8479 // or because we are not going to sort (and therefore compact) all of the
8480 // elements.
8481 NumberDictionary* dict = element_dictionary();
8482 HeapNumber* result_double = NULL;
8483 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
8484 // Allocate space for result before we start mutating the object.
John Reck59135872010-11-02 12:39:01 -07008485 Object* new_double;
8486 { MaybeObject* maybe_new_double = Heap::AllocateHeapNumber(0.0);
8487 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
8488 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008489 result_double = HeapNumber::cast(new_double);
8490 }
8491
John Reck59135872010-11-02 12:39:01 -07008492 Object* obj;
8493 { MaybeObject* maybe_obj =
8494 NumberDictionary::Allocate(dict->NumberOfElements());
8495 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8496 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008497 NumberDictionary* new_dict = NumberDictionary::cast(obj);
8498
8499 AssertNoAllocation no_alloc;
8500
8501 uint32_t pos = 0;
8502 uint32_t undefs = 0;
Steve Block6ded16b2010-05-10 14:33:55 +01008503 int capacity = dict->Capacity();
Steve Blocka7e24c12009-10-30 11:49:00 +00008504 for (int i = 0; i < capacity; i++) {
8505 Object* k = dict->KeyAt(i);
8506 if (dict->IsKey(k)) {
8507 ASSERT(k->IsNumber());
8508 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
8509 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
8510 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
8511 Object* value = dict->ValueAt(i);
8512 PropertyDetails details = dict->DetailsAt(i);
8513 if (details.type() == CALLBACKS) {
8514 // Bail out and do the sorting of undefineds and array holes in JS.
8515 return Smi::FromInt(-1);
8516 }
8517 uint32_t key = NumberToUint32(k);
John Reck59135872010-11-02 12:39:01 -07008518 // In the following we assert that adding the entry to the new dictionary
8519 // does not cause GC. This is the case because we made sure to allocate
8520 // the dictionary big enough above, so it need not grow.
Steve Blocka7e24c12009-10-30 11:49:00 +00008521 if (key < limit) {
8522 if (value->IsUndefined()) {
8523 undefs++;
8524 } else {
John Reck59135872010-11-02 12:39:01 -07008525 new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008526 pos++;
8527 }
8528 } else {
John Reck59135872010-11-02 12:39:01 -07008529 new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008530 }
8531 }
8532 }
8533
8534 uint32_t result = pos;
8535 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
8536 while (undefs > 0) {
John Reck59135872010-11-02 12:39:01 -07008537 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details)->
8538 ToObjectUnchecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00008539 pos++;
8540 undefs--;
8541 }
8542
8543 set_elements(new_dict);
8544
8545 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
8546 return Smi::FromInt(static_cast<int>(result));
8547 }
8548
8549 ASSERT_NE(NULL, result_double);
8550 result_double->set_value(static_cast<double>(result));
8551 return result_double;
8552}
8553
8554
8555// Collects all defined (non-hole) and non-undefined (array) elements at
8556// the start of the elements array.
8557// If the object is in dictionary mode, it is converted to fast elements
8558// mode.
John Reck59135872010-11-02 12:39:01 -07008559MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
Steve Block3ce2e202009-11-05 08:53:23 +00008560 ASSERT(!HasPixelElements() && !HasExternalArrayElements());
Steve Blocka7e24c12009-10-30 11:49:00 +00008561
8562 if (HasDictionaryElements()) {
8563 // Convert to fast elements containing only the existing properties.
8564 // Ordering is irrelevant, since we are going to sort anyway.
8565 NumberDictionary* dict = element_dictionary();
8566 if (IsJSArray() || dict->requires_slow_elements() ||
8567 dict->max_number_key() >= limit) {
8568 return PrepareSlowElementsForSort(limit);
8569 }
8570 // Convert to fast elements.
8571
John Reck59135872010-11-02 12:39:01 -07008572 Object* obj;
8573 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
8574 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8575 }
Steve Block8defd9f2010-07-08 12:39:36 +01008576 Map* new_map = Map::cast(obj);
8577
Steve Blocka7e24c12009-10-30 11:49:00 +00008578 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED;
John Reck59135872010-11-02 12:39:01 -07008579 Object* new_array;
8580 { MaybeObject* maybe_new_array =
8581 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure);
8582 if (!maybe_new_array->ToObject(&new_array)) return maybe_new_array;
8583 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008584 FixedArray* fast_elements = FixedArray::cast(new_array);
8585 dict->CopyValuesTo(fast_elements);
Steve Block8defd9f2010-07-08 12:39:36 +01008586
8587 set_map(new_map);
Steve Blocka7e24c12009-10-30 11:49:00 +00008588 set_elements(fast_elements);
Iain Merrick75681382010-08-19 15:07:18 +01008589 } else {
John Reck59135872010-11-02 12:39:01 -07008590 Object* obj;
8591 { MaybeObject* maybe_obj = EnsureWritableFastElements();
8592 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8593 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008594 }
8595 ASSERT(HasFastElements());
8596
8597 // Collect holes at the end, undefined before that and the rest at the
8598 // start, and return the number of non-hole, non-undefined values.
8599
8600 FixedArray* elements = FixedArray::cast(this->elements());
8601 uint32_t elements_length = static_cast<uint32_t>(elements->length());
8602 if (limit > elements_length) {
8603 limit = elements_length ;
8604 }
8605 if (limit == 0) {
8606 return Smi::FromInt(0);
8607 }
8608
8609 HeapNumber* result_double = NULL;
8610 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
8611 // Pessimistically allocate space for return value before
8612 // we start mutating the array.
John Reck59135872010-11-02 12:39:01 -07008613 Object* new_double;
8614 { MaybeObject* maybe_new_double = Heap::AllocateHeapNumber(0.0);
8615 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
8616 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008617 result_double = HeapNumber::cast(new_double);
8618 }
8619
8620 AssertNoAllocation no_alloc;
8621
8622 // Split elements into defined, undefined and the_hole, in that order.
8623 // Only count locations for undefined and the hole, and fill them afterwards.
Leon Clarke4515c472010-02-03 11:58:03 +00008624 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
Steve Blocka7e24c12009-10-30 11:49:00 +00008625 unsigned int undefs = limit;
8626 unsigned int holes = limit;
8627 // Assume most arrays contain no holes and undefined values, so minimize the
8628 // number of stores of non-undefined, non-the-hole values.
8629 for (unsigned int i = 0; i < undefs; i++) {
8630 Object* current = elements->get(i);
8631 if (current->IsTheHole()) {
8632 holes--;
8633 undefs--;
8634 } else if (current->IsUndefined()) {
8635 undefs--;
8636 } else {
8637 continue;
8638 }
8639 // Position i needs to be filled.
8640 while (undefs > i) {
8641 current = elements->get(undefs);
8642 if (current->IsTheHole()) {
8643 holes--;
8644 undefs--;
8645 } else if (current->IsUndefined()) {
8646 undefs--;
8647 } else {
8648 elements->set(i, current, write_barrier);
8649 break;
8650 }
8651 }
8652 }
8653 uint32_t result = undefs;
8654 while (undefs < holes) {
8655 elements->set_undefined(undefs);
8656 undefs++;
8657 }
8658 while (holes < limit) {
8659 elements->set_the_hole(holes);
8660 holes++;
8661 }
8662
8663 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
8664 return Smi::FromInt(static_cast<int>(result));
8665 }
8666 ASSERT_NE(NULL, result_double);
8667 result_double->set_value(static_cast<double>(result));
8668 return result_double;
8669}
8670
8671
8672Object* PixelArray::SetValue(uint32_t index, Object* value) {
8673 uint8_t clamped_value = 0;
8674 if (index < static_cast<uint32_t>(length())) {
8675 if (value->IsSmi()) {
8676 int int_value = Smi::cast(value)->value();
8677 if (int_value < 0) {
8678 clamped_value = 0;
8679 } else if (int_value > 255) {
8680 clamped_value = 255;
8681 } else {
8682 clamped_value = static_cast<uint8_t>(int_value);
8683 }
8684 } else if (value->IsHeapNumber()) {
8685 double double_value = HeapNumber::cast(value)->value();
8686 if (!(double_value > 0)) {
8687 // NaN and less than zero clamp to zero.
8688 clamped_value = 0;
8689 } else if (double_value > 255) {
8690 // Greater than 255 clamp to 255.
8691 clamped_value = 255;
8692 } else {
8693 // Other doubles are rounded to the nearest integer.
8694 clamped_value = static_cast<uint8_t>(double_value + 0.5);
8695 }
8696 } else {
8697 // Clamp undefined to zero (default). All other types have been
8698 // converted to a number type further up in the call chain.
8699 ASSERT(value->IsUndefined());
8700 }
8701 set(index, clamped_value);
8702 }
8703 return Smi::FromInt(clamped_value);
8704}
8705
8706
Steve Block3ce2e202009-11-05 08:53:23 +00008707template<typename ExternalArrayClass, typename ValueType>
John Reck59135872010-11-02 12:39:01 -07008708static MaybeObject* ExternalArrayIntSetter(ExternalArrayClass* receiver,
8709 uint32_t index,
8710 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008711 ValueType cast_value = 0;
8712 if (index < static_cast<uint32_t>(receiver->length())) {
8713 if (value->IsSmi()) {
8714 int int_value = Smi::cast(value)->value();
8715 cast_value = static_cast<ValueType>(int_value);
8716 } else if (value->IsHeapNumber()) {
8717 double double_value = HeapNumber::cast(value)->value();
8718 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
8719 } else {
8720 // Clamp undefined to zero (default). All other types have been
8721 // converted to a number type further up in the call chain.
8722 ASSERT(value->IsUndefined());
8723 }
8724 receiver->set(index, cast_value);
8725 }
8726 return Heap::NumberFromInt32(cast_value);
8727}
8728
8729
John Reck59135872010-11-02 12:39:01 -07008730MaybeObject* ExternalByteArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008731 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
8732 (this, index, value);
8733}
8734
8735
John Reck59135872010-11-02 12:39:01 -07008736MaybeObject* ExternalUnsignedByteArray::SetValue(uint32_t index,
8737 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008738 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
8739 (this, index, value);
8740}
8741
8742
John Reck59135872010-11-02 12:39:01 -07008743MaybeObject* ExternalShortArray::SetValue(uint32_t index,
8744 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008745 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
8746 (this, index, value);
8747}
8748
8749
John Reck59135872010-11-02 12:39:01 -07008750MaybeObject* ExternalUnsignedShortArray::SetValue(uint32_t index,
8751 Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008752 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
8753 (this, index, value);
8754}
8755
8756
John Reck59135872010-11-02 12:39:01 -07008757MaybeObject* ExternalIntArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008758 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
8759 (this, index, value);
8760}
8761
8762
John Reck59135872010-11-02 12:39:01 -07008763MaybeObject* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008764 uint32_t cast_value = 0;
8765 if (index < static_cast<uint32_t>(length())) {
8766 if (value->IsSmi()) {
8767 int int_value = Smi::cast(value)->value();
8768 cast_value = static_cast<uint32_t>(int_value);
8769 } else if (value->IsHeapNumber()) {
8770 double double_value = HeapNumber::cast(value)->value();
8771 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
8772 } else {
8773 // Clamp undefined to zero (default). All other types have been
8774 // converted to a number type further up in the call chain.
8775 ASSERT(value->IsUndefined());
8776 }
8777 set(index, cast_value);
8778 }
8779 return Heap::NumberFromUint32(cast_value);
8780}
8781
8782
John Reck59135872010-11-02 12:39:01 -07008783MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
Steve Block3ce2e202009-11-05 08:53:23 +00008784 float cast_value = 0;
8785 if (index < static_cast<uint32_t>(length())) {
8786 if (value->IsSmi()) {
8787 int int_value = Smi::cast(value)->value();
8788 cast_value = static_cast<float>(int_value);
8789 } else if (value->IsHeapNumber()) {
8790 double double_value = HeapNumber::cast(value)->value();
8791 cast_value = static_cast<float>(double_value);
8792 } else {
8793 // Clamp undefined to zero (default). All other types have been
8794 // converted to a number type further up in the call chain.
8795 ASSERT(value->IsUndefined());
8796 }
8797 set(index, cast_value);
8798 }
8799 return Heap::AllocateHeapNumber(cast_value);
8800}
8801
8802
Ben Murdochb0fe1622011-05-05 13:52:32 +01008803JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008804 ASSERT(!HasFastProperties());
8805 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
Ben Murdochb0fe1622011-05-05 13:52:32 +01008806 return JSGlobalPropertyCell::cast(value);
Steve Blocka7e24c12009-10-30 11:49:00 +00008807}
8808
8809
John Reck59135872010-11-02 12:39:01 -07008810MaybeObject* GlobalObject::EnsurePropertyCell(String* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008811 ASSERT(!HasFastProperties());
8812 int entry = property_dictionary()->FindEntry(name);
8813 if (entry == StringDictionary::kNotFound) {
John Reck59135872010-11-02 12:39:01 -07008814 Object* cell;
8815 { MaybeObject* maybe_cell =
8816 Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value());
8817 if (!maybe_cell->ToObject(&cell)) return maybe_cell;
8818 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008819 PropertyDetails details(NONE, NORMAL);
8820 details = details.AsDeleted();
John Reck59135872010-11-02 12:39:01 -07008821 Object* dictionary;
8822 { MaybeObject* maybe_dictionary =
8823 property_dictionary()->Add(name, cell, details);
8824 if (!maybe_dictionary->ToObject(&dictionary)) return maybe_dictionary;
8825 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008826 set_properties(StringDictionary::cast(dictionary));
8827 return cell;
8828 } else {
8829 Object* value = property_dictionary()->ValueAt(entry);
8830 ASSERT(value->IsJSGlobalPropertyCell());
8831 return value;
8832 }
8833}
8834
8835
John Reck59135872010-11-02 12:39:01 -07008836MaybeObject* SymbolTable::LookupString(String* string, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008837 SymbolKey key(string);
8838 return LookupKey(&key, s);
8839}
8840
8841
Steve Blockd0582a62009-12-15 09:54:21 +00008842// This class is used for looking up two character strings in the symbol table.
8843// If we don't have a hit we don't want to waste much time so we unroll the
8844// string hash calculation loop here for speed. Doesn't work if the two
8845// characters form a decimal integer, since such strings have a different hash
8846// algorithm.
8847class TwoCharHashTableKey : public HashTableKey {
8848 public:
8849 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
8850 : c1_(c1), c2_(c2) {
8851 // Char 1.
8852 uint32_t hash = c1 + (c1 << 10);
8853 hash ^= hash >> 6;
8854 // Char 2.
8855 hash += c2;
8856 hash += hash << 10;
8857 hash ^= hash >> 6;
8858 // GetHash.
8859 hash += hash << 3;
8860 hash ^= hash >> 11;
8861 hash += hash << 15;
8862 if (hash == 0) hash = 27;
8863#ifdef DEBUG
8864 StringHasher hasher(2);
8865 hasher.AddCharacter(c1);
8866 hasher.AddCharacter(c2);
8867 // If this assert fails then we failed to reproduce the two-character
8868 // version of the string hashing algorithm above. One reason could be
8869 // that we were passed two digits as characters, since the hash
8870 // algorithm is different in that case.
8871 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
8872#endif
8873 hash_ = hash;
8874 }
8875
8876 bool IsMatch(Object* o) {
8877 if (!o->IsString()) return false;
8878 String* other = String::cast(o);
8879 if (other->length() != 2) return false;
8880 if (other->Get(0) != c1_) return false;
8881 return other->Get(1) == c2_;
8882 }
8883
8884 uint32_t Hash() { return hash_; }
8885 uint32_t HashForObject(Object* key) {
8886 if (!key->IsString()) return 0;
8887 return String::cast(key)->Hash();
8888 }
8889
8890 Object* AsObject() {
8891 // The TwoCharHashTableKey is only used for looking in the symbol
8892 // table, not for adding to it.
8893 UNREACHABLE();
8894 return NULL;
8895 }
8896 private:
8897 uint32_t c1_;
8898 uint32_t c2_;
8899 uint32_t hash_;
8900};
8901
8902
Steve Blocka7e24c12009-10-30 11:49:00 +00008903bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
8904 SymbolKey key(string);
8905 int entry = FindEntry(&key);
8906 if (entry == kNotFound) {
8907 return false;
8908 } else {
8909 String* result = String::cast(KeyAt(entry));
8910 ASSERT(StringShape(result).IsSymbol());
8911 *symbol = result;
8912 return true;
8913 }
8914}
8915
8916
Steve Blockd0582a62009-12-15 09:54:21 +00008917bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
8918 uint32_t c2,
8919 String** symbol) {
8920 TwoCharHashTableKey key(c1, c2);
8921 int entry = FindEntry(&key);
8922 if (entry == kNotFound) {
8923 return false;
8924 } else {
8925 String* result = String::cast(KeyAt(entry));
8926 ASSERT(StringShape(result).IsSymbol());
8927 *symbol = result;
8928 return true;
8929 }
8930}
8931
8932
John Reck59135872010-11-02 12:39:01 -07008933MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008934 Utf8SymbolKey key(str);
8935 return LookupKey(&key, s);
8936}
8937
8938
Steve Block9fac8402011-05-12 15:51:54 +01008939MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str,
8940 Object** s) {
8941 AsciiSymbolKey key(str);
8942 return LookupKey(&key, s);
8943}
8944
8945
8946MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str,
8947 Object** s) {
8948 TwoByteSymbolKey key(str);
8949 return LookupKey(&key, s);
8950}
8951
John Reck59135872010-11-02 12:39:01 -07008952MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008953 int entry = FindEntry(key);
8954
8955 // Symbol already in table.
8956 if (entry != kNotFound) {
8957 *s = KeyAt(entry);
8958 return this;
8959 }
8960
8961 // Adding new symbol. Grow table if needed.
John Reck59135872010-11-02 12:39:01 -07008962 Object* obj;
8963 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
8964 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8965 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008966
8967 // Create symbol object.
John Reck59135872010-11-02 12:39:01 -07008968 Object* symbol;
8969 { MaybeObject* maybe_symbol = key->AsObject();
8970 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
8971 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008972
8973 // If the symbol table grew as part of EnsureCapacity, obj is not
8974 // the current symbol table and therefore we cannot use
8975 // SymbolTable::cast here.
8976 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
8977
8978 // Add the new symbol and return it along with the symbol table.
8979 entry = table->FindInsertionEntry(key->Hash());
8980 table->set(EntryToIndex(entry), symbol);
8981 table->ElementAdded();
8982 *s = symbol;
8983 return table;
8984}
8985
8986
8987Object* CompilationCacheTable::Lookup(String* src) {
8988 StringKey key(src);
8989 int entry = FindEntry(&key);
8990 if (entry == kNotFound) return Heap::undefined_value();
8991 return get(EntryToIndex(entry) + 1);
8992}
8993
8994
8995Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
8996 StringSharedKey key(src, context->closure()->shared());
8997 int entry = FindEntry(&key);
8998 if (entry == kNotFound) return Heap::undefined_value();
8999 return get(EntryToIndex(entry) + 1);
9000}
9001
9002
9003Object* CompilationCacheTable::LookupRegExp(String* src,
9004 JSRegExp::Flags flags) {
9005 RegExpKey key(src, flags);
9006 int entry = FindEntry(&key);
9007 if (entry == kNotFound) return Heap::undefined_value();
9008 return get(EntryToIndex(entry) + 1);
9009}
9010
9011
John Reck59135872010-11-02 12:39:01 -07009012MaybeObject* CompilationCacheTable::Put(String* src, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009013 StringKey key(src);
John Reck59135872010-11-02 12:39:01 -07009014 Object* obj;
9015 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
9016 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9017 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009018
9019 CompilationCacheTable* cache =
9020 reinterpret_cast<CompilationCacheTable*>(obj);
9021 int entry = cache->FindInsertionEntry(key.Hash());
9022 cache->set(EntryToIndex(entry), src);
9023 cache->set(EntryToIndex(entry) + 1, value);
9024 cache->ElementAdded();
9025 return cache;
9026}
9027
9028
John Reck59135872010-11-02 12:39:01 -07009029MaybeObject* CompilationCacheTable::PutEval(String* src,
9030 Context* context,
9031 Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009032 StringSharedKey key(src, context->closure()->shared());
John Reck59135872010-11-02 12:39:01 -07009033 Object* obj;
9034 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
9035 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9036 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009037
9038 CompilationCacheTable* cache =
9039 reinterpret_cast<CompilationCacheTable*>(obj);
9040 int entry = cache->FindInsertionEntry(key.Hash());
9041
John Reck59135872010-11-02 12:39:01 -07009042 Object* k;
9043 { MaybeObject* maybe_k = key.AsObject();
9044 if (!maybe_k->ToObject(&k)) return maybe_k;
9045 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009046
9047 cache->set(EntryToIndex(entry), k);
9048 cache->set(EntryToIndex(entry) + 1, value);
9049 cache->ElementAdded();
9050 return cache;
9051}
9052
9053
John Reck59135872010-11-02 12:39:01 -07009054MaybeObject* CompilationCacheTable::PutRegExp(String* src,
9055 JSRegExp::Flags flags,
9056 FixedArray* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009057 RegExpKey key(src, flags);
John Reck59135872010-11-02 12:39:01 -07009058 Object* obj;
9059 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
9060 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9061 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009062
9063 CompilationCacheTable* cache =
9064 reinterpret_cast<CompilationCacheTable*>(obj);
9065 int entry = cache->FindInsertionEntry(key.Hash());
Steve Block3ce2e202009-11-05 08:53:23 +00009066 // We store the value in the key slot, and compare the search key
9067 // to the stored value with a custon IsMatch function during lookups.
Steve Blocka7e24c12009-10-30 11:49:00 +00009068 cache->set(EntryToIndex(entry), value);
9069 cache->set(EntryToIndex(entry) + 1, value);
9070 cache->ElementAdded();
9071 return cache;
9072}
9073
9074
Ben Murdochb0fe1622011-05-05 13:52:32 +01009075void CompilationCacheTable::Remove(Object* value) {
9076 for (int entry = 0, size = Capacity(); entry < size; entry++) {
9077 int entry_index = EntryToIndex(entry);
9078 int value_index = entry_index + 1;
9079 if (get(value_index) == value) {
9080 fast_set(this, entry_index, Heap::null_value());
9081 fast_set(this, value_index, Heap::null_value());
9082 ElementRemoved();
9083 }
9084 }
9085 return;
9086}
9087
9088
Steve Blocka7e24c12009-10-30 11:49:00 +00009089// SymbolsKey used for HashTable where key is array of symbols.
9090class SymbolsKey : public HashTableKey {
9091 public:
9092 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
9093
9094 bool IsMatch(Object* symbols) {
9095 FixedArray* o = FixedArray::cast(symbols);
9096 int len = symbols_->length();
9097 if (o->length() != len) return false;
9098 for (int i = 0; i < len; i++) {
9099 if (o->get(i) != symbols_->get(i)) return false;
9100 }
9101 return true;
9102 }
9103
9104 uint32_t Hash() { return HashForObject(symbols_); }
9105
9106 uint32_t HashForObject(Object* obj) {
9107 FixedArray* symbols = FixedArray::cast(obj);
9108 int len = symbols->length();
9109 uint32_t hash = 0;
9110 for (int i = 0; i < len; i++) {
9111 hash ^= String::cast(symbols->get(i))->Hash();
9112 }
9113 return hash;
9114 }
9115
9116 Object* AsObject() { return symbols_; }
9117
9118 private:
9119 FixedArray* symbols_;
9120};
9121
9122
9123Object* MapCache::Lookup(FixedArray* array) {
9124 SymbolsKey key(array);
9125 int entry = FindEntry(&key);
9126 if (entry == kNotFound) return Heap::undefined_value();
9127 return get(EntryToIndex(entry) + 1);
9128}
9129
9130
John Reck59135872010-11-02 12:39:01 -07009131MaybeObject* MapCache::Put(FixedArray* array, Map* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009132 SymbolsKey key(array);
John Reck59135872010-11-02 12:39:01 -07009133 Object* obj;
9134 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
9135 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9136 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009137
9138 MapCache* cache = reinterpret_cast<MapCache*>(obj);
9139 int entry = cache->FindInsertionEntry(key.Hash());
9140 cache->set(EntryToIndex(entry), array);
9141 cache->set(EntryToIndex(entry) + 1, value);
9142 cache->ElementAdded();
9143 return cache;
9144}
9145
9146
9147template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009148MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
9149 Object* obj;
9150 { MaybeObject* maybe_obj =
9151 HashTable<Shape, Key>::Allocate(at_least_space_for);
9152 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +00009153 }
John Reck59135872010-11-02 12:39:01 -07009154 // Initialize the next enumeration index.
9155 Dictionary<Shape, Key>::cast(obj)->
9156 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
Steve Blocka7e24c12009-10-30 11:49:00 +00009157 return obj;
9158}
9159
9160
9161template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009162MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
Steve Blocka7e24c12009-10-30 11:49:00 +00009163 int length = HashTable<Shape, Key>::NumberOfElements();
9164
9165 // Allocate and initialize iteration order array.
John Reck59135872010-11-02 12:39:01 -07009166 Object* obj;
9167 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(length);
9168 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9169 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009170 FixedArray* iteration_order = FixedArray::cast(obj);
9171 for (int i = 0; i < length; i++) {
Leon Clarke4515c472010-02-03 11:58:03 +00009172 iteration_order->set(i, Smi::FromInt(i));
Steve Blocka7e24c12009-10-30 11:49:00 +00009173 }
9174
9175 // Allocate array with enumeration order.
John Reck59135872010-11-02 12:39:01 -07009176 { MaybeObject* maybe_obj = Heap::AllocateFixedArray(length);
9177 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9178 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009179 FixedArray* enumeration_order = FixedArray::cast(obj);
9180
9181 // Fill the enumeration order array with property details.
9182 int capacity = HashTable<Shape, Key>::Capacity();
9183 int pos = 0;
9184 for (int i = 0; i < capacity; i++) {
9185 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
Leon Clarke4515c472010-02-03 11:58:03 +00009186 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00009187 }
9188 }
9189
9190 // Sort the arrays wrt. enumeration order.
9191 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
9192
9193 // Overwrite the enumeration_order with the enumeration indices.
9194 for (int i = 0; i < length; i++) {
9195 int index = Smi::cast(iteration_order->get(i))->value();
9196 int enum_index = PropertyDetails::kInitialIndex + i;
Leon Clarke4515c472010-02-03 11:58:03 +00009197 enumeration_order->set(index, Smi::FromInt(enum_index));
Steve Blocka7e24c12009-10-30 11:49:00 +00009198 }
9199
9200 // Update the dictionary with new indices.
9201 capacity = HashTable<Shape, Key>::Capacity();
9202 pos = 0;
9203 for (int i = 0; i < capacity; i++) {
9204 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
9205 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
9206 PropertyDetails details = DetailsAt(i);
9207 PropertyDetails new_details =
9208 PropertyDetails(details.attributes(), details.type(), enum_index);
9209 DetailsAtPut(i, new_details);
9210 }
9211 }
9212
9213 // Set the next enumeration index.
9214 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
9215 return this;
9216}
9217
9218template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009219MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009220 // Check whether there are enough enumeration indices to add n elements.
9221 if (Shape::kIsEnumerable &&
9222 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
9223 // If not, we generate new indices for the properties.
John Reck59135872010-11-02 12:39:01 -07009224 Object* result;
9225 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
9226 if (!maybe_result->ToObject(&result)) return maybe_result;
9227 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009228 }
9229 return HashTable<Shape, Key>::EnsureCapacity(n, key);
9230}
9231
9232
9233void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
9234 // Do nothing if the interval [from, to) is empty.
9235 if (from >= to) return;
9236
9237 int removed_entries = 0;
9238 Object* sentinel = Heap::null_value();
9239 int capacity = Capacity();
9240 for (int i = 0; i < capacity; i++) {
9241 Object* key = KeyAt(i);
9242 if (key->IsNumber()) {
9243 uint32_t number = static_cast<uint32_t>(key->Number());
9244 if (from <= number && number < to) {
9245 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
9246 removed_entries++;
9247 }
9248 }
9249 }
9250
9251 // Update the number of elements.
Leon Clarkee46be812010-01-19 14:06:41 +00009252 ElementsRemoved(removed_entries);
Steve Blocka7e24c12009-10-30 11:49:00 +00009253}
9254
9255
9256template<typename Shape, typename Key>
9257Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
9258 JSObject::DeleteMode mode) {
9259 PropertyDetails details = DetailsAt(entry);
9260 // Ignore attributes if forcing a deletion.
9261 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
9262 return Heap::false_value();
9263 }
9264 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
9265 HashTable<Shape, Key>::ElementRemoved();
9266 return Heap::true_value();
9267}
9268
9269
9270template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009271MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009272 int entry = this->FindEntry(key);
Steve Blocka7e24c12009-10-30 11:49:00 +00009273
9274 // If the entry is present set the value;
9275 if (entry != Dictionary<Shape, Key>::kNotFound) {
9276 ValueAtPut(entry, value);
9277 return this;
9278 }
9279
9280 // Check whether the dictionary should be extended.
John Reck59135872010-11-02 12:39:01 -07009281 Object* obj;
9282 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
9283 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9284 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009285
John Reck59135872010-11-02 12:39:01 -07009286 Object* k;
9287 { MaybeObject* maybe_k = Shape::AsObject(key);
9288 if (!maybe_k->ToObject(&k)) return maybe_k;
9289 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009290 PropertyDetails details = PropertyDetails(NONE, NORMAL);
9291 return Dictionary<Shape, Key>::cast(obj)->
9292 AddEntry(key, value, details, Shape::Hash(key));
9293}
9294
9295
9296template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009297MaybeObject* Dictionary<Shape, Key>::Add(Key key,
9298 Object* value,
9299 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009300 // Valdate key is absent.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009301 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
Steve Blocka7e24c12009-10-30 11:49:00 +00009302 // Check whether the dictionary should be extended.
John Reck59135872010-11-02 12:39:01 -07009303 Object* obj;
9304 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
9305 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9306 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009307 return Dictionary<Shape, Key>::cast(obj)->
9308 AddEntry(key, value, details, Shape::Hash(key));
9309}
9310
9311
9312// Add a key, value pair to the dictionary.
9313template<typename Shape, typename Key>
John Reck59135872010-11-02 12:39:01 -07009314MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key,
9315 Object* value,
9316 PropertyDetails details,
9317 uint32_t hash) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009318 // Compute the key object.
John Reck59135872010-11-02 12:39:01 -07009319 Object* k;
9320 { MaybeObject* maybe_k = Shape::AsObject(key);
9321 if (!maybe_k->ToObject(&k)) return maybe_k;
9322 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009323
9324 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
9325 // Insert element at empty or deleted entry
9326 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
9327 // Assign an enumeration index to the property and update
9328 // SetNextEnumerationIndex.
9329 int index = NextEnumerationIndex();
9330 details = PropertyDetails(details.attributes(), details.type(), index);
9331 SetNextEnumerationIndex(index + 1);
9332 }
9333 SetEntry(entry, k, value, details);
9334 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
9335 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
9336 HashTable<Shape, Key>::ElementAdded();
9337 return this;
9338}
9339
9340
9341void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
9342 // If the dictionary requires slow elements an element has already
9343 // been added at a high index.
9344 if (requires_slow_elements()) return;
9345 // Check if this index is high enough that we should require slow
9346 // elements.
9347 if (key > kRequiresSlowElementsLimit) {
9348 set_requires_slow_elements();
9349 return;
9350 }
9351 // Update max key value.
9352 Object* max_index_object = get(kMaxNumberKeyIndex);
9353 if (!max_index_object->IsSmi() || max_number_key() < key) {
9354 FixedArray::set(kMaxNumberKeyIndex,
Leon Clarke4515c472010-02-03 11:58:03 +00009355 Smi::FromInt(key << kRequiresSlowElementsTagSize));
Steve Blocka7e24c12009-10-30 11:49:00 +00009356 }
9357}
9358
9359
John Reck59135872010-11-02 12:39:01 -07009360MaybeObject* NumberDictionary::AddNumberEntry(uint32_t key,
9361 Object* value,
9362 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009363 UpdateMaxNumberKey(key);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009364 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
Steve Blocka7e24c12009-10-30 11:49:00 +00009365 return Add(key, value, details);
9366}
9367
9368
John Reck59135872010-11-02 12:39:01 -07009369MaybeObject* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009370 UpdateMaxNumberKey(key);
9371 return AtPut(key, value);
9372}
9373
9374
John Reck59135872010-11-02 12:39:01 -07009375MaybeObject* NumberDictionary::Set(uint32_t key,
9376 Object* value,
9377 PropertyDetails details) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009378 int entry = FindEntry(key);
9379 if (entry == kNotFound) return AddNumberEntry(key, value, details);
9380 // Preserve enumeration index.
9381 details = PropertyDetails(details.attributes(),
9382 details.type(),
9383 DetailsAt(entry).index());
John Reck59135872010-11-02 12:39:01 -07009384 MaybeObject* maybe_object_key = NumberDictionaryShape::AsObject(key);
9385 Object* object_key;
9386 if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
Ben Murdochf87a2032010-10-22 12:50:53 +01009387 SetEntry(entry, object_key, value, details);
Steve Blocka7e24c12009-10-30 11:49:00 +00009388 return this;
9389}
9390
9391
9392
9393template<typename Shape, typename Key>
9394int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
9395 PropertyAttributes filter) {
9396 int capacity = HashTable<Shape, Key>::Capacity();
9397 int result = 0;
9398 for (int i = 0; i < capacity; i++) {
9399 Object* k = HashTable<Shape, Key>::KeyAt(i);
9400 if (HashTable<Shape, Key>::IsKey(k)) {
9401 PropertyDetails details = DetailsAt(i);
9402 if (details.IsDeleted()) continue;
9403 PropertyAttributes attr = details.attributes();
9404 if ((attr & filter) == 0) result++;
9405 }
9406 }
9407 return result;
9408}
9409
9410
9411template<typename Shape, typename Key>
9412int Dictionary<Shape, Key>::NumberOfEnumElements() {
9413 return NumberOfElementsFilterAttributes(
9414 static_cast<PropertyAttributes>(DONT_ENUM));
9415}
9416
9417
9418template<typename Shape, typename Key>
9419void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage,
9420 PropertyAttributes filter) {
9421 ASSERT(storage->length() >= NumberOfEnumElements());
9422 int capacity = HashTable<Shape, Key>::Capacity();
9423 int index = 0;
9424 for (int i = 0; i < capacity; i++) {
9425 Object* k = HashTable<Shape, Key>::KeyAt(i);
9426 if (HashTable<Shape, Key>::IsKey(k)) {
9427 PropertyDetails details = DetailsAt(i);
9428 if (details.IsDeleted()) continue;
9429 PropertyAttributes attr = details.attributes();
9430 if ((attr & filter) == 0) storage->set(index++, k);
9431 }
9432 }
9433 storage->SortPairs(storage, index);
9434 ASSERT(storage->length() >= index);
9435}
9436
9437
9438void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
9439 FixedArray* sort_array) {
9440 ASSERT(storage->length() >= NumberOfEnumElements());
9441 int capacity = Capacity();
9442 int index = 0;
9443 for (int i = 0; i < capacity; i++) {
9444 Object* k = KeyAt(i);
9445 if (IsKey(k)) {
9446 PropertyDetails details = DetailsAt(i);
9447 if (details.IsDeleted() || details.IsDontEnum()) continue;
9448 storage->set(index, k);
Leon Clarke4515c472010-02-03 11:58:03 +00009449 sort_array->set(index, Smi::FromInt(details.index()));
Steve Blocka7e24c12009-10-30 11:49:00 +00009450 index++;
9451 }
9452 }
9453 storage->SortPairs(sort_array, sort_array->length());
9454 ASSERT(storage->length() >= index);
9455}
9456
9457
9458template<typename Shape, typename Key>
9459void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) {
9460 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
9461 static_cast<PropertyAttributes>(NONE)));
9462 int capacity = HashTable<Shape, Key>::Capacity();
9463 int index = 0;
9464 for (int i = 0; i < capacity; i++) {
9465 Object* k = HashTable<Shape, Key>::KeyAt(i);
9466 if (HashTable<Shape, Key>::IsKey(k)) {
9467 PropertyDetails details = DetailsAt(i);
9468 if (details.IsDeleted()) continue;
9469 storage->set(index++, k);
9470 }
9471 }
9472 ASSERT(storage->length() >= index);
9473}
9474
9475
9476// Backwards lookup (slow).
9477template<typename Shape, typename Key>
9478Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
9479 int capacity = HashTable<Shape, Key>::Capacity();
9480 for (int i = 0; i < capacity; i++) {
9481 Object* k = HashTable<Shape, Key>::KeyAt(i);
9482 if (Dictionary<Shape, Key>::IsKey(k)) {
9483 Object* e = ValueAt(i);
9484 if (e->IsJSGlobalPropertyCell()) {
9485 e = JSGlobalPropertyCell::cast(e)->value();
9486 }
9487 if (e == value) return k;
9488 }
9489 }
9490 return Heap::undefined_value();
9491}
9492
9493
John Reck59135872010-11-02 12:39:01 -07009494MaybeObject* StringDictionary::TransformPropertiesToFastFor(
Steve Blocka7e24c12009-10-30 11:49:00 +00009495 JSObject* obj, int unused_property_fields) {
9496 // Make sure we preserve dictionary representation if there are too many
9497 // descriptors.
9498 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
9499
9500 // Figure out if it is necessary to generate new enumeration indices.
9501 int max_enumeration_index =
9502 NextEnumerationIndex() +
9503 (DescriptorArray::kMaxNumberOfDescriptors -
9504 NumberOfElements());
9505 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
John Reck59135872010-11-02 12:39:01 -07009506 Object* result;
9507 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
9508 if (!maybe_result->ToObject(&result)) return maybe_result;
9509 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009510 }
9511
9512 int instance_descriptor_length = 0;
9513 int number_of_fields = 0;
9514
9515 // Compute the length of the instance descriptor.
9516 int capacity = Capacity();
9517 for (int i = 0; i < capacity; i++) {
9518 Object* k = KeyAt(i);
9519 if (IsKey(k)) {
9520 Object* value = ValueAt(i);
9521 PropertyType type = DetailsAt(i).type();
9522 ASSERT(type != FIELD);
9523 instance_descriptor_length++;
Leon Clarkee46be812010-01-19 14:06:41 +00009524 if (type == NORMAL &&
9525 (!value->IsJSFunction() || Heap::InNewSpace(value))) {
9526 number_of_fields += 1;
9527 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009528 }
9529 }
9530
9531 // Allocate the instance descriptor.
John Reck59135872010-11-02 12:39:01 -07009532 Object* descriptors_unchecked;
9533 { MaybeObject* maybe_descriptors_unchecked =
9534 DescriptorArray::Allocate(instance_descriptor_length);
9535 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
9536 return maybe_descriptors_unchecked;
9537 }
9538 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009539 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
9540
9541 int inobject_props = obj->map()->inobject_properties();
9542 int number_of_allocated_fields =
9543 number_of_fields + unused_property_fields - inobject_props;
Ben Murdochf87a2032010-10-22 12:50:53 +01009544 if (number_of_allocated_fields < 0) {
9545 // There is enough inobject space for all fields (including unused).
9546 number_of_allocated_fields = 0;
9547 unused_property_fields = inobject_props - number_of_fields;
9548 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009549
9550 // Allocate the fixed array for the fields.
John Reck59135872010-11-02 12:39:01 -07009551 Object* fields;
9552 { MaybeObject* maybe_fields =
9553 Heap::AllocateFixedArray(number_of_allocated_fields);
9554 if (!maybe_fields->ToObject(&fields)) return maybe_fields;
9555 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009556
9557 // Fill in the instance descriptor and the fields.
9558 int next_descriptor = 0;
9559 int current_offset = 0;
9560 for (int i = 0; i < capacity; i++) {
9561 Object* k = KeyAt(i);
9562 if (IsKey(k)) {
9563 Object* value = ValueAt(i);
9564 // Ensure the key is a symbol before writing into the instance descriptor.
John Reck59135872010-11-02 12:39:01 -07009565 Object* key;
9566 { MaybeObject* maybe_key = Heap::LookupSymbol(String::cast(k));
9567 if (!maybe_key->ToObject(&key)) return maybe_key;
9568 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009569 PropertyDetails details = DetailsAt(i);
9570 PropertyType type = details.type();
9571
Leon Clarkee46be812010-01-19 14:06:41 +00009572 if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009573 ConstantFunctionDescriptor d(String::cast(key),
9574 JSFunction::cast(value),
9575 details.attributes(),
9576 details.index());
9577 descriptors->Set(next_descriptor++, &d);
9578 } else if (type == NORMAL) {
9579 if (current_offset < inobject_props) {
9580 obj->InObjectPropertyAtPut(current_offset,
9581 value,
9582 UPDATE_WRITE_BARRIER);
9583 } else {
9584 int offset = current_offset - inobject_props;
9585 FixedArray::cast(fields)->set(offset, value);
9586 }
9587 FieldDescriptor d(String::cast(key),
9588 current_offset++,
9589 details.attributes(),
9590 details.index());
9591 descriptors->Set(next_descriptor++, &d);
9592 } else if (type == CALLBACKS) {
9593 CallbacksDescriptor d(String::cast(key),
9594 value,
9595 details.attributes(),
9596 details.index());
9597 descriptors->Set(next_descriptor++, &d);
9598 } else {
9599 UNREACHABLE();
9600 }
9601 }
9602 }
9603 ASSERT(current_offset == number_of_fields);
9604
9605 descriptors->Sort();
9606 // Allocate new map.
John Reck59135872010-11-02 12:39:01 -07009607 Object* new_map;
9608 { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
9609 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
9610 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009611
9612 // Transform the object.
9613 obj->set_map(Map::cast(new_map));
9614 obj->map()->set_instance_descriptors(descriptors);
9615 obj->map()->set_unused_property_fields(unused_property_fields);
9616
9617 obj->set_properties(FixedArray::cast(fields));
9618 ASSERT(obj->IsJSObject());
9619
9620 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
9621 // Check that it really works.
9622 ASSERT(obj->HasFastProperties());
9623
9624 return obj;
9625}
9626
9627
9628#ifdef ENABLE_DEBUGGER_SUPPORT
9629// Check if there is a break point at this code position.
9630bool DebugInfo::HasBreakPoint(int code_position) {
9631 // Get the break point info object for this code position.
9632 Object* break_point_info = GetBreakPointInfo(code_position);
9633
9634 // If there is no break point info object or no break points in the break
9635 // point info object there is no break point at this code position.
9636 if (break_point_info->IsUndefined()) return false;
9637 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
9638}
9639
9640
9641// Get the break point info object for this code position.
9642Object* DebugInfo::GetBreakPointInfo(int code_position) {
9643 // Find the index of the break point info object for this code position.
9644 int index = GetBreakPointInfoIndex(code_position);
9645
9646 // Return the break point info object if any.
9647 if (index == kNoBreakPointInfo) return Heap::undefined_value();
9648 return BreakPointInfo::cast(break_points()->get(index));
9649}
9650
9651
9652// Clear a break point at the specified code position.
9653void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
9654 int code_position,
9655 Handle<Object> break_point_object) {
9656 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
9657 if (break_point_info->IsUndefined()) return;
9658 BreakPointInfo::ClearBreakPoint(
9659 Handle<BreakPointInfo>::cast(break_point_info),
9660 break_point_object);
9661}
9662
9663
9664void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
9665 int code_position,
9666 int source_position,
9667 int statement_position,
9668 Handle<Object> break_point_object) {
9669 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
9670 if (!break_point_info->IsUndefined()) {
9671 BreakPointInfo::SetBreakPoint(
9672 Handle<BreakPointInfo>::cast(break_point_info),
9673 break_point_object);
9674 return;
9675 }
9676
9677 // Adding a new break point for a code position which did not have any
9678 // break points before. Try to find a free slot.
9679 int index = kNoBreakPointInfo;
9680 for (int i = 0; i < debug_info->break_points()->length(); i++) {
9681 if (debug_info->break_points()->get(i)->IsUndefined()) {
9682 index = i;
9683 break;
9684 }
9685 }
9686 if (index == kNoBreakPointInfo) {
9687 // No free slot - extend break point info array.
9688 Handle<FixedArray> old_break_points =
9689 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
Steve Blocka7e24c12009-10-30 11:49:00 +00009690 Handle<FixedArray> new_break_points =
Kristian Monsen0d5e1162010-09-30 15:31:59 +01009691 Factory::NewFixedArray(old_break_points->length() +
9692 Debug::kEstimatedNofBreakPointsInFunction);
9693
9694 debug_info->set_break_points(*new_break_points);
Steve Blocka7e24c12009-10-30 11:49:00 +00009695 for (int i = 0; i < old_break_points->length(); i++) {
9696 new_break_points->set(i, old_break_points->get(i));
9697 }
9698 index = old_break_points->length();
9699 }
9700 ASSERT(index != kNoBreakPointInfo);
9701
9702 // Allocate new BreakPointInfo object and set the break point.
9703 Handle<BreakPointInfo> new_break_point_info =
9704 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
9705 new_break_point_info->set_code_position(Smi::FromInt(code_position));
9706 new_break_point_info->set_source_position(Smi::FromInt(source_position));
9707 new_break_point_info->
9708 set_statement_position(Smi::FromInt(statement_position));
9709 new_break_point_info->set_break_point_objects(Heap::undefined_value());
9710 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
9711 debug_info->break_points()->set(index, *new_break_point_info);
9712}
9713
9714
9715// Get the break point objects for a code position.
9716Object* DebugInfo::GetBreakPointObjects(int code_position) {
9717 Object* break_point_info = GetBreakPointInfo(code_position);
9718 if (break_point_info->IsUndefined()) {
9719 return Heap::undefined_value();
9720 }
9721 return BreakPointInfo::cast(break_point_info)->break_point_objects();
9722}
9723
9724
9725// Get the total number of break points.
9726int DebugInfo::GetBreakPointCount() {
9727 if (break_points()->IsUndefined()) return 0;
9728 int count = 0;
9729 for (int i = 0; i < break_points()->length(); i++) {
9730 if (!break_points()->get(i)->IsUndefined()) {
9731 BreakPointInfo* break_point_info =
9732 BreakPointInfo::cast(break_points()->get(i));
9733 count += break_point_info->GetBreakPointCount();
9734 }
9735 }
9736 return count;
9737}
9738
9739
9740Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
9741 Handle<Object> break_point_object) {
9742 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
9743 for (int i = 0; i < debug_info->break_points()->length(); i++) {
9744 if (!debug_info->break_points()->get(i)->IsUndefined()) {
9745 Handle<BreakPointInfo> break_point_info =
9746 Handle<BreakPointInfo>(BreakPointInfo::cast(
9747 debug_info->break_points()->get(i)));
9748 if (BreakPointInfo::HasBreakPointObject(break_point_info,
9749 break_point_object)) {
9750 return *break_point_info;
9751 }
9752 }
9753 }
9754 return Heap::undefined_value();
9755}
9756
9757
9758// Find the index of the break point info object for the specified code
9759// position.
9760int DebugInfo::GetBreakPointInfoIndex(int code_position) {
9761 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
9762 for (int i = 0; i < break_points()->length(); i++) {
9763 if (!break_points()->get(i)->IsUndefined()) {
9764 BreakPointInfo* break_point_info =
9765 BreakPointInfo::cast(break_points()->get(i));
9766 if (break_point_info->code_position()->value() == code_position) {
9767 return i;
9768 }
9769 }
9770 }
9771 return kNoBreakPointInfo;
9772}
9773
9774
9775// Remove the specified break point object.
9776void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
9777 Handle<Object> break_point_object) {
9778 // If there are no break points just ignore.
9779 if (break_point_info->break_point_objects()->IsUndefined()) return;
9780 // If there is a single break point clear it if it is the same.
9781 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9782 if (break_point_info->break_point_objects() == *break_point_object) {
9783 break_point_info->set_break_point_objects(Heap::undefined_value());
9784 }
9785 return;
9786 }
9787 // If there are multiple break points shrink the array
9788 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
9789 Handle<FixedArray> old_array =
9790 Handle<FixedArray>(
9791 FixedArray::cast(break_point_info->break_point_objects()));
9792 Handle<FixedArray> new_array =
9793 Factory::NewFixedArray(old_array->length() - 1);
9794 int found_count = 0;
9795 for (int i = 0; i < old_array->length(); i++) {
9796 if (old_array->get(i) == *break_point_object) {
9797 ASSERT(found_count == 0);
9798 found_count++;
9799 } else {
9800 new_array->set(i - found_count, old_array->get(i));
9801 }
9802 }
9803 // If the break point was found in the list change it.
9804 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
9805}
9806
9807
9808// Add the specified break point object.
9809void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
9810 Handle<Object> break_point_object) {
9811 // If there was no break point objects before just set it.
9812 if (break_point_info->break_point_objects()->IsUndefined()) {
9813 break_point_info->set_break_point_objects(*break_point_object);
9814 return;
9815 }
9816 // If the break point object is the same as before just ignore.
9817 if (break_point_info->break_point_objects() == *break_point_object) return;
9818 // If there was one break point object before replace with array.
9819 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9820 Handle<FixedArray> array = Factory::NewFixedArray(2);
9821 array->set(0, break_point_info->break_point_objects());
9822 array->set(1, *break_point_object);
9823 break_point_info->set_break_point_objects(*array);
9824 return;
9825 }
9826 // If there was more than one break point before extend array.
9827 Handle<FixedArray> old_array =
9828 Handle<FixedArray>(
9829 FixedArray::cast(break_point_info->break_point_objects()));
9830 Handle<FixedArray> new_array =
9831 Factory::NewFixedArray(old_array->length() + 1);
9832 for (int i = 0; i < old_array->length(); i++) {
9833 // If the break point was there before just ignore.
9834 if (old_array->get(i) == *break_point_object) return;
9835 new_array->set(i, old_array->get(i));
9836 }
9837 // Add the new break point.
9838 new_array->set(old_array->length(), *break_point_object);
9839 break_point_info->set_break_point_objects(*new_array);
9840}
9841
9842
9843bool BreakPointInfo::HasBreakPointObject(
9844 Handle<BreakPointInfo> break_point_info,
9845 Handle<Object> break_point_object) {
9846 // No break point.
9847 if (break_point_info->break_point_objects()->IsUndefined()) return false;
9848 // Single beak point.
9849 if (!break_point_info->break_point_objects()->IsFixedArray()) {
9850 return break_point_info->break_point_objects() == *break_point_object;
9851 }
9852 // Multiple break points.
9853 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
9854 for (int i = 0; i < array->length(); i++) {
9855 if (array->get(i) == *break_point_object) {
9856 return true;
9857 }
9858 }
9859 return false;
9860}
9861
9862
9863// Get the number of break points.
9864int BreakPointInfo::GetBreakPointCount() {
9865 // No break point.
9866 if (break_point_objects()->IsUndefined()) return 0;
9867 // Single beak point.
9868 if (!break_point_objects()->IsFixedArray()) return 1;
9869 // Multiple break points.
9870 return FixedArray::cast(break_point_objects())->length();
9871}
9872#endif
9873
9874
9875} } // namespace v8::internal