blob: 55fc971ed5ea390fe3e859e8b583d0ccea4aa495 [file] [log] [blame]
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +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 "bootstrapper.h"
32#include "debug.h"
33#include "execution.h"
34#include "objects-inl.h"
35#include "macro-assembler.h"
36#include "scanner.h"
37#include "scopeinfo.h"
38#include "string-stream.h"
39
mads.s.ager31e71382008-08-13 09:32:07 +000040#ifdef ENABLE_DISASSEMBLER
41#include "disassembler.h"
42#endif
43
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044namespace v8 { namespace internal {
45
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000046// Getters and setters are stored in a fixed array property. These are
47// constants for their indices.
48const int kGetterIndex = 0;
49const int kSetterIndex = 1;
50
51bool Object::IsInstanceOf(FunctionTemplateInfo* expected) {
52 // There is a constraint on the object; check
53 if (!this->IsJSObject()) return false;
54 // Fetch the constructor function of the object
55 Object* cons_obj = JSObject::cast(this)->map()->constructor();
56 if (!cons_obj->IsJSFunction()) return false;
57 JSFunction* fun = JSFunction::cast(cons_obj);
58 // Iterate through the chain of inheriting function templates to
59 // see if the required one occurs.
60 for (Object* type = fun->shared()->function_data();
61 type->IsFunctionTemplateInfo();
62 type = FunctionTemplateInfo::cast(type)->parent_template()) {
63 if (type == expected) return true;
64 }
65 // Didn't find the required type in the inheritance chain.
66 return false;
67}
68
69
70static Object* CreateJSValue(JSFunction* constructor, Object* value) {
71 Object* result = Heap::AllocateJSObject(constructor);
72 if (result->IsFailure()) return result;
73 JSValue::cast(result)->set_value(value);
74 return result;
75}
76
77
78Object* Object::ToObject(Context* global_context) {
79 if (IsNumber()) {
80 return CreateJSValue(global_context->number_function(), this);
81 } else if (IsBoolean()) {
82 return CreateJSValue(global_context->boolean_function(), this);
83 } else if (IsString()) {
84 return CreateJSValue(global_context->string_function(), this);
85 }
86 ASSERT(IsJSObject());
87 return this;
88}
89
90
91Object* Object::ToObject() {
92 Context* global_context = Top::context()->global_context();
93 if (IsJSObject()) {
94 return this;
95 } else if (IsNumber()) {
96 return CreateJSValue(global_context->number_function(), this);
97 } else if (IsBoolean()) {
98 return CreateJSValue(global_context->boolean_function(), this);
99 } else if (IsString()) {
100 return CreateJSValue(global_context->string_function(), this);
101 }
102
103 // Throw a type error.
104 return Failure::InternalError();
105}
106
107
108Object* Object::ToBoolean() {
109 if (IsTrue()) return Heap::true_value();
110 if (IsFalse()) return Heap::false_value();
111 if (IsSmi()) {
112 return Heap::ToBoolean(Smi::cast(this)->value() != 0);
113 }
114 if (IsUndefined() || IsNull()) return Heap::false_value();
115 // Undetectable object is false
116 if (IsUndetectableObject()) {
117 return Heap::false_value();
118 }
119 if (IsString()) {
120 return Heap::ToBoolean(String::cast(this)->length() != 0);
121 }
122 if (IsHeapNumber()) {
123 return HeapNumber::cast(this)->HeapNumberToBoolean();
124 }
125 return Heap::true_value();
126}
127
128
129void Object::Lookup(String* name, LookupResult* result) {
130 if (IsJSObject()) return JSObject::cast(this)->Lookup(name, result);
131 Object* holder = NULL;
132 Context* global_context = Top::context()->global_context();
133 if (IsString()) {
134 holder = global_context->string_function()->instance_prototype();
135 } else if (IsNumber()) {
136 holder = global_context->number_function()->instance_prototype();
137 } else if (IsBoolean()) {
138 holder = global_context->boolean_function()->instance_prototype();
139 }
140 ASSERT(holder != NULL); // cannot handle null or undefined.
141 JSObject::cast(holder)->Lookup(name, result);
142}
143
144
145Object* Object::GetPropertyWithReceiver(Object* receiver,
146 String* name,
147 PropertyAttributes* attributes) {
148 LookupResult result;
149 Lookup(name, &result);
ager@chromium.org8bb60582008-12-11 12:02:20 +0000150 Object* value = GetProperty(receiver, &result, name, attributes);
151 ASSERT(*attributes <= ABSENT);
152 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000153}
154
155
156Object* Object::GetPropertyWithCallback(Object* receiver,
157 Object* structure,
158 String* name,
159 Object* holder) {
160 // To accommodate both the old and the new api we switch on the
161 // data structure used to store the callbacks. Eventually proxy
162 // callbacks should be phased out.
163 if (structure->IsProxy()) {
164 AccessorDescriptor* callback =
165 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
166 Object* value = (callback->getter)(receiver, callback->data);
167 RETURN_IF_SCHEDULED_EXCEPTION();
168 return value;
169 }
170
171 // api style callbacks.
172 if (structure->IsAccessorInfo()) {
173 AccessorInfo* data = AccessorInfo::cast(structure);
174 Object* fun_obj = data->getter();
175 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
176 HandleScope scope;
177 Handle<JSObject> self(JSObject::cast(receiver));
178 Handle<JSObject> holder_handle(JSObject::cast(holder));
179 Handle<String> key(name);
180 Handle<Object> fun_data(data->data());
181 LOG(ApiNamedPropertyAccess("load", *self, name));
182 v8::AccessorInfo info(v8::Utils::ToLocal(self),
183 v8::Utils::ToLocal(fun_data),
184 v8::Utils::ToLocal(holder_handle));
185 v8::Handle<v8::Value> result;
186 {
187 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +0000188 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000189 result = call_fun(v8::Utils::ToLocal(key), info);
190 }
191 RETURN_IF_SCHEDULED_EXCEPTION();
192 if (result.IsEmpty()) return Heap::undefined_value();
193 return *v8::Utils::OpenHandle(*result);
194 }
195
196 // __defineGetter__ callback
197 if (structure->IsFixedArray()) {
198 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
199 if (getter->IsJSFunction()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000200 return Object::GetPropertyWithDefinedGetter(receiver,
201 JSFunction::cast(getter));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000202 }
203 // Getter is not a function.
204 return Heap::undefined_value();
205 }
206
207 UNREACHABLE();
208 return 0;
209}
210
211
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000212Object* Object::GetPropertyWithDefinedGetter(Object* receiver,
213 JSFunction* getter) {
214 HandleScope scope;
215 Handle<JSFunction> fun(JSFunction::cast(getter));
216 Handle<Object> self(receiver);
217 bool has_pending_exception;
218 Handle<Object> result =
219 Execution::Call(fun, self, 0, NULL, &has_pending_exception);
220 // Check for pending exception and return the result.
221 if (has_pending_exception) return Failure::Exception();
222 return *result;
223}
224
225
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000226// Only deal with CALLBACKS and INTERCEPTOR
ager@chromium.org8bb60582008-12-11 12:02:20 +0000227Object* JSObject::GetPropertyWithFailedAccessCheck(
228 Object* receiver,
229 LookupResult* result,
230 String* name,
231 PropertyAttributes* attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000232 if (result->IsValid()) {
233 switch (result->type()) {
234 case CALLBACKS: {
235 // Only allow API accessors.
236 Object* obj = result->GetCallbackObject();
237 if (obj->IsAccessorInfo()) {
238 AccessorInfo* info = AccessorInfo::cast(obj);
239 if (info->all_can_read()) {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000240 *attributes = result->GetAttributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000241 return GetPropertyWithCallback(receiver,
242 result->GetCallbackObject(),
243 name,
244 result->holder());
245 }
246 }
247 break;
248 }
249 case NORMAL:
250 case FIELD:
251 case CONSTANT_FUNCTION: {
252 // Search ALL_CAN_READ accessors in prototype chain.
253 LookupResult r;
254 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
255 if (r.IsValid()) {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000256 return GetPropertyWithFailedAccessCheck(receiver,
257 &r,
258 name,
259 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000260 }
261 break;
262 }
263 case INTERCEPTOR: {
264 // If the object has an interceptor, try real named properties.
265 // No access check in GetPropertyAttributeWithInterceptor.
266 LookupResult r;
267 result->holder()->LookupRealNamedProperty(name, &r);
268 if (r.IsValid()) {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000269 return GetPropertyWithFailedAccessCheck(receiver,
270 &r,
271 name,
272 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000274 }
275 default: {
276 break;
277 }
278 }
279 }
280
ager@chromium.org8bb60582008-12-11 12:02:20 +0000281 // No accessible property found.
282 *attributes = ABSENT;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000283 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
284 return Heap::undefined_value();
285}
286
287
ager@chromium.org870a0b62008-11-04 11:43:05 +0000288PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
289 Object* receiver,
290 LookupResult* result,
291 String* name,
292 bool continue_search) {
293 if (result->IsValid()) {
294 switch (result->type()) {
295 case CALLBACKS: {
296 // Only allow API accessors.
297 Object* obj = result->GetCallbackObject();
298 if (obj->IsAccessorInfo()) {
299 AccessorInfo* info = AccessorInfo::cast(obj);
300 if (info->all_can_read()) {
301 return result->GetAttributes();
302 }
303 }
304 break;
305 }
306
307 case NORMAL:
308 case FIELD:
309 case CONSTANT_FUNCTION: {
310 if (!continue_search) break;
311 // Search ALL_CAN_READ accessors in prototype chain.
312 LookupResult r;
313 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
314 if (r.IsValid()) {
315 return GetPropertyAttributeWithFailedAccessCheck(receiver,
316 &r,
317 name,
318 continue_search);
319 }
320 break;
321 }
322
323 case INTERCEPTOR: {
324 // If the object has an interceptor, try real named properties.
325 // No access check in GetPropertyAttributeWithInterceptor.
326 LookupResult r;
327 if (continue_search) {
328 result->holder()->LookupRealNamedProperty(name, &r);
329 } else {
330 result->holder()->LocalLookupRealNamedProperty(name, &r);
331 }
332 if (r.IsValid()) {
333 return GetPropertyAttributeWithFailedAccessCheck(receiver,
334 &r,
335 name,
336 continue_search);
337 }
338 break;
339 }
340
341 default: {
342 break;
343 }
344 }
345 }
346
347 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
348 return ABSENT;
349}
350
351
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000352Object* JSObject::GetLazyProperty(Object* receiver,
353 LookupResult* result,
354 String* name,
355 PropertyAttributes* attributes) {
356 HandleScope scope;
357 Handle<Object> this_handle(this);
358 Handle<Object> receiver_handle(receiver);
359 Handle<String> name_handle(name);
360 bool pending_exception;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +0000361 LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000362 &pending_exception);
363 if (pending_exception) return Failure::Exception();
364 return this_handle->GetPropertyWithReceiver(*receiver_handle,
365 *name_handle,
366 attributes);
367}
368
369
370Object* JSObject::SetLazyProperty(LookupResult* result,
371 String* name,
372 Object* value,
373 PropertyAttributes attributes) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000374 ASSERT(!IsJSGlobalProxy());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000375 HandleScope scope;
376 Handle<JSObject> this_handle(this);
377 Handle<String> name_handle(name);
378 Handle<Object> value_handle(value);
379 bool pending_exception;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +0000380 LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000381 &pending_exception);
382 if (pending_exception) return Failure::Exception();
383 return this_handle->SetProperty(*name_handle, *value_handle, attributes);
384}
385
386
387Object* JSObject::DeleteLazyProperty(LookupResult* result, String* name) {
388 HandleScope scope;
389 Handle<JSObject> this_handle(this);
390 Handle<String> name_handle(name);
391 bool pending_exception;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +0000392 LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000393 &pending_exception);
394 if (pending_exception) return Failure::Exception();
395 return this_handle->DeleteProperty(*name_handle);
396}
397
398
399Object* Object::GetProperty(Object* receiver,
400 LookupResult* result,
401 String* name,
402 PropertyAttributes* attributes) {
403 // Make sure that the top context does not change when doing
404 // callbacks or interceptor calls.
405 AssertNoContextChange ncc;
406
407 // Traverse the prototype chain from the current object (this) to
408 // the holder and check for access rights. This avoid traversing the
409 // objects more than once in case of interceptors, because the
410 // holder will always be the interceptor holder and the search may
411 // only continue with a current object just after the interceptor
412 // holder in the prototype chain.
413 Object* last = result->IsValid() ? result->holder() : Heap::null_value();
414 for (Object* current = this; true; current = current->GetPrototype()) {
415 if (current->IsAccessCheckNeeded()) {
416 // Check if we're allowed to read from the current object. Note
417 // that even though we may not actually end up loading the named
418 // property from the current object, we still check that we have
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000419 // access to it.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000420 JSObject* checked = JSObject::cast(current);
421 if (!Top::MayNamedAccess(checked, name, v8::ACCESS_GET)) {
422 return checked->GetPropertyWithFailedAccessCheck(receiver,
423 result,
ager@chromium.org8bb60582008-12-11 12:02:20 +0000424 name,
425 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000426 }
427 }
428 // Stop traversing the chain once we reach the last object in the
429 // chain; either the holder of the result or null in case of an
430 // absent property.
431 if (current == last) break;
432 }
433
kasper.lund44510672008-07-25 07:37:58 +0000434 if (!result->IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000435 *attributes = ABSENT;
436 return Heap::undefined_value();
437 }
438 *attributes = result->GetAttributes();
439 if (!result->IsLoaded()) {
440 return JSObject::cast(this)->GetLazyProperty(receiver,
441 result,
442 name,
443 attributes);
444 }
445 Object* value;
446 JSObject* holder = result->holder();
447 switch (result->type()) {
448 case NORMAL:
449 value =
450 holder->property_dictionary()->ValueAt(result->GetDictionaryEntry());
451 ASSERT(!value->IsTheHole() || result->IsReadOnly());
452 return value->IsTheHole() ? Heap::undefined_value() : value;
453 case FIELD:
ager@chromium.org7c537e22008-10-16 08:43:32 +0000454 value = holder->FastPropertyAt(result->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000455 ASSERT(!value->IsTheHole() || result->IsReadOnly());
456 return value->IsTheHole() ? Heap::undefined_value() : value;
457 case CONSTANT_FUNCTION:
458 return result->GetConstantFunction();
459 case CALLBACKS:
460 return GetPropertyWithCallback(receiver,
461 result->GetCallbackObject(),
462 name,
463 holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000464 case INTERCEPTOR: {
465 JSObject* recvr = JSObject::cast(receiver);
466 return holder->GetPropertyWithInterceptor(recvr, name, attributes);
467 }
kasper.lund44510672008-07-25 07:37:58 +0000468 default:
469 UNREACHABLE();
470 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000471 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000472}
473
474
475Object* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
476 // Non-JS objects do not have integer indexed properties.
477 if (!IsJSObject()) return Heap::undefined_value();
478 return JSObject::cast(this)->GetElementWithReceiver(JSObject::cast(receiver),
479 index);
480}
481
482
483Object* Object::GetPrototype() {
484 // The object is either a number, a string, a boolean, or a real JS object.
485 if (IsJSObject()) return JSObject::cast(this)->map()->prototype();
486 Context* context = Top::context()->global_context();
487
488 if (IsNumber()) return context->number_function()->instance_prototype();
489 if (IsString()) return context->string_function()->instance_prototype();
490 if (IsBoolean()) {
491 return context->boolean_function()->instance_prototype();
492 } else {
493 return Heap::null_value();
494 }
495}
496
497
498void Object::ShortPrint() {
499 HeapStringAllocator allocator;
500 StringStream accumulator(&allocator);
501 ShortPrint(&accumulator);
502 accumulator.OutputToStdOut();
503}
504
505
506void Object::ShortPrint(StringStream* accumulator) {
507 if (IsSmi()) {
508 Smi::cast(this)->SmiPrint(accumulator);
509 } else if (IsFailure()) {
510 Failure::cast(this)->FailurePrint(accumulator);
511 } else {
512 HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
513 }
514}
515
516
517void Smi::SmiPrint() {
518 PrintF("%d", value());
519}
520
521
522void Smi::SmiPrint(StringStream* accumulator) {
523 accumulator->Add("%d", value());
524}
525
526
527void Failure::FailurePrint(StringStream* accumulator) {
528 accumulator->Add("Failure(%d)", value());
529}
530
531
532void Failure::FailurePrint() {
533 PrintF("Failure(%d)", value());
534}
535
536
537Failure* Failure::RetryAfterGC(int requested_bytes, AllocationSpace space) {
538 ASSERT((space & ~kSpaceTagMask) == 0);
539 int requested = requested_bytes >> kObjectAlignmentBits;
540 int value = (requested << kSpaceTagSize) | space;
541 // We can't very well allocate a heap number in this situation, and if the
542 // requested memory is so large it seems reasonable to say that this is an
543 // out of memory situation. This fixes a crash in
544 // js1_5/Regress/regress-303213.js.
545 if (value >> kSpaceTagSize != requested ||
546 !Smi::IsValid(value) ||
547 value != ((value << kFailureTypeTagSize) >> kFailureTypeTagSize) ||
548 !Smi::IsValid(value << kFailureTypeTagSize)) {
549 Top::context()->mark_out_of_memory();
550 return Failure::OutOfMemoryException();
551 }
552 return Construct(RETRY_AFTER_GC, value);
553}
554
555
556// Should a word be prefixed by 'a' or 'an' in order to read naturally in
557// English? Returns false for non-ASCII or words that don't start with
558// a capital letter. The a/an rule follows pronunciation in English.
559// We don't use the BBC's overcorrect "an historic occasion" though if
560// you speak a dialect you may well say "an 'istoric occasion".
561static bool AnWord(String* str) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000562 if (str->length() == 0) return false; // A nothing.
563 int c0 = str->Get(0);
564 int c1 = str->length() > 1 ? str->Get(1) : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000565 if (c0 == 'U') {
566 if (c1 > 'Z') {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000567 return true; // An Umpire, but a UTF8String, a U.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000568 }
569 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000570 return true; // An Ape, an ABCBook.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000571 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
572 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
573 c0 == 'S' || c0 == 'X')) {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000574 return true; // An MP3File, an M.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000575 }
576 return false;
577}
578
579
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000580Object* String::TryFlatten() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000581#ifdef DEBUG
582 // Do not attempt to flatten in debug mode when allocation is not
583 // allowed. This is to avoid an assertion failure when allocating.
584 // Flattening strings is the only case where we always allow
585 // allocation because no GC is performed if the allocation fails.
586 if (!Heap::IsAllocationAllowed()) return this;
587#endif
588
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000589 switch (StringShape(this).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000590 case kSlicedStringTag: {
591 SlicedString* ss = SlicedString::cast(this);
592 // The SlicedString constructor should ensure that there are no
593 // SlicedStrings that are constructed directly on top of other
594 // SlicedStrings.
ager@chromium.org870a0b62008-11-04 11:43:05 +0000595 String* buf = ss->buffer();
596 ASSERT(!buf->IsSlicedString());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000597 Object* ok = buf->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000598 if (ok->IsFailure()) return ok;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000599 // Under certain circumstances (TryFlattenIfNotFlat fails in
600 // String::Slice) we can have a cons string under a slice.
601 // In this case we need to get the flat string out of the cons!
ager@chromium.org870a0b62008-11-04 11:43:05 +0000602 if (StringShape(String::cast(ok)).IsCons()) {
kasperl@chromium.org7ee65c92008-10-21 08:45:58 +0000603 ss->set_buffer(ConsString::cast(ok)->first());
604 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000605 ASSERT(StringShape(this).IsAsciiRepresentation() ==
606 StringShape(ss->buffer()).IsAsciiRepresentation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000607 return this;
608 }
609 case kConsStringTag: {
610 ConsString* cs = ConsString::cast(this);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000611 if (cs->second()->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000612 return this;
613 }
614 // There's little point in putting the flat string in new space if the
615 // cons string is in old space. It can never get GCed until there is
616 // an old space GC.
617 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED : TENURED;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000618 int len = length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000619 Object* object;
620 String* result;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000621 if (StringShape(this).IsAsciiRepresentation()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000622 object = Heap::AllocateRawAsciiString(len, tenure);
623 if (object->IsFailure()) return object;
624 result = String::cast(object);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000625 String* first = cs->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000626 int first_length = first->length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000627 char* dest = SeqAsciiString::cast(result)->GetChars();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000628 WriteToFlat(first, dest, 0, first_length);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000629 String* second = cs->second();
630 WriteToFlat(second,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000631 dest + first_length,
632 0,
633 len - first_length);
634 } else {
635 object = Heap::AllocateRawTwoByteString(len, tenure);
636 if (object->IsFailure()) return object;
637 result = String::cast(object);
638 uc16* dest = SeqTwoByteString::cast(result)->GetChars();
ager@chromium.org870a0b62008-11-04 11:43:05 +0000639 String* first = cs->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000640 int first_length = first->length();
641 WriteToFlat(first, dest, 0, first_length);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000642 String* second = cs->second();
643 WriteToFlat(second,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000644 dest + first_length,
645 0,
646 len - first_length);
647 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000648 cs->set_first(result);
649 cs->set_second(Heap::empty_string());
650 return this;
651 }
652 default:
653 return this;
654 }
655}
656
657
ager@chromium.org6f10e412009-02-13 10:11:16 +0000658bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
659#ifdef DEBUG
660 { // NOLINT (presubmit.py gets confused about if and braces)
661 // Assert that the resource and the string are equivalent.
662 ASSERT(static_cast<size_t>(this->length()) == resource->length());
663 SmartPointer<uc16> smart_chars = this->ToWideCString();
664 ASSERT(memcmp(*smart_chars,
665 resource->data(),
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +0000666 resource->length() * sizeof(**smart_chars)) == 0);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000667 }
668#endif // DEBUG
669
670 int size = this->Size(); // Byte size of the original string.
671 if (size < ExternalString::kSize) {
672 // The string is too small to fit an external String in its place. This can
673 // only happen for zero length strings.
674 return false;
675 }
676 ASSERT(size >= ExternalString::kSize);
677 bool is_symbol = this->IsSymbol();
678 int length = this->length();
679
680 // Morph the object to an external string by adjusting the map and
681 // reinitializing the fields.
682 this->set_map(ExternalTwoByteString::StringMap(length));
683 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
684 self->set_length(length);
685 self->set_resource(resource);
686 // Additionally make the object into an external symbol if the original string
687 // was a symbol to start with.
688 if (is_symbol) {
689 self->Hash(); // Force regeneration of the hash value.
690 // Now morph this external string into a external symbol.
691 self->set_map(ExternalTwoByteString::SymbolMap(length));
692 }
693
694 // Fill the remainder of the string with dead wood.
695 int new_size = this->Size(); // Byte size of the external String object.
696 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
697 return true;
698}
699
700
701bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
702#ifdef DEBUG
703 { // NOLINT (presubmit.py gets confused about if and braces)
704 // Assert that the resource and the string are equivalent.
705 ASSERT(static_cast<size_t>(this->length()) == resource->length());
706 SmartPointer<char> smart_chars = this->ToCString();
707 ASSERT(memcmp(*smart_chars,
708 resource->data(),
709 resource->length()*sizeof(**smart_chars)) == 0);
710 }
711#endif // DEBUG
712
713 int size = this->Size(); // Byte size of the original string.
714 if (size < ExternalString::kSize) {
715 // The string is too small to fit an external String in its place. This can
716 // only happen for zero length strings.
717 return false;
718 }
719 ASSERT(size >= ExternalString::kSize);
720 bool is_symbol = this->IsSymbol();
721 int length = this->length();
722
723 // Morph the object to an external string by adjusting the map and
724 // reinitializing the fields.
725 this->set_map(ExternalAsciiString::StringMap(length));
726 ExternalAsciiString* self = ExternalAsciiString::cast(this);
727 self->set_length(length);
728 self->set_resource(resource);
729 // Additionally make the object into an external symbol if the original string
730 // was a symbol to start with.
731 if (is_symbol) {
732 self->Hash(); // Force regeneration of the hash value.
733 // Now morph this external string into a external symbol.
734 self->set_map(ExternalAsciiString::SymbolMap(length));
735 }
736
737 // Fill the remainder of the string with dead wood.
738 int new_size = this->Size(); // Byte size of the external String object.
739 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
740 return true;
741}
742
743
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000744void String::StringShortPrint(StringStream* accumulator) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000745 int len = length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000746 if (len > kMaxMediumStringSize) {
747 accumulator->Add("<Very long string[%u]>", len);
748 return;
749 }
750
751 if (!LooksValid()) {
752 accumulator->Add("<Invalid String>");
753 return;
754 }
755
756 StringInputBuffer buf(this);
757
758 bool truncated = false;
kasper.lund7276f142008-07-30 08:49:36 +0000759 if (len > kMaxShortPrintLength) {
760 len = kMaxShortPrintLength;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000761 truncated = true;
762 }
763 bool ascii = true;
764 for (int i = 0; i < len; i++) {
765 int c = buf.GetNext();
766
767 if (c < 32 || c >= 127) {
768 ascii = false;
769 }
770 }
771 buf.Reset(this);
772 if (ascii) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000773 accumulator->Add("<String[%u]: ", length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000774 for (int i = 0; i < len; i++) {
775 accumulator->Put(buf.GetNext());
776 }
777 accumulator->Put('>');
778 } else {
779 // Backslash indicates that the string contains control
780 // characters and that backslashes are therefore escaped.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000781 accumulator->Add("<String[%u]\\: ", length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000782 for (int i = 0; i < len; i++) {
783 int c = buf.GetNext();
784 if (c == '\n') {
785 accumulator->Add("\\n");
786 } else if (c == '\r') {
787 accumulator->Add("\\r");
788 } else if (c == '\\') {
789 accumulator->Add("\\\\");
790 } else if (c < 32 || c > 126) {
791 accumulator->Add("\\x%02x", c);
792 } else {
793 accumulator->Put(c);
794 }
795 }
796 if (truncated) {
797 accumulator->Put('.');
798 accumulator->Put('.');
799 accumulator->Put('.');
800 }
801 accumulator->Put('>');
802 }
803 return;
804}
805
806
807void JSObject::JSObjectShortPrint(StringStream* accumulator) {
808 switch (map()->instance_type()) {
809 case JS_ARRAY_TYPE: {
810 double length = JSArray::cast(this)->length()->Number();
811 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
812 break;
813 }
ager@chromium.org236ad962008-09-25 09:45:57 +0000814 case JS_REGEXP_TYPE: {
815 accumulator->Add("<JS RegExp>");
816 break;
817 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000818 case JS_FUNCTION_TYPE: {
819 Object* fun_name = JSFunction::cast(this)->shared()->name();
820 bool printed = false;
821 if (fun_name->IsString()) {
822 String* str = String::cast(fun_name);
823 if (str->length() > 0) {
824 accumulator->Add("<JS Function ");
825 accumulator->Put(str);
826 accumulator->Put('>');
827 printed = true;
828 }
829 }
830 if (!printed) {
831 accumulator->Add("<JS Function>");
832 }
833 break;
834 }
835 // All other JSObjects are rather similar to each other (JSObject,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000836 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837 default: {
838 Object* constructor = map()->constructor();
839 bool printed = false;
840 if (constructor->IsHeapObject() &&
841 !Heap::Contains(HeapObject::cast(constructor))) {
842 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
843 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000844 bool global_object = IsJSGlobalProxy();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000845 if (constructor->IsJSFunction()) {
846 if (!Heap::Contains(JSFunction::cast(constructor)->shared())) {
847 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
848 } else {
849 Object* constructor_name =
850 JSFunction::cast(constructor)->shared()->name();
851 if (constructor_name->IsString()) {
852 String* str = String::cast(constructor_name);
853 if (str->length() > 0) {
854 bool vowel = AnWord(str);
855 accumulator->Add("<%sa%s ",
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000856 global_object ? "Global Object: " : "",
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000857 vowel ? "n" : "");
858 accumulator->Put(str);
859 accumulator->Put('>');
860 printed = true;
861 }
862 }
863 }
864 }
865 if (!printed) {
866 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
867 }
868 }
869 if (IsJSValue()) {
870 accumulator->Add(" value = ");
871 JSValue::cast(this)->value()->ShortPrint(accumulator);
872 }
873 accumulator->Put('>');
874 break;
875 }
876 }
877}
878
879
880void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
881 // if (!Heap::InNewSpace(this)) PrintF("*", this);
882 if (!Heap::Contains(this)) {
883 accumulator->Add("!!!INVALID POINTER!!!");
884 return;
885 }
886 if (!Heap::Contains(map())) {
887 accumulator->Add("!!!INVALID MAP!!!");
888 return;
889 }
890
891 accumulator->Add("%p ", this);
892
893 if (IsString()) {
894 String::cast(this)->StringShortPrint(accumulator);
895 return;
896 }
897 if (IsJSObject()) {
898 JSObject::cast(this)->JSObjectShortPrint(accumulator);
899 return;
900 }
901 switch (map()->instance_type()) {
902 case MAP_TYPE:
903 accumulator->Add("<Map>");
904 break;
905 case FIXED_ARRAY_TYPE:
906 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
907 break;
908 case BYTE_ARRAY_TYPE:
909 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
910 break;
911 case SHARED_FUNCTION_INFO_TYPE:
912 accumulator->Add("<SharedFunctionInfo>");
913 break;
914#define MAKE_STRUCT_CASE(NAME, Name, name) \
915 case NAME##_TYPE: \
ager@chromium.org381abbb2009-02-25 13:23:22 +0000916 accumulator->Put('<'); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000917 accumulator->Add(#Name); \
ager@chromium.org381abbb2009-02-25 13:23:22 +0000918 accumulator->Put('>'); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000919 break;
920 STRUCT_LIST(MAKE_STRUCT_CASE)
921#undef MAKE_STRUCT_CASE
922 case CODE_TYPE:
923 accumulator->Add("<Code>");
924 break;
925 case ODDBALL_TYPE: {
926 if (IsUndefined())
927 accumulator->Add("<undefined>");
928 else if (IsTheHole())
929 accumulator->Add("<the hole>");
930 else if (IsNull())
931 accumulator->Add("<null>");
932 else if (IsTrue())
933 accumulator->Add("<true>");
934 else if (IsFalse())
935 accumulator->Add("<false>");
936 else
937 accumulator->Add("<Odd Oddball>");
938 break;
939 }
940 case HEAP_NUMBER_TYPE:
941 accumulator->Add("<Number: ");
942 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
943 accumulator->Put('>');
944 break;
945 case PROXY_TYPE:
946 accumulator->Add("<Proxy>");
947 break;
948 default:
949 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
950 break;
951 }
952}
953
954
955int HeapObject::SlowSizeFromMap(Map* map) {
956 // Avoid calling functions such as FixedArray::cast during GC, which
957 // read map pointer of this object again.
958 InstanceType instance_type = map->instance_type();
959
960 if (instance_type < FIRST_NONSTRING_TYPE
ager@chromium.org870a0b62008-11-04 11:43:05 +0000961 && (StringShape(instance_type).IsSequential())) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000962 if (StringShape(instance_type).IsAsciiRepresentation()) {
963 SeqAsciiString* seq_ascii_this = reinterpret_cast<SeqAsciiString*>(this);
964 return seq_ascii_this->SeqAsciiStringSize(instance_type);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000965 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000966 SeqTwoByteString* self = reinterpret_cast<SeqTwoByteString*>(this);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000967 return self->SeqTwoByteStringSize(instance_type);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000968 }
969 }
970
971 switch (instance_type) {
972 case FIXED_ARRAY_TYPE:
973 return reinterpret_cast<FixedArray*>(this)->FixedArraySize();
974 case BYTE_ARRAY_TYPE:
975 return reinterpret_cast<ByteArray*>(this)->ByteArraySize();
976 case CODE_TYPE:
977 return reinterpret_cast<Code*>(this)->CodeSize();
978 case MAP_TYPE:
979 return Map::kSize;
980 default:
981 return map->instance_size();
982 }
983}
984
985
986void HeapObject::Iterate(ObjectVisitor* v) {
987 // Handle header
988 IteratePointer(v, kMapOffset);
989 // Handle object body
990 Map* m = map();
991 IterateBody(m->instance_type(), SizeFromMap(m), v);
992}
993
994
995void HeapObject::IterateBody(InstanceType type, int object_size,
996 ObjectVisitor* v) {
997 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
998 // During GC, the map pointer field is encoded.
999 if (type < FIRST_NONSTRING_TYPE) {
1000 switch (type & kStringRepresentationMask) {
1001 case kSeqStringTag:
1002 break;
1003 case kConsStringTag:
1004 reinterpret_cast<ConsString*>(this)->ConsStringIterateBody(v);
1005 break;
1006 case kSlicedStringTag:
1007 reinterpret_cast<SlicedString*>(this)->SlicedStringIterateBody(v);
1008 break;
1009 }
1010 return;
1011 }
1012
1013 switch (type) {
1014 case FIXED_ARRAY_TYPE:
1015 reinterpret_cast<FixedArray*>(this)->FixedArrayIterateBody(v);
1016 break;
1017 case JS_OBJECT_TYPE:
ager@chromium.org32912102009-01-16 10:38:43 +00001018 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001019 case JS_VALUE_TYPE:
1020 case JS_ARRAY_TYPE:
ager@chromium.org236ad962008-09-25 09:45:57 +00001021 case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001022 case JS_FUNCTION_TYPE:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001023 case JS_GLOBAL_PROXY_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001024 case JS_GLOBAL_OBJECT_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001025 case JS_BUILTINS_OBJECT_TYPE:
1026 reinterpret_cast<JSObject*>(this)->JSObjectIterateBody(object_size, v);
1027 break;
1028 case ODDBALL_TYPE:
1029 reinterpret_cast<Oddball*>(this)->OddballIterateBody(v);
1030 break;
1031 case PROXY_TYPE:
1032 reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
1033 break;
1034 case MAP_TYPE:
1035 reinterpret_cast<Map*>(this)->MapIterateBody(v);
1036 break;
1037 case CODE_TYPE:
1038 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1039 break;
1040 case HEAP_NUMBER_TYPE:
1041 case FILLER_TYPE:
1042 case BYTE_ARRAY_TYPE:
1043 break;
1044 case SHARED_FUNCTION_INFO_TYPE: {
1045 SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(this);
1046 shared->SharedFunctionInfoIterateBody(v);
1047 break;
1048 }
1049#define MAKE_STRUCT_CASE(NAME, Name, name) \
1050 case NAME##_TYPE:
1051 STRUCT_LIST(MAKE_STRUCT_CASE)
1052#undef MAKE_STRUCT_CASE
1053 IterateStructBody(object_size, v);
1054 break;
1055 default:
1056 PrintF("Unknown type: %d\n", type);
1057 UNREACHABLE();
1058 }
1059}
1060
1061
1062void HeapObject::IterateStructBody(int object_size, ObjectVisitor* v) {
ager@chromium.org236ad962008-09-25 09:45:57 +00001063 IteratePointers(v, HeapObject::kHeaderSize, object_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001064}
1065
1066
1067Object* HeapNumber::HeapNumberToBoolean() {
1068 // NaN, +0, and -0 should return the false object
1069 switch (fpclassify(value())) {
1070 case FP_NAN: // fall through
1071 case FP_ZERO: return Heap::false_value();
1072 default: return Heap::true_value();
1073 }
1074}
1075
1076
1077void HeapNumber::HeapNumberPrint() {
1078 PrintF("%.16g", Number());
1079}
1080
1081
1082void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1083 // The Windows version of vsnprintf can allocate when printing a %g string
1084 // into a buffer that may not be big enough. We don't want random memory
1085 // allocation when producing post-crash stack traces, so we print into a
1086 // buffer that is plenty big enough for any floating point number, then
1087 // print that using vsnprintf (which may truncate but never allocate if
1088 // there is no more space in the buffer).
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001089 EmbeddedVector<char, 100> buffer;
1090 OS::SNPrintF(buffer, "%.16g", Number());
1091 accumulator->Add("%s", buffer.start());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001092}
1093
1094
1095String* JSObject::class_name() {
1096 if (IsJSFunction()) return Heap::function_class_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001097 if (map()->constructor()->IsJSFunction()) {
1098 JSFunction* constructor = JSFunction::cast(map()->constructor());
1099 return String::cast(constructor->shared()->instance_class_name());
1100 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001101 // If the constructor is not present, return "Object".
1102 return Heap::Object_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001103}
1104
1105
1106void JSObject::JSObjectIterateBody(int object_size, ObjectVisitor* v) {
1107 // Iterate over all fields in the body. Assumes all are Object*.
1108 IteratePointers(v, kPropertiesOffset, object_size);
1109}
1110
1111
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001112Object* JSObject::AddFastPropertyUsingMap(Map* new_map,
1113 String* name,
1114 Object* value) {
1115 int index = new_map->PropertyIndexFor(name);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001116 if (map()->unused_property_fields() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001117 ASSERT(map()->unused_property_fields() == 0);
1118 int new_unused = new_map->unused_property_fields();
1119 Object* values =
1120 properties()->CopySize(properties()->length() + new_unused + 1);
1121 if (values->IsFailure()) return values;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001122 set_properties(FixedArray::cast(values));
1123 }
1124 set_map(new_map);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001125 return FastPropertyAtPut(index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001126}
1127
1128
1129Object* JSObject::AddFastProperty(String* name,
1130 Object* value,
1131 PropertyAttributes attributes) {
ager@chromium.org8c51fc92009-04-22 11:54:55 +00001132 // Normalize the object if the name is an actual string (not the
1133 // hidden symbols) and is not a real identifier.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001134 StringInputBuffer buffer(name);
ager@chromium.org8c51fc92009-04-22 11:54:55 +00001135 if (!Scanner::IsIdentifier(&buffer) && name != Heap::hidden_symbol()) {
ager@chromium.org32912102009-01-16 10:38:43 +00001136 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001137 if (obj->IsFailure()) return obj;
1138 return AddSlowProperty(name, value, attributes);
1139 }
1140
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001141 DescriptorArray* old_descriptors = map()->instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001142 // Compute the new index for new field.
1143 int index = map()->NextFreePropertyIndex();
1144
1145 // Allocate new instance descriptors with (name, index) added
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001146 FieldDescriptor new_field(name, index, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001147 Object* new_descriptors =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001148 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001149 if (new_descriptors->IsFailure()) return new_descriptors;
1150
1151 // Only allow map transition if the object's map is NOT equal to the
1152 // global object_function's map and there is not a transition for name.
1153 bool allow_map_transition =
mads.s.ager31e71382008-08-13 09:32:07 +00001154 !old_descriptors->Contains(name) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001155 (Top::context()->global_context()->object_function()->map() != map());
1156
ager@chromium.org7c537e22008-10-16 08:43:32 +00001157 ASSERT(index < map()->inobject_properties() ||
1158 (index - map()->inobject_properties()) < properties()->length() ||
1159 map()->unused_property_fields() == 0);
1160 // Allocate a new map for the object.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00001161 Object* r = map()->CopyDropDescriptors();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001162 if (r->IsFailure()) return r;
1163 Map* new_map = Map::cast(r);
1164 if (allow_map_transition) {
1165 // Allocate new instance descriptors for the old map with map transition.
1166 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
1167 Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
mads.s.ager31e71382008-08-13 09:32:07 +00001168 if (r->IsFailure()) return r;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001169 old_descriptors = DescriptorArray::cast(r);
1170 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001171
ager@chromium.org7c537e22008-10-16 08:43:32 +00001172 if (map()->unused_property_fields() == 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001173 if (properties()->length() > kMaxFastProperties) {
ager@chromium.org32912102009-01-16 10:38:43 +00001174 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001175 if (obj->IsFailure()) return obj;
1176 return AddSlowProperty(name, value, attributes);
1177 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001178 // Make room for the new value
1179 Object* values =
ager@chromium.org7c537e22008-10-16 08:43:32 +00001180 properties()->CopySize(properties()->length() + kFieldsAdded);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001181 if (values->IsFailure()) return values;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001182 set_properties(FixedArray::cast(values));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001183 new_map->set_unused_property_fields(kFieldsAdded - 1);
1184 } else {
1185 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001186 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001187 // We have now allocated all the necessary objects.
1188 // All the changes can be applied at once, so they are atomic.
1189 map()->set_instance_descriptors(old_descriptors);
1190 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1191 set_map(new_map);
1192 return FastPropertyAtPut(index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001193}
1194
1195
1196Object* JSObject::AddConstantFunctionProperty(String* name,
1197 JSFunction* function,
1198 PropertyAttributes attributes) {
1199 // Allocate new instance descriptors with (name, function) added
1200 ConstantFunctionDescriptor d(name, function, attributes);
1201 Object* new_descriptors =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001202 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001203 if (new_descriptors->IsFailure()) return new_descriptors;
1204
1205 // Allocate a new map for the object.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00001206 Object* new_map = map()->CopyDropDescriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001207 if (new_map->IsFailure()) return new_map;
1208
1209 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1210 Map::cast(new_map)->set_instance_descriptors(descriptors);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001211 Map* old_map = map();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001212 set_map(Map::cast(new_map));
1213
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001214 // If the old map is the global object map (from new Object()),
1215 // then transitions are not added to it, so we are done.
1216 if (old_map == Top::context()->global_context()->object_function()->map()) {
1217 return function;
1218 }
1219
1220 // Do not add CONSTANT_TRANSITIONS to global objects
1221 if (IsGlobalObject()) {
1222 return function;
1223 }
1224
1225 // Add a CONSTANT_TRANSITION descriptor to the old map,
1226 // so future assignments to this property on other objects
1227 // of the same type will create a normal field, not a constant function.
1228 // Don't do this for special properties, with non-trival attributes.
1229 if (attributes != NONE) {
1230 return function;
1231 }
1232 ConstTransitionDescriptor mark(name);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001233 new_descriptors =
1234 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001235 if (new_descriptors->IsFailure()) {
mads.s.ager31e71382008-08-13 09:32:07 +00001236 return function; // We have accomplished the main goal, so return success.
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001237 }
1238 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1239
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001240 return function;
1241}
1242
1243
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001244// Add property in slow mode
1245Object* JSObject::AddSlowProperty(String* name,
1246 Object* value,
1247 PropertyAttributes attributes) {
1248 PropertyDetails details = PropertyDetails(attributes, NORMAL);
1249 Object* result = property_dictionary()->AddStringEntry(name, value, details);
1250 if (result->IsFailure()) return result;
1251 if (property_dictionary() != result) {
1252 set_properties(Dictionary::cast(result));
1253 }
1254 return value;
1255}
1256
1257
1258Object* JSObject::AddProperty(String* name,
1259 Object* value,
1260 PropertyAttributes attributes) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001261 ASSERT(!IsJSGlobalProxy());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001262 if (HasFastProperties()) {
1263 // Ensure the descriptor array does not get too big.
1264 if (map()->instance_descriptors()->number_of_descriptors() <
1265 DescriptorArray::kMaxNumberOfDescriptors) {
1266 if (value->IsJSFunction()) {
1267 return AddConstantFunctionProperty(name,
1268 JSFunction::cast(value),
1269 attributes);
1270 } else {
1271 return AddFastProperty(name, value, attributes);
1272 }
1273 } else {
1274 // Normalize the object to prevent very large instance descriptors.
1275 // This eliminates unwanted N^2 allocation and lookup behavior.
ager@chromium.org32912102009-01-16 10:38:43 +00001276 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001277 if (obj->IsFailure()) return obj;
1278 }
1279 }
1280 return AddSlowProperty(name, value, attributes);
1281}
1282
1283
1284Object* JSObject::SetPropertyPostInterceptor(String* name,
1285 Object* value,
1286 PropertyAttributes attributes) {
1287 // Check local property, ignore interceptor.
1288 LookupResult result;
1289 LocalLookupRealNamedProperty(name, &result);
1290 if (result.IsValid()) return SetProperty(&result, name, value, attributes);
1291 // Add real property.
1292 return AddProperty(name, value, attributes);
1293}
1294
1295
ager@chromium.org7c537e22008-10-16 08:43:32 +00001296Object* JSObject::ReplaceSlowProperty(String* name,
1297 Object* value,
1298 PropertyAttributes attributes) {
1299 Dictionary* dictionary = property_dictionary();
1300 PropertyDetails old_details =
1301 dictionary->DetailsAt(dictionary->FindStringEntry(name));
1302 int new_index = old_details.index();
1303 if (old_details.IsTransition()) new_index = 0;
1304
1305 PropertyDetails new_details(attributes, NORMAL, old_details.index());
1306 Object* result =
1307 property_dictionary()->SetOrAddStringEntry(name, value, new_details);
1308 if (result->IsFailure()) return result;
1309 if (property_dictionary() != result) {
1310 set_properties(Dictionary::cast(result));
1311 }
1312 return value;
1313}
1314
1315Object* JSObject::ConvertDescriptorToFieldAndMapTransition(
1316 String* name,
1317 Object* new_value,
1318 PropertyAttributes attributes) {
1319 Map* old_map = map();
1320 Object* result = ConvertDescriptorToField(name, new_value, attributes);
1321 if (result->IsFailure()) return result;
1322 // If we get to this point we have succeeded - do not return failure
1323 // after this point. Later stuff is optional.
1324 if (!HasFastProperties()) {
1325 return result;
1326 }
1327 // Do not add transitions to the map of "new Object()".
1328 if (map() == Top::context()->global_context()->object_function()->map()) {
1329 return result;
1330 }
1331
1332 MapTransitionDescriptor transition(name,
1333 map(),
1334 attributes);
1335 Object* new_descriptors =
1336 old_map->instance_descriptors()->
1337 CopyInsert(&transition, KEEP_TRANSITIONS);
1338 if (new_descriptors->IsFailure()) return result; // Yes, return _result_.
1339 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1340 return result;
1341}
1342
1343
1344Object* JSObject::ConvertDescriptorToField(String* name,
1345 Object* new_value,
1346 PropertyAttributes attributes) {
1347 if (map()->unused_property_fields() == 0 &&
1348 properties()->length() > kMaxFastProperties) {
ager@chromium.org32912102009-01-16 10:38:43 +00001349 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001350 if (obj->IsFailure()) return obj;
1351 return ReplaceSlowProperty(name, new_value, attributes);
1352 }
1353
1354 int index = map()->NextFreePropertyIndex();
1355 FieldDescriptor new_field(name, index, attributes);
1356 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
1357 Object* descriptors_unchecked = map()->instance_descriptors()->
1358 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1359 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
1360 DescriptorArray* new_descriptors =
1361 DescriptorArray::cast(descriptors_unchecked);
1362
1363 // Make a new map for the object.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00001364 Object* new_map_unchecked = map()->CopyDropDescriptors();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001365 if (new_map_unchecked->IsFailure()) return new_map_unchecked;
1366 Map* new_map = Map::cast(new_map_unchecked);
1367 new_map->set_instance_descriptors(new_descriptors);
1368
1369 // Make new properties array if necessary.
1370 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1371 int new_unused_property_fields = map()->unused_property_fields() - 1;
1372 if (map()->unused_property_fields() == 0) {
1373 new_unused_property_fields = kFieldsAdded - 1;
1374 Object* new_properties_unchecked =
1375 properties()->CopySize(properties()->length() + kFieldsAdded);
1376 if (new_properties_unchecked->IsFailure()) return new_properties_unchecked;
1377 new_properties = FixedArray::cast(new_properties_unchecked);
1378 }
1379
1380 // Update pointers to commit changes.
1381 // Object points to the new map.
1382 new_map->set_unused_property_fields(new_unused_property_fields);
1383 set_map(new_map);
1384 if (new_properties) {
1385 set_properties(FixedArray::cast(new_properties));
1386 }
1387 return FastPropertyAtPut(index, new_value);
1388}
1389
1390
1391
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001392Object* JSObject::SetPropertyWithInterceptor(String* name,
1393 Object* value,
1394 PropertyAttributes attributes) {
1395 HandleScope scope;
1396 Handle<JSObject> this_handle(this);
1397 Handle<String> name_handle(name);
1398 Handle<Object> value_handle(value);
1399 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1400 if (!interceptor->setter()->IsUndefined()) {
1401 Handle<Object> data_handle(interceptor->data());
1402 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name));
1403 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
1404 v8::Utils::ToLocal(data_handle),
1405 v8::Utils::ToLocal(this_handle));
1406 v8::NamedPropertySetter setter =
1407 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1408 v8::Handle<v8::Value> result;
1409 {
1410 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00001411 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001412 Handle<Object> value_unhole(value->IsTheHole() ?
1413 Heap::undefined_value() :
1414 value);
1415 result = setter(v8::Utils::ToLocal(name_handle),
1416 v8::Utils::ToLocal(value_unhole),
1417 info);
1418 }
1419 RETURN_IF_SCHEDULED_EXCEPTION();
1420 if (!result.IsEmpty()) return *value_handle;
1421 }
1422 Object* raw_result = this_handle->SetPropertyPostInterceptor(*name_handle,
1423 *value_handle,
1424 attributes);
1425 RETURN_IF_SCHEDULED_EXCEPTION();
1426 return raw_result;
1427}
1428
1429
1430Object* JSObject::SetProperty(String* name,
1431 Object* value,
1432 PropertyAttributes attributes) {
1433 LookupResult result;
1434 LocalLookup(name, &result);
1435 return SetProperty(&result, name, value, attributes);
1436}
1437
1438
1439Object* JSObject::SetPropertyWithCallback(Object* structure,
1440 String* name,
1441 Object* value,
1442 JSObject* holder) {
1443 HandleScope scope;
1444
1445 // We should never get here to initialize a const with the hole
1446 // value since a const declaration would conflict with the setter.
1447 ASSERT(!value->IsTheHole());
1448 Handle<Object> value_handle(value);
1449
1450 // To accommodate both the old and the new api we switch on the
1451 // data structure used to store the callbacks. Eventually proxy
1452 // callbacks should be phased out.
1453 if (structure->IsProxy()) {
1454 AccessorDescriptor* callback =
1455 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
1456 Object* obj = (callback->setter)(this, value, callback->data);
1457 RETURN_IF_SCHEDULED_EXCEPTION();
1458 if (obj->IsFailure()) return obj;
1459 return *value_handle;
1460 }
1461
1462 if (structure->IsAccessorInfo()) {
1463 // api style callbacks
1464 AccessorInfo* data = AccessorInfo::cast(structure);
1465 Object* call_obj = data->setter();
1466 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1467 if (call_fun == NULL) return value;
1468 Handle<JSObject> self(this);
1469 Handle<JSObject> holder_handle(JSObject::cast(holder));
1470 Handle<String> key(name);
1471 Handle<Object> fun_data(data->data());
1472 LOG(ApiNamedPropertyAccess("store", this, name));
1473 v8::AccessorInfo info(v8::Utils::ToLocal(self),
1474 v8::Utils::ToLocal(fun_data),
1475 v8::Utils::ToLocal(holder_handle));
1476 {
1477 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00001478 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001479 call_fun(v8::Utils::ToLocal(key),
1480 v8::Utils::ToLocal(value_handle),
1481 info);
1482 }
1483 RETURN_IF_SCHEDULED_EXCEPTION();
1484 return *value_handle;
1485 }
1486
1487 if (structure->IsFixedArray()) {
1488 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1489 if (setter->IsJSFunction()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001490 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001491 } else {
1492 Handle<String> key(name);
1493 Handle<Object> holder_handle(holder);
1494 Handle<Object> args[2] = { key, holder_handle };
1495 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
1496 HandleVector(args, 2)));
1497 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001498 }
1499
1500 UNREACHABLE();
1501 return 0;
1502}
1503
1504
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001505Object* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1506 Object* value) {
1507 Handle<Object> value_handle(value);
1508 Handle<JSFunction> fun(JSFunction::cast(setter));
1509 Handle<JSObject> self(this);
1510 bool has_pending_exception;
1511 Object** argv[] = { value_handle.location() };
1512 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1513 // Check for pending exception and return the result.
1514 if (has_pending_exception) return Failure::Exception();
1515 return *value_handle;
1516}
1517
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001518void JSObject::LookupCallbackSetterInPrototypes(String* name,
1519 LookupResult* result) {
1520 for (Object* pt = GetPrototype();
1521 pt != Heap::null_value();
1522 pt = pt->GetPrototype()) {
1523 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
1524 if (result->IsValid()) {
1525 if (!result->IsTransitionType() && result->IsReadOnly()) {
1526 result->NotFound();
1527 return;
1528 }
1529 if (result->type() == CALLBACKS) {
1530 return;
1531 }
1532 }
1533 }
1534 result->NotFound();
1535}
1536
1537
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001538Object* JSObject::LookupCallbackSetterInPrototypes(uint32_t index) {
1539 for (Object* pt = GetPrototype();
1540 pt != Heap::null_value();
1541 pt = pt->GetPrototype()) {
1542 if (JSObject::cast(pt)->HasFastElements()) continue;
1543 Dictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1544 int entry = dictionary->FindNumberEntry(index);
1545 if (entry != -1) {
1546 Object* element = dictionary->ValueAt(entry);
1547 PropertyDetails details = dictionary->DetailsAt(entry);
1548 if (details.type() == CALLBACKS) {
1549 // Only accessors allowed as elements.
1550 return FixedArray::cast(element)->get(kSetterIndex);
1551 }
1552 }
1553 }
1554 return Heap::undefined_value();
1555}
1556
1557
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001558void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1559 DescriptorArray* descriptors = map()->instance_descriptors();
1560 int number = descriptors->Search(name);
1561 if (number != DescriptorArray::kNotFound) {
1562 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1563 } else {
1564 result->NotFound();
1565 }
1566}
1567
1568
1569void JSObject::LocalLookupRealNamedProperty(String* name,
1570 LookupResult* result) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001571 if (IsJSGlobalProxy()) {
1572 Object* proto = GetPrototype();
1573 if (proto->IsNull()) return result->NotFound();
1574 ASSERT(proto->IsJSGlobalObject());
1575 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
1576 }
1577
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001578 if (HasFastProperties()) {
1579 LookupInDescriptor(name, result);
1580 if (result->IsValid()) {
1581 ASSERT(result->holder() == this && result->type() != NORMAL);
1582 // Disallow caching for uninitialized constants. These can only
1583 // occur as fields.
1584 if (result->IsReadOnly() && result->type() == FIELD &&
ager@chromium.org7c537e22008-10-16 08:43:32 +00001585 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001586 result->DisallowCaching();
1587 }
1588 return;
1589 }
1590 } else {
1591 int entry = property_dictionary()->FindStringEntry(name);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001592 if (entry != DescriptorArray::kNotFound) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001593 // Make sure to disallow caching for uninitialized constants
1594 // found in the dictionary-mode objects.
1595 if (property_dictionary()->ValueAt(entry)->IsTheHole()) {
1596 result->DisallowCaching();
1597 }
1598 result->DictionaryResult(this, entry);
1599 return;
1600 }
1601 // Slow case object skipped during lookup. Do not use inline caching.
1602 result->DisallowCaching();
1603 }
1604 result->NotFound();
1605}
1606
1607
1608void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
1609 LocalLookupRealNamedProperty(name, result);
1610 if (result->IsProperty()) return;
1611
1612 LookupRealNamedPropertyInPrototypes(name, result);
1613}
1614
1615
1616void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
1617 LookupResult* result) {
1618 for (Object* pt = GetPrototype();
1619 pt != Heap::null_value();
1620 pt = JSObject::cast(pt)->GetPrototype()) {
1621 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
1622 if (result->IsValid()) {
1623 switch (result->type()) {
1624 case NORMAL:
1625 case FIELD:
1626 case CONSTANT_FUNCTION:
1627 case CALLBACKS:
1628 return;
1629 default: break;
1630 }
1631 }
1632 }
1633 result->NotFound();
1634}
1635
1636
1637// We only need to deal with CALLBACKS and INTERCEPTORS
1638Object* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
1639 String* name,
1640 Object* value) {
1641 if (!result->IsProperty()) {
1642 LookupCallbackSetterInPrototypes(name, result);
1643 }
1644
1645 if (result->IsProperty()) {
1646 if (!result->IsReadOnly()) {
1647 switch (result->type()) {
1648 case CALLBACKS: {
1649 Object* obj = result->GetCallbackObject();
1650 if (obj->IsAccessorInfo()) {
1651 AccessorInfo* info = AccessorInfo::cast(obj);
1652 if (info->all_can_write()) {
1653 return SetPropertyWithCallback(result->GetCallbackObject(),
1654 name,
1655 value,
1656 result->holder());
1657 }
1658 }
1659 break;
1660 }
1661 case INTERCEPTOR: {
1662 // Try lookup real named properties. Note that only property can be
1663 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
1664 LookupResult r;
1665 LookupRealNamedProperty(name, &r);
1666 if (r.IsProperty()) {
1667 return SetPropertyWithFailedAccessCheck(&r, name, value);
1668 }
1669 break;
1670 }
1671 default: {
1672 break;
1673 }
1674 }
1675 }
1676 }
1677
1678 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
1679 return value;
1680}
1681
1682
1683Object* JSObject::SetProperty(LookupResult* result,
1684 String* name,
1685 Object* value,
1686 PropertyAttributes attributes) {
1687 // Make sure that the top context does not change when doing callbacks or
1688 // interceptor calls.
1689 AssertNoContextChange ncc;
1690
1691 // Check access rights if needed.
1692 if (IsAccessCheckNeeded()
1693 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1694 return SetPropertyWithFailedAccessCheck(result, name, value);
1695 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001696
1697 if (IsJSGlobalProxy()) {
1698 Object* proto = GetPrototype();
1699 if (proto->IsNull()) return value;
1700 ASSERT(proto->IsJSGlobalObject());
1701 return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
1702 }
1703
ager@chromium.org32912102009-01-16 10:38:43 +00001704 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001705 // We could not find a local property so let's check whether there is an
1706 // accessor that wants to handle the property.
1707 LookupResult accessor_result;
1708 LookupCallbackSetterInPrototypes(name, &accessor_result);
1709 if (accessor_result.IsValid()) {
1710 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
1711 name,
1712 value,
1713 accessor_result.holder());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001714 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001715 }
1716 if (result->IsNotFound()) {
1717 return AddProperty(name, value, attributes);
1718 }
1719 if (!result->IsLoaded()) {
1720 return SetLazyProperty(result, name, value, attributes);
1721 }
1722 if (result->IsReadOnly() && result->IsProperty()) return value;
1723 // This is a real property that is not read-only, or it is a
1724 // transition or null descriptor and there are no setters in the prototypes.
1725 switch (result->type()) {
1726 case NORMAL:
1727 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
1728 return value;
1729 case FIELD:
ager@chromium.org7c537e22008-10-16 08:43:32 +00001730 return FastPropertyAtPut(result->GetFieldIndex(), value);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001731 case MAP_TRANSITION:
1732 if (attributes == result->GetAttributes()) {
1733 // Only use map transition if the attributes match.
1734 return AddFastPropertyUsingMap(result->GetTransitionMap(),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001735 name,
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001736 value);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001737 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001738 return ConvertDescriptorToField(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001739 case CONSTANT_FUNCTION:
1740 if (value == result->GetConstantFunction()) return value;
1741 // Only replace the function if necessary.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001742 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001743 case CALLBACKS:
1744 return SetPropertyWithCallback(result->GetCallbackObject(),
1745 name,
1746 value,
1747 result->holder());
1748 case INTERCEPTOR:
1749 return SetPropertyWithInterceptor(name, value, attributes);
1750 case CONSTANT_TRANSITION:
1751 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
1752 // if the value is a function.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001753 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001754 case NULL_DESCRIPTOR:
ager@chromium.org7c537e22008-10-16 08:43:32 +00001755 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001756 default:
1757 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001758 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001759 UNREACHABLE();
1760 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001761}
1762
1763
1764// Set a real local property, even if it is READ_ONLY. If the property is not
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001765// present, add it with attributes NONE. This code is an exact clone of
1766// SetProperty, with the check for IsReadOnly and the check for a
1767// callback setter removed. The two lines looking up the LookupResult
1768// result are also added. If one of the functions is changed, the other
1769// should be.
1770Object* JSObject::IgnoreAttributesAndSetLocalProperty(
1771 String* name,
1772 Object* value,
1773 PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001774 // Make sure that the top context does not change when doing callbacks or
1775 // interceptor calls.
1776 AssertNoContextChange ncc;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001777 // ADDED TO CLONE
1778 LookupResult result_struct;
1779 LocalLookup(name, &result_struct);
1780 LookupResult* result = &result_struct;
1781 // END ADDED TO CLONE
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001782 // Check access rights if needed.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001783 if (IsAccessCheckNeeded()
1784 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
1785 return SetPropertyWithFailedAccessCheck(result, name, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001786 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001787
1788 if (IsJSGlobalProxy()) {
1789 Object* proto = GetPrototype();
1790 if (proto->IsNull()) return value;
1791 ASSERT(proto->IsJSGlobalObject());
1792 return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
1793 name,
1794 value,
1795 attributes);
1796 }
1797
ager@chromium.org7c537e22008-10-16 08:43:32 +00001798 // Check for accessor in prototype chain removed here in clone.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001799 if (result->IsNotFound()) {
1800 return AddProperty(name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001801 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001802 if (!result->IsLoaded()) {
1803 return SetLazyProperty(result, name, value, attributes);
1804 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001805 // Check of IsReadOnly removed from here in clone.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001806 switch (result->type()) {
1807 case NORMAL:
1808 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
1809 return value;
1810 case FIELD:
ager@chromium.org7c537e22008-10-16 08:43:32 +00001811 return FastPropertyAtPut(result->GetFieldIndex(), value);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001812 case MAP_TRANSITION:
1813 if (attributes == result->GetAttributes()) {
1814 // Only use map transition if the attributes match.
1815 return AddFastPropertyUsingMap(result->GetTransitionMap(),
1816 name,
1817 value);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001818 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001819 return ConvertDescriptorToField(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001820 case CONSTANT_FUNCTION:
1821 if (value == result->GetConstantFunction()) return value;
1822 // Only replace the function if necessary.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001823 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001824 case CALLBACKS:
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001825 case INTERCEPTOR:
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001826 // Override callback in clone
1827 return ConvertDescriptorToField(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001828 case CONSTANT_TRANSITION:
1829 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
1830 // if the value is a function.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001831 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001832 case NULL_DESCRIPTOR:
ager@chromium.org7c537e22008-10-16 08:43:32 +00001833 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001834 default:
1835 UNREACHABLE();
1836 }
1837 UNREACHABLE();
1838 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001839}
1840
1841
1842PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
1843 JSObject* receiver,
1844 String* name,
1845 bool continue_search) {
1846 // Check local property, ignore interceptor.
1847 LookupResult result;
1848 LocalLookupRealNamedProperty(name, &result);
kasper.lund44510672008-07-25 07:37:58 +00001849 if (result.IsProperty()) return result.GetAttributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001850
1851 if (continue_search) {
1852 // Continue searching via the prototype chain.
1853 Object* pt = GetPrototype();
1854 if (pt != Heap::null_value()) {
1855 return JSObject::cast(pt)->
1856 GetPropertyAttributeWithReceiver(receiver, name);
1857 }
1858 }
1859 return ABSENT;
1860}
1861
1862
1863PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
1864 JSObject* receiver,
1865 String* name,
1866 bool continue_search) {
1867 // Make sure that the top context does not change when doing
1868 // callbacks or interceptor calls.
1869 AssertNoContextChange ncc;
1870
1871 HandleScope scope;
1872 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1873 Handle<JSObject> receiver_handle(receiver);
1874 Handle<JSObject> holder_handle(this);
1875 Handle<String> name_handle(name);
1876 Handle<Object> data_handle(interceptor->data());
1877 v8::AccessorInfo info(v8::Utils::ToLocal(receiver_handle),
1878 v8::Utils::ToLocal(data_handle),
1879 v8::Utils::ToLocal(holder_handle));
1880 if (!interceptor->query()->IsUndefined()) {
1881 v8::NamedPropertyQuery query =
1882 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
1883 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
1884 v8::Handle<v8::Boolean> result;
1885 {
1886 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00001887 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001888 result = query(v8::Utils::ToLocal(name_handle), info);
1889 }
1890 if (!result.IsEmpty()) {
1891 // Convert the boolean result to a property attribute
1892 // specification.
1893 return result->IsTrue() ? NONE : ABSENT;
1894 }
1895 } else if (!interceptor->getter()->IsUndefined()) {
1896 v8::NamedPropertyGetter getter =
1897 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
1898 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
1899 v8::Handle<v8::Value> result;
1900 {
1901 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00001902 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001903 result = getter(v8::Utils::ToLocal(name_handle), info);
1904 }
1905 if (!result.IsEmpty()) return NONE;
1906 }
1907 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
1908 *name_handle,
1909 continue_search);
1910}
1911
1912
1913PropertyAttributes JSObject::GetPropertyAttributeWithReceiver(
1914 JSObject* receiver,
1915 String* key) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001916 uint32_t index = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001917 if (key->AsArrayIndex(&index)) {
1918 if (HasElementWithReceiver(receiver, index)) return NONE;
1919 return ABSENT;
1920 }
1921 // Named property.
1922 LookupResult result;
1923 Lookup(key, &result);
1924 return GetPropertyAttribute(receiver, &result, key, true);
1925}
1926
1927
1928PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
1929 LookupResult* result,
1930 String* name,
1931 bool continue_search) {
1932 // Check access rights if needed.
1933 if (IsAccessCheckNeeded() &&
1934 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001935 return GetPropertyAttributeWithFailedAccessCheck(receiver,
1936 result,
1937 name,
1938 continue_search);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001939 }
1940 if (result->IsValid()) {
1941 switch (result->type()) {
1942 case NORMAL: // fall through
1943 case FIELD:
1944 case CONSTANT_FUNCTION:
1945 case CALLBACKS:
1946 return result->GetAttributes();
1947 case INTERCEPTOR:
1948 return result->holder()->
1949 GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001950 case MAP_TRANSITION:
1951 case CONSTANT_TRANSITION:
1952 case NULL_DESCRIPTOR:
1953 return ABSENT;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001954 default:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001955 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001956 break;
1957 }
1958 }
1959 return ABSENT;
1960}
1961
1962
1963PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) {
1964 // Check whether the name is an array index.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001965 uint32_t index = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001966 if (name->AsArrayIndex(&index)) {
1967 if (HasLocalElement(index)) return NONE;
1968 return ABSENT;
1969 }
1970 // Named property.
1971 LookupResult result;
1972 LocalLookup(name, &result);
1973 return GetPropertyAttribute(this, &result, name, false);
1974}
1975
1976
ager@chromium.org32912102009-01-16 10:38:43 +00001977Object* JSObject::NormalizeProperties(PropertyNormalizationMode mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001978 if (!HasFastProperties()) return this;
1979
1980 // Allocate new content
1981 Object* obj =
1982 Dictionary::Allocate(map()->NumberOfDescribedProperties() * 2 + 4);
1983 if (obj->IsFailure()) return obj;
1984 Dictionary* dictionary = Dictionary::cast(obj);
1985
1986 for (DescriptorReader r(map()->instance_descriptors());
1987 !r.eos();
1988 r.advance()) {
1989 PropertyDetails details = r.GetDetails();
1990 switch (details.type()) {
1991 case CONSTANT_FUNCTION: {
1992 PropertyDetails d =
1993 PropertyDetails(details.attributes(), NORMAL, details.index());
1994 Object* value = r.GetConstantFunction();
1995 Object* result = dictionary->AddStringEntry(r.GetKey(), value, d);
1996 if (result->IsFailure()) return result;
1997 dictionary = Dictionary::cast(result);
1998 break;
1999 }
2000 case FIELD: {
2001 PropertyDetails d =
2002 PropertyDetails(details.attributes(), NORMAL, details.index());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002003 Object* value = FastPropertyAt(r.GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002004 Object* result = dictionary->AddStringEntry(r.GetKey(), value, d);
2005 if (result->IsFailure()) return result;
2006 dictionary = Dictionary::cast(result);
2007 break;
2008 }
2009 case CALLBACKS: {
2010 PropertyDetails d =
2011 PropertyDetails(details.attributes(), CALLBACKS, details.index());
2012 Object* value = r.GetCallbacksObject();
2013 Object* result = dictionary->AddStringEntry(r.GetKey(), value, d);
2014 if (result->IsFailure()) return result;
2015 dictionary = Dictionary::cast(result);
2016 break;
2017 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002018 case MAP_TRANSITION:
2019 case CONSTANT_TRANSITION:
2020 case NULL_DESCRIPTOR:
2021 case INTERCEPTOR:
2022 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002023 default:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002024 case NORMAL:
2025 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002026 break;
2027 }
2028 }
2029
2030 // Copy the next enumeration index from instance descriptor.
2031 int index = map()->instance_descriptors()->NextEnumerationIndex();
2032 dictionary->SetNextEnumerationIndex(index);
2033
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002034 // Allocate new map.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002035 obj = map()->CopyDropDescriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002036 if (obj->IsFailure()) return obj;
ager@chromium.org32912102009-01-16 10:38:43 +00002037 Map* new_map = Map::cast(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002038
ager@chromium.org6f10e412009-02-13 10:11:16 +00002039 // Clear inobject properties if needed by adjusting the instance size and
2040 // putting in a filler object instead of the inobject properties.
ager@chromium.org32912102009-01-16 10:38:43 +00002041 if (mode == CLEAR_INOBJECT_PROPERTIES && map()->inobject_properties() > 0) {
2042 int instance_size_delta = map()->inobject_properties() * kPointerSize;
2043 int new_instance_size = map()->instance_size() - instance_size_delta;
2044 new_map->set_inobject_properties(0);
2045 new_map->set_instance_size(new_instance_size);
ager@chromium.org6f10e412009-02-13 10:11:16 +00002046 Heap::CreateFillerObjectAt(this->address() + new_instance_size,
2047 instance_size_delta);
ager@chromium.org32912102009-01-16 10:38:43 +00002048 }
2049 new_map->set_unused_property_fields(0);
2050
2051 // We have now successfully allocated all the necessary objects.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002052 // Changes can now be made with the guarantee that all of them take effect.
ager@chromium.org32912102009-01-16 10:38:43 +00002053 set_map(new_map);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002054 map()->set_instance_descriptors(Heap::empty_descriptor_array());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002055
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002056 set_properties(dictionary);
2057
2058 Counters::props_to_dictionary.Increment();
2059
2060#ifdef DEBUG
2061 if (FLAG_trace_normalization) {
2062 PrintF("Object properties have been normalized:\n");
2063 Print();
2064 }
2065#endif
2066 return this;
2067}
2068
2069
2070Object* JSObject::TransformToFastProperties(int unused_property_fields) {
2071 if (HasFastProperties()) return this;
2072 return property_dictionary()->
2073 TransformPropertiesToFastFor(this, unused_property_fields);
2074}
2075
2076
2077Object* JSObject::NormalizeElements() {
2078 if (!HasFastElements()) return this;
2079
2080 // Get number of entries.
2081 FixedArray* array = FixedArray::cast(elements());
2082
2083 // Compute the effective length.
2084 int length = IsJSArray() ?
2085 Smi::cast(JSArray::cast(this)->length())->value() :
2086 array->length();
2087 Object* obj = Dictionary::Allocate(length);
2088 if (obj->IsFailure()) return obj;
2089 Dictionary* dictionary = Dictionary::cast(obj);
2090 // Copy entries.
2091 for (int i = 0; i < length; i++) {
2092 Object* value = array->get(i);
2093 if (!value->IsTheHole()) {
2094 PropertyDetails details = PropertyDetails(NONE, NORMAL);
2095 Object* result = dictionary->AddNumberEntry(i, array->get(i), details);
2096 if (result->IsFailure()) return result;
2097 dictionary = Dictionary::cast(result);
2098 }
2099 }
2100 // Switch to using the dictionary as the backing storage for elements.
2101 set_elements(dictionary);
2102
2103 Counters::elements_to_dictionary.Increment();
2104
2105#ifdef DEBUG
2106 if (FLAG_trace_normalization) {
2107 PrintF("Object elements have been normalized:\n");
2108 Print();
2109 }
2110#endif
2111
2112 return this;
2113}
2114
2115
2116Object* JSObject::DeletePropertyPostInterceptor(String* name) {
2117 // Check local property, ignore interceptor.
2118 LookupResult result;
2119 LocalLookupRealNamedProperty(name, &result);
2120 if (!result.IsValid()) return Heap::true_value();
2121
2122 // Normalize object if needed.
ager@chromium.org32912102009-01-16 10:38:43 +00002123 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002124 if (obj->IsFailure()) return obj;
2125
2126 ASSERT(!HasFastProperties());
2127 // Attempt to remove the property from the property dictionary.
2128 Dictionary* dictionary = property_dictionary();
2129 int entry = dictionary->FindStringEntry(name);
2130 if (entry != -1) return dictionary->DeleteProperty(entry);
2131 return Heap::true_value();
2132}
2133
2134
2135Object* JSObject::DeletePropertyWithInterceptor(String* name) {
2136 HandleScope scope;
2137 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2138 Handle<String> name_handle(name);
2139 Handle<JSObject> this_handle(this);
2140 if (!interceptor->deleter()->IsUndefined()) {
2141 v8::NamedPropertyDeleter deleter =
2142 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
2143 Handle<Object> data_handle(interceptor->data());
2144 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
2145 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
2146 v8::Utils::ToLocal(data_handle),
2147 v8::Utils::ToLocal(this_handle));
2148 v8::Handle<v8::Boolean> result;
2149 {
2150 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00002151 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002152 result = deleter(v8::Utils::ToLocal(name_handle), info);
2153 }
2154 RETURN_IF_SCHEDULED_EXCEPTION();
2155 if (!result.IsEmpty()) {
2156 ASSERT(result->IsBoolean());
2157 return *v8::Utils::OpenHandle(*result);
2158 }
2159 }
2160 Object* raw_result = this_handle->DeletePropertyPostInterceptor(*name_handle);
2161 RETURN_IF_SCHEDULED_EXCEPTION();
2162 return raw_result;
2163}
2164
2165
2166Object* JSObject::DeleteElementPostInterceptor(uint32_t index) {
2167 if (HasFastElements()) {
2168 uint32_t length = IsJSArray() ?
2169 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2170 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2171 if (index < length) {
2172 FixedArray::cast(elements())->set_the_hole(index);
2173 }
2174 return Heap::true_value();
2175 }
2176 ASSERT(!HasFastElements());
2177 Dictionary* dictionary = element_dictionary();
2178 int entry = dictionary->FindNumberEntry(index);
2179 if (entry != -1) return dictionary->DeleteProperty(entry);
2180 return Heap::true_value();
2181}
2182
2183
2184Object* JSObject::DeleteElementWithInterceptor(uint32_t index) {
2185 // Make sure that the top context does not change when doing
2186 // callbacks or interceptor calls.
2187 AssertNoContextChange ncc;
2188 HandleScope scope;
2189 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
2190 if (interceptor->deleter()->IsUndefined()) return Heap::false_value();
2191 v8::IndexedPropertyDeleter deleter =
2192 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
2193 Handle<JSObject> this_handle(this);
2194 Handle<Object> data_handle(interceptor->data());
2195 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
2196 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
2197 v8::Utils::ToLocal(data_handle),
2198 v8::Utils::ToLocal(this_handle));
2199 v8::Handle<v8::Boolean> result;
2200 {
2201 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00002202 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002203 result = deleter(index, info);
2204 }
2205 RETURN_IF_SCHEDULED_EXCEPTION();
2206 if (!result.IsEmpty()) {
2207 ASSERT(result->IsBoolean());
2208 return *v8::Utils::OpenHandle(*result);
2209 }
2210 Object* raw_result = this_handle->DeleteElementPostInterceptor(index);
2211 RETURN_IF_SCHEDULED_EXCEPTION();
2212 return raw_result;
2213}
2214
2215
2216Object* JSObject::DeleteElement(uint32_t index) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002217 // Check access rights if needed.
2218 if (IsAccessCheckNeeded() &&
2219 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
2220 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2221 return Heap::false_value();
2222 }
2223
2224 if (IsJSGlobalProxy()) {
2225 Object* proto = GetPrototype();
2226 if (proto->IsNull()) return Heap::false_value();
2227 ASSERT(proto->IsJSGlobalObject());
2228 return JSGlobalObject::cast(proto)->DeleteElement(index);
2229 }
2230
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002231 if (HasIndexedInterceptor()) {
2232 return DeleteElementWithInterceptor(index);
2233 }
2234
2235 if (HasFastElements()) {
2236 uint32_t length = IsJSArray() ?
2237 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
2238 static_cast<uint32_t>(FixedArray::cast(elements())->length());
2239 if (index < length) {
2240 FixedArray::cast(elements())->set_the_hole(index);
2241 }
2242 return Heap::true_value();
2243 } else {
2244 Dictionary* dictionary = element_dictionary();
2245 int entry = dictionary->FindNumberEntry(index);
2246 if (entry != -1) return dictionary->DeleteProperty(entry);
2247 }
2248 return Heap::true_value();
2249}
2250
2251
2252Object* JSObject::DeleteProperty(String* name) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002253 // ECMA-262, 3rd, 8.6.2.5
2254 ASSERT(name->IsString());
2255
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002256 // Check access rights if needed.
2257 if (IsAccessCheckNeeded() &&
2258 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
2259 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
2260 return Heap::false_value();
2261 }
2262
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002263 if (IsJSGlobalProxy()) {
2264 Object* proto = GetPrototype();
2265 if (proto->IsNull()) return Heap::false_value();
2266 ASSERT(proto->IsJSGlobalObject());
2267 return JSGlobalObject::cast(proto)->DeleteProperty(name);
2268 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002269
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002270 uint32_t index = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002271 if (name->AsArrayIndex(&index)) {
2272 return DeleteElement(index);
2273 } else {
2274 LookupResult result;
2275 LocalLookup(name, &result);
2276 if (!result.IsValid()) return Heap::true_value();
2277 if (result.IsDontDelete()) return Heap::false_value();
2278 // Check for interceptor.
2279 if (result.type() == INTERCEPTOR) {
2280 return DeletePropertyWithInterceptor(name);
2281 }
2282 if (!result.IsLoaded()) {
2283 return JSObject::cast(this)->DeleteLazyProperty(&result, name);
2284 }
2285 // Normalize object if needed.
ager@chromium.org32912102009-01-16 10:38:43 +00002286 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002287 if (obj->IsFailure()) return obj;
2288 // Make sure the properties are normalized before removing the entry.
2289 Dictionary* dictionary = property_dictionary();
2290 int entry = dictionary->FindStringEntry(name);
2291 if (entry != -1) return dictionary->DeleteProperty(entry);
2292 return Heap::true_value();
2293 }
2294}
2295
2296
2297// Check whether this object references another object.
2298bool JSObject::ReferencesObject(Object* obj) {
2299 AssertNoAllocation no_alloc;
2300
2301 // Is the object the constructor for this object?
2302 if (map()->constructor() == obj) {
2303 return true;
2304 }
2305
2306 // Is the object the prototype for this object?
2307 if (map()->prototype() == obj) {
2308 return true;
2309 }
2310
2311 // Check if the object is among the named properties.
2312 Object* key = SlowReverseLookup(obj);
2313 if (key != Heap::undefined_value()) {
2314 return true;
2315 }
2316
2317 // Check if the object is among the indexed properties.
2318 if (HasFastElements()) {
2319 int length = IsJSArray()
2320 ? Smi::cast(JSArray::cast(this)->length())->value()
2321 : FixedArray::cast(elements())->length();
2322 for (int i = 0; i < length; i++) {
2323 Object* element = FixedArray::cast(elements())->get(i);
2324 if (!element->IsTheHole() && element == obj) {
2325 return true;
2326 }
2327 }
2328 } else {
2329 key = element_dictionary()->SlowReverseLookup(obj);
2330 if (key != Heap::undefined_value()) {
2331 return true;
2332 }
2333 }
2334
2335 // For functions check the context. Boilerplate functions do
2336 // not have to be traversed since they have no real context.
2337 if (IsJSFunction() && !JSFunction::cast(this)->IsBoilerplate()) {
2338 // Get the constructor function for arguments array.
2339 JSObject* arguments_boilerplate =
2340 Top::context()->global_context()->arguments_boilerplate();
2341 JSFunction* arguments_function =
2342 JSFunction::cast(arguments_boilerplate->map()->constructor());
2343
2344 // Get the context and don't check if it is the global context.
2345 JSFunction* f = JSFunction::cast(this);
2346 Context* context = f->context();
2347 if (context->IsGlobalContext()) {
2348 return false;
2349 }
2350
2351 // Check the non-special context slots.
2352 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
2353 // Only check JS objects.
2354 if (context->get(i)->IsJSObject()) {
2355 JSObject* ctxobj = JSObject::cast(context->get(i));
2356 // If it is an arguments array check the content.
2357 if (ctxobj->map()->constructor() == arguments_function) {
2358 if (ctxobj->ReferencesObject(obj)) {
2359 return true;
2360 }
2361 } else if (ctxobj == obj) {
2362 return true;
2363 }
2364 }
2365 }
2366
2367 // Check the context extension if any.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002368 if (context->has_extension()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002369 return context->extension()->ReferencesObject(obj);
2370 }
2371 }
2372
2373 // No references to object.
2374 return false;
2375}
2376
2377
2378// Tests for the fast common case for property enumeration:
2379// - this object has an enum cache
2380// - this object has no elements
2381// - no prototype has enumerable properties/elements
2382// - neither this object nor any prototype has interceptors
2383bool JSObject::IsSimpleEnum() {
2384 JSObject* arguments_boilerplate =
2385 Top::context()->global_context()->arguments_boilerplate();
2386 JSFunction* arguments_function =
2387 JSFunction::cast(arguments_boilerplate->map()->constructor());
2388 if (IsAccessCheckNeeded()) return false;
2389 if (map()->constructor() == arguments_function) return false;
2390
2391 for (Object* o = this;
2392 o != Heap::null_value();
2393 o = JSObject::cast(o)->GetPrototype()) {
2394 JSObject* curr = JSObject::cast(o);
2395 if (!curr->HasFastProperties()) return false;
2396 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
2397 if (curr->NumberOfEnumElements() > 0) return false;
2398 if (curr->HasNamedInterceptor()) return false;
2399 if (curr->HasIndexedInterceptor()) return false;
2400 if (curr != this) {
2401 FixedArray* curr_fixed_array =
2402 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
2403 if (curr_fixed_array->length() > 0) {
2404 return false;
2405 }
2406 }
2407 }
2408 return true;
2409}
2410
2411
2412int Map::NumberOfDescribedProperties() {
2413 int result = 0;
2414 for (DescriptorReader r(instance_descriptors()); !r.eos(); r.advance()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002415 if (r.IsProperty()) result++;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002416 }
2417 return result;
2418}
2419
2420
2421int Map::PropertyIndexFor(String* name) {
2422 for (DescriptorReader r(instance_descriptors()); !r.eos(); r.advance()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002423 if (r.Equals(name) && !r.IsNullDescriptor()) return r.GetFieldIndex();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002424 }
2425 return -1;
2426}
2427
2428
2429int Map::NextFreePropertyIndex() {
2430 int index = -1;
2431 for (DescriptorReader r(instance_descriptors()); !r.eos(); r.advance()) {
2432 if (r.type() == FIELD) {
2433 if (r.GetFieldIndex() > index) index = r.GetFieldIndex();
2434 }
2435 }
2436 return index+1;
2437}
2438
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002439
2440AccessorDescriptor* Map::FindAccessor(String* name) {
2441 for (DescriptorReader r(instance_descriptors()); !r.eos(); r.advance()) {
2442 if (r.Equals(name) && r.type() == CALLBACKS) return r.GetCallbacks();
2443 }
2444 return NULL;
2445}
2446
2447
2448void JSObject::LocalLookup(String* name, LookupResult* result) {
2449 ASSERT(name->IsString());
2450
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002451 if (IsJSGlobalProxy()) {
2452 Object* proto = GetPrototype();
2453 if (proto->IsNull()) return result->NotFound();
2454 ASSERT(proto->IsJSGlobalObject());
2455 return JSObject::cast(proto)->LocalLookup(name, result);
2456 }
2457
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002458 // Do not use inline caching if the object is a non-global object
2459 // that requires access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002460 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002461 result->DisallowCaching();
2462 }
2463
2464 // Check __proto__ before interceptor.
ager@chromium.org32912102009-01-16 10:38:43 +00002465 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002466 result->ConstantResult(this);
2467 return;
2468 }
2469
2470 // Check for lookup interceptor except when bootstrapping.
2471 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) {
2472 result->InterceptorResult(this);
2473 return;
2474 }
2475
2476 LocalLookupRealNamedProperty(name, result);
2477}
2478
2479
2480void JSObject::Lookup(String* name, LookupResult* result) {
2481 // Ecma-262 3rd 8.6.2.4
2482 for (Object* current = this;
2483 current != Heap::null_value();
2484 current = JSObject::cast(current)->GetPrototype()) {
2485 JSObject::cast(current)->LocalLookup(name, result);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002486 if (result->IsValid() && !result->IsTransitionType()) return;
2487 }
2488 result->NotFound();
2489}
2490
2491
2492// Search object and it's prototype chain for callback properties.
2493void JSObject::LookupCallback(String* name, LookupResult* result) {
2494 for (Object* current = this;
2495 current != Heap::null_value();
2496 current = JSObject::cast(current)->GetPrototype()) {
2497 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
2498 if (result->IsValid() && result->type() == CALLBACKS) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002499 }
2500 result->NotFound();
2501}
2502
2503
2504Object* JSObject::DefineGetterSetter(String* name,
2505 PropertyAttributes attributes) {
2506 // Make sure that the top context does not change when doing callbacks or
2507 // interceptor calls.
2508 AssertNoContextChange ncc;
2509
2510 // Check access rights if needed.
2511 if (IsAccessCheckNeeded() &&
2512 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
2513 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
2514 return Heap::undefined_value();
2515 }
2516
ager@chromium.orgddb913d2009-01-27 10:01:48 +00002517 // Try to flatten before operating on the string.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002518 name->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002519
ager@chromium.org870a0b62008-11-04 11:43:05 +00002520 // Check if there is an API defined callback object which prohibits
2521 // callback overwriting in this object or it's prototype chain.
2522 // This mechanism is needed for instance in a browser setting, where
2523 // certain accessors such as window.location should not be allowed
ager@chromium.org32912102009-01-16 10:38:43 +00002524 // to be overwritten because allowing overwriting could potentially
ager@chromium.org870a0b62008-11-04 11:43:05 +00002525 // cause security problems.
2526 LookupResult callback_result;
2527 LookupCallback(name, &callback_result);
2528 if (callback_result.IsValid()) {
2529 Object* obj = callback_result.GetCallbackObject();
2530 if (obj->IsAccessorInfo() &&
2531 AccessorInfo::cast(obj)->prohibits_overwriting()) {
2532 return Heap::undefined_value();
2533 }
2534 }
2535
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002536 uint32_t index;
2537 bool is_element = name->AsArrayIndex(&index);
2538 if (is_element && IsJSArray()) return Heap::undefined_value();
2539
2540 if (is_element) {
2541 // Lookup the index.
2542 if (!HasFastElements()) {
2543 Dictionary* dictionary = element_dictionary();
2544 int entry = dictionary->FindNumberEntry(index);
2545 if (entry != -1) {
2546 Object* result = dictionary->ValueAt(entry);
2547 PropertyDetails details = dictionary->DetailsAt(entry);
2548 if (details.IsReadOnly()) return Heap::undefined_value();
2549 if (details.type() == CALLBACKS) {
2550 // Only accessors allowed as elements.
2551 ASSERT(result->IsFixedArray());
2552 return result;
2553 }
2554 }
2555 }
2556 } else {
2557 // Lookup the name.
2558 LookupResult result;
2559 LocalLookup(name, &result);
2560 if (result.IsValid()) {
2561 if (result.IsReadOnly()) return Heap::undefined_value();
2562 if (result.type() == CALLBACKS) {
2563 Object* obj = result.GetCallbackObject();
2564 if (obj->IsFixedArray()) return obj;
2565 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002566 }
2567 }
2568
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569 // Allocate the fixed array to hold getter and setter.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002570 Object* structure = Heap::AllocateFixedArray(2, TENURED);
2571 if (structure->IsFailure()) return structure;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002572 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002573
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002574 if (is_element) {
2575 // Normalize object to make this operation simple.
2576 Object* ok = NormalizeElements();
2577 if (ok->IsFailure()) return ok;
2578
2579 // Update the dictionary with the new CALLBACKS property.
2580 Object* dict =
2581 element_dictionary()->SetOrAddNumberEntry(index, structure, details);
2582 if (dict->IsFailure()) return dict;
2583
2584 // If name is an index we need to stay in slow case.
2585 Dictionary* elements = Dictionary::cast(dict);
2586 elements->set_requires_slow_elements();
2587 // Set the potential new dictionary on the object.
2588 set_elements(Dictionary::cast(dict));
2589 } else {
2590 // Normalize object to make this operation simple.
2591 Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
2592 if (ok->IsFailure()) return ok;
2593
2594 // Update the dictionary with the new CALLBACKS property.
2595 Object* dict =
2596 property_dictionary()->SetOrAddStringEntry(name, structure, details);
2597 if (dict->IsFailure()) return dict;
2598
2599 // Set the potential new dictionary on the object.
2600 set_properties(Dictionary::cast(dict));
2601 }
2602
2603 return structure;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002604}
2605
2606
2607Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
2608 PropertyAttributes attributes) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002609 // Check access rights if needed.
2610 if (IsAccessCheckNeeded() &&
2611 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2612 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2613 return Heap::undefined_value();
2614 }
2615
2616 if (IsJSGlobalProxy()) {
2617 Object* proto = GetPrototype();
2618 if (proto->IsNull()) return this;
2619 ASSERT(proto->IsJSGlobalObject());
2620 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
2621 fun, attributes);
2622 }
2623
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002624 Object* array = DefineGetterSetter(name, attributes);
2625 if (array->IsFailure() || array->IsUndefined()) return array;
2626 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
2627 return this;
2628}
2629
2630
2631Object* JSObject::LookupAccessor(String* name, bool is_getter) {
2632 // Make sure that the top context does not change when doing callbacks or
2633 // interceptor calls.
2634 AssertNoContextChange ncc;
2635
2636 // Check access rights if needed.
2637 if (IsAccessCheckNeeded() &&
2638 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
2639 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
2640 return Heap::undefined_value();
2641 }
2642
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002643 // Make the lookup and include prototypes.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002644 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
2645 uint32_t index;
2646 if (name->AsArrayIndex(&index)) {
2647 for (Object* obj = this;
2648 obj != Heap::null_value();
2649 obj = JSObject::cast(obj)->GetPrototype()) {
2650 JSObject* jsObject = JSObject::cast(obj);
2651 if (!jsObject->HasFastElements()) {
2652 Dictionary* dictionary = jsObject->element_dictionary();
2653 int entry = dictionary->FindNumberEntry(index);
2654 if (entry != -1) {
2655 Object* element = dictionary->ValueAt(entry);
2656 PropertyDetails details = dictionary->DetailsAt(entry);
2657 if (details.type() == CALLBACKS) {
2658 // Only accessors allowed as elements.
2659 return FixedArray::cast(element)->get(accessor_index);
2660 }
2661 }
2662 }
2663 }
2664 } else {
2665 for (Object* obj = this;
2666 obj != Heap::null_value();
2667 obj = JSObject::cast(obj)->GetPrototype()) {
2668 LookupResult result;
2669 JSObject::cast(obj)->LocalLookup(name, &result);
2670 if (result.IsValid()) {
2671 if (result.IsReadOnly()) return Heap::undefined_value();
2672 if (result.type() == CALLBACKS) {
2673 Object* obj = result.GetCallbackObject();
2674 if (obj->IsFixedArray()) {
2675 return FixedArray::cast(obj)->get(accessor_index);
2676 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002677 }
2678 }
2679 }
2680 }
2681 return Heap::undefined_value();
2682}
2683
2684
2685Object* JSObject::SlowReverseLookup(Object* value) {
2686 if (HasFastProperties()) {
2687 for (DescriptorReader r(map()->instance_descriptors());
2688 !r.eos();
2689 r.advance()) {
2690 if (r.type() == FIELD) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002691 if (FastPropertyAt(r.GetFieldIndex()) == value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002692 return r.GetKey();
2693 }
2694 } else if (r.type() == CONSTANT_FUNCTION) {
2695 if (r.GetConstantFunction() == value) {
2696 return r.GetKey();
2697 }
2698 }
2699 }
2700 return Heap::undefined_value();
2701 } else {
2702 return property_dictionary()->SlowReverseLookup(value);
2703 }
2704}
2705
2706
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002707Object* Map::CopyDropDescriptors() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708 Object* result = Heap::AllocateMap(instance_type(), instance_size());
2709 if (result->IsFailure()) return result;
2710 Map::cast(result)->set_prototype(prototype());
2711 Map::cast(result)->set_constructor(constructor());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002712 // Don't copy descriptors, so map transitions always remain a forest.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002713 // If we retained the same descriptors we would have two maps
2714 // pointing to the same transition which is bad because the garbage
2715 // collector relies on being able to reverse pointers from transitions
2716 // to maps. If properties need to be retained use CopyDropTransitions.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002717 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002718 // Please note instance_type and instance_size are set when allocated.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002719 Map::cast(result)->set_inobject_properties(inobject_properties());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002720 Map::cast(result)->set_unused_property_fields(unused_property_fields());
2721 Map::cast(result)->set_bit_field(bit_field());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002722 Map::cast(result)->set_bit_field2(bit_field2());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002723 Map::cast(result)->ClearCodeCache();
2724 return result;
2725}
2726
2727
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002728Object* Map::CopyDropTransitions() {
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002729 Object* new_map = CopyDropDescriptors();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002730 if (new_map->IsFailure()) return new_map;
2731 Object* descriptors = instance_descriptors()->RemoveTransitions();
2732 if (descriptors->IsFailure()) return descriptors;
2733 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
2734 return cast(new_map);
2735}
2736
2737
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002738Object* Map::UpdateCodeCache(String* name, Code* code) {
kasper.lund7276f142008-07-30 08:49:36 +00002739 ASSERT(code->ic_state() == MONOMORPHIC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002740 FixedArray* cache = code_cache();
2741
2742 // When updating the code cache we disregard the type encoded in the
2743 // flags. This allows call constant stubs to overwrite call field
2744 // stubs, etc.
2745 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
2746
2747 // First check whether we can update existing code cache without
2748 // extending it.
2749 int length = cache->length();
ager@chromium.org236ad962008-09-25 09:45:57 +00002750 int deleted_index = -1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002751 for (int i = 0; i < length; i += 2) {
2752 Object* key = cache->get(i);
ager@chromium.org236ad962008-09-25 09:45:57 +00002753 if (key->IsNull()) {
2754 if (deleted_index < 0) deleted_index = i;
2755 continue;
2756 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002757 if (key->IsUndefined()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00002758 if (deleted_index >= 0) i = deleted_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002759 cache->set(i + 0, name);
2760 cache->set(i + 1, code);
2761 return this;
2762 }
2763 if (name->Equals(String::cast(key))) {
2764 Code::Flags found = Code::cast(cache->get(i + 1))->flags();
2765 if (Code::RemoveTypeFromFlags(found) == flags) {
2766 cache->set(i + 1, code);
2767 return this;
2768 }
2769 }
2770 }
2771
ager@chromium.org236ad962008-09-25 09:45:57 +00002772 // Reached the end of the code cache. If there were deleted
2773 // elements, reuse the space for the first of them.
2774 if (deleted_index >= 0) {
2775 cache->set(deleted_index + 0, name);
2776 cache->set(deleted_index + 1, code);
2777 return this;
2778 }
2779
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002780 // Extend the code cache with some new entries (at least one).
2781 int new_length = length + ((length >> 1) & ~1) + 2;
2782 ASSERT((new_length & 1) == 0); // must be a multiple of two
2783 Object* result = cache->CopySize(new_length);
2784 if (result->IsFailure()) return result;
2785
2786 // Add the (name, code) pair to the new cache.
2787 cache = FixedArray::cast(result);
2788 cache->set(length + 0, name);
2789 cache->set(length + 1, code);
2790 set_code_cache(cache);
2791 return this;
2792}
2793
2794
2795Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
2796 FixedArray* cache = code_cache();
2797 int length = cache->length();
2798 for (int i = 0; i < length; i += 2) {
2799 Object* key = cache->get(i);
ager@chromium.org236ad962008-09-25 09:45:57 +00002800 // Skip deleted elements.
2801 if (key->IsNull()) continue;
2802 if (key->IsUndefined()) return key;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002803 if (name->Equals(String::cast(key))) {
2804 Code* code = Code::cast(cache->get(i + 1));
2805 if (code->flags() == flags) return code;
2806 }
2807 }
2808 return Heap::undefined_value();
2809}
2810
2811
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002812int Map::IndexInCodeCache(Code* code) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002813 FixedArray* array = code_cache();
2814 int len = array->length();
2815 for (int i = 0; i < len; i += 2) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002816 if (array->get(i + 1) == code) return i + 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002817 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002818 return -1;
2819}
2820
2821
2822void Map::RemoveFromCodeCache(int index) {
2823 FixedArray* array = code_cache();
2824 ASSERT(array->length() >= index && array->get(index)->IsCode());
ager@chromium.org236ad962008-09-25 09:45:57 +00002825 // Use null instead of undefined for deleted elements to distinguish
2826 // deleted elements from unused elements. This distinction is used
2827 // when looking up in the cache and when updating the cache.
2828 array->set_null(index - 1); // key
2829 array->set_null(index); // code
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002830}
2831
2832
2833void FixedArray::FixedArrayIterateBody(ObjectVisitor* v) {
2834 IteratePointers(v, kHeaderSize, kHeaderSize + length() * kPointerSize);
2835}
2836
2837
2838static bool HasKey(FixedArray* array, Object* key) {
2839 int len0 = array->length();
2840 for (int i = 0; i < len0; i++) {
2841 Object* element = array->get(i);
2842 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
2843 if (element->IsString() &&
2844 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
2845 return true;
2846 }
2847 }
2848 return false;
2849}
2850
2851
2852Object* FixedArray::AddKeysFromJSArray(JSArray* array) {
2853 // Remove array holes from array if any.
2854 Object* object = array->RemoveHoles();
2855 if (object->IsFailure()) return object;
2856 JSArray* compacted_array = JSArray::cast(object);
2857
2858 // Allocate a temporary fixed array.
2859 int compacted_array_length = Smi::cast(compacted_array->length())->value();
2860 object = Heap::AllocateFixedArray(compacted_array_length);
2861 if (object->IsFailure()) return object;
2862 FixedArray* key_array = FixedArray::cast(object);
2863
2864 // Copy the elements from the JSArray to the temporary fixed array.
2865 for (int i = 0; i < compacted_array_length; i++) {
2866 key_array->set(i, compacted_array->GetElement(i));
2867 }
2868
2869 // Compute the union of this and the temporary fixed array.
2870 return UnionOfKeys(key_array);
2871}
2872
2873
2874Object* FixedArray::UnionOfKeys(FixedArray* other) {
2875 int len0 = length();
2876 int len1 = other->length();
2877 // Optimize if either is empty.
2878 if (len0 == 0) return other;
2879 if (len1 == 0) return this;
2880
2881 // Compute how many elements are not in this.
2882 int extra = 0;
2883 for (int y = 0; y < len1; y++) {
2884 if (!HasKey(this, other->get(y))) extra++;
2885 }
2886
2887 // Allocate the result
2888 Object* obj = Heap::AllocateFixedArray(len0 + extra);
2889 if (obj->IsFailure()) return obj;
2890 // Fill in the content
2891 FixedArray* result = FixedArray::cast(obj);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002892 WriteBarrierMode mode = result->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002893 for (int i = 0; i < len0; i++) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002894 result->set(i, get(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002895 }
2896 // Fill in the extra keys.
2897 int index = 0;
2898 for (int y = 0; y < len1; y++) {
2899 if (!HasKey(this, other->get(y))) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002900 result->set(len0 + index, other->get(y), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002901 index++;
2902 }
2903 }
2904 ASSERT(extra == index);
2905 return result;
2906}
2907
2908
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002909Object* FixedArray::CopySize(int new_length) {
2910 if (new_length == 0) return Heap::empty_fixed_array();
2911 Object* obj = Heap::AllocateFixedArray(new_length);
2912 if (obj->IsFailure()) return obj;
2913 FixedArray* result = FixedArray::cast(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002914 // Copy the content
2915 int len = length();
2916 if (new_length < len) len = new_length;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002917 result->set_map(map());
2918 WriteBarrierMode mode = result->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002919 for (int i = 0; i < len; i++) {
2920 result->set(i, get(i), mode);
2921 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002922 return result;
2923}
2924
2925
2926void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
2927 WriteBarrierMode mode = dest->GetWriteBarrierMode();
2928 for (int index = 0; index < len; index++) {
2929 dest->set(dest_pos+index, get(pos+index), mode);
2930 }
2931}
2932
2933
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002934#ifdef DEBUG
2935bool FixedArray::IsEqualTo(FixedArray* other) {
2936 if (length() != other->length()) return false;
2937 for (int i = 0 ; i < length(); ++i) {
2938 if (get(i) != other->get(i)) return false;
2939 }
2940 return true;
2941}
2942#endif
2943
2944
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002945Object* DescriptorArray::Allocate(int number_of_descriptors) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002946 if (number_of_descriptors == 0) {
2947 return Heap::empty_descriptor_array();
2948 }
2949 // Allocate the array of keys.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002950 Object* array = Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
2951 if (array->IsFailure()) return array;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002952 // Do not use DescriptorArray::cast on incomplete object.
2953 FixedArray* result = FixedArray::cast(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002954
2955 // Allocate the content array and set it in the descriptor array.
2956 array = Heap::AllocateFixedArray(number_of_descriptors << 1);
2957 if (array->IsFailure()) return array;
2958 result->set(kContentArrayIndex, array);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002959 result->set(kEnumerationIndexIndex,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002960 Smi::FromInt(PropertyDetails::kInitialIndex),
2961 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002962 return result;
2963}
2964
2965
2966void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
2967 FixedArray* new_cache) {
2968 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
2969 if (HasEnumCache()) {
2970 FixedArray::cast(get(kEnumerationIndexIndex))->
2971 set(kEnumCacheBridgeCacheIndex, new_cache);
2972 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002973 if (IsEmpty()) return; // Do nothing for empty descriptor array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002974 FixedArray::cast(bridge_storage)->
2975 set(kEnumCacheBridgeCacheIndex, new_cache);
2976 fast_set(FixedArray::cast(bridge_storage),
2977 kEnumCacheBridgeEnumIndex,
2978 get(kEnumerationIndexIndex));
2979 set(kEnumerationIndexIndex, bridge_storage);
2980 }
2981}
2982
2983
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002984Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
2985 TransitionFlag transition_flag) {
2986 // Transitions are only kept when inserting another transition.
2987 // This precondition is not required by this function's implementation, but
2988 // is currently required by the semantics of maps, so we check it.
2989 // Conversely, we filter after replacing, so replacing a transition and
2990 // removing all other transitions is not supported.
2991 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
2992 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
2993 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002994
2995 // Ensure the key is a symbol.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002996 Object* result = descriptor->KeyToSymbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002997 if (result->IsFailure()) return result;
2998
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002999 int transitions = 0;
3000 int null_descriptors = 0;
3001 if (remove_transitions) {
3002 for (DescriptorReader r(this); !r.eos(); r.advance()) {
3003 if (r.IsTransition()) transitions++;
3004 if (r.IsNullDescriptor()) null_descriptors++;
3005 }
3006 } else {
3007 for (DescriptorReader r(this); !r.eos(); r.advance()) {
3008 if (r.IsNullDescriptor()) null_descriptors++;
3009 }
3010 }
3011 int new_size = number_of_descriptors() - transitions - null_descriptors;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003012
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003013 // If key is in descriptor, we replace it in-place when filtering.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003014 // Count a null descriptor for key as inserted, not replaced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003015 int index = Search(descriptor->GetKey());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003016 const bool inserting = (index == kNotFound);
3017 const bool replacing = !inserting;
3018 bool keep_enumeration_index = false;
3019 if (inserting) {
3020 ++new_size;
3021 }
3022 if (replacing) {
3023 // We are replacing an existing descriptor. We keep the enumeration
3024 // index of a visible property.
3025 PropertyType t = PropertyDetails(GetDetails(index)).type();
3026 if (t == CONSTANT_FUNCTION ||
3027 t == FIELD ||
3028 t == CALLBACKS ||
3029 t == INTERCEPTOR) {
3030 keep_enumeration_index = true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003031 } else if (remove_transitions) {
3032 // Replaced descriptor has been counted as removed if it is
3033 // a transition that will be replaced. Adjust count in this case.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003034 ++new_size;
3035 }
3036 }
3037 result = Allocate(new_size);
3038 if (result->IsFailure()) return result;
3039 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003040 // Set the enumeration index in the descriptors and set the enumeration index
3041 // in the result.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003042 int enumeration_index = NextEnumerationIndex();
3043 if (!descriptor->GetDetails().IsTransition()) {
3044 if (keep_enumeration_index) {
3045 descriptor->SetEnumerationIndex(
3046 PropertyDetails(GetDetails(index)).index());
3047 } else {
3048 descriptor->SetEnumerationIndex(enumeration_index);
3049 ++enumeration_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003050 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003051 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003052 new_descriptors->SetNextEnumerationIndex(enumeration_index);
3053
3054 // Copy the descriptors, filtering out transitions and null descriptors,
3055 // and inserting or replacing a descriptor.
3056 DescriptorWriter w(new_descriptors);
3057 DescriptorReader r(this);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003058 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003059
3060 for (; !r.eos(); r.advance()) {
3061 if (r.GetKey()->Hash() > descriptor_hash ||
3062 r.GetKey() == descriptor->GetKey()) break;
3063 if (r.IsNullDescriptor()) continue;
3064 if (remove_transitions && r.IsTransition()) continue;
3065 w.WriteFrom(&r);
3066 }
3067 w.Write(descriptor);
3068 if (replacing) {
3069 ASSERT(r.GetKey() == descriptor->GetKey());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003070 r.advance();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003071 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003072 ASSERT(r.eos() ||
3073 r.GetKey()->Hash() > descriptor_hash ||
3074 r.IsNullDescriptor());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003075 }
3076 for (; !r.eos(); r.advance()) {
3077 if (r.IsNullDescriptor()) continue;
3078 if (remove_transitions && r.IsTransition()) continue;
3079 w.WriteFrom(&r);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003080 }
3081 ASSERT(w.eos());
3082
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003083 return new_descriptors;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003084}
3085
3086
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003087Object* DescriptorArray::RemoveTransitions() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003088 // Remove all transitions and null descriptors. Return a copy of the array
3089 // with all transitions removed, or a Failure object if the new array could
3090 // not be allocated.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003091
3092 // Compute the size of the map transition entries to be removed.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003093 int num_removed = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003094 for (DescriptorReader r(this); !r.eos(); r.advance()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003095 if (!r.IsProperty()) num_removed++;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003096 }
3097
3098 // Allocate the new descriptor array.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003099 Object* result = Allocate(number_of_descriptors() - num_removed);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003100 if (result->IsFailure()) return result;
3101 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
3102
3103 // Copy the content.
3104 DescriptorWriter w(new_descriptors);
3105 for (DescriptorReader r(this); !r.eos(); r.advance()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003106 if (r.IsProperty()) w.WriteFrom(&r);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003107 }
3108 ASSERT(w.eos());
3109
3110 return new_descriptors;
3111}
3112
3113
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003114void DescriptorArray::Sort() {
3115 // In-place heap sort.
3116 int len = number_of_descriptors();
3117
3118 // Bottom-up max-heap construction.
3119 for (int i = 1; i < len; ++i) {
3120 int child_index = i;
3121 while (child_index > 0) {
3122 int parent_index = ((child_index + 1) >> 1) - 1;
3123 uint32_t parent_hash = GetKey(parent_index)->Hash();
3124 uint32_t child_hash = GetKey(child_index)->Hash();
3125 if (parent_hash < child_hash) {
3126 Swap(parent_index, child_index);
3127 } else {
3128 break;
3129 }
3130 child_index = parent_index;
3131 }
3132 }
3133
3134 // Extract elements and create sorted array.
3135 for (int i = len - 1; i > 0; --i) {
3136 // Put max element at the back of the array.
3137 Swap(0, i);
3138 // Sift down the new top element.
3139 int parent_index = 0;
3140 while (true) {
3141 int child_index = ((parent_index + 1) << 1) - 1;
3142 if (child_index >= i) break;
3143 uint32_t child1_hash = GetKey(child_index)->Hash();
3144 uint32_t child2_hash = GetKey(child_index + 1)->Hash();
3145 uint32_t parent_hash = GetKey(parent_index)->Hash();
3146 if (child_index + 1 >= i || child1_hash > child2_hash) {
3147 if (parent_hash > child1_hash) break;
3148 Swap(parent_index, child_index);
3149 parent_index = child_index;
3150 } else {
3151 if (parent_hash > child2_hash) break;
3152 Swap(parent_index, child_index + 1);
3153 parent_index = child_index + 1;
3154 }
3155 }
3156 }
3157
3158 SLOW_ASSERT(IsSortedNoDuplicates());
3159}
3160
3161
3162int DescriptorArray::BinarySearch(String* name, int low, int high) {
3163 uint32_t hash = name->Hash();
3164
3165 while (low <= high) {
3166 int mid = (low + high) / 2;
3167 String* mid_name = GetKey(mid);
3168 uint32_t mid_hash = mid_name->Hash();
3169
3170 if (mid_hash > hash) {
3171 high = mid - 1;
3172 continue;
3173 }
3174 if (mid_hash < hash) {
3175 low = mid + 1;
3176 continue;
3177 }
3178 // Found an element with the same hash-code.
3179 ASSERT(hash == mid_hash);
3180 // There might be more, so we find the first one and
3181 // check them all to see if we have a match.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003182 if (name == mid_name && !is_null_descriptor(mid)) return mid;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003183 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
3184 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003185 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003186 }
3187 break;
3188 }
3189 return kNotFound;
3190}
3191
3192
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003193int DescriptorArray::LinearSearch(String* name, int len) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00003194 uint32_t hash = name->Hash();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003195 for (int number = 0; number < len; number++) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00003196 String* entry = GetKey(number);
3197 if ((entry->Hash() == hash) &&
3198 name->Equals(entry) &&
3199 !is_null_descriptor(number)) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003200 return number;
3201 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003202 }
3203 return kNotFound;
3204}
3205
3206
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003207#ifdef DEBUG
3208bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
3209 if (IsEmpty()) return other->IsEmpty();
3210 if (other->IsEmpty()) return false;
3211 if (length() != other->length()) return false;
3212 for (int i = 0; i < length(); ++i) {
3213 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
3214 }
3215 return GetContentArray()->IsEqualTo(other->GetContentArray());
3216}
3217#endif
3218
3219
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003220static StaticResource<StringInputBuffer> string_input_buffer;
3221
3222
3223bool String::LooksValid() {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003224 if (!Heap::Contains(this)) return false;
3225 return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003226}
3227
3228
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003229int String::Utf8Length() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003230 if (StringShape(this).IsAsciiRepresentation()) return length();
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003231 // Attempt to flatten before accessing the string. It probably
3232 // doesn't make Utf8Length faster, but it is very likely that
3233 // the string will be accessed later (for example by WriteUtf8)
3234 // so it's still a good idea.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003235 TryFlattenIfNotFlat();
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003236 Access<StringInputBuffer> buffer(&string_input_buffer);
3237 buffer->Reset(0, this);
3238 int result = 0;
3239 while (buffer->has_more())
3240 result += unibrow::Utf8::Length(buffer->GetNext());
3241 return result;
3242}
3243
3244
ager@chromium.org7c537e22008-10-16 08:43:32 +00003245Vector<const char> String::ToAsciiVector() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003246 ASSERT(StringShape(this).IsAsciiRepresentation());
3247 ASSERT(IsFlat());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003248
3249 int offset = 0;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003250 int length = this->length();
3251 StringRepresentationTag string_tag = StringShape(this).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003252 String* string = this;
3253 if (string_tag == kSlicedStringTag) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003254 SlicedString* sliced = SlicedString::cast(string);
3255 offset += sliced->start();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003256 string = sliced->buffer();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003257 string_tag = StringShape(string).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003258 } else if (string_tag == kConsStringTag) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003259 ConsString* cons = ConsString::cast(string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003260 ASSERT(cons->second()->length() == 0);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003261 string = cons->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003262 string_tag = StringShape(string).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003263 }
3264 if (string_tag == kSeqStringTag) {
3265 SeqAsciiString* seq = SeqAsciiString::cast(string);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003266 char* start = seq->GetChars();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003267 return Vector<const char>(start + offset, length);
3268 }
3269 ASSERT(string_tag == kExternalStringTag);
3270 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
3271 const char* start = ext->resource()->data();
3272 return Vector<const char>(start + offset, length);
3273}
3274
3275
3276Vector<const uc16> String::ToUC16Vector() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003277 ASSERT(StringShape(this).IsTwoByteRepresentation());
3278 ASSERT(IsFlat());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003279
3280 int offset = 0;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003281 int length = this->length();
3282 StringRepresentationTag string_tag = StringShape(this).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003283 String* string = this;
3284 if (string_tag == kSlicedStringTag) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003285 SlicedString* sliced = SlicedString::cast(string);
3286 offset += sliced->start();
3287 string = String::cast(sliced->buffer());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003288 string_tag = StringShape(string).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003289 } else if (string_tag == kConsStringTag) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003290 ConsString* cons = ConsString::cast(string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003291 ASSERT(cons->second()->length() == 0);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003292 string = cons->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003293 string_tag = StringShape(string).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003294 }
3295 if (string_tag == kSeqStringTag) {
3296 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003297 return Vector<const uc16>(seq->GetChars() + offset, length);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003298 }
3299 ASSERT(string_tag == kExternalStringTag);
3300 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
kasperl@chromium.orgacae3782009-04-11 09:17:08 +00003301 // This is a workaround for Chromium bug 9746: http://crbug.com/9746
3302 // For external strings with a deleted resource we return a special
3303 // Vector which will not compare to any string when doing SymbolTable
3304 // lookups.
3305 if (ext->resource() == NULL) {
3306 return Vector<const uc16>(NULL, length);
3307 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003308 const uc16* start =
3309 reinterpret_cast<const uc16*>(ext->resource()->data());
3310 return Vector<const uc16>(start + offset, length);
3311}
3312
3313
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003314SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3315 RobustnessFlag robust_flag,
3316 int offset,
3317 int length,
3318 int* length_return) {
3319 ASSERT(NativeAllocationChecker::allocation_allowed());
3320 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
3321 return SmartPointer<char>(NULL);
3322 }
3323
3324 // Negative length means the to the end of the string.
3325 if (length < 0) length = kMaxInt - offset;
3326
3327 // Compute the size of the UTF-8 string. Start at the specified offset.
3328 Access<StringInputBuffer> buffer(&string_input_buffer);
3329 buffer->Reset(offset, this);
3330 int character_position = offset;
3331 int utf8_bytes = 0;
3332 while (buffer->has_more()) {
3333 uint16_t character = buffer->GetNext();
3334 if (character_position < offset + length) {
3335 utf8_bytes += unibrow::Utf8::Length(character);
3336 }
3337 character_position++;
3338 }
3339
3340 if (length_return) {
3341 *length_return = utf8_bytes;
3342 }
3343
3344 char* result = NewArray<char>(utf8_bytes + 1);
3345
3346 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
3347 buffer->Rewind();
3348 buffer->Seek(offset);
3349 character_position = offset;
3350 int utf8_byte_position = 0;
3351 while (buffer->has_more()) {
3352 uint16_t character = buffer->GetNext();
3353 if (character_position < offset + length) {
3354 if (allow_nulls == DISALLOW_NULLS && character == 0) {
3355 character = ' ';
3356 }
3357 utf8_byte_position +=
3358 unibrow::Utf8::Encode(result + utf8_byte_position, character);
3359 }
3360 character_position++;
3361 }
3362 result[utf8_byte_position] = 0;
3363 return SmartPointer<char>(result);
3364}
3365
3366
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00003367SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
3368 RobustnessFlag robust_flag,
3369 int* length_return) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003370 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
3371}
3372
3373
3374const uc16* String::GetTwoByteData() {
3375 return GetTwoByteData(0);
3376}
3377
3378
3379const uc16* String::GetTwoByteData(unsigned start) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003380 ASSERT(!StringShape(this).IsAsciiRepresentation());
3381 switch (StringShape(this).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003382 case kSeqStringTag:
ager@chromium.org7c537e22008-10-16 08:43:32 +00003383 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003384 case kExternalStringTag:
3385 return ExternalTwoByteString::cast(this)->
3386 ExternalTwoByteStringGetData(start);
3387 case kSlicedStringTag: {
3388 SlicedString* sliced_string = SlicedString::cast(this);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003389 String* buffer = sliced_string->buffer();
3390 if (StringShape(buffer).IsCons()) {
3391 ConsString* cs = ConsString::cast(buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003392 // Flattened string.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003393 ASSERT(cs->second()->length() == 0);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003394 buffer = cs->first();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003395 }
3396 return buffer->GetTwoByteData(start + sliced_string->start());
3397 }
3398 case kConsStringTag:
3399 UNREACHABLE();
3400 return NULL;
3401 }
3402 UNREACHABLE();
3403 return NULL;
3404}
3405
3406
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003407SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003408 ASSERT(NativeAllocationChecker::allocation_allowed());
3409
3410 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003411 return SmartPointer<uc16>();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003412 }
3413
3414 Access<StringInputBuffer> buffer(&string_input_buffer);
3415 buffer->Reset(this);
3416
3417 uc16* result = NewArray<uc16>(length() + 1);
3418
3419 int i = 0;
3420 while (buffer->has_more()) {
3421 uint16_t character = buffer->GetNext();
3422 result[i++] = character;
3423 }
3424 result[i] = 0;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003425 return SmartPointer<uc16>(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003426}
3427
3428
ager@chromium.org7c537e22008-10-16 08:43:32 +00003429const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003430 return reinterpret_cast<uc16*>(
3431 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
3432}
3433
3434
ager@chromium.org7c537e22008-10-16 08:43:32 +00003435void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003436 unsigned* offset_ptr,
3437 unsigned max_chars) {
3438 unsigned chars_read = 0;
3439 unsigned offset = *offset_ptr;
3440 while (chars_read < max_chars) {
3441 uint16_t c = *reinterpret_cast<uint16_t*>(
3442 reinterpret_cast<char*>(this) -
3443 kHeapObjectTag + kHeaderSize + offset * kShortSize);
3444 if (c <= kMaxAsciiCharCode) {
3445 // Fast case for ASCII characters. Cursor is an input output argument.
3446 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
3447 rbb->util_buffer,
3448 rbb->capacity,
3449 rbb->cursor)) {
3450 break;
3451 }
3452 } else {
3453 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
3454 rbb->util_buffer,
3455 rbb->capacity,
3456 rbb->cursor)) {
3457 break;
3458 }
3459 }
3460 offset++;
3461 chars_read++;
3462 }
3463 *offset_ptr = offset;
3464 rbb->remaining += chars_read;
3465}
3466
3467
ager@chromium.org7c537e22008-10-16 08:43:32 +00003468const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
3469 unsigned* remaining,
3470 unsigned* offset_ptr,
3471 unsigned max_chars) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003472 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
3473 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
3474 *remaining = max_chars;
3475 *offset_ptr += max_chars;
3476 return b;
3477}
3478
3479
3480// This will iterate unless the block of string data spans two 'halves' of
3481// a ConsString, in which case it will recurse. Since the block of string
3482// data to be read has a maximum size this limits the maximum recursion
3483// depth to something sane. Since C++ does not have tail call recursion
3484// elimination, the iteration must be explicit. Since this is not an
3485// -IntoBuffer method it can delegate to one of the efficient
3486// *AsciiStringReadBlock routines.
3487const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
3488 unsigned* offset_ptr,
3489 unsigned max_chars) {
3490 ConsString* current = this;
3491 unsigned offset = *offset_ptr;
3492 int offset_correction = 0;
3493
3494 while (true) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003495 String* left = current->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003496 unsigned left_length = (unsigned)left->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003497 if (left_length > offset &&
3498 (max_chars <= left_length - offset ||
3499 (rbb->capacity <= left_length - offset &&
3500 (max_chars = left_length - offset, true)))) { // comma operator!
3501 // Left hand side only - iterate unless we have reached the bottom of
3502 // the cons tree. The assignment on the left of the comma operator is
3503 // in order to make use of the fact that the -IntoBuffer routines can
3504 // produce at most 'capacity' characters. This enables us to postpone
3505 // the point where we switch to the -IntoBuffer routines (below) in order
3506 // to maximize the chances of delegating a big chunk of work to the
3507 // efficient *AsciiStringReadBlock routines.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003508 if (StringShape(left).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003509 current = ConsString::cast(left);
3510 continue;
3511 } else {
3512 const unibrow::byte* answer =
3513 String::ReadBlock(left, rbb, &offset, max_chars);
3514 *offset_ptr = offset + offset_correction;
3515 return answer;
3516 }
3517 } else if (left_length <= offset) {
3518 // Right hand side only - iterate unless we have reached the bottom of
3519 // the cons tree.
ager@chromium.org870a0b62008-11-04 11:43:05 +00003520 String* right = current->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003521 offset -= left_length;
3522 offset_correction += left_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003523 if (StringShape(right).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003524 current = ConsString::cast(right);
3525 continue;
3526 } else {
3527 const unibrow::byte* answer =
3528 String::ReadBlock(right, rbb, &offset, max_chars);
3529 *offset_ptr = offset + offset_correction;
3530 return answer;
3531 }
3532 } else {
3533 // The block to be read spans two sides of the ConsString, so we call the
3534 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
3535 // are able to assemble data from several part strings because they use
3536 // the util_buffer to store their data and never return direct pointers
3537 // to their storage. We don't try to read more than the buffer capacity
3538 // here or we can get too much recursion.
3539 ASSERT(rbb->remaining == 0);
3540 ASSERT(rbb->cursor == 0);
3541 current->ConsStringReadBlockIntoBuffer(
3542 rbb,
3543 &offset,
3544 max_chars > rbb->capacity ? rbb->capacity : max_chars);
3545 *offset_ptr = offset + offset_correction;
3546 return rbb->util_buffer;
3547 }
3548 }
3549}
3550
3551
3552const unibrow::byte* SlicedString::SlicedStringReadBlock(ReadBlockBuffer* rbb,
3553 unsigned* offset_ptr,
3554 unsigned max_chars) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003555 String* backing = buffer();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003556 unsigned offset = start() + *offset_ptr;
3557 unsigned length = backing->length();
3558 if (max_chars > length - offset) {
3559 max_chars = length - offset;
3560 }
3561 const unibrow::byte* answer =
3562 String::ReadBlock(backing, rbb, &offset, max_chars);
3563 *offset_ptr = offset - start();
3564 return answer;
3565}
3566
3567
3568uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
3569 ASSERT(index >= 0 && index < length());
3570 return resource()->data()[index];
3571}
3572
3573
3574const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
3575 unsigned* remaining,
3576 unsigned* offset_ptr,
3577 unsigned max_chars) {
3578 // Cast const char* to unibrow::byte* (signedness difference).
3579 const unibrow::byte* b =
3580 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
3581 *remaining = max_chars;
3582 *offset_ptr += max_chars;
3583 return b;
3584}
3585
3586
3587const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
3588 unsigned start) {
3589 return resource()->data() + start;
3590}
3591
3592
3593uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
3594 ASSERT(index >= 0 && index < length());
3595 return resource()->data()[index];
3596}
3597
3598
3599void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
3600 ReadBlockBuffer* rbb,
3601 unsigned* offset_ptr,
3602 unsigned max_chars) {
3603 unsigned chars_read = 0;
3604 unsigned offset = *offset_ptr;
3605 const uint16_t* data = resource()->data();
3606 while (chars_read < max_chars) {
3607 uint16_t c = data[offset];
3608 if (c <= kMaxAsciiCharCode) {
ager@chromium.org80787b72009-04-17 10:24:24 +00003609 // Fast case for ASCII characters. Cursor is an input output argument.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003610 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
3611 rbb->util_buffer,
3612 rbb->capacity,
3613 rbb->cursor))
3614 break;
3615 } else {
3616 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
3617 rbb->util_buffer,
3618 rbb->capacity,
3619 rbb->cursor))
3620 break;
3621 }
3622 offset++;
3623 chars_read++;
3624 }
3625 *offset_ptr = offset;
3626 rbb->remaining += chars_read;
3627}
3628
3629
ager@chromium.org7c537e22008-10-16 08:43:32 +00003630void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003631 unsigned* offset_ptr,
3632 unsigned max_chars) {
3633 unsigned capacity = rbb->capacity - rbb->cursor;
3634 if (max_chars > capacity) max_chars = capacity;
3635 memcpy(rbb->util_buffer + rbb->cursor,
3636 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
3637 *offset_ptr * kCharSize,
3638 max_chars);
3639 rbb->remaining += max_chars;
3640 *offset_ptr += max_chars;
3641 rbb->cursor += max_chars;
3642}
3643
3644
3645void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
3646 ReadBlockBuffer* rbb,
3647 unsigned* offset_ptr,
3648 unsigned max_chars) {
3649 unsigned capacity = rbb->capacity - rbb->cursor;
3650 if (max_chars > capacity) max_chars = capacity;
3651 memcpy(rbb->util_buffer + rbb->cursor,
3652 resource()->data() + *offset_ptr,
3653 max_chars);
3654 rbb->remaining += max_chars;
3655 *offset_ptr += max_chars;
3656 rbb->cursor += max_chars;
3657}
3658
3659
3660// This method determines the type of string involved and then copies
3661// a whole chunk of characters into a buffer, or returns a pointer to a buffer
3662// where they can be found. The pointer is not necessarily valid across a GC
3663// (see AsciiStringReadBlock).
3664const unibrow::byte* String::ReadBlock(String* input,
3665 ReadBlockBuffer* rbb,
3666 unsigned* offset_ptr,
3667 unsigned max_chars) {
3668 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
3669 if (max_chars == 0) {
3670 rbb->remaining = 0;
3671 return NULL;
3672 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003673 switch (StringShape(input).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003674 case kSeqStringTag:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003675 if (StringShape(input).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003676 SeqAsciiString* str = SeqAsciiString::cast(input);
3677 return str->SeqAsciiStringReadBlock(&rbb->remaining,
3678 offset_ptr,
3679 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003680 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003681 SeqTwoByteString* str = SeqTwoByteString::cast(input);
3682 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
3683 offset_ptr,
3684 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003685 return rbb->util_buffer;
3686 }
3687 case kConsStringTag:
3688 return ConsString::cast(input)->ConsStringReadBlock(rbb,
3689 offset_ptr,
3690 max_chars);
3691 case kSlicedStringTag:
3692 return SlicedString::cast(input)->SlicedStringReadBlock(rbb,
3693 offset_ptr,
3694 max_chars);
3695 case kExternalStringTag:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003696 if (StringShape(input).IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003697 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
3698 &rbb->remaining,
3699 offset_ptr,
3700 max_chars);
3701 } else {
3702 ExternalTwoByteString::cast(input)->
3703 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
3704 offset_ptr,
3705 max_chars);
3706 return rbb->util_buffer;
3707 }
3708 default:
3709 break;
3710 }
3711
3712 UNREACHABLE();
3713 return 0;
3714}
3715
3716
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003717FlatStringReader* FlatStringReader::top_ = NULL;
3718
3719
3720FlatStringReader::FlatStringReader(Handle<String> str)
3721 : str_(str.location()),
3722 length_(str->length()),
3723 prev_(top_) {
3724 top_ = this;
3725 RefreshState();
3726}
3727
3728
3729FlatStringReader::FlatStringReader(Vector<const char> input)
3730 : str_(NULL),
3731 is_ascii_(true),
3732 length_(input.length()),
3733 start_(input.start()),
3734 prev_(top_) {
3735 top_ = this;
3736}
3737
3738
3739FlatStringReader::~FlatStringReader() {
3740 ASSERT_EQ(top_, this);
3741 top_ = prev_;
3742}
3743
3744
3745void FlatStringReader::RefreshState() {
3746 if (str_ == NULL) return;
3747 Handle<String> str(str_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003748 ASSERT(str->IsFlat());
3749 is_ascii_ = StringShape(*str).IsAsciiRepresentation();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003750 if (is_ascii_) {
3751 start_ = str->ToAsciiVector().start();
3752 } else {
3753 start_ = str->ToUC16Vector().start();
3754 }
3755}
3756
3757
3758void FlatStringReader::PostGarbageCollectionProcessing() {
3759 FlatStringReader* current = top_;
3760 while (current != NULL) {
3761 current->RefreshState();
3762 current = current->prev_;
3763 }
3764}
3765
3766
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003767void StringInputBuffer::Seek(unsigned pos) {
3768 Reset(pos, input_);
3769}
3770
3771
3772void SafeStringInputBuffer::Seek(unsigned pos) {
3773 Reset(pos, input_);
3774}
3775
3776
3777// This method determines the type of string involved and then copies
3778// a whole chunk of characters into a buffer. It can be used with strings
3779// that have been glued together to form a ConsString and which must cooperate
3780// to fill up a buffer.
3781void String::ReadBlockIntoBuffer(String* input,
3782 ReadBlockBuffer* rbb,
3783 unsigned* offset_ptr,
3784 unsigned max_chars) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003785 ASSERT(*offset_ptr <= (unsigned)input->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003786 if (max_chars == 0) return;
3787
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003788 switch (StringShape(input).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003789 case kSeqStringTag:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003790 if (StringShape(input).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003791 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003792 offset_ptr,
3793 max_chars);
3794 return;
3795 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003796 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003797 offset_ptr,
3798 max_chars);
3799 return;
3800 }
3801 case kConsStringTag:
3802 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
3803 offset_ptr,
3804 max_chars);
3805 return;
3806 case kSlicedStringTag:
3807 SlicedString::cast(input)->SlicedStringReadBlockIntoBuffer(rbb,
3808 offset_ptr,
3809 max_chars);
3810 return;
3811 case kExternalStringTag:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003812 if (StringShape(input).IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003813 ExternalAsciiString::cast(input)->
3814 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
3815 } else {
3816 ExternalTwoByteString::cast(input)->
3817 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
3818 offset_ptr,
3819 max_chars);
3820 }
3821 return;
3822 default:
3823 break;
3824 }
3825
3826 UNREACHABLE();
3827 return;
3828}
3829
3830
3831const unibrow::byte* String::ReadBlock(String* input,
3832 unibrow::byte* util_buffer,
3833 unsigned capacity,
3834 unsigned* remaining,
3835 unsigned* offset_ptr) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003836 ASSERT(*offset_ptr <= (unsigned)input->length());
3837 unsigned chars = input->length() - *offset_ptr;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003838 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
3839 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003840 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003841 *remaining = rbb.remaining;
3842 return answer;
3843}
3844
3845
3846const unibrow::byte* String::ReadBlock(String** raw_input,
3847 unibrow::byte* util_buffer,
3848 unsigned capacity,
3849 unsigned* remaining,
3850 unsigned* offset_ptr) {
3851 Handle<String> input(raw_input);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003852 ASSERT(*offset_ptr <= (unsigned)input->length());
3853 unsigned chars = input->length() - *offset_ptr;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003854 if (chars > capacity) chars = capacity;
3855 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
3856 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003857 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003858 *remaining = rbb.remaining;
3859 return rbb.util_buffer;
3860}
3861
3862
3863// This will iterate unless the block of string data spans two 'halves' of
3864// a ConsString, in which case it will recurse. Since the block of string
3865// data to be read has a maximum size this limits the maximum recursion
3866// depth to something sane. Since C++ does not have tail call recursion
3867// elimination, the iteration must be explicit.
3868void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
3869 unsigned* offset_ptr,
3870 unsigned max_chars) {
3871 ConsString* current = this;
3872 unsigned offset = *offset_ptr;
3873 int offset_correction = 0;
3874
3875 while (true) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003876 String* left = current->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003877 unsigned left_length = (unsigned)left->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003878 if (left_length > offset &&
3879 max_chars <= left_length - offset) {
3880 // Left hand side only - iterate unless we have reached the bottom of
3881 // the cons tree.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003882 if (StringShape(left).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003883 current = ConsString::cast(left);
3884 continue;
3885 } else {
3886 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
3887 *offset_ptr = offset + offset_correction;
3888 return;
3889 }
3890 } else if (left_length <= offset) {
3891 // Right hand side only - iterate unless we have reached the bottom of
3892 // the cons tree.
3893 offset -= left_length;
3894 offset_correction += left_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003895 String* right = current->second();
3896 if (StringShape(right).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003897 current = ConsString::cast(right);
3898 continue;
3899 } else {
3900 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
3901 *offset_ptr = offset + offset_correction;
3902 return;
3903 }
3904 } else {
3905 // The block to be read spans two sides of the ConsString, so we recurse.
3906 // First recurse on the left.
3907 max_chars -= left_length - offset;
3908 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
3909 // We may have reached the max or there may not have been enough space
3910 // in the buffer for the characters in the left hand side.
3911 if (offset == left_length) {
3912 // Recurse on the right.
3913 String* right = String::cast(current->second());
3914 offset -= left_length;
3915 offset_correction += left_length;
3916 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
3917 }
3918 *offset_ptr = offset + offset_correction;
3919 return;
3920 }
3921 }
3922}
3923
3924
3925void SlicedString::SlicedStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
3926 unsigned* offset_ptr,
3927 unsigned max_chars) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003928 String* backing = buffer();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003929 unsigned offset = start() + *offset_ptr;
3930 unsigned length = backing->length();
3931 if (max_chars > length - offset) {
3932 max_chars = length - offset;
3933 }
3934 String::ReadBlockIntoBuffer(backing, rbb, &offset, max_chars);
3935 *offset_ptr = offset - start();
3936}
3937
3938
3939void ConsString::ConsStringIterateBody(ObjectVisitor* v) {
3940 IteratePointers(v, kFirstOffset, kSecondOffset + kPointerSize);
3941}
3942
3943
3944uint16_t ConsString::ConsStringGet(int index) {
3945 ASSERT(index >= 0 && index < this->length());
3946
3947 // Check for a flattened cons string
ager@chromium.org870a0b62008-11-04 11:43:05 +00003948 if (second()->length() == 0) {
3949 String* left = first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003950 return left->Get(index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003951 }
3952
3953 String* string = String::cast(this);
3954
3955 while (true) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003956 if (StringShape(string).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003957 ConsString* cons_string = ConsString::cast(string);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003958 String* left = cons_string->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003959 if (left->length() > index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003960 string = left;
3961 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003962 index -= left->length();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003963 string = cons_string->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003964 }
3965 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003966 return string->Get(index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003967 }
3968 }
3969
3970 UNREACHABLE();
3971 return 0;
3972}
3973
3974
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003975template <typename sinkchar>
3976void String::WriteToFlat(String* src,
3977 sinkchar* sink,
3978 int f,
3979 int t) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003980 String* source = src;
3981 int from = f;
3982 int to = t;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003983 while (true) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003984 ASSERT(0 <= from && from <= to && to <= source->length());
3985 switch (StringShape(source).full_representation_tag()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003986 case kAsciiStringTag | kExternalStringTag: {
3987 CopyChars(sink,
3988 ExternalAsciiString::cast(source)->resource()->data() + from,
3989 to - from);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003990 return;
3991 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003992 case kTwoByteStringTag | kExternalStringTag: {
3993 const uc16* data =
3994 ExternalTwoByteString::cast(source)->resource()->data();
3995 CopyChars(sink,
3996 data + from,
3997 to - from);
3998 return;
3999 }
4000 case kAsciiStringTag | kSeqStringTag: {
4001 CopyChars(sink,
4002 SeqAsciiString::cast(source)->GetChars() + from,
4003 to - from);
4004 return;
4005 }
4006 case kTwoByteStringTag | kSeqStringTag: {
4007 CopyChars(sink,
4008 SeqTwoByteString::cast(source)->GetChars() + from,
4009 to - from);
4010 return;
4011 }
4012 case kAsciiStringTag | kSlicedStringTag:
4013 case kTwoByteStringTag | kSlicedStringTag: {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004014 SlicedString* sliced_string = SlicedString::cast(source);
4015 int start = sliced_string->start();
4016 from += start;
4017 to += start;
4018 source = String::cast(sliced_string->buffer());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004019 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004020 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004021 case kAsciiStringTag | kConsStringTag:
4022 case kTwoByteStringTag | kConsStringTag: {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004023 ConsString* cons_string = ConsString::cast(source);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004024 String* first = cons_string->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004025 int boundary = first->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004026 if (to - boundary >= boundary - from) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004027 // Right hand side is longer. Recurse over left.
4028 if (from < boundary) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004029 WriteToFlat(first, sink, from, boundary);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004030 sink += boundary - from;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004031 from = 0;
4032 } else {
4033 from -= boundary;
4034 }
4035 to -= boundary;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004036 source = cons_string->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004037 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004038 // Left hand side is longer. Recurse over right.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004039 if (to > boundary) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004040 String* second = cons_string->second();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004041 WriteToFlat(second,
4042 sink + boundary - from,
4043 0,
4044 to - boundary);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004045 to = boundary;
4046 }
4047 source = first;
4048 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004049 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004050 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004051 }
4052 }
4053}
4054
4055
4056void SlicedString::SlicedStringIterateBody(ObjectVisitor* v) {
4057 IteratePointer(v, kBufferOffset);
4058}
4059
4060
4061uint16_t SlicedString::SlicedStringGet(int index) {
4062 ASSERT(index >= 0 && index < this->length());
4063 // Delegate to the buffer string.
ager@chromium.org870a0b62008-11-04 11:43:05 +00004064 String* underlying = buffer();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004065 return underlying->Get(start() + index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004066}
4067
4068
ager@chromium.org7c537e22008-10-16 08:43:32 +00004069template <typename IteratorA, typename IteratorB>
4070static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
4071 // General slow case check. We know that the ia and ib iterators
4072 // have the same length.
4073 while (ia->has_more()) {
4074 uc32 ca = ia->GetNext();
4075 uc32 cb = ib->GetNext();
4076 if (ca != cb)
4077 return false;
4078 }
4079 return true;
4080}
4081
4082
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004083// Compares the contents of two strings by reading and comparing
4084// int-sized blocks of characters.
4085template <typename Char>
4086static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
4087 int length = a.length();
4088 ASSERT_EQ(length, b.length());
4089 const Char* pa = a.start();
4090 const Char* pb = b.start();
4091 int i = 0;
4092#ifndef CAN_READ_UNALIGNED
4093 // If this architecture isn't comfortable reading unaligned ints
4094 // then we have to check that the strings are aligned before
4095 // comparing them blockwise.
4096 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
4097 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
4098 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004099 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004100#endif
4101 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
4102 int endpoint = length - kStepSize;
4103 // Compare blocks until we reach near the end of the string.
4104 for (; i <= endpoint; i += kStepSize) {
4105 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
4106 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
4107 if (wa != wb) {
4108 return false;
4109 }
4110 }
4111#ifndef CAN_READ_UNALIGNED
4112 }
4113#endif
4114 // Compare the remaining characters that didn't fit into a block.
4115 for (; i < length; i++) {
4116 if (a[i] != b[i]) {
4117 return false;
4118 }
4119 }
4120 return true;
4121}
4122
4123
kasperl@chromium.orgacae3782009-04-11 09:17:08 +00004124// This is a workaround for Chromium bug 9746: http://crbug.com/9746
4125// Returns true if this Vector matches the problem exposed in the bug.
4126template <typename T>
4127static bool CheckVectorForBug9746(Vector<T> vec) {
4128 // The problem is that somehow external string entries in the symbol
4129 // table can have their resources collected while they are still in the
4130 // table. This should not happen according to the test in the function
4131 // DisposeExternalString in api.cc, but we have evidence that it does.
4132 return (vec.start() == NULL) ? true : false;
4133}
4134
4135
ager@chromium.org7c537e22008-10-16 08:43:32 +00004136static StringInputBuffer string_compare_buffer_b;
4137
4138
4139template <typename IteratorA>
4140static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004141 if (b->IsFlat()) {
4142 if (StringShape(b).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004143 VectorIterator<char> ib(b->ToAsciiVector());
4144 return CompareStringContents(ia, &ib);
4145 } else {
kasperl@chromium.orgacae3782009-04-11 09:17:08 +00004146 Vector<const uc16> vb = b->ToUC16Vector();
4147 if (CheckVectorForBug9746(vb)) return false;
4148 VectorIterator<uc16> ib(vb);
ager@chromium.org7c537e22008-10-16 08:43:32 +00004149 return CompareStringContents(ia, &ib);
4150 }
4151 } else {
4152 string_compare_buffer_b.Reset(0, b);
4153 return CompareStringContents(ia, &string_compare_buffer_b);
4154 }
4155}
4156
4157
4158static StringInputBuffer string_compare_buffer_a;
4159
4160
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004161bool String::SlowEquals(String* other) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004162 // Fast check: negative check with lengths.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004163 int len = length();
4164 if (len != other->length()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004165 if (len == 0) return true;
4166
4167 // Fast check: if hash code is computed for both strings
4168 // a fast negative check can be performed.
4169 if (HasHashCode() && other->HasHashCode()) {
4170 if (Hash() != other->Hash()) return false;
4171 }
4172
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004173 if (StringShape(this).IsSequentialAscii() &&
4174 StringShape(other).IsSequentialAscii()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004175 const char* str1 = SeqAsciiString::cast(this)->GetChars();
4176 const char* str2 = SeqAsciiString::cast(other)->GetChars();
4177 return CompareRawStringContents(Vector<const char>(str1, len),
4178 Vector<const char>(str2, len));
4179 }
4180
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004181 if (this->IsFlat()) {
4182 if (StringShape(this).IsAsciiRepresentation()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004183 Vector<const char> vec1 = this->ToAsciiVector();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004184 if (other->IsFlat()) {
4185 if (StringShape(other).IsAsciiRepresentation()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004186 Vector<const char> vec2 = other->ToAsciiVector();
4187 return CompareRawStringContents(vec1, vec2);
4188 } else {
4189 VectorIterator<char> buf1(vec1);
kasperl@chromium.orgacae3782009-04-11 09:17:08 +00004190 Vector<const uc16> vec2 = other->ToUC16Vector();
4191 if (CheckVectorForBug9746(vec2)) return false;
4192 VectorIterator<uc16> ib(vec2);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004193 return CompareStringContents(&buf1, &ib);
4194 }
4195 } else {
4196 VectorIterator<char> buf1(vec1);
4197 string_compare_buffer_b.Reset(0, other);
4198 return CompareStringContents(&buf1, &string_compare_buffer_b);
4199 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00004200 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004201 Vector<const uc16> vec1 = this->ToUC16Vector();
kasperl@chromium.orgacae3782009-04-11 09:17:08 +00004202 if (CheckVectorForBug9746(vec1)) return false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004203 if (other->IsFlat()) {
4204 if (StringShape(other).IsAsciiRepresentation()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004205 VectorIterator<uc16> buf1(vec1);
4206 VectorIterator<char> ib(other->ToAsciiVector());
4207 return CompareStringContents(&buf1, &ib);
4208 } else {
kasperl@chromium.orgacae3782009-04-11 09:17:08 +00004209 Vector<const uc16> vec2 = other->ToUC16Vector();
4210 if (CheckVectorForBug9746(vec2)) return false;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004211 return CompareRawStringContents(vec1, vec2);
4212 }
4213 } else {
4214 VectorIterator<uc16> buf1(vec1);
4215 string_compare_buffer_b.Reset(0, other);
4216 return CompareStringContents(&buf1, &string_compare_buffer_b);
4217 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004218 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00004219 } else {
4220 string_compare_buffer_a.Reset(0, this);
4221 return CompareStringContentsPartial(&string_compare_buffer_a, other);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004222 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004223}
4224
4225
4226bool String::MarkAsUndetectable() {
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004227 if (StringShape(this).IsSymbol()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004228
4229 Map* map = this->map();
4230 if (map == Heap::short_string_map()) {
4231 this->set_map(Heap::undetectable_short_string_map());
4232 return true;
4233 } else if (map == Heap::medium_string_map()) {
4234 this->set_map(Heap::undetectable_medium_string_map());
4235 return true;
4236 } else if (map == Heap::long_string_map()) {
4237 this->set_map(Heap::undetectable_long_string_map());
4238 return true;
4239 } else if (map == Heap::short_ascii_string_map()) {
4240 this->set_map(Heap::undetectable_short_ascii_string_map());
4241 return true;
4242 } else if (map == Heap::medium_ascii_string_map()) {
4243 this->set_map(Heap::undetectable_medium_ascii_string_map());
4244 return true;
4245 } else if (map == Heap::long_ascii_string_map()) {
4246 this->set_map(Heap::undetectable_long_ascii_string_map());
4247 return true;
4248 }
4249 // Rest cannot be marked as undetectable
4250 return false;
4251}
4252
4253
4254bool String::IsEqualTo(Vector<const char> str) {
kasperl@chromium.orgacae3782009-04-11 09:17:08 +00004255 // This is a workaround for Chromium bug 9746: http://crbug.com/9746
4256 // The problem is that somehow external string entries in the symbol
4257 // table can have their resources deleted while they are still in the
4258 // table. This should not happen according to the test in the function
4259 // DisposeExternalString in api.cc but we have evidence that it does.
4260 // Thus we add this bailout here.
4261 StringShape shape(this);
4262 if (shape.IsExternalTwoByte()) {
4263 ExternalTwoByteString* ext = ExternalTwoByteString::cast(this);
4264 if (ext->resource() == NULL) return false;
4265 }
4266
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004267 int slen = length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004268 Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
4269 decoder->Reset(str.start(), str.length());
4270 int i;
4271 for (i = 0; i < slen && decoder->has_more(); i++) {
4272 uc32 r = decoder->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004273 if (Get(i) != r) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004274 }
4275 return i == slen && !decoder->has_more();
4276}
4277
4278
4279uint32_t String::ComputeAndSetHash() {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00004280 // Should only be called if hash code has not yet been computed.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004281 ASSERT(!(length_field() & kHashComputedMask));
4282
4283 // Compute the hash code.
4284 StringInputBuffer buffer(this);
ager@chromium.org7c537e22008-10-16 08:43:32 +00004285 uint32_t field = ComputeLengthAndHashField(&buffer, length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004286
4287 // Store the hash code in the object.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004288 set_length_field(field);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004289
4290 // Check the hash code is there.
4291 ASSERT(length_field() & kHashComputedMask);
ager@chromium.org3b45ab52009-03-19 22:21:34 +00004292 uint32_t result = field >> kHashShift;
4293 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
4294 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004295}
4296
4297
4298bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
4299 uint32_t* index,
4300 int length) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004301 if (length == 0 || length > kMaxArrayIndexSize) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004302 uc32 ch = buffer->GetNext();
4303
4304 // If the string begins with a '0' character, it must only consist
4305 // of it to be a legal array index.
4306 if (ch == '0') {
4307 *index = 0;
4308 return length == 1;
4309 }
4310
4311 // Convert string to uint32 array index; character by character.
4312 int d = ch - '0';
4313 if (d < 0 || d > 9) return false;
4314 uint32_t result = d;
4315 while (buffer->has_more()) {
4316 d = buffer->GetNext() - '0';
4317 if (d < 0 || d > 9) return false;
4318 // Check that the new result is below the 32 bit limit.
4319 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
4320 result = (result * 10) + d;
4321 }
4322
4323 *index = result;
4324 return true;
4325}
4326
4327
4328bool String::SlowAsArrayIndex(uint32_t* index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004329 if (length() <= kMaxCachedArrayIndexLength) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004330 Hash(); // force computation of hash code
4331 uint32_t field = length_field();
4332 if ((field & kIsArrayIndexMask) == 0) return false;
4333 *index = (field & ((1 << kShortLengthShift) - 1)) >> kLongLengthShift;
4334 return true;
4335 } else {
4336 StringInputBuffer buffer(this);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004337 return ComputeArrayIndex(&buffer, index, length());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004338 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004339}
4340
4341
4342static inline uint32_t HashField(uint32_t hash, bool is_array_index) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004343 uint32_t result =
4344 (hash << String::kLongLengthShift) | String::kHashComputedMask;
4345 if (is_array_index) result |= String::kIsArrayIndexMask;
4346 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004347}
4348
4349
ager@chromium.org7c537e22008-10-16 08:43:32 +00004350uint32_t StringHasher::GetHashField() {
4351 ASSERT(is_valid());
4352 if (length_ <= String::kMaxShortStringSize) {
4353 uint32_t payload;
4354 if (is_array_index()) {
4355 payload = v8::internal::HashField(array_index(), true);
4356 } else {
4357 payload = v8::internal::HashField(GetHash(), false);
4358 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004359 return (payload & ((1 << String::kShortLengthShift) - 1)) |
4360 (length_ << String::kShortLengthShift);
ager@chromium.org7c537e22008-10-16 08:43:32 +00004361 } else if (length_ <= String::kMaxMediumStringSize) {
4362 uint32_t payload = v8::internal::HashField(GetHash(), false);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004363 return (payload & ((1 << String::kMediumLengthShift) - 1)) |
4364 (length_ << String::kMediumLengthShift);
ager@chromium.org7c537e22008-10-16 08:43:32 +00004365 } else {
4366 return v8::internal::HashField(length_, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004367 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00004368}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004369
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004370
ager@chromium.org7c537e22008-10-16 08:43:32 +00004371uint32_t String::ComputeLengthAndHashField(unibrow::CharacterStream* buffer,
4372 int length) {
4373 StringHasher hasher(length);
4374
4375 // Very long strings have a trivial hash that doesn't inspect the
4376 // string contents.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004377 if (hasher.has_trivial_hash()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004378 return hasher.GetHashField();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004379 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00004380
4381 // Do the iterative array index computation as long as there is a
4382 // chance this is an array index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004383 while (buffer->has_more() && hasher.is_array_index()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004384 hasher.AddCharacter(buffer->GetNext());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004385 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00004386
4387 // Process the remaining characters without updating the array
4388 // index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004389 while (buffer->has_more()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004390 hasher.AddCharacterNoIndex(buffer->GetNext());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004391 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00004392
4393 return hasher.GetHashField();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004394}
4395
4396
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004397Object* String::Slice(int start, int end) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004398 if (start == 0 && end == length()) return this;
4399 if (StringShape(this).representation_tag() == kSlicedStringTag) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004400 // Translate slices of a SlicedString into slices of the
4401 // underlying string buffer.
4402 SlicedString* str = SlicedString::cast(this);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004403 String* buf = str->buffer();
4404 return Heap::AllocateSlicedString(buf,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004405 str->start() + start,
4406 str->start() + end);
4407 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004408 Object* result = Heap::AllocateSlicedString(this, start, end);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004409 if (result->IsFailure()) {
4410 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004411 }
4412 // Due to the way we retry after GC on allocation failure we are not allowed
4413 // to fail on allocation after this point. This is the one-allocation rule.
4414
4415 // Try to flatten a cons string that is under the sliced string.
4416 // This is to avoid memory leaks and possible stack overflows caused by
4417 // building 'towers' of sliced strings on cons strings.
4418 // This may fail due to an allocation failure (when a GC is needed), but it
4419 // will succeed often enough to avoid the problem. We only have to do this
4420 // if Heap::AllocateSlicedString actually returned a SlicedString. It will
4421 // return flat strings for small slices for efficiency reasons.
ager@chromium.org870a0b62008-11-04 11:43:05 +00004422 String* answer = String::cast(result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004423 if (StringShape(answer).IsSliced() &&
4424 StringShape(this).representation_tag() == kConsStringTag) {
4425 TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004426 // If the flatten succeeded we might as well make the sliced string point
4427 // to the flat string rather than the cons string.
ager@chromium.org870a0b62008-11-04 11:43:05 +00004428 String* second = ConsString::cast(this)->second();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004429 if (second->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004430 SlicedString::cast(answer)->set_buffer(ConsString::cast(this)->first());
4431 }
4432 }
4433 return answer;
4434}
4435
4436
4437void String::PrintOn(FILE* file) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004438 int length = this->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439 for (int i = 0; i < length; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004440 fprintf(file, "%c", Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004441 }
4442}
4443
4444
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004445void Map::CreateBackPointers() {
4446 DescriptorArray* descriptors = instance_descriptors();
4447 for (DescriptorReader r(descriptors); !r.eos(); r.advance()) {
4448 if (r.type() == MAP_TRANSITION) {
4449 // Get target.
4450 Map* target = Map::cast(r.GetValue());
4451#ifdef DEBUG
4452 // Verify target.
4453 Object* source_prototype = prototype();
4454 Object* target_prototype = target->prototype();
4455 ASSERT(source_prototype->IsJSObject() ||
4456 source_prototype->IsMap() ||
4457 source_prototype->IsNull());
4458 ASSERT(target_prototype->IsJSObject() ||
4459 target_prototype->IsNull());
4460 ASSERT(source_prototype->IsMap() ||
4461 source_prototype == target_prototype);
4462#endif
4463 // Point target back to source. set_prototype() will not let us set
4464 // the prototype to a map, as we do here.
4465 *RawField(target, kPrototypeOffset) = this;
4466 }
4467 }
4468}
4469
4470
4471void Map::ClearNonLiveTransitions(Object* real_prototype) {
4472 // Live DescriptorArray objects will be marked, so we must use
4473 // low-level accessors to get and modify their data.
4474 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
4475 *RawField(this, Map::kInstanceDescriptorsOffset));
4476 if (d == Heap::empty_descriptor_array()) return;
4477 Smi* NullDescriptorDetails =
4478 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
4479 FixedArray* contents = reinterpret_cast<FixedArray*>(
4480 d->get(DescriptorArray::kContentArrayIndex));
4481 ASSERT(contents->length() >= 2);
4482 for (int i = 0; i < contents->length(); i += 2) {
4483 // If the pair (value, details) is a map transition,
4484 // check if the target is live. If not, null the descriptor.
4485 // Also drop the back pointer for that map transition, so that this
4486 // map is not reached again by following a back pointer from a
4487 // non-live object.
4488 PropertyDetails details(Smi::cast(contents->get(i + 1)));
4489 if (details.type() == MAP_TRANSITION) {
4490 Map* target = reinterpret_cast<Map*>(contents->get(i));
4491 ASSERT(target->IsHeapObject());
4492 if (!target->IsMarked()) {
4493 ASSERT(target->IsMap());
4494 contents->set(i + 1, NullDescriptorDetails, SKIP_WRITE_BARRIER);
4495 contents->set(i, Heap::null_value(), SKIP_WRITE_BARRIER);
4496 ASSERT(target->prototype() == this ||
4497 target->prototype() == real_prototype);
4498 // Getter prototype() is read-only, set_prototype() has side effects.
4499 *RawField(target, Map::kPrototypeOffset) = real_prototype;
4500 }
4501 }
4502 }
4503}
4504
4505
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004506void Map::MapIterateBody(ObjectVisitor* v) {
4507 // Assumes all Object* members are contiguously allocated!
4508 IteratePointers(v, kPrototypeOffset, kCodeCacheOffset + kPointerSize);
4509}
4510
4511
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004512Object* JSFunction::SetInstancePrototype(Object* value) {
4513 ASSERT(value->IsJSObject());
4514
4515 if (has_initial_map()) {
4516 initial_map()->set_prototype(value);
4517 } else {
4518 // Put the value in the initial map field until an initial map is
4519 // needed. At that point, a new initial map is created and the
4520 // prototype is put into the initial map where it belongs.
4521 set_prototype_or_initial_map(value);
4522 }
4523 return value;
4524}
4525
4526
4527
4528Object* JSFunction::SetPrototype(Object* value) {
4529 Object* construct_prototype = value;
4530
4531 // If the value is not a JSObject, store the value in the map's
4532 // constructor field so it can be accessed. Also, set the prototype
4533 // used for constructing objects to the original object prototype.
4534 // See ECMA-262 13.2.2.
4535 if (!value->IsJSObject()) {
4536 // Copy the map so this does not affect unrelated functions.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004537 // Remove map transitions because they point to maps with a
4538 // different prototype.
4539 Object* new_map = map()->CopyDropTransitions();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004540 if (new_map->IsFailure()) return new_map;
4541 set_map(Map::cast(new_map));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004542 map()->set_constructor(value);
4543 map()->set_non_instance_prototype(true);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004544 construct_prototype =
4545 Top::context()->global_context()->initial_object_prototype();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004546 } else {
4547 map()->set_non_instance_prototype(false);
4548 }
4549
4550 return SetInstancePrototype(construct_prototype);
4551}
4552
4553
4554Object* JSFunction::SetInstanceClassName(String* name) {
4555 shared()->set_instance_class_name(name);
4556 return this;
4557}
4558
4559
ager@chromium.org236ad962008-09-25 09:45:57 +00004560Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
4561 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
4562}
4563
4564
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004565void Oddball::OddballIterateBody(ObjectVisitor* v) {
4566 // Assumes all Object* members are contiguously allocated!
4567 IteratePointers(v, kToStringOffset, kToNumberOffset + kPointerSize);
4568}
4569
4570
4571Object* Oddball::Initialize(const char* to_string, Object* to_number) {
4572 Object* symbol = Heap::LookupAsciiSymbol(to_string);
4573 if (symbol->IsFailure()) return symbol;
4574 set_to_string(String::cast(symbol));
4575 set_to_number(to_number);
4576 return this;
4577}
4578
4579
4580bool SharedFunctionInfo::HasSourceCode() {
4581 return !script()->IsUndefined() &&
4582 !Script::cast(script())->source()->IsUndefined();
4583}
4584
4585
4586Object* SharedFunctionInfo::GetSourceCode() {
4587 HandleScope scope;
4588 if (script()->IsUndefined()) return Heap::undefined_value();
4589 Object* source = Script::cast(script())->source();
4590 if (source->IsUndefined()) return Heap::undefined_value();
4591 return *SubString(Handle<String>(String::cast(source)),
4592 start_position(), end_position());
4593}
4594
4595
4596// Support function for printing the source code to a StringStream
4597// without any allocation in the heap.
4598void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
4599 int max_length) {
4600 // For some native functions there is no source.
4601 if (script()->IsUndefined() ||
4602 Script::cast(script())->source()->IsUndefined()) {
4603 accumulator->Add("<No Source>");
4604 return;
4605 }
4606
4607 // Get the slice of the source for this function.
4608 // Don't use String::cast because we don't want more assertion errors while
4609 // we are already creating a stack dump.
4610 String* script_source =
4611 reinterpret_cast<String*>(Script::cast(script())->source());
4612
4613 if (!script_source->LooksValid()) {
4614 accumulator->Add("<Invalid Source>");
4615 return;
4616 }
4617
4618 if (!is_toplevel()) {
4619 accumulator->Add("function ");
4620 Object* name = this->name();
4621 if (name->IsString() && String::cast(name)->length() > 0) {
4622 accumulator->PrintName(name);
4623 }
4624 }
4625
4626 int len = end_position() - start_position();
4627 if (len > max_length) {
4628 accumulator->Put(script_source,
4629 start_position(),
4630 start_position() + max_length);
4631 accumulator->Add("...\n");
4632 } else {
4633 accumulator->Put(script_source, start_position(), end_position());
4634 }
4635}
4636
4637
4638void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
4639 IteratePointers(v, kNameOffset, kCodeOffset + kPointerSize);
4640 IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00004641 IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004642}
4643
4644
4645void ObjectVisitor::BeginCodeIteration(Code* code) {
4646 ASSERT(code->ic_flag() == Code::IC_TARGET_IS_OBJECT);
4647}
4648
4649
4650void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004651 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004652 VisitPointer(rinfo->target_object_address());
4653}
4654
4655
4656void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
iposva@chromium.org245aa852009-02-10 00:49:54 +00004657 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) && rinfo->IsCallInstruction());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004658 VisitPointer(rinfo->call_object_address());
4659}
4660
4661
4662// Convert relocatable targets from address to code object address. This is
4663// mainly IC call targets but for debugging straight-line code can be replaced
4664// with a call instruction which also has to be relocated.
4665void Code::ConvertICTargetsFromAddressToObject() {
4666 ASSERT(ic_flag() == IC_TARGET_IS_ADDRESS);
4667
4668 for (RelocIterator it(this, RelocInfo::kCodeTargetMask);
4669 !it.done(); it.next()) {
4670 Address ic_addr = it.rinfo()->target_address();
4671 ASSERT(ic_addr != NULL);
4672 HeapObject* code = HeapObject::FromAddress(ic_addr - Code::kHeaderSize);
4673 ASSERT(code->IsHeapObject());
4674 it.rinfo()->set_target_object(code);
4675 }
4676
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004677#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004678 if (Debug::has_break_points()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004679 for (RelocIterator it(this, RelocInfo::ModeMask(RelocInfo::JS_RETURN));
4680 !it.done();
4681 it.next()) {
iposva@chromium.org245aa852009-02-10 00:49:54 +00004682 if (it.rinfo()->IsCallInstruction()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004683 Address addr = it.rinfo()->call_address();
4684 ASSERT(addr != NULL);
4685 HeapObject* code = HeapObject::FromAddress(addr - Code::kHeaderSize);
4686 ASSERT(code->IsHeapObject());
4687 it.rinfo()->set_call_object(code);
4688 }
4689 }
4690 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004691#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004692 set_ic_flag(IC_TARGET_IS_OBJECT);
4693}
4694
4695
4696void Code::CodeIterateBody(ObjectVisitor* v) {
4697 v->BeginCodeIteration(this);
4698
4699 int mode_mask = RelocInfo::kCodeTargetMask |
ager@chromium.org236ad962008-09-25 09:45:57 +00004700 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
4701 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
4702 RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
4703 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004704
4705 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004706 RelocInfo::Mode rmode = it.rinfo()->rmode();
4707 if (rmode == RelocInfo::EMBEDDED_OBJECT) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004708 v->VisitPointer(it.rinfo()->target_object_address());
ager@chromium.org236ad962008-09-25 09:45:57 +00004709 } else if (RelocInfo::IsCodeTarget(rmode)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004710 v->VisitCodeTarget(it.rinfo());
ager@chromium.org236ad962008-09-25 09:45:57 +00004711 } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004712 v->VisitExternalReference(it.rinfo()->target_reference_address());
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004713#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004714 } else if (Debug::has_break_points() &&
ager@chromium.org236ad962008-09-25 09:45:57 +00004715 RelocInfo::IsJSReturn(rmode) &&
iposva@chromium.org245aa852009-02-10 00:49:54 +00004716 it.rinfo()->IsCallInstruction()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004717 v->VisitDebugTarget(it.rinfo());
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004718#endif
ager@chromium.org236ad962008-09-25 09:45:57 +00004719 } else if (rmode == RelocInfo::RUNTIME_ENTRY) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004720 v->VisitRuntimeEntry(it.rinfo());
4721 }
4722 }
4723
4724 ScopeInfo<>::IterateScopeInfo(this, v);
4725
4726 v->EndCodeIteration(this);
4727}
4728
4729
4730void Code::ConvertICTargetsFromObjectToAddress() {
4731 ASSERT(ic_flag() == IC_TARGET_IS_OBJECT);
4732
4733 for (RelocIterator it(this, RelocInfo::kCodeTargetMask);
4734 !it.done(); it.next()) {
4735 // We cannot use the safe cast (Code::cast) here, because we may be in
4736 // the middle of relocating old objects during GC and the map pointer in
4737 // the code object may be mangled
4738 Code* code = reinterpret_cast<Code*>(it.rinfo()->target_object());
4739 ASSERT((code != NULL) && code->IsHeapObject());
4740 it.rinfo()->set_target_address(code->instruction_start());
4741 }
4742
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004743#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004744 if (Debug::has_break_points()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004745 for (RelocIterator it(this, RelocInfo::ModeMask(RelocInfo::JS_RETURN));
4746 !it.done();
4747 it.next()) {
iposva@chromium.org245aa852009-02-10 00:49:54 +00004748 if (it.rinfo()->IsCallInstruction()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004749 Code* code = reinterpret_cast<Code*>(it.rinfo()->call_object());
4750 ASSERT((code != NULL) && code->IsHeapObject());
4751 it.rinfo()->set_call_address(code->instruction_start());
4752 }
4753 }
4754 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004755#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004756 set_ic_flag(IC_TARGET_IS_ADDRESS);
4757}
4758
4759
4760void Code::Relocate(int delta) {
4761 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
4762 it.rinfo()->apply(delta);
4763 }
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004764 CPU::FlushICache(instruction_start(), instruction_size());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765}
4766
4767
4768void Code::CopyFrom(const CodeDesc& desc) {
4769 // copy code
4770 memmove(instruction_start(), desc.buffer, desc.instr_size);
4771
4772 // fill gap with zero bytes
4773 { byte* p = instruction_start() + desc.instr_size;
4774 byte* q = relocation_start();
4775 while (p < q) {
4776 *p++ = 0;
4777 }
4778 }
4779
4780 // copy reloc info
4781 memmove(relocation_start(),
4782 desc.buffer + desc.buffer_size - desc.reloc_size,
4783 desc.reloc_size);
4784
4785 // unbox handles and relocate
4786 int delta = instruction_start() - desc.buffer;
4787 int mode_mask = RelocInfo::kCodeTargetMask |
ager@chromium.org236ad962008-09-25 09:45:57 +00004788 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004789 RelocInfo::kApplyMask;
4790 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004791 RelocInfo::Mode mode = it.rinfo()->rmode();
4792 if (mode == RelocInfo::EMBEDDED_OBJECT) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004793 Object** p = reinterpret_cast<Object**>(it.rinfo()->target_object());
4794 it.rinfo()->set_target_object(*p);
ager@chromium.org236ad962008-09-25 09:45:57 +00004795 } else if (RelocInfo::IsCodeTarget(mode)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004796 // rewrite code handles in inline cache targets to direct
4797 // pointers to the first instruction in the code object
4798 Object** p = reinterpret_cast<Object**>(it.rinfo()->target_object());
4799 Code* code = Code::cast(*p);
4800 it.rinfo()->set_target_address(code->instruction_start());
4801 } else {
4802 it.rinfo()->apply(delta);
4803 }
4804 }
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004805 CPU::FlushICache(instruction_start(), instruction_size());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004806}
4807
4808
4809// Locate the source position which is closest to the address in the code. This
4810// is using the source position information embedded in the relocation info.
4811// The position returned is relative to the beginning of the script where the
4812// source for this function is found.
4813int Code::SourcePosition(Address pc) {
4814 int distance = kMaxInt;
ager@chromium.org236ad962008-09-25 09:45:57 +00004815 int position = RelocInfo::kNoPosition; // Initially no position found.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004816 // Run through all the relocation info to find the best matching source
4817 // position. All the code needs to be considered as the sequence of the
4818 // instructions in the code does not necessarily follow the same order as the
4819 // source.
4820 RelocIterator it(this, RelocInfo::kPositionMask);
4821 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004822 // Only look at positions after the current pc.
4823 if (it.rinfo()->pc() < pc) {
4824 // Get position and distance.
4825 int dist = pc - it.rinfo()->pc();
4826 int pos = it.rinfo()->data();
4827 // If this position is closer than the current candidate or if it has the
4828 // same distance as the current candidate and the position is higher then
4829 // this position is the new candidate.
4830 if ((dist < distance) ||
4831 (dist == distance && pos > position)) {
4832 position = pos;
4833 distance = dist;
4834 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004835 }
4836 it.next();
4837 }
4838 return position;
4839}
4840
4841
4842// Same as Code::SourcePosition above except it only looks for statement
4843// positions.
4844int Code::SourceStatementPosition(Address pc) {
4845 // First find the position as close as possible using all position
4846 // information.
4847 int position = SourcePosition(pc);
4848 // Now find the closest statement position before the position.
4849 int statement_position = 0;
4850 RelocIterator it(this, RelocInfo::kPositionMask);
4851 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004852 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853 int p = it.rinfo()->data();
4854 if (statement_position < p && p <= position) {
4855 statement_position = p;
4856 }
4857 }
4858 it.next();
4859 }
4860 return statement_position;
4861}
4862
4863
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00004864#ifdef ENABLE_DISASSEMBLER
4865// Identify kind of code.
4866const char* Code::Kind2String(Kind kind) {
4867 switch (kind) {
4868 case FUNCTION: return "FUNCTION";
4869 case STUB: return "STUB";
4870 case BUILTIN: return "BUILTIN";
4871 case LOAD_IC: return "LOAD_IC";
4872 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
4873 case STORE_IC: return "STORE_IC";
4874 case KEYED_STORE_IC: return "KEYED_STORE_IC";
4875 case CALL_IC: return "CALL_IC";
4876 }
4877 UNREACHABLE();
4878 return NULL;
4879}
mads.s.ager31e71382008-08-13 09:32:07 +00004880
4881
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004882const char* Code::ICState2String(InlineCacheState state) {
4883 switch (state) {
4884 case UNINITIALIZED: return "UNINITIALIZED";
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004885 case UNINITIALIZED_IN_LOOP: return "UNINITIALIZED_IN_LOOP";
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004886 case PREMONOMORPHIC: return "PREMONOMORPHIC";
4887 case MONOMORPHIC: return "MONOMORPHIC";
4888 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
4889 case MEGAMORPHIC: return "MEGAMORPHIC";
4890 case DEBUG_BREAK: return "DEBUG_BREAK";
4891 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
4892 }
4893 UNREACHABLE();
4894 return NULL;
4895}
4896
4897
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004898void Code::Disassemble(const char* name) {
4899 PrintF("kind = %s\n", Kind2String(kind()));
4900 if ((name != NULL) && (name[0] != '\0')) {
4901 PrintF("name = %s\n", name);
4902 }
mads.s.ager31e71382008-08-13 09:32:07 +00004903
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004904 PrintF("Instructions (size = %d)\n", instruction_size());
mads.s.ager31e71382008-08-13 09:32:07 +00004905 Disassembler::Decode(NULL, this);
4906 PrintF("\n");
4907
4908 PrintF("RelocInfo (size = %d)\n", relocation_size());
4909 for (RelocIterator it(this); !it.done(); it.next())
4910 it.rinfo()->Print();
4911 PrintF("\n");
4912}
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00004913#endif // ENABLE_DISASSEMBLER
4914
4915
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004916void JSObject::SetFastElements(FixedArray* elems) {
4917#ifdef DEBUG
4918 // Check the provided array is filled with the_hole.
4919 uint32_t len = static_cast<uint32_t>(elems->length());
4920 for (uint32_t i = 0; i < len; i++) ASSERT(elems->get(i)->IsTheHole());
4921#endif
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004922 WriteBarrierMode mode = elems->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004923 if (HasFastElements()) {
4924 FixedArray* old_elements = FixedArray::cast(elements());
4925 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
4926 // Fill out the new array with this content and array holes.
4927 for (uint32_t i = 0; i < old_length; i++) {
4928 elems->set(i, old_elements->get(i), mode);
4929 }
4930 } else {
4931 Dictionary* dictionary = Dictionary::cast(elements());
4932 for (int i = 0; i < dictionary->Capacity(); i++) {
4933 Object* key = dictionary->KeyAt(i);
4934 if (key->IsNumber()) {
4935 uint32_t entry = static_cast<uint32_t>(key->Number());
4936 elems->set(entry, dictionary->ValueAt(i), mode);
4937 }
4938 }
4939 }
4940 set_elements(elems);
4941}
4942
4943
4944Object* JSObject::SetSlowElements(Object* len) {
4945 uint32_t new_length = static_cast<uint32_t>(len->Number());
4946
4947 if (!HasFastElements()) {
4948 if (IsJSArray()) {
4949 uint32_t old_length =
4950 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
4951 element_dictionary()->RemoveNumberEntries(new_length, old_length),
4952 JSArray::cast(this)->set_length(len);
4953 }
4954 return this;
4955 }
4956
4957 // Make sure we never try to shrink dense arrays into sparse arrays.
4958 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
4959 new_length);
4960 Object* obj = NormalizeElements();
4961 if (obj->IsFailure()) return obj;
4962
4963 // Update length for JSArrays.
4964 if (IsJSArray()) JSArray::cast(this)->set_length(len);
4965 return this;
4966}
4967
4968
4969Object* JSArray::Initialize(int capacity) {
4970 ASSERT(capacity >= 0);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004971 set_length(Smi::FromInt(0), SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004972 FixedArray* new_elements;
4973 if (capacity == 0) {
4974 new_elements = Heap::empty_fixed_array();
4975 } else {
4976 Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
4977 if (obj->IsFailure()) return obj;
4978 new_elements = FixedArray::cast(obj);
4979 }
4980 set_elements(new_elements);
4981 return this;
4982}
4983
4984
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004985void JSArray::EnsureSize(int required_size) {
4986 Handle<JSArray> self(this);
4987 ASSERT(HasFastElements());
4988 if (elements()->length() >= required_size) return;
4989 Handle<FixedArray> old_backing(elements());
4990 int old_size = old_backing->length();
4991 // Doubling in size would be overkill, but leave some slack to avoid
4992 // constantly growing.
4993 int new_size = required_size + (required_size >> 3);
4994 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
4995 // Can't use this any more now because we may have had a GC!
4996 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
4997 self->SetContent(*new_backing);
4998}
4999
5000
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005001// Computes the new capacity when expanding the elements of a JSObject.
5002static int NewElementsCapacity(int old_capacity) {
5003 // (old_capacity + 50%) + 16
5004 return old_capacity + (old_capacity >> 1) + 16;
5005}
5006
5007
5008static Object* ArrayLengthRangeError() {
5009 HandleScope scope;
5010 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
5011 HandleVector<Object>(NULL, 0)));
5012}
5013
5014
5015Object* JSObject::SetElementsLength(Object* len) {
5016 Object* smi_length = len->ToSmi();
5017 if (smi_length->IsSmi()) {
5018 int value = Smi::cast(smi_length)->value();
5019 if (value < 0) return ArrayLengthRangeError();
5020 if (HasFastElements()) {
5021 int old_capacity = FixedArray::cast(elements())->length();
5022 if (value <= old_capacity) {
5023 if (IsJSArray()) {
5024 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
5025 // NOTE: We may be able to optimize this by removing the
5026 // last part of the elements backing storage array and
5027 // setting the capacity to the new size.
5028 for (int i = value; i < old_length; i++) {
5029 FixedArray::cast(elements())->set_the_hole(i);
5030 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005031 JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005032 }
5033 return this;
5034 }
5035 int min = NewElementsCapacity(old_capacity);
5036 int new_capacity = value > min ? value : min;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005037 if (new_capacity <= kMaxFastElementsLength ||
5038 !ShouldConvertToSlowElements(new_capacity)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005039 Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
5040 if (obj->IsFailure()) return obj;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005041 if (IsJSArray()) JSArray::cast(this)->set_length(smi_length,
5042 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005043 SetFastElements(FixedArray::cast(obj));
5044 return this;
5045 }
5046 } else {
5047 if (IsJSArray()) {
5048 if (value == 0) {
5049 // If the length of a slow array is reset to zero, we clear
5050 // the array and flush backing storage. This has the added
5051 // benefit that the array returns to fast mode.
5052 initialize_elements();
5053 } else {
5054 // Remove deleted elements.
5055 uint32_t old_length =
5056 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
5057 element_dictionary()->RemoveNumberEntries(value, old_length);
5058 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005059 JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005060 }
5061 return this;
5062 }
5063 }
5064
5065 // General slow case.
5066 if (len->IsNumber()) {
5067 uint32_t length;
5068 if (Array::IndexFromObject(len, &length)) {
5069 return SetSlowElements(len);
5070 } else {
5071 return ArrayLengthRangeError();
5072 }
5073 }
5074
5075 // len is not a number so make the array size one and
5076 // set only element to len.
5077 Object* obj = Heap::AllocateFixedArray(1);
5078 if (obj->IsFailure()) return obj;
5079 FixedArray::cast(obj)->set(0, len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005080 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1),
5081 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005082 set_elements(FixedArray::cast(obj));
5083 return this;
5084}
5085
5086
5087bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
5088 if (HasFastElements()) {
5089 uint32_t length = IsJSArray() ?
5090 static_cast<uint32_t>(
5091 Smi::cast(JSArray::cast(this)->length())->value()) :
5092 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5093 if ((index < length) &&
5094 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
5095 return true;
5096 }
5097 } else {
5098 if (element_dictionary()->FindNumberEntry(index) != -1) return true;
5099 }
5100
5101 // Handle [] on String objects.
5102 if (this->IsStringObjectWithCharacterAt(index)) return true;
5103
5104 Object* pt = GetPrototype();
5105 if (pt == Heap::null_value()) return false;
5106 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5107}
5108
5109
5110bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) {
5111 // Make sure that the top context does not change when doing
5112 // callbacks or interceptor calls.
5113 AssertNoContextChange ncc;
5114 HandleScope scope;
5115 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5116 Handle<JSObject> receiver_handle(receiver);
5117 Handle<JSObject> holder_handle(this);
5118 Handle<Object> data_handle(interceptor->data());
5119 v8::AccessorInfo info(v8::Utils::ToLocal(receiver_handle),
5120 v8::Utils::ToLocal(data_handle),
5121 v8::Utils::ToLocal(holder_handle));
5122 if (!interceptor->query()->IsUndefined()) {
5123 v8::IndexedPropertyQuery query =
5124 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
5125 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
5126 v8::Handle<v8::Boolean> result;
5127 {
5128 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00005129 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005130 result = query(index, info);
5131 }
5132 if (!result.IsEmpty()) return result->IsTrue();
5133 } else if (!interceptor->getter()->IsUndefined()) {
5134 v8::IndexedPropertyGetter getter =
5135 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
5136 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
5137 v8::Handle<v8::Value> result;
5138 {
5139 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00005140 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005141 result = getter(index, info);
5142 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005143 if (!result.IsEmpty()) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005144 }
5145 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
5146}
5147
5148
5149bool JSObject::HasLocalElement(uint32_t index) {
5150 // Check access rights if needed.
5151 if (IsAccessCheckNeeded() &&
5152 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5153 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5154 return false;
5155 }
5156
5157 // Check for lookup interceptor
5158 if (HasIndexedInterceptor()) {
5159 return HasElementWithInterceptor(this, index);
5160 }
5161
5162 // Handle [] on String objects.
5163 if (this->IsStringObjectWithCharacterAt(index)) return true;
5164
5165 if (HasFastElements()) {
5166 uint32_t length = IsJSArray() ?
5167 static_cast<uint32_t>(
5168 Smi::cast(JSArray::cast(this)->length())->value()) :
5169 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5170 return (index < length) &&
5171 !FixedArray::cast(elements())->get(index)->IsTheHole();
5172 } else {
5173 return element_dictionary()->FindNumberEntry(index) != -1;
5174 }
5175}
5176
5177
ager@chromium.org3b45ab52009-03-19 22:21:34 +00005178Object* JSObject::GetHiddenProperties(bool create_if_needed) {
5179 String* key = Heap::hidden_symbol();
5180 if (this->HasFastProperties()) {
ager@chromium.org8c51fc92009-04-22 11:54:55 +00005181 // If the object has fast properties, check whether the first slot
5182 // in the descriptor array matches the hidden symbol. Since the
5183 // hidden symbols hash code is zero (and no other string has hash
5184 // code zero) it will always occupy the first entry if present.
ager@chromium.org3b45ab52009-03-19 22:21:34 +00005185 DescriptorArray* descriptors = this->map()->instance_descriptors();
ager@chromium.org8c51fc92009-04-22 11:54:55 +00005186 DescriptorReader r(descriptors);
5187 if (!r.eos() && (r.GetKey() == key) && r.IsProperty()) {
5188 ASSERT(r.type() == FIELD);
5189 return FastPropertyAt(r.GetFieldIndex());
ager@chromium.org3b45ab52009-03-19 22:21:34 +00005190 }
5191 }
5192
5193 // Only attempt to find the hidden properties in the local object and not
5194 // in the prototype chain.
5195 if (!this->HasLocalProperty(key)) {
5196 // Hidden properties object not found. Allocate a new hidden properties
5197 // object if requested. Otherwise return the undefined value.
5198 if (create_if_needed) {
5199 Object* obj = Heap::AllocateJSObject(
5200 Top::context()->global_context()->object_function());
5201 if (obj->IsFailure()) {
5202 return obj;
5203 }
5204 return this->SetProperty(key, obj, DONT_ENUM);
5205 } else {
5206 return Heap::undefined_value();
5207 }
5208 }
5209 return this->GetProperty(key);
5210}
5211
5212
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005213bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
5214 // Check access rights if needed.
5215 if (IsAccessCheckNeeded() &&
5216 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5217 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5218 return false;
5219 }
5220
5221 // Check for lookup interceptor
5222 if (HasIndexedInterceptor()) {
5223 return HasElementWithInterceptor(receiver, index);
5224 }
5225
5226 if (HasFastElements()) {
5227 uint32_t length = IsJSArray() ?
5228 static_cast<uint32_t>(
5229 Smi::cast(JSArray::cast(this)->length())->value()) :
5230 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5231 if ((index < length) &&
5232 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
5233 } else {
5234 if (element_dictionary()->FindNumberEntry(index) != -1) return true;
5235 }
5236
5237 // Handle [] on String objects.
5238 if (this->IsStringObjectWithCharacterAt(index)) return true;
5239
5240 Object* pt = GetPrototype();
5241 if (pt == Heap::null_value()) return false;
5242 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
5243}
5244
5245
5246Object* JSObject::SetElementPostInterceptor(uint32_t index, Object* value) {
5247 if (HasFastElements()) return SetFastElement(index, value);
5248
5249 // Dictionary case.
5250 ASSERT(!HasFastElements());
5251
5252 FixedArray* elms = FixedArray::cast(elements());
5253 Object* result = Dictionary::cast(elms)->AtNumberPut(index, value);
5254 if (result->IsFailure()) return result;
5255 if (elms != FixedArray::cast(result)) {
5256 set_elements(FixedArray::cast(result));
5257 }
5258
5259 if (IsJSArray()) {
5260 return JSArray::cast(this)->JSArrayUpdateLengthFromIndex(index, value);
5261 }
5262
5263 return value;
5264}
5265
5266
5267Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) {
5268 // Make sure that the top context does not change when doing
5269 // callbacks or interceptor calls.
5270 AssertNoContextChange ncc;
5271 HandleScope scope;
5272 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5273 Handle<JSObject> this_handle(this);
5274 Handle<Object> value_handle(value);
5275 if (!interceptor->setter()->IsUndefined()) {
5276 v8::IndexedPropertySetter setter =
5277 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
5278 Handle<Object> data_handle(interceptor->data());
5279 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
5280 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
5281 v8::Utils::ToLocal(data_handle),
5282 v8::Utils::ToLocal(this_handle));
5283 v8::Handle<v8::Value> result;
5284 {
5285 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00005286 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005287 result = setter(index, v8::Utils::ToLocal(value_handle), info);
5288 }
5289 RETURN_IF_SCHEDULED_EXCEPTION();
5290 if (!result.IsEmpty()) return *value_handle;
5291 }
5292 Object* raw_result =
5293 this_handle->SetElementPostInterceptor(index, *value_handle);
5294 RETURN_IF_SCHEDULED_EXCEPTION();
5295 return raw_result;
5296}
5297
5298
5299// Adding n elements in fast case is O(n*n).
5300// Note: revisit design to have dual undefined values to capture absent
5301// elements.
5302Object* JSObject::SetFastElement(uint32_t index, Object* value) {
5303 ASSERT(HasFastElements());
5304
5305 FixedArray* elms = FixedArray::cast(elements());
5306 uint32_t elms_length = static_cast<uint32_t>(elms->length());
5307
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005308 if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
5309 Object* setter = LookupCallbackSetterInPrototypes(index);
5310 if (setter->IsJSFunction()) {
5311 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
5312 }
5313 }
5314
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005315 // Check whether there is extra space in fixed array..
5316 if (index < elms_length) {
5317 elms->set(index, value);
5318 if (IsJSArray()) {
5319 // Update the length of the array if needed.
5320 uint32_t array_length = 0;
5321 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(),
5322 &array_length));
5323 if (index >= array_length) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005324 JSArray::cast(this)->set_length(Smi::FromInt(index + 1),
5325 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005326 }
5327 }
ager@chromium.orgbdf2f942008-10-17 07:23:00 +00005328 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005329 }
5330
5331 // Allow gap in fast case.
5332 if ((index - elms_length) < kMaxGap) {
5333 // Try allocating extra space.
5334 int new_capacity = NewElementsCapacity(index+1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005335 if (new_capacity <= kMaxFastElementsLength ||
5336 !ShouldConvertToSlowElements(new_capacity)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005337 ASSERT(static_cast<uint32_t>(new_capacity) > index);
5338 Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
5339 if (obj->IsFailure()) return obj;
5340 SetFastElements(FixedArray::cast(obj));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005341 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(index + 1),
5342 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005343 FixedArray::cast(elements())->set(index, value);
ager@chromium.orgbdf2f942008-10-17 07:23:00 +00005344 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005345 }
5346 }
5347
5348 // Otherwise default to slow case.
5349 Object* obj = NormalizeElements();
5350 if (obj->IsFailure()) return obj;
5351 ASSERT(!HasFastElements());
5352 return SetElement(index, value);
5353}
5354
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005355Object* JSObject::SetElement(uint32_t index, Object* value) {
5356 // Check access rights if needed.
5357 if (IsAccessCheckNeeded() &&
5358 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
5359 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
5360 return value;
5361 }
5362
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005363 if (IsJSGlobalProxy()) {
5364 Object* proto = GetPrototype();
5365 if (proto->IsNull()) return value;
5366 ASSERT(proto->IsJSGlobalObject());
5367 return JSObject::cast(proto)->SetElement(index, value);
5368 }
5369
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005370 // Check for lookup interceptor
5371 if (HasIndexedInterceptor()) {
5372 return SetElementWithInterceptor(index, value);
5373 }
5374
5375 // Fast case.
5376 if (HasFastElements()) return SetFastElement(index, value);
5377
5378 // Dictionary case.
5379 ASSERT(!HasFastElements());
5380
5381 // Insert element in the dictionary.
5382 FixedArray* elms = FixedArray::cast(elements());
5383 Dictionary* dictionary = Dictionary::cast(elms);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005384
5385 int entry = dictionary->FindNumberEntry(index);
5386 if (entry != -1) {
5387 Object* element = dictionary->ValueAt(entry);
5388 PropertyDetails details = dictionary->DetailsAt(entry);
5389 if (details.type() == CALLBACKS) {
5390 // Only accessors allowed as elements.
5391 FixedArray* structure = FixedArray::cast(element);
5392 if (structure->get(kSetterIndex)->IsJSFunction()) {
5393 JSFunction* setter = JSFunction::cast(structure->get(kSetterIndex));
5394 return SetPropertyWithDefinedSetter(setter, value);
5395 } else {
5396 Handle<Object> self(this);
5397 Handle<Object> key(Factory::NewNumberFromUint(index));
5398 Handle<Object> args[2] = { key, self };
5399 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
5400 HandleVector(args, 2)));
5401 }
5402 } else {
5403 dictionary->UpdateMaxNumberKey(index);
5404 dictionary->ValueAtPut(entry, value);
5405 }
5406 } else {
5407 // Index not already used. Look for an accessor in the prototype chain.
5408 if (!IsJSArray()) {
5409 Object* setter = LookupCallbackSetterInPrototypes(index);
5410 if (setter->IsJSFunction()) {
5411 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
5412 }
5413 }
5414 Object* result = dictionary->AtNumberPut(index, value);
5415 if (result->IsFailure()) return result;
5416 if (elms != FixedArray::cast(result)) {
5417 set_elements(FixedArray::cast(result));
5418 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005419 }
5420
5421 // Update the array length if this JSObject is an array.
5422 if (IsJSArray()) {
5423 JSArray* array = JSArray::cast(this);
5424 Object* return_value = array->JSArrayUpdateLengthFromIndex(index, value);
5425 if (return_value->IsFailure()) return return_value;
5426 }
5427
5428 // Attempt to put this object back in fast case.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005429 if (ShouldConvertToFastElements()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005430 uint32_t new_length = 0;
5431 if (IsJSArray()) {
5432 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), &new_length));
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005433 JSArray::cast(this)->set_length(Smi::FromInt(new_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005434 } else {
5435 new_length = Dictionary::cast(elements())->max_number_key() + 1;
5436 }
5437 Object* obj = Heap::AllocateFixedArrayWithHoles(new_length);
5438 if (obj->IsFailure()) return obj;
5439 SetFastElements(FixedArray::cast(obj));
5440#ifdef DEBUG
5441 if (FLAG_trace_normalization) {
5442 PrintF("Object elements are fast case again:\n");
5443 Print();
5444 }
5445#endif
5446 }
5447
5448 return value;
5449}
5450
5451
5452Object* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index, Object* value) {
5453 uint32_t old_len = 0;
5454 CHECK(Array::IndexFromObject(length(), &old_len));
5455 // Check to see if we need to update the length. For now, we make
5456 // sure that the length stays within 32-bits (unsigned).
5457 if (index >= old_len && index != 0xffffffff) {
5458 Object* len =
5459 Heap::NumberFromDouble(static_cast<double>(index) + 1);
5460 if (len->IsFailure()) return len;
5461 set_length(len);
5462 }
5463 return value;
5464}
5465
5466
5467Object* JSObject::GetElementPostInterceptor(JSObject* receiver,
5468 uint32_t index) {
5469 // Get element works for both JSObject and JSArray since
5470 // JSArray::length cannot change.
5471 if (HasFastElements()) {
5472 FixedArray* elms = FixedArray::cast(elements());
5473 if (index < static_cast<uint32_t>(elms->length())) {
5474 Object* value = elms->get(index);
5475 if (!value->IsTheHole()) return value;
5476 }
5477 } else {
5478 Dictionary* dictionary = element_dictionary();
5479 int entry = dictionary->FindNumberEntry(index);
5480 if (entry != -1) {
5481 return dictionary->ValueAt(entry);
5482 }
5483 }
5484
5485 // Continue searching via the prototype chain.
5486 Object* pt = GetPrototype();
5487 if (pt == Heap::null_value()) return Heap::undefined_value();
5488 return pt->GetElementWithReceiver(receiver, index);
5489}
5490
5491
5492Object* JSObject::GetElementWithInterceptor(JSObject* receiver,
5493 uint32_t index) {
5494 // Make sure that the top context does not change when doing
5495 // callbacks or interceptor calls.
5496 AssertNoContextChange ncc;
5497 HandleScope scope;
5498 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
5499 Handle<JSObject> this_handle(receiver);
5500 Handle<JSObject> holder_handle(this);
5501
5502 if (!interceptor->getter()->IsUndefined()) {
5503 Handle<Object> data_handle(interceptor->data());
5504 v8::IndexedPropertyGetter getter =
5505 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
5506 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
5507 v8::AccessorInfo info(v8::Utils::ToLocal(this_handle),
5508 v8::Utils::ToLocal(data_handle),
5509 v8::Utils::ToLocal(holder_handle));
5510 v8::Handle<v8::Value> result;
5511 {
5512 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00005513 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005514 result = getter(index, info);
5515 }
5516 RETURN_IF_SCHEDULED_EXCEPTION();
5517 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5518 }
5519
5520 Object* raw_result =
5521 holder_handle->GetElementPostInterceptor(*this_handle, index);
5522 RETURN_IF_SCHEDULED_EXCEPTION();
5523 return raw_result;
5524}
5525
5526
5527Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) {
5528 // Check access rights if needed.
5529 if (IsAccessCheckNeeded() &&
5530 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) {
5531 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET);
5532 return Heap::undefined_value();
5533 }
5534
5535 if (HasIndexedInterceptor()) {
5536 return GetElementWithInterceptor(receiver, index);
5537 }
5538
5539 // Get element works for both JSObject and JSArray since
5540 // JSArray::length cannot change.
5541 if (HasFastElements()) {
5542 FixedArray* elms = FixedArray::cast(elements());
5543 if (index < static_cast<uint32_t>(elms->length())) {
5544 Object* value = elms->get(index);
5545 if (!value->IsTheHole()) return value;
5546 }
5547 } else {
5548 Dictionary* dictionary = element_dictionary();
5549 int entry = dictionary->FindNumberEntry(index);
5550 if (entry != -1) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005551 Object* element = dictionary->ValueAt(entry);
5552 PropertyDetails details = dictionary->DetailsAt(entry);
5553 if (details.type() == CALLBACKS) {
5554 // Only accessors allowed as elements.
5555 FixedArray* structure = FixedArray::cast(element);
5556 Object* getter = structure->get(kGetterIndex);
5557 if (getter->IsJSFunction()) {
5558 return GetPropertyWithDefinedGetter(receiver,
5559 JSFunction::cast(getter));
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00005560 } else {
5561 // Getter is not a function.
5562 return Heap::undefined_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005563 }
5564 }
5565 return element;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005566 }
5567 }
5568
5569 Object* pt = GetPrototype();
5570 if (pt == Heap::null_value()) return Heap::undefined_value();
5571 return pt->GetElementWithReceiver(receiver, index);
5572}
5573
5574
5575bool JSObject::HasDenseElements() {
5576 int capacity = 0;
5577 int number_of_elements = 0;
5578
5579 if (HasFastElements()) {
5580 FixedArray* elms = FixedArray::cast(elements());
5581 capacity = elms->length();
5582 for (int i = 0; i < capacity; i++) {
5583 if (!elms->get(i)->IsTheHole()) number_of_elements++;
5584 }
5585 } else {
5586 Dictionary* dictionary = Dictionary::cast(elements());
5587 capacity = dictionary->Capacity();
5588 number_of_elements = dictionary->NumberOfElements();
5589 }
5590
5591 if (capacity == 0) return true;
5592 return (number_of_elements > (capacity / 2));
5593}
5594
5595
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005596bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005597 ASSERT(HasFastElements());
5598 // Keep the array in fast case if the current backing storage is
5599 // almost filled and if the new capacity is no more than twice the
5600 // old capacity.
5601 int elements_length = FixedArray::cast(elements())->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005602 return !HasDenseElements() || ((new_capacity / 2) > elements_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005603}
5604
5605
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005606bool JSObject::ShouldConvertToFastElements() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005607 ASSERT(!HasFastElements());
5608 Dictionary* dictionary = Dictionary::cast(elements());
5609 // If the elements are sparse, we should not go back to fast case.
5610 if (!HasDenseElements()) return false;
5611 // If an element has been added at a very high index in the elements
5612 // dictionary, we cannot go back to fast case.
5613 if (dictionary->requires_slow_elements()) return false;
5614 // An object requiring access checks is never allowed to have fast
5615 // elements. If it had fast elements we would skip security checks.
5616 if (IsAccessCheckNeeded()) return false;
5617 // If the dictionary backing storage takes up roughly half as much
5618 // space as a fast-case backing storage would the array should have
5619 // fast elements.
5620 uint32_t length = 0;
5621 if (IsJSArray()) {
5622 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), &length));
5623 } else {
5624 length = dictionary->max_number_key();
5625 }
5626 return static_cast<uint32_t>(dictionary->Capacity()) >=
5627 (length / (2 * Dictionary::kElementSize));
5628}
5629
5630
5631Object* Dictionary::RemoveHoles() {
5632 int capacity = Capacity();
5633 Object* obj = Allocate(NumberOfElements());
5634 if (obj->IsFailure()) return obj;
5635 Dictionary* dict = Dictionary::cast(obj);
5636 uint32_t pos = 0;
5637 for (int i = 0; i < capacity; i++) {
5638 Object* k = KeyAt(i);
5639 if (IsKey(k)) {
5640 dict->AddNumberEntry(pos++, ValueAt(i), DetailsAt(i));
5641 }
5642 }
5643 return dict;
5644}
5645
5646
5647void Dictionary::CopyValuesTo(FixedArray* elements) {
5648 int pos = 0;
5649 int capacity = Capacity();
5650 for (int i = 0; i < capacity; i++) {
5651 Object* k = KeyAt(i);
5652 if (IsKey(k)) elements->set(pos++, ValueAt(i));
5653 }
5654 ASSERT(pos == elements->length());
5655}
5656
5657
5658Object* JSArray::RemoveHoles() {
5659 if (HasFastElements()) {
5660 int len = Smi::cast(length())->value();
5661 int pos = 0;
5662 FixedArray* elms = FixedArray::cast(elements());
5663 for (int index = 0; index < len; index++) {
5664 Object* e = elms->get(index);
5665 if (!e->IsTheHole()) {
5666 if (index != pos) elms->set(pos, e);
5667 pos++;
5668 }
5669 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005670 set_length(Smi::FromInt(pos), SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005671 for (int index = pos; index < len; index++) {
5672 elms->set_the_hole(index);
5673 }
5674 return this;
5675 }
5676
5677 // Compact the sparse array if possible.
5678 Dictionary* dict = element_dictionary();
5679 int length = dict->NumberOfElements();
5680
5681 // Try to make this a fast array again.
5682 if (length <= kMaxFastElementsLength) {
5683 Object* obj = Heap::AllocateFixedArray(length);
5684 if (obj->IsFailure()) return obj;
5685 dict->CopyValuesTo(FixedArray::cast(obj));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005686 set_length(Smi::FromInt(length), SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005687 set_elements(FixedArray::cast(obj));
5688 return this;
5689 }
5690
5691 // Make another dictionary with smaller indices.
5692 Object* obj = dict->RemoveHoles();
5693 if (obj->IsFailure()) return obj;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005694 set_length(Smi::FromInt(length), SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005695 set_elements(Dictionary::cast(obj));
5696 return this;
5697}
5698
5699
5700InterceptorInfo* JSObject::GetNamedInterceptor() {
5701 ASSERT(map()->has_named_interceptor());
5702 JSFunction* constructor = JSFunction::cast(map()->constructor());
5703 Object* template_info = constructor->shared()->function_data();
5704 Object* result =
5705 FunctionTemplateInfo::cast(template_info)->named_property_handler();
5706 return InterceptorInfo::cast(result);
5707}
5708
5709
5710InterceptorInfo* JSObject::GetIndexedInterceptor() {
5711 ASSERT(map()->has_indexed_interceptor());
5712 JSFunction* constructor = JSFunction::cast(map()->constructor());
5713 Object* template_info = constructor->shared()->function_data();
5714 Object* result =
5715 FunctionTemplateInfo::cast(template_info)->indexed_property_handler();
5716 return InterceptorInfo::cast(result);
5717}
5718
5719
5720Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver,
5721 String* name,
5722 PropertyAttributes* attributes) {
5723 // Check local property in holder, ignore interceptor.
5724 LookupResult result;
5725 LocalLookupRealNamedProperty(name, &result);
5726 if (result.IsValid()) return GetProperty(receiver, &result, name, attributes);
5727 // Continue searching via the prototype chain.
5728 Object* pt = GetPrototype();
5729 *attributes = ABSENT;
5730 if (pt == Heap::null_value()) return Heap::undefined_value();
5731 return pt->GetPropertyWithReceiver(receiver, name, attributes);
5732}
5733
5734
5735Object* JSObject::GetPropertyWithInterceptor(JSObject* receiver,
5736 String* name,
5737 PropertyAttributes* attributes) {
5738 HandleScope scope;
5739 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
5740 Handle<JSObject> receiver_handle(receiver);
5741 Handle<JSObject> holder_handle(this);
5742 Handle<String> name_handle(name);
5743 Handle<Object> data_handle(interceptor->data());
5744
5745 if (!interceptor->getter()->IsUndefined()) {
5746 v8::NamedPropertyGetter getter =
5747 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
5748 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
5749 v8::AccessorInfo info(v8::Utils::ToLocal(receiver_handle),
5750 v8::Utils::ToLocal(data_handle),
5751 v8::Utils::ToLocal(holder_handle));
5752 v8::Handle<v8::Value> result;
5753 {
5754 // Leaving JavaScript.
ager@chromium.org41826e72009-03-30 13:30:57 +00005755 VMState state(EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005756 result = getter(v8::Utils::ToLocal(name_handle), info);
5757 }
5758 RETURN_IF_SCHEDULED_EXCEPTION();
5759 if (!result.IsEmpty()) {
5760 *attributes = NONE;
5761 return *v8::Utils::OpenHandle(*result);
5762 }
5763 }
5764
5765 Object* raw_result = holder_handle->GetPropertyPostInterceptor(
5766 *receiver_handle,
5767 *name_handle,
5768 attributes);
5769 RETURN_IF_SCHEDULED_EXCEPTION();
5770 return raw_result;
5771}
5772
5773
5774bool JSObject::HasRealNamedProperty(String* key) {
5775 // Check access rights if needed.
5776 if (IsAccessCheckNeeded() &&
5777 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
5778 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5779 return false;
5780 }
5781
5782 LookupResult result;
5783 LocalLookupRealNamedProperty(key, &result);
5784 if (result.IsValid()) {
5785 switch (result.type()) {
5786 case NORMAL: // fall through.
5787 case FIELD: // fall through.
5788 case CALLBACKS: // fall through.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005789 case CONSTANT_FUNCTION:
5790 return true;
5791 case INTERCEPTOR:
5792 case MAP_TRANSITION:
5793 case CONSTANT_TRANSITION:
5794 case NULL_DESCRIPTOR:
5795 return false;
5796 default:
5797 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005798 }
5799 }
5800
5801 return false;
5802}
5803
5804
5805bool JSObject::HasRealElementProperty(uint32_t index) {
5806 // Check access rights if needed.
5807 if (IsAccessCheckNeeded() &&
5808 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
5809 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5810 return false;
5811 }
5812
5813 // Handle [] on String objects.
5814 if (this->IsStringObjectWithCharacterAt(index)) return true;
5815
5816 if (HasFastElements()) {
5817 uint32_t length = IsJSArray() ?
5818 static_cast<uint32_t>(
5819 Smi::cast(JSArray::cast(this)->length())->value()) :
5820 static_cast<uint32_t>(FixedArray::cast(elements())->length());
5821 return (index < length) &&
5822 !FixedArray::cast(elements())->get(index)->IsTheHole();
5823 }
5824 return element_dictionary()->FindNumberEntry(index) != -1;
5825}
5826
5827
5828bool JSObject::HasRealNamedCallbackProperty(String* key) {
5829 // Check access rights if needed.
5830 if (IsAccessCheckNeeded() &&
5831 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) {
5832 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
5833 return false;
5834 }
5835
5836 LookupResult result;
5837 LocalLookupRealNamedProperty(key, &result);
5838 return result.IsValid() && (result.type() == CALLBACKS);
5839}
5840
5841
5842int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
5843 if (HasFastProperties()) {
5844 int result = 0;
5845 for (DescriptorReader r(map()->instance_descriptors());
5846 !r.eos();
5847 r.advance()) {
5848 PropertyDetails details = r.GetDetails();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005849 if (details.IsProperty() &&
5850 (details.attributes() & filter) == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005851 result++;
5852 }
5853 }
5854 return result;
5855 } else {
5856 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
5857 }
5858}
5859
5860
5861int JSObject::NumberOfEnumProperties() {
5862 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
5863}
5864
5865
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005866void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005867 Object* temp = get(i);
5868 set(i, get(j));
5869 set(j, temp);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005870 if (this != numbers) {
5871 temp = numbers->get(i);
5872 numbers->set(i, numbers->get(j));
5873 numbers->set(j, temp);
5874 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005875}
5876
5877
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005878static void InsertionSortPairs(FixedArray* content,
5879 FixedArray* numbers,
5880 int len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005881 for (int i = 1; i < len; i++) {
5882 int j = i;
5883 while (j > 0 &&
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005884 (NumberToUint32(numbers->get(j - 1)) >
5885 NumberToUint32(numbers->get(j)))) {
5886 content->SwapPairs(numbers, j - 1, j);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005887 j--;
5888 }
5889 }
5890}
5891
5892
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005893void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005894 // In-place heap sort.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005895 ASSERT(content->length() == numbers->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005896
5897 // Bottom-up max-heap construction.
5898 for (int i = 1; i < len; ++i) {
5899 int child_index = i;
5900 while (child_index > 0) {
5901 int parent_index = ((child_index + 1) >> 1) - 1;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005902 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
5903 uint32_t child_value = NumberToUint32(numbers->get(child_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005904 if (parent_value < child_value) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005905 content->SwapPairs(numbers, parent_index, child_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005906 } else {
5907 break;
5908 }
5909 child_index = parent_index;
5910 }
5911 }
5912
5913 // Extract elements and create sorted array.
5914 for (int i = len - 1; i > 0; --i) {
5915 // Put max element at the back of the array.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005916 content->SwapPairs(numbers, 0, i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005917 // Sift down the new top element.
5918 int parent_index = 0;
5919 while (true) {
5920 int child_index = ((parent_index + 1) << 1) - 1;
5921 if (child_index >= i) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005922 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
5923 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
5924 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005925 if (child_index + 1 >= i || child1_value > child2_value) {
5926 if (parent_value > child1_value) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005927 content->SwapPairs(numbers, parent_index, child_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005928 parent_index = child_index;
5929 } else {
5930 if (parent_value > child2_value) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005931 content->SwapPairs(numbers, parent_index, child_index + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005932 parent_index = child_index + 1;
5933 }
5934 }
5935 }
5936}
5937
5938
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005939// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
5940void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
5941 ASSERT(this->length() == numbers->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005942 // For small arrays, simply use insertion sort.
5943 if (len <= 10) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005944 InsertionSortPairs(this, numbers, len);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005945 return;
5946 }
5947 // Check the range of indices.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005948 uint32_t min_index = NumberToUint32(numbers->get(0));
5949 uint32_t max_index = min_index;
5950 uint32_t i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005951 for (i = 1; i < len; i++) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005952 if (NumberToUint32(numbers->get(i)) < min_index) {
5953 min_index = NumberToUint32(numbers->get(i));
5954 } else if (NumberToUint32(numbers->get(i)) > max_index) {
5955 max_index = NumberToUint32(numbers->get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005956 }
5957 }
5958 if (max_index - min_index + 1 == len) {
5959 // Indices form a contiguous range, unless there are duplicates.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005960 // Do an in-place linear time sort assuming distinct numbers, but
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005961 // avoid hanging in case they are not.
5962 for (i = 0; i < len; i++) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005963 uint32_t p;
5964 uint32_t j = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005965 // While the current element at i is not at its correct position p,
5966 // swap the elements at these two positions.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005967 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005968 j++ < len) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005969 SwapPairs(numbers, i, p);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005970 }
5971 }
5972 } else {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005973 HeapSortPairs(this, numbers, len);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005974 return;
5975 }
5976}
5977
5978
5979// Fill in the names of local properties into the supplied storage. The main
5980// purpose of this function is to provide reflection information for the object
5981// mirrors.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005982void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
5983 ASSERT(storage->length() >=
5984 NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE)) -
5985 index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005986 if (HasFastProperties()) {
5987 for (DescriptorReader r(map()->instance_descriptors());
5988 !r.eos();
5989 r.advance()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005990 if (r.IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005991 storage->set(index++, r.GetKey());
5992 }
5993 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005994 ASSERT(storage->length() >= index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005995 } else {
5996 property_dictionary()->CopyKeysTo(storage);
5997 }
5998}
5999
6000
6001int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
6002 return GetLocalElementKeys(NULL, filter);
6003}
6004
6005
6006int JSObject::NumberOfEnumElements() {
6007 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
6008}
6009
6010
6011int JSObject::GetLocalElementKeys(FixedArray* storage,
6012 PropertyAttributes filter) {
6013 int counter = 0;
6014 if (HasFastElements()) {
6015 int length = IsJSArray()
6016 ? Smi::cast(JSArray::cast(this)->length())->value()
6017 : FixedArray::cast(elements())->length();
6018 for (int i = 0; i < length; i++) {
6019 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
6020 if (storage) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006021 storage->set(counter, Smi::FromInt(i), SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006022 }
6023 counter++;
6024 }
6025 }
6026 ASSERT(!storage || storage->length() >= counter);
6027 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006028 if (storage) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006029 element_dictionary()->CopyKeysTo(storage, filter);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006030 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006031 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
6032 }
6033
6034 if (this->IsJSValue()) {
6035 Object* val = JSValue::cast(this)->value();
6036 if (val->IsString()) {
6037 String* str = String::cast(val);
6038 if (storage) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006039 for (int i = 0; i < str->length(); i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006040 storage->set(counter + i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006041 }
6042 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006043 counter += str->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006044 }
6045 }
6046 ASSERT(!storage || storage->length() == counter);
6047 return counter;
6048}
6049
6050
6051int JSObject::GetEnumElementKeys(FixedArray* storage) {
6052 return GetLocalElementKeys(storage,
6053 static_cast<PropertyAttributes>(DONT_ENUM));
6054}
6055
6056
ager@chromium.org381abbb2009-02-25 13:23:22 +00006057// Thomas Wang, Integer Hash Functions.
6058// http://www.concentric.net/~Ttwang/tech/inthash.htm
6059static uint32_t ComputeIntegerHash(uint32_t key) {
6060 uint32_t hash = key;
6061 hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1;
6062 hash = hash ^ (hash >> 12);
6063 hash = hash + (hash << 2);
6064 hash = hash ^ (hash >> 4);
6065 hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11);
6066 hash = hash ^ (hash >> 16);
6067 return hash;
6068}
6069
6070
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006071// The NumberKey uses carries the uint32_t as key.
6072// This avoids allocation in HasProperty.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006073class NumberKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006074 public:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006075 explicit NumberKey(uint32_t number) : number_(number) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006076
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006077 bool IsMatch(Object* number) {
6078 return number_ == ToUint32(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006079 }
6080
ager@chromium.org381abbb2009-02-25 13:23:22 +00006081 uint32_t Hash() { return ComputeIntegerHash(number_); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006082
6083 HashFunction GetHashFunction() { return NumberHash; }
6084
6085 Object* GetObject() {
6086 return Heap::NumberFromDouble(number_);
6087 }
6088
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006089 bool IsStringKey() { return false; }
6090
6091 private:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006092 static uint32_t NumberHash(Object* obj) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00006093 return ComputeIntegerHash(ToUint32(obj));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006094 }
6095
6096 static uint32_t ToUint32(Object* obj) {
6097 ASSERT(obj->IsNumber());
6098 return static_cast<uint32_t>(obj->Number());
6099 }
6100
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006101 uint32_t number_;
6102};
6103
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006104
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006105// StringKey simply carries a string object as key.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006106class StringKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006107 public:
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006108 explicit StringKey(String* string) :
6109 string_(string),
6110 hash_(StringHash(string)) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006111
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006112 bool IsMatch(Object* string) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006113 // We know that all entries in a hash table had their hash keys created.
6114 // Use that knowledge to have fast failure.
6115 if (hash_ != StringHash(string)) {
6116 return false;
6117 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006118 return string_->Equals(String::cast(string));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006119 }
6120
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006121 uint32_t Hash() { return hash_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006122
6123 HashFunction GetHashFunction() { return StringHash; }
6124
6125 Object* GetObject() { return string_; }
6126
6127 static uint32_t StringHash(Object* obj) {
6128 return String::cast(obj)->Hash();
6129 }
6130
6131 bool IsStringKey() { return true; }
6132
6133 String* string_;
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006134 uint32_t hash_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006135};
6136
ager@chromium.org381abbb2009-02-25 13:23:22 +00006137
6138// StringSharedKeys are used as keys in the eval cache.
6139class StringSharedKey : public HashTableKey {
6140 public:
6141 StringSharedKey(String* source, SharedFunctionInfo* shared)
6142 : source_(source), shared_(shared) { }
6143
6144 bool IsMatch(Object* other) {
6145 if (!other->IsFixedArray()) return false;
6146 FixedArray* pair = FixedArray::cast(other);
6147 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
6148 if (shared != shared_) return false;
6149 String* source = String::cast(pair->get(1));
6150 return source->Equals(source_);
6151 }
6152
6153 typedef uint32_t (*HashFunction)(Object* obj);
6154
6155 virtual HashFunction GetHashFunction() { return StringSharedHash; }
6156
6157 static uint32_t StringSharedHashHelper(String* source,
6158 SharedFunctionInfo* shared) {
6159 uint32_t hash = source->Hash();
6160 if (shared->HasSourceCode()) {
6161 // Instead of using the SharedFunctionInfo pointer in the hash
6162 // code computation, we use a combination of the hash of the
6163 // script source code and the start and end positions. We do
6164 // this to ensure that the cache entries can survive garbage
6165 // collection.
6166 Script* script = Script::cast(shared->script());
6167 hash ^= String::cast(script->source())->Hash();
6168 hash += shared->start_position();
6169 }
6170 return hash;
6171 }
6172
6173 static uint32_t StringSharedHash(Object* obj) {
6174 FixedArray* pair = FixedArray::cast(obj);
6175 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
6176 String* source = String::cast(pair->get(1));
6177 return StringSharedHashHelper(source, shared);
6178 }
6179
6180 virtual uint32_t Hash() {
6181 return StringSharedHashHelper(source_, shared_);
6182 }
6183
6184 virtual Object* GetObject() {
6185 Object* obj = Heap::AllocateFixedArray(2);
6186 if (obj->IsFailure()) return obj;
6187 FixedArray* pair = FixedArray::cast(obj);
6188 pair->set(0, shared_);
6189 pair->set(1, source_);
6190 return pair;
6191 }
6192
6193 virtual bool IsStringKey() { return false; }
6194
6195 private:
6196 String* source_;
6197 SharedFunctionInfo* shared_;
6198};
6199
6200
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006201// RegExpKey carries the source and flags of a regular expression as key.
6202class RegExpKey : public HashTableKey {
6203 public:
6204 RegExpKey(String* string, JSRegExp::Flags flags)
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006205 : string_(string),
6206 flags_(Smi::FromInt(flags.value())) { }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006207
6208 bool IsMatch(Object* obj) {
6209 FixedArray* val = FixedArray::cast(obj);
6210 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
6211 && (flags_ == val->get(JSRegExp::kFlagsIndex));
6212 }
6213
6214 uint32_t Hash() { return RegExpHash(string_, flags_); }
6215
6216 HashFunction GetHashFunction() { return RegExpObjectHash; }
6217
6218 Object* GetObject() {
6219 // Plain hash maps, which is where regexp keys are used, don't
6220 // use this function.
6221 UNREACHABLE();
6222 return NULL;
6223 }
6224
6225 static uint32_t RegExpObjectHash(Object* obj) {
6226 FixedArray* val = FixedArray::cast(obj);
6227 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
6228 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
6229 }
6230
6231 static uint32_t RegExpHash(String* string, Smi* flags) {
6232 return string->Hash() + flags->value();
6233 }
6234
6235 bool IsStringKey() { return false; }
6236
6237 String* string_;
6238 Smi* flags_;
6239};
6240
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006241// Utf8SymbolKey carries a vector of chars as key.
6242class Utf8SymbolKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006243 public:
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006244 explicit Utf8SymbolKey(Vector<const char> string)
ager@chromium.org7c537e22008-10-16 08:43:32 +00006245 : string_(string), length_field_(0) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006246
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006247 bool IsMatch(Object* string) {
6248 return String::cast(string)->IsEqualTo(string_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006249 }
6250
6251 HashFunction GetHashFunction() {
6252 return StringHash;
6253 }
6254
6255 uint32_t Hash() {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006256 if (length_field_ != 0) return length_field_ >> String::kHashShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006257 unibrow::Utf8InputBuffer<> buffer(string_.start(),
6258 static_cast<unsigned>(string_.length()));
6259 chars_ = buffer.Length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006260 length_field_ = String::ComputeLengthAndHashField(&buffer, chars_);
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006261 uint32_t result = length_field_ >> String::kHashShift;
6262 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
6263 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006264 }
6265
6266 Object* GetObject() {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006267 if (length_field_ == 0) Hash();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006268 return Heap::AllocateSymbol(string_, chars_, length_field_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006269 }
6270
6271 static uint32_t StringHash(Object* obj) {
6272 return String::cast(obj)->Hash();
6273 }
6274
6275 bool IsStringKey() { return true; }
6276
6277 Vector<const char> string_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00006278 uint32_t length_field_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006279 int chars_; // Caches the number of characters when computing the hash code.
6280};
6281
6282
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006283// SymbolKey carries a string/symbol object as key.
6284class SymbolKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006285 public:
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006286 explicit SymbolKey(String* string) : string_(string) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006287
6288 HashFunction GetHashFunction() {
6289 return StringHash;
6290 }
6291
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006292 bool IsMatch(Object* string) {
6293 return String::cast(string)->Equals(string_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006294 }
6295
6296 uint32_t Hash() { return string_->Hash(); }
6297
6298 Object* GetObject() {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006299 // If the string is a cons string, attempt to flatten it so that
6300 // symbols will most often be flat strings.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006301 if (StringShape(string_).IsCons()) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006302 ConsString* cons_string = ConsString::cast(string_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006303 cons_string->TryFlatten();
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006304 if (cons_string->second()->length() == 0) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00006305 string_ = cons_string->first();
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006306 }
6307 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006308 // Transform string to symbol if possible.
6309 Map* map = Heap::SymbolMapForString(string_);
6310 if (map != NULL) {
6311 string_->set_map(map);
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006312 ASSERT(string_->IsSymbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006313 return string_;
6314 }
6315 // Otherwise allocate a new symbol.
6316 StringInputBuffer buffer(string_);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006317 return Heap::AllocateInternalSymbol(&buffer,
6318 string_->length(),
6319 string_->length_field());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006320 }
6321
6322 static uint32_t StringHash(Object* obj) {
6323 return String::cast(obj)->Hash();
6324 }
6325
6326 bool IsStringKey() { return true; }
6327
6328 String* string_;
6329};
6330
6331
6332template<int prefix_size, int element_size>
6333void HashTable<prefix_size, element_size>::IteratePrefix(ObjectVisitor* v) {
6334 IteratePointers(v, 0, kElementsStartOffset);
6335}
6336
6337
6338template<int prefix_size, int element_size>
6339void HashTable<prefix_size, element_size>::IterateElements(ObjectVisitor* v) {
6340 IteratePointers(v,
6341 kElementsStartOffset,
6342 kHeaderSize + length() * kPointerSize);
6343}
6344
6345
6346template<int prefix_size, int element_size>
6347Object* HashTable<prefix_size, element_size>::Allocate(int at_least_space_for) {
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00006348 int capacity = RoundUpToPowerOf2(at_least_space_for);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006349 if (capacity < 4) capacity = 4; // Guarantee min capacity.
6350 Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity));
6351 if (!obj->IsFailure()) {
6352 HashTable::cast(obj)->SetNumberOfElements(0);
6353 HashTable::cast(obj)->SetCapacity(capacity);
6354 }
6355 return obj;
6356}
6357
6358
6359// Find entry for key otherwise return -1.
6360template <int prefix_size, int element_size>
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006361int HashTable<prefix_size, element_size>::FindEntry(HashTableKey* key) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006362 uint32_t nof = NumberOfElements();
6363 if (nof == 0) return -1; // Bail out if empty.
6364
6365 uint32_t capacity = Capacity();
6366 uint32_t hash = key->Hash();
6367 uint32_t entry = GetProbe(hash, 0, capacity);
6368
6369 Object* element = KeyAt(entry);
6370 uint32_t passed_elements = 0;
6371 if (!element->IsNull()) {
6372 if (!element->IsUndefined() && key->IsMatch(element)) return entry;
6373 if (++passed_elements == nof) return -1;
6374 }
6375 for (uint32_t i = 1; !element->IsUndefined(); i++) {
6376 entry = GetProbe(hash, i, capacity);
6377 element = KeyAt(entry);
6378 if (!element->IsNull()) {
6379 if (!element->IsUndefined() && key->IsMatch(element)) return entry;
6380 if (++passed_elements == nof) return -1;
6381 }
6382 }
6383 return -1;
6384}
6385
6386
6387template<int prefix_size, int element_size>
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006388Object* HashTable<prefix_size, element_size>::EnsureCapacity(
6389 int n, HashTableKey* key) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006390 int capacity = Capacity();
6391 int nof = NumberOfElements() + n;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006392 // Make sure 25% is free
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006393 if (nof + (nof >> 2) <= capacity) return this;
6394
6395 Object* obj = Allocate(nof * 2);
6396 if (obj->IsFailure()) return obj;
ager@chromium.org236ad962008-09-25 09:45:57 +00006397 HashTable* table = HashTable::cast(obj);
6398 WriteBarrierMode mode = table->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006399
6400 // Copy prefix to new array.
6401 for (int i = kPrefixStartIndex; i < kPrefixStartIndex + prefix_size; i++) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006402 table->set(i, get(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006403 }
6404 // Rehash the elements.
6405 uint32_t (*Hash)(Object* key) = key->GetHashFunction();
6406 for (int i = 0; i < capacity; i++) {
6407 uint32_t from_index = EntryToIndex(i);
6408 Object* key = get(from_index);
6409 if (IsKey(key)) {
6410 uint32_t insertion_index =
ager@chromium.org236ad962008-09-25 09:45:57 +00006411 EntryToIndex(table->FindInsertionEntry(key, Hash(key)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006412 for (int j = 0; j < element_size; j++) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006413 table->set(insertion_index + j, get(from_index + j), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006414 }
6415 }
6416 }
ager@chromium.org236ad962008-09-25 09:45:57 +00006417 table->SetNumberOfElements(NumberOfElements());
6418 return table;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006419}
6420
6421
6422template<int prefix_size, int element_size>
6423uint32_t HashTable<prefix_size, element_size>::FindInsertionEntry(
6424 Object* key,
6425 uint32_t hash) {
6426 uint32_t capacity = Capacity();
6427 uint32_t entry = GetProbe(hash, 0, capacity);
6428 Object* element = KeyAt(entry);
6429
6430 for (uint32_t i = 1; !(element->IsUndefined() || element->IsNull()); i++) {
6431 entry = GetProbe(hash, i, capacity);
6432 element = KeyAt(entry);
6433 }
6434
6435 return entry;
6436}
6437
6438
6439// Force instantiation of SymbolTable's base class
6440template class HashTable<0, 1>;
6441
6442
6443// Force instantiation of Dictionary's base class
6444template class HashTable<2, 3>;
6445
6446
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006447// Force instantiation of EvalCache's base class
6448template class HashTable<0, 2>;
6449
6450
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006451Object* SymbolTable::LookupString(String* string, Object** s) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006452 SymbolKey key(string);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006453 return LookupKey(&key, s);
6454}
6455
6456
ager@chromium.org7c537e22008-10-16 08:43:32 +00006457bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
6458 SymbolKey key(string);
6459 int entry = FindEntry(&key);
6460 if (entry == -1) {
6461 return false;
6462 } else {
6463 String* result = String::cast(KeyAt(entry));
ager@chromium.org870a0b62008-11-04 11:43:05 +00006464 ASSERT(StringShape(result).IsSymbol());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006465 *symbol = result;
6466 return true;
6467 }
6468}
6469
6470
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006471Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006472 Utf8SymbolKey key(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006473 return LookupKey(&key, s);
6474}
6475
6476
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006477Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006478 int entry = FindEntry(key);
6479
6480 // Symbol already in table.
6481 if (entry != -1) {
6482 *s = KeyAt(entry);
6483 return this;
6484 }
6485
6486 // Adding new symbol. Grow table if needed.
6487 Object* obj = EnsureCapacity(1, key);
6488 if (obj->IsFailure()) return obj;
6489
6490 // Create symbol object.
6491 Object* symbol = key->GetObject();
6492 if (symbol->IsFailure()) return symbol;
6493
6494 // If the symbol table grew as part of EnsureCapacity, obj is not
6495 // the current symbol table and therefore we cannot use
6496 // SymbolTable::cast here.
6497 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
6498
6499 // Add the new symbol and return it along with the symbol table.
6500 entry = table->FindInsertionEntry(symbol, key->Hash());
6501 table->set(EntryToIndex(entry), symbol);
6502 table->ElementAdded();
6503 *s = symbol;
6504 return table;
6505}
6506
6507
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00006508Object* CompilationCacheTable::Lookup(String* src) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006509 StringKey key(src);
6510 int entry = FindEntry(&key);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006511 if (entry == -1) return Heap::undefined_value();
6512 return get(EntryToIndex(entry) + 1);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006513}
6514
6515
ager@chromium.org381abbb2009-02-25 13:23:22 +00006516Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
6517 StringSharedKey key(src, context->closure()->shared());
6518 int entry = FindEntry(&key);
6519 if (entry == -1) return Heap::undefined_value();
6520 return get(EntryToIndex(entry) + 1);
6521}
6522
6523
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006524Object* CompilationCacheTable::LookupRegExp(String* src,
6525 JSRegExp::Flags flags) {
6526 RegExpKey key(src, flags);
6527 int entry = FindEntry(&key);
6528 if (entry == -1) return Heap::undefined_value();
6529 return get(EntryToIndex(entry) + 1);
6530}
6531
6532
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00006533Object* CompilationCacheTable::Put(String* src, Object* value) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006534 StringKey key(src);
6535 Object* obj = EnsureCapacity(1, &key);
6536 if (obj->IsFailure()) return obj;
6537
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00006538 CompilationCacheTable* cache =
6539 reinterpret_cast<CompilationCacheTable*>(obj);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006540 int entry = cache->FindInsertionEntry(src, key.Hash());
6541 cache->set(EntryToIndex(entry), src);
6542 cache->set(EntryToIndex(entry) + 1, value);
6543 cache->ElementAdded();
6544 return cache;
6545}
6546
6547
ager@chromium.org381abbb2009-02-25 13:23:22 +00006548Object* CompilationCacheTable::PutEval(String* src,
6549 Context* context,
6550 Object* value) {
6551 StringSharedKey key(src, context->closure()->shared());
6552 Object* obj = EnsureCapacity(1, &key);
6553 if (obj->IsFailure()) return obj;
6554
6555 CompilationCacheTable* cache =
6556 reinterpret_cast<CompilationCacheTable*>(obj);
6557 int entry = cache->FindInsertionEntry(src, key.Hash());
6558
6559 Object* k = key.GetObject();
6560 if (k->IsFailure()) return k;
6561
6562 cache->set(EntryToIndex(entry), k);
6563 cache->set(EntryToIndex(entry) + 1, value);
6564 cache->ElementAdded();
6565 return cache;
6566}
6567
6568
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006569Object* CompilationCacheTable::PutRegExp(String* src,
6570 JSRegExp::Flags flags,
6571 FixedArray* value) {
6572 RegExpKey key(src, flags);
6573 Object* obj = EnsureCapacity(1, &key);
6574 if (obj->IsFailure()) return obj;
6575
6576 CompilationCacheTable* cache =
6577 reinterpret_cast<CompilationCacheTable*>(obj);
6578 int entry = cache->FindInsertionEntry(value, key.Hash());
6579 cache->set(EntryToIndex(entry), value);
6580 cache->set(EntryToIndex(entry) + 1, value);
6581 cache->ElementAdded();
6582 return cache;
6583}
6584
6585
ager@chromium.org236ad962008-09-25 09:45:57 +00006586// SymbolsKey used for HashTable where key is array of symbols.
6587class SymbolsKey : public HashTableKey {
6588 public:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006589 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
ager@chromium.org236ad962008-09-25 09:45:57 +00006590
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006591 bool IsMatch(Object* symbols) {
6592 FixedArray* o = FixedArray::cast(symbols);
ager@chromium.org236ad962008-09-25 09:45:57 +00006593 int len = symbols_->length();
6594 if (o->length() != len) return false;
6595 for (int i = 0; i < len; i++) {
6596 if (o->get(i) != symbols_->get(i)) return false;
6597 }
6598 return true;
6599 }
6600
6601 uint32_t Hash() { return SymbolsHash(symbols_); }
6602
6603 HashFunction GetHashFunction() { return SymbolsHash; }
6604
6605 Object* GetObject() { return symbols_; }
6606
6607 static uint32_t SymbolsHash(Object* obj) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006608 FixedArray* symbols = FixedArray::cast(obj);
6609 int len = symbols->length();
6610 uint32_t hash = 0;
ager@chromium.org236ad962008-09-25 09:45:57 +00006611 for (int i = 0; i < len; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006612 hash ^= String::cast(symbols->get(i))->Hash();
ager@chromium.org236ad962008-09-25 09:45:57 +00006613 }
6614 return hash;
6615 }
6616
6617 bool IsStringKey() { return false; }
6618
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006619 private:
ager@chromium.org236ad962008-09-25 09:45:57 +00006620 FixedArray* symbols_;
6621};
6622
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006623
6624// MapNameKeys are used as keys in lookup caches.
6625class MapNameKey : public HashTableKey {
6626 public:
6627 MapNameKey(Map* map, String* name)
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006628 : map_(map), name_(name) { }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006629
6630 bool IsMatch(Object* other) {
6631 if (!other->IsFixedArray()) return false;
6632 FixedArray* pair = FixedArray::cast(other);
6633 Map* map = Map::cast(pair->get(0));
6634 if (map != map_) return false;
6635 String* name = String::cast(pair->get(1));
6636 return name->Equals(name_);
6637 }
6638
6639 typedef uint32_t (*HashFunction)(Object* obj);
6640
6641 virtual HashFunction GetHashFunction() { return MapNameHash; }
6642
6643 static uint32_t MapNameHashHelper(Map* map, String* name) {
6644 return reinterpret_cast<uint32_t>(map) ^ name->Hash();
6645 }
6646
6647 static uint32_t MapNameHash(Object* obj) {
6648 FixedArray* pair = FixedArray::cast(obj);
6649 Map* map = Map::cast(pair->get(0));
6650 String* name = String::cast(pair->get(1));
6651 return MapNameHashHelper(map, name);
6652 }
6653
6654 virtual uint32_t Hash() {
6655 return MapNameHashHelper(map_, name_);
6656 }
6657
6658 virtual Object* GetObject() {
6659 Object* obj = Heap::AllocateFixedArray(2);
6660 if (obj->IsFailure()) return obj;
6661 FixedArray* pair = FixedArray::cast(obj);
6662 pair->set(0, map_);
6663 pair->set(1, name_);
6664 return pair;
6665 }
6666
6667 virtual bool IsStringKey() { return false; }
6668
6669 private:
6670 Map* map_;
6671 String* name_;
6672};
6673
6674
ager@chromium.org236ad962008-09-25 09:45:57 +00006675Object* MapCache::Lookup(FixedArray* array) {
6676 SymbolsKey key(array);
6677 int entry = FindEntry(&key);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006678 if (entry == -1) return Heap::undefined_value();
6679 return get(EntryToIndex(entry) + 1);
ager@chromium.org236ad962008-09-25 09:45:57 +00006680}
6681
6682
6683Object* MapCache::Put(FixedArray* array, Map* value) {
6684 SymbolsKey key(array);
6685 Object* obj = EnsureCapacity(1, &key);
6686 if (obj->IsFailure()) return obj;
6687
6688 MapCache* cache = reinterpret_cast<MapCache*>(obj);
6689 int entry = cache->FindInsertionEntry(array, key.Hash());
6690 cache->set(EntryToIndex(entry), array);
6691 cache->set(EntryToIndex(entry) + 1, value);
6692 cache->ElementAdded();
6693 return cache;
6694}
6695
6696
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006697int LookupCache::Lookup(Map* map, String* name) {
6698 MapNameKey key(map, name);
6699 int entry = FindEntry(&key);
6700 if (entry == -1) return kNotFound;
6701 return Smi::cast(get(EntryToIndex(entry) + 1))->value();
6702}
6703
6704
6705Object* LookupCache::Put(Map* map, String* name, int value) {
6706 MapNameKey key(map, name);
6707 Object* obj = EnsureCapacity(1, &key);
6708 if (obj->IsFailure()) return obj;
6709 Object* k = key.GetObject();
6710 if (k->IsFailure()) return k;
6711
6712 LookupCache* cache = reinterpret_cast<LookupCache*>(obj);
6713 int entry = cache->FindInsertionEntry(k, key.Hash());
6714 int index = EntryToIndex(entry);
6715 cache->set(index, k);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006716 cache->set(index + 1, Smi::FromInt(value), SKIP_WRITE_BARRIER);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006717 cache->ElementAdded();
6718 return cache;
6719}
6720
6721
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006722Object* Dictionary::Allocate(int at_least_space_for) {
6723 Object* obj = DictionaryBase::Allocate(at_least_space_for);
6724 // Initialize the next enumeration index.
6725 if (!obj->IsFailure()) {
6726 Dictionary::cast(obj)->
6727 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
6728 }
6729 return obj;
6730}
6731
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006732
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006733Object* Dictionary::GenerateNewEnumerationIndices() {
6734 int length = NumberOfElements();
6735
6736 // Allocate and initialize iteration order array.
6737 Object* obj = Heap::AllocateFixedArray(length);
6738 if (obj->IsFailure()) return obj;
6739 FixedArray* iteration_order = FixedArray::cast(obj);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006740 for (int i = 0; i < length; i++) {
6741 iteration_order->set(i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
6742 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006743
6744 // Allocate array with enumeration order.
6745 obj = Heap::AllocateFixedArray(length);
6746 if (obj->IsFailure()) return obj;
6747 FixedArray* enumeration_order = FixedArray::cast(obj);
6748
6749 // Fill the enumeration order array with property details.
6750 int capacity = Capacity();
6751 int pos = 0;
6752 for (int i = 0; i < capacity; i++) {
6753 if (IsKey(KeyAt(i))) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006754 enumeration_order->set(pos++,
6755 Smi::FromInt(DetailsAt(i).index()),
6756 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006757 }
6758 }
6759
6760 // Sort the arrays wrt. enumeration order.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006761 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006762
6763 // Overwrite the enumeration_order with the enumeration indices.
6764 for (int i = 0; i < length; i++) {
6765 int index = Smi::cast(iteration_order->get(i))->value();
6766 int enum_index = PropertyDetails::kInitialIndex + i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006767 enumeration_order->set(index,
6768 Smi::FromInt(enum_index),
6769 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006770 }
6771
6772 // Update the dictionary with new indices.
6773 capacity = Capacity();
6774 pos = 0;
6775 for (int i = 0; i < capacity; i++) {
6776 if (IsKey(KeyAt(i))) {
6777 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
6778 PropertyDetails details = DetailsAt(i);
6779 PropertyDetails new_details =
6780 PropertyDetails(details.attributes(), details.type(), enum_index);
6781 DetailsAtPut(i, new_details);
6782 }
6783 }
6784
6785 // Set the next enumeration index.
6786 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
6787 return this;
6788}
6789
6790
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006791Object* Dictionary::EnsureCapacity(int n, HashTableKey* key) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006792 // Check whether there are enough enumeration indices to add n elements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793 if (key->IsStringKey() &&
6794 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
6795 // If not, we generate new indices for the properties.
6796 Object* result = GenerateNewEnumerationIndices();
6797 if (result->IsFailure()) return result;
6798 }
6799 return DictionaryBase::EnsureCapacity(n, key);
6800}
6801
6802
6803void Dictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
6804 // Do nothing if the interval [from, to) is empty.
6805 if (from >= to) return;
6806
6807 int removed_entries = 0;
6808 Object* sentinel = Heap::null_value();
6809 int capacity = Capacity();
6810 for (int i = 0; i < capacity; i++) {
6811 Object* key = KeyAt(i);
6812 if (key->IsNumber()) {
6813 uint32_t number = static_cast<uint32_t>(key->Number());
6814 if (from <= number && number < to) {
6815 SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
6816 removed_entries++;
6817 }
6818 }
6819 }
6820
6821 // Update the number of elements.
6822 SetNumberOfElements(NumberOfElements() - removed_entries);
6823}
6824
6825
6826Object* Dictionary::DeleteProperty(int entry) {
6827 PropertyDetails details = DetailsAt(entry);
6828 if (details.IsDontDelete()) return Heap::false_value();
6829 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
6830 ElementRemoved();
6831 return Heap::true_value();
6832}
6833
6834
6835int Dictionary::FindStringEntry(String* key) {
6836 StringKey k(key);
6837 return FindEntry(&k);
6838}
6839
6840
6841int Dictionary::FindNumberEntry(uint32_t index) {
6842 NumberKey k(index);
6843 return FindEntry(&k);
6844}
6845
6846
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006847Object* Dictionary::AtPut(HashTableKey* key, Object* value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006848 int entry = FindEntry(key);
6849
6850 // If the entry is present set the value;
6851 if (entry != -1) {
6852 ValueAtPut(entry, value);
6853 return this;
6854 }
6855
6856 // Check whether the dictionary should be extended.
6857 Object* obj = EnsureCapacity(1, key);
6858 if (obj->IsFailure()) return obj;
6859 Object* k = key->GetObject();
6860 if (k->IsFailure()) return k;
6861 PropertyDetails details = PropertyDetails(NONE, NORMAL);
6862 Dictionary::cast(obj)->AddEntry(k, value, details, key->Hash());
6863 return obj;
6864}
6865
6866
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006867Object* Dictionary::Add(HashTableKey* key, Object* value,
6868 PropertyDetails details) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006869 // Check whether the dictionary should be extended.
6870 Object* obj = EnsureCapacity(1, key);
6871 if (obj->IsFailure()) return obj;
6872 // Compute the key object.
6873 Object* k = key->GetObject();
6874 if (k->IsFailure()) return k;
6875 Dictionary::cast(obj)->AddEntry(k, value, details, key->Hash());
6876 return obj;
6877}
6878
6879
6880// Add a key, value pair to the dictionary.
6881void Dictionary::AddEntry(Object* key,
6882 Object* value,
6883 PropertyDetails details,
6884 uint32_t hash) {
6885 uint32_t entry = FindInsertionEntry(key, hash);
6886 // Insert element at empty or deleted entry
6887 if (details.index() == 0 && key->IsString()) {
6888 // Assign an enumeration index to the property and update
6889 // SetNextEnumerationIndex.
6890 int index = NextEnumerationIndex();
6891 details = PropertyDetails(details.attributes(), details.type(), index);
6892 SetNextEnumerationIndex(index + 1);
6893 }
6894 SetEntry(entry, key, value, details);
6895 ASSERT(KeyAt(entry)->IsNumber() || KeyAt(entry)->IsString());
6896 ElementAdded();
6897}
6898
6899
6900void Dictionary::UpdateMaxNumberKey(uint32_t key) {
6901 // If the dictionary requires slow elements an element has already
6902 // been added at a high index.
6903 if (requires_slow_elements()) return;
6904 // Check if this index is high enough that we should require slow
6905 // elements.
6906 if (key > kRequiresSlowElementsLimit) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006907 set_requires_slow_elements();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006908 return;
6909 }
6910 // Update max key value.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006911 Object* max_index_object = get(kMaxNumberKeyIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006912 if (!max_index_object->IsSmi() || max_number_key() < key) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006913 set(kMaxNumberKeyIndex,
6914 Smi::FromInt(key << kRequiresSlowElementsTagSize),
6915 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006916 }
6917}
6918
6919
6920Object* Dictionary::AddStringEntry(String* key,
6921 Object* value,
6922 PropertyDetails details) {
6923 StringKey k(key);
6924 SLOW_ASSERT(FindEntry(&k) == -1);
6925 return Add(&k, value, details);
6926}
6927
6928
6929Object* Dictionary::AddNumberEntry(uint32_t key,
6930 Object* value,
6931 PropertyDetails details) {
6932 NumberKey k(key);
6933 UpdateMaxNumberKey(key);
6934 SLOW_ASSERT(FindEntry(&k) == -1);
6935 return Add(&k, value, details);
6936}
6937
6938
6939Object* Dictionary::AtStringPut(String* key, Object* value) {
6940 StringKey k(key);
6941 return AtPut(&k, value);
6942}
6943
6944
6945Object* Dictionary::AtNumberPut(uint32_t key, Object* value) {
6946 NumberKey k(key);
6947 UpdateMaxNumberKey(key);
6948 return AtPut(&k, value);
6949}
6950
6951
6952Object* Dictionary::SetOrAddStringEntry(String* key,
6953 Object* value,
6954 PropertyDetails details) {
6955 StringKey k(key);
6956 int entry = FindEntry(&k);
6957 if (entry == -1) return AddStringEntry(key, value, details);
6958 // Preserve enumeration index.
6959 details = PropertyDetails(details.attributes(),
6960 details.type(),
6961 DetailsAt(entry).index());
6962 SetEntry(entry, key, value, details);
6963 return this;
6964}
6965
6966
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006967Object* Dictionary::SetOrAddNumberEntry(uint32_t key,
6968 Object* value,
6969 PropertyDetails details) {
6970 NumberKey k(key);
6971 int entry = FindEntry(&k);
6972 if (entry == -1) return AddNumberEntry(key, value, details);
6973 // Preserve enumeration index.
6974 details = PropertyDetails(details.attributes(),
6975 details.type(),
6976 DetailsAt(entry).index());
6977 SetEntry(entry, k.GetObject(), value, details);
6978 return this;
6979}
6980
6981
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006982int Dictionary::NumberOfElementsFilterAttributes(PropertyAttributes filter) {
6983 int capacity = Capacity();
6984 int result = 0;
6985 for (int i = 0; i < capacity; i++) {
6986 Object* k = KeyAt(i);
6987 if (IsKey(k)) {
6988 PropertyAttributes attr = DetailsAt(i).attributes();
6989 if ((attr & filter) == 0) result++;
6990 }
6991 }
6992 return result;
6993}
6994
6995
6996int Dictionary::NumberOfEnumElements() {
6997 return NumberOfElementsFilterAttributes(
6998 static_cast<PropertyAttributes>(DONT_ENUM));
6999}
7000
7001
7002void Dictionary::CopyKeysTo(FixedArray* storage, PropertyAttributes filter) {
7003 ASSERT(storage->length() >= NumberOfEnumElements());
7004 int capacity = Capacity();
7005 int index = 0;
7006 for (int i = 0; i < capacity; i++) {
7007 Object* k = KeyAt(i);
7008 if (IsKey(k)) {
7009 PropertyAttributes attr = DetailsAt(i).attributes();
7010 if ((attr & filter) == 0) storage->set(index++, k);
7011 }
7012 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007013 storage->SortPairs(storage, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007014 ASSERT(storage->length() >= index);
7015}
7016
7017
7018void Dictionary::CopyEnumKeysTo(FixedArray* storage, FixedArray* sort_array) {
7019 ASSERT(storage->length() >= NumberOfEnumElements());
7020 int capacity = Capacity();
7021 int index = 0;
7022 for (int i = 0; i < capacity; i++) {
7023 Object* k = KeyAt(i);
7024 if (IsKey(k)) {
7025 PropertyDetails details = DetailsAt(i);
7026 if (!details.IsDontEnum()) {
7027 storage->set(index, k);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007028 sort_array->set(index,
7029 Smi::FromInt(details.index()),
7030 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007031 index++;
7032 }
7033 }
7034 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007035 storage->SortPairs(sort_array, sort_array->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007036 ASSERT(storage->length() >= index);
7037}
7038
7039
7040void Dictionary::CopyKeysTo(FixedArray* storage) {
7041 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
7042 static_cast<PropertyAttributes>(NONE)));
7043 int capacity = Capacity();
7044 int index = 0;
7045 for (int i = 0; i < capacity; i++) {
7046 Object* k = KeyAt(i);
7047 if (IsKey(k)) {
7048 storage->set(index++, k);
7049 }
7050 }
7051 ASSERT(storage->length() >= index);
7052}
7053
7054
7055// Backwards lookup (slow).
7056Object* Dictionary::SlowReverseLookup(Object* value) {
7057 int capacity = Capacity();
7058 for (int i = 0; i < capacity; i++) {
7059 Object* k = KeyAt(i);
7060 if (IsKey(k) && ValueAt(i) == value) {
7061 return k;
7062 }
7063 }
7064 return Heap::undefined_value();
7065}
7066
7067
7068Object* Dictionary::TransformPropertiesToFastFor(JSObject* obj,
7069 int unused_property_fields) {
7070 // Make sure we preserve dictionary representation if there are too many
7071 // descriptors.
7072 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
7073
7074 // Figure out if it is necessary to generate new enumeration indices.
7075 int max_enumeration_index =
7076 NextEnumerationIndex() +
7077 (DescriptorArray::kMaxNumberOfDescriptors - NumberOfElements());
7078 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
7079 Object* result = GenerateNewEnumerationIndices();
7080 if (result->IsFailure()) return result;
7081 }
7082
7083 int instance_descriptor_length = 0;
7084 int number_of_fields = 0;
7085
7086 // Compute the length of the instance descriptor.
7087 int capacity = Capacity();
7088 for (int i = 0; i < capacity; i++) {
7089 Object* k = KeyAt(i);
7090 if (IsKey(k)) {
7091 Object* value = ValueAt(i);
7092 PropertyType type = DetailsAt(i).type();
7093 ASSERT(type != FIELD);
7094 instance_descriptor_length++;
7095 if (type == NORMAL && !value->IsJSFunction()) number_of_fields += 1;
7096 }
7097 }
7098
7099 // Allocate the instance descriptor.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007100 Object* descriptors_unchecked =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007101 DescriptorArray::Allocate(instance_descriptor_length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007102 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
7103 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007104
ager@chromium.org32912102009-01-16 10:38:43 +00007105 int inobject_props = obj->map()->inobject_properties();
7106 int number_of_allocated_fields =
7107 number_of_fields + unused_property_fields - inobject_props;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108
7109 // Allocate the fixed array for the fields.
7110 Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields);
7111 if (fields->IsFailure()) return fields;
7112
7113 // Fill in the instance descriptor and the fields.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007114 DescriptorWriter w(descriptors);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007115 int current_offset = 0;
7116 for (int i = 0; i < capacity; i++) {
7117 Object* k = KeyAt(i);
7118 if (IsKey(k)) {
7119 Object* value = ValueAt(i);
7120 // Ensure the key is a symbol before writing into the instance descriptor.
7121 Object* key = Heap::LookupSymbol(String::cast(k));
7122 if (key->IsFailure()) return key;
7123 PropertyDetails details = DetailsAt(i);
7124 PropertyType type = details.type();
ager@chromium.org32912102009-01-16 10:38:43 +00007125
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007126 if (value->IsJSFunction()) {
7127 ConstantFunctionDescriptor d(String::cast(key),
7128 JSFunction::cast(value),
7129 details.attributes(),
7130 details.index());
7131 w.Write(&d);
7132 } else if (type == NORMAL) {
ager@chromium.org32912102009-01-16 10:38:43 +00007133 if (current_offset < inobject_props) {
7134 obj->InObjectPropertyAtPut(current_offset,
7135 value,
7136 UPDATE_WRITE_BARRIER);
7137 } else {
7138 int offset = current_offset - inobject_props;
7139 FixedArray::cast(fields)->set(offset, value);
7140 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007141 FieldDescriptor d(String::cast(key),
7142 current_offset++,
7143 details.attributes(),
7144 details.index());
7145 w.Write(&d);
7146 } else if (type == CALLBACKS) {
7147 CallbacksDescriptor d(String::cast(key),
7148 value,
7149 details.attributes(),
7150 details.index());
7151 w.Write(&d);
7152 } else {
7153 UNREACHABLE();
7154 }
7155 }
7156 }
7157 ASSERT(current_offset == number_of_fields);
7158
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007159 descriptors->Sort();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007160 // Allocate new map.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007161 Object* new_map = obj->map()->CopyDropDescriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007162 if (new_map->IsFailure()) return new_map;
7163
7164 // Transform the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007165 obj->set_map(Map::cast(new_map));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007166 obj->map()->set_instance_descriptors(descriptors);
7167 obj->map()->set_unused_property_fields(unused_property_fields);
7168
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007169 obj->set_properties(FixedArray::cast(fields));
7170 ASSERT(obj->IsJSObject());
7171
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007172 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
ager@chromium.org32912102009-01-16 10:38:43 +00007173 // Check that it really works.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007174 ASSERT(obj->HasFastProperties());
ager@chromium.org32912102009-01-16 10:38:43 +00007175
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007176 return obj;
7177}
7178
7179
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007180#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007181// Check if there is a break point at this code position.
7182bool DebugInfo::HasBreakPoint(int code_position) {
7183 // Get the break point info object for this code position.
7184 Object* break_point_info = GetBreakPointInfo(code_position);
7185
7186 // If there is no break point info object or no break points in the break
7187 // point info object there is no break point at this code position.
7188 if (break_point_info->IsUndefined()) return false;
7189 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
7190}
7191
7192
7193// Get the break point info object for this code position.
7194Object* DebugInfo::GetBreakPointInfo(int code_position) {
7195 // Find the index of the break point info object for this code position.
7196 int index = GetBreakPointInfoIndex(code_position);
7197
7198 // Return the break point info object if any.
7199 if (index == kNoBreakPointInfo) return Heap::undefined_value();
7200 return BreakPointInfo::cast(break_points()->get(index));
7201}
7202
7203
7204// Clear a break point at the specified code position.
7205void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
7206 int code_position,
7207 Handle<Object> break_point_object) {
7208 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
7209 if (break_point_info->IsUndefined()) return;
7210 BreakPointInfo::ClearBreakPoint(
7211 Handle<BreakPointInfo>::cast(break_point_info),
7212 break_point_object);
7213}
7214
7215
7216void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
7217 int code_position,
7218 int source_position,
7219 int statement_position,
7220 Handle<Object> break_point_object) {
7221 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
7222 if (!break_point_info->IsUndefined()) {
7223 BreakPointInfo::SetBreakPoint(
7224 Handle<BreakPointInfo>::cast(break_point_info),
7225 break_point_object);
7226 return;
7227 }
7228
7229 // Adding a new break point for a code position which did not have any
7230 // break points before. Try to find a free slot.
7231 int index = kNoBreakPointInfo;
7232 for (int i = 0; i < debug_info->break_points()->length(); i++) {
7233 if (debug_info->break_points()->get(i)->IsUndefined()) {
7234 index = i;
7235 break;
7236 }
7237 }
7238 if (index == kNoBreakPointInfo) {
7239 // No free slot - extend break point info array.
7240 Handle<FixedArray> old_break_points =
7241 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
7242 debug_info->set_break_points(*Factory::NewFixedArray(
7243 old_break_points->length() +
7244 Debug::kEstimatedNofBreakPointsInFunction));
7245 Handle<FixedArray> new_break_points =
7246 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
7247 for (int i = 0; i < old_break_points->length(); i++) {
7248 new_break_points->set(i, old_break_points->get(i));
7249 }
7250 index = old_break_points->length();
7251 }
7252 ASSERT(index != kNoBreakPointInfo);
7253
7254 // Allocate new BreakPointInfo object and set the break point.
7255 Handle<BreakPointInfo> new_break_point_info =
7256 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE));
7257 new_break_point_info->set_code_position(Smi::FromInt(code_position));
7258 new_break_point_info->set_source_position(Smi::FromInt(source_position));
7259 new_break_point_info->
7260 set_statement_position(Smi::FromInt(statement_position));
7261 new_break_point_info->set_break_point_objects(Heap::undefined_value());
7262 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
7263 debug_info->break_points()->set(index, *new_break_point_info);
7264}
7265
7266
7267// Get the break point objects for a code position.
7268Object* DebugInfo::GetBreakPointObjects(int code_position) {
7269 Object* break_point_info = GetBreakPointInfo(code_position);
7270 if (break_point_info->IsUndefined()) {
7271 return Heap::undefined_value();
7272 }
7273 return BreakPointInfo::cast(break_point_info)->break_point_objects();
7274}
7275
7276
7277// Get the total number of break points.
7278int DebugInfo::GetBreakPointCount() {
7279 if (break_points()->IsUndefined()) return 0;
7280 int count = 0;
7281 for (int i = 0; i < break_points()->length(); i++) {
7282 if (!break_points()->get(i)->IsUndefined()) {
7283 BreakPointInfo* break_point_info =
7284 BreakPointInfo::cast(break_points()->get(i));
7285 count += break_point_info->GetBreakPointCount();
7286 }
7287 }
7288 return count;
7289}
7290
7291
7292Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
7293 Handle<Object> break_point_object) {
7294 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value();
7295 for (int i = 0; i < debug_info->break_points()->length(); i++) {
7296 if (!debug_info->break_points()->get(i)->IsUndefined()) {
7297 Handle<BreakPointInfo> break_point_info =
7298 Handle<BreakPointInfo>(BreakPointInfo::cast(
7299 debug_info->break_points()->get(i)));
7300 if (BreakPointInfo::HasBreakPointObject(break_point_info,
7301 break_point_object)) {
7302 return *break_point_info;
7303 }
7304 }
7305 }
7306 return Heap::undefined_value();
7307}
7308
7309
7310// Find the index of the break point info object for the specified code
7311// position.
7312int DebugInfo::GetBreakPointInfoIndex(int code_position) {
7313 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
7314 for (int i = 0; i < break_points()->length(); i++) {
7315 if (!break_points()->get(i)->IsUndefined()) {
7316 BreakPointInfo* break_point_info =
7317 BreakPointInfo::cast(break_points()->get(i));
7318 if (break_point_info->code_position()->value() == code_position) {
7319 return i;
7320 }
7321 }
7322 }
7323 return kNoBreakPointInfo;
7324}
7325
7326
7327// Remove the specified break point object.
7328void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
7329 Handle<Object> break_point_object) {
7330 // If there are no break points just ignore.
7331 if (break_point_info->break_point_objects()->IsUndefined()) return;
7332 // If there is a single break point clear it if it is the same.
7333 if (!break_point_info->break_point_objects()->IsFixedArray()) {
7334 if (break_point_info->break_point_objects() == *break_point_object) {
7335 break_point_info->set_break_point_objects(Heap::undefined_value());
7336 }
7337 return;
7338 }
7339 // If there are multiple break points shrink the array
7340 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
7341 Handle<FixedArray> old_array =
7342 Handle<FixedArray>(
7343 FixedArray::cast(break_point_info->break_point_objects()));
7344 Handle<FixedArray> new_array =
7345 Factory::NewFixedArray(old_array->length() - 1);
7346 int found_count = 0;
7347 for (int i = 0; i < old_array->length(); i++) {
7348 if (old_array->get(i) == *break_point_object) {
7349 ASSERT(found_count == 0);
7350 found_count++;
7351 } else {
7352 new_array->set(i - found_count, old_array->get(i));
7353 }
7354 }
7355 // If the break point was found in the list change it.
7356 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
7357}
7358
7359
7360// Add the specified break point object.
7361void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
7362 Handle<Object> break_point_object) {
7363 // If there was no break point objects before just set it.
7364 if (break_point_info->break_point_objects()->IsUndefined()) {
7365 break_point_info->set_break_point_objects(*break_point_object);
7366 return;
7367 }
7368 // If the break point object is the same as before just ignore.
7369 if (break_point_info->break_point_objects() == *break_point_object) return;
7370 // If there was one break point object before replace with array.
7371 if (!break_point_info->break_point_objects()->IsFixedArray()) {
7372 Handle<FixedArray> array = Factory::NewFixedArray(2);
7373 array->set(0, break_point_info->break_point_objects());
7374 array->set(1, *break_point_object);
7375 break_point_info->set_break_point_objects(*array);
7376 return;
7377 }
7378 // If there was more than one break point before extend array.
7379 Handle<FixedArray> old_array =
7380 Handle<FixedArray>(
7381 FixedArray::cast(break_point_info->break_point_objects()));
7382 Handle<FixedArray> new_array =
7383 Factory::NewFixedArray(old_array->length() + 1);
7384 for (int i = 0; i < old_array->length(); i++) {
7385 // If the break point was there before just ignore.
7386 if (old_array->get(i) == *break_point_object) return;
7387 new_array->set(i, old_array->get(i));
7388 }
7389 // Add the new break point.
7390 new_array->set(old_array->length(), *break_point_object);
7391 break_point_info->set_break_point_objects(*new_array);
7392}
7393
7394
7395bool BreakPointInfo::HasBreakPointObject(
7396 Handle<BreakPointInfo> break_point_info,
7397 Handle<Object> break_point_object) {
7398 // No break point.
7399 if (break_point_info->break_point_objects()->IsUndefined()) return false;
7400 // Single beak point.
7401 if (!break_point_info->break_point_objects()->IsFixedArray()) {
7402 return break_point_info->break_point_objects() == *break_point_object;
7403 }
7404 // Multiple break points.
7405 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
7406 for (int i = 0; i < array->length(); i++) {
7407 if (array->get(i) == *break_point_object) {
7408 return true;
7409 }
7410 }
7411 return false;
7412}
7413
7414
7415// Get the number of break points.
7416int BreakPointInfo::GetBreakPointCount() {
7417 // No break point.
7418 if (break_point_objects()->IsUndefined()) return 0;
7419 // Single beak point.
7420 if (!break_point_objects()->IsFixedArray()) return 1;
7421 // Multiple break points.
7422 return FixedArray::cast(break_point_objects())->length();
7423}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007424#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007425
7426} } // namespace v8::internal