blob: 337dd65db00702897c9d828921861cd58b80ef63 [file] [log] [blame]
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +00001// Copyright 2011 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"
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +000031#include "arguments.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032#include "bootstrapper.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000033#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000034#include "debug.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000035#include "deoptimizer.h"
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000036#include "elements.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000037#include "execution.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000038#include "full-codegen.h"
39#include "hydrogen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000040#include "objects-inl.h"
ager@chromium.orgea4f62e2010-08-16 16:28:43 +000041#include "objects-visiting.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000042#include "macro-assembler.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000043#include "safepoint-table.h"
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +000044#include "scanner-base.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000045#include "string-stream.h"
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000046#include "utils.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000047#include "vm-state-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000048
mads.s.ager31e71382008-08-13 09:32:07 +000049#ifdef ENABLE_DISASSEMBLER
kasperl@chromium.orga5551262010-12-07 12:49:48 +000050#include "disasm.h"
mads.s.ager31e71382008-08-13 09:32:07 +000051#include "disassembler.h"
52#endif
53
kasperl@chromium.org71affb52009-05-26 05:44:31 +000054namespace v8 {
55namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000056
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057// Getters and setters are stored in a fixed array property. These are
58// constants for their indices.
59const int kGetterIndex = 0;
60const int kSetterIndex = 1;
61
lrn@chromium.org303ada72010-10-27 09:33:13 +000062MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor,
63 Object* value) {
64 Object* result;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000065 { MaybeObject* maybe_result =
66 constructor->GetHeap()->AllocateJSObject(constructor);
lrn@chromium.org303ada72010-10-27 09:33:13 +000067 if (!maybe_result->ToObject(&result)) return maybe_result;
68 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000069 JSValue::cast(result)->set_value(value);
70 return result;
71}
72
73
lrn@chromium.org303ada72010-10-27 09:33:13 +000074MaybeObject* Object::ToObject(Context* global_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000075 if (IsNumber()) {
76 return CreateJSValue(global_context->number_function(), this);
77 } else if (IsBoolean()) {
78 return CreateJSValue(global_context->boolean_function(), this);
79 } else if (IsString()) {
80 return CreateJSValue(global_context->string_function(), this);
81 }
82 ASSERT(IsJSObject());
83 return this;
84}
85
86
lrn@chromium.org303ada72010-10-27 09:33:13 +000087MaybeObject* Object::ToObject() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000088 if (IsJSObject()) {
89 return this;
90 } else if (IsNumber()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000091 Isolate* isolate = Isolate::Current();
92 Context* global_context = isolate->context()->global_context();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000093 return CreateJSValue(global_context->number_function(), this);
94 } else if (IsBoolean()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000095 Isolate* isolate = HeapObject::cast(this)->GetIsolate();
96 Context* global_context = isolate->context()->global_context();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097 return CreateJSValue(global_context->boolean_function(), this);
98 } else if (IsString()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000099 Isolate* isolate = HeapObject::cast(this)->GetIsolate();
100 Context* global_context = isolate->context()->global_context();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000101 return CreateJSValue(global_context->string_function(), this);
102 }
103
104 // Throw a type error.
105 return Failure::InternalError();
106}
107
108
109Object* Object::ToBoolean() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000110 if (IsTrue()) return this;
111 if (IsFalse()) return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000112 if (IsSmi()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000113 return Isolate::Current()->heap()->ToBoolean(Smi::cast(this)->value() != 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000114 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000115 HeapObject* heap_object = HeapObject::cast(this);
116 if (heap_object->IsUndefined() || heap_object->IsNull()) {
117 return heap_object->GetHeap()->false_value();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000118 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000119 // Undetectable object is false
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000120 if (heap_object->IsUndetectableObject()) {
121 return heap_object->GetHeap()->false_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000122 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000123 if (heap_object->IsString()) {
124 return heap_object->GetHeap()->ToBoolean(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000125 String::cast(this)->length() != 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000126 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000127 if (heap_object->IsHeapNumber()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000128 return HeapNumber::cast(this)->HeapNumberToBoolean();
129 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000130 return heap_object->GetHeap()->true_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000131}
132
133
134void Object::Lookup(String* name, LookupResult* result) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000135 Object* holder = NULL;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000136 if (IsSmi()) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000137 Context* global_context = Isolate::Current()->context()->global_context();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000138 holder = global_context->number_function()->instance_prototype();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000139 } else {
140 HeapObject* heap_object = HeapObject::cast(this);
141 if (heap_object->IsJSObject()) {
142 return JSObject::cast(this)->Lookup(name, result);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000143 } else if (heap_object->IsJSProxy()) {
144 return result->HandlerResult();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000145 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000146 Context* global_context = Isolate::Current()->context()->global_context();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000147 if (heap_object->IsString()) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000148 holder = global_context->string_function()->instance_prototype();
149 } else if (heap_object->IsHeapNumber()) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000150 holder = global_context->number_function()->instance_prototype();
151 } else if (heap_object->IsBoolean()) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000152 holder = global_context->boolean_function()->instance_prototype();
153 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000154 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000155 ASSERT(holder != NULL); // Cannot handle null or undefined.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000156 JSObject::cast(holder)->Lookup(name, result);
157}
158
159
lrn@chromium.org303ada72010-10-27 09:33:13 +0000160MaybeObject* Object::GetPropertyWithReceiver(Object* receiver,
161 String* name,
162 PropertyAttributes* attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000163 LookupResult result;
164 Lookup(name, &result);
lrn@chromium.org303ada72010-10-27 09:33:13 +0000165 MaybeObject* value = GetProperty(receiver, &result, name, attributes);
ager@chromium.org8bb60582008-12-11 12:02:20 +0000166 ASSERT(*attributes <= ABSENT);
167 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000168}
169
170
lrn@chromium.org303ada72010-10-27 09:33:13 +0000171MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
172 Object* structure,
173 String* name,
174 Object* holder) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000175 Isolate* isolate = name->GetIsolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000176 // To accommodate both the old and the new api we switch on the
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000177 // data structure used to store the callbacks. Eventually foreign
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000178 // callbacks should be phased out.
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000179 if (structure->IsForeign()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000180 AccessorDescriptor* callback =
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000181 reinterpret_cast<AccessorDescriptor*>(
182 Foreign::cast(structure)->address());
lrn@chromium.org303ada72010-10-27 09:33:13 +0000183 MaybeObject* value = (callback->getter)(receiver, callback->data);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000184 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000185 return value;
186 }
187
188 // api style callbacks.
189 if (structure->IsAccessorInfo()) {
190 AccessorInfo* data = AccessorInfo::cast(structure);
191 Object* fun_obj = data->getter();
192 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +0000193 HandleScope scope(isolate);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000194 JSObject* self = JSObject::cast(receiver);
195 JSObject* holder_handle = JSObject::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000196 Handle<String> key(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000197 LOG(isolate, ApiNamedPropertyAccess("load", self, name));
198 CustomArguments args(isolate, data->data(), self, holder_handle);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000199 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000200 v8::Handle<v8::Value> result;
201 {
202 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000203 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204 result = call_fun(v8::Utils::ToLocal(key), info);
205 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000206 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
207 if (result.IsEmpty()) {
208 return isolate->heap()->undefined_value();
209 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000210 return *v8::Utils::OpenHandle(*result);
211 }
212
213 // __defineGetter__ callback
214 if (structure->IsFixedArray()) {
215 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
216 if (getter->IsJSFunction()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000217 return Object::GetPropertyWithDefinedGetter(receiver,
218 JSFunction::cast(getter));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000219 }
220 // Getter is not a function.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000221 return isolate->heap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000222 }
223
224 UNREACHABLE();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000225 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000226}
227
228
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000229MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw,
230 String* name_raw,
231 Object* handler_raw) {
232 Isolate* isolate = name_raw->GetIsolate();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +0000233 HandleScope scope(isolate);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000234 Handle<Object> receiver(receiver_raw);
235 Handle<Object> name(name_raw);
236 Handle<Object> handler(handler_raw);
237
238 // Extract trap function.
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000239 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("get");
240 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
241 if (trap->IsUndefined()) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000242 // Get the derived `get' property.
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000243 trap = isolate->derived_get_trap();
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000244 }
245
246 // Call trap function.
247 Object** args[] = { receiver.location(), name.location() };
248 bool has_exception;
249 Handle<Object> result =
250 Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
251 if (has_exception) return Failure::Exception();
252
253 return *result;
254}
255
256
lrn@chromium.org303ada72010-10-27 09:33:13 +0000257MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver,
258 JSFunction* getter) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000259 HandleScope scope;
260 Handle<JSFunction> fun(JSFunction::cast(getter));
261 Handle<Object> self(receiver);
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000262#ifdef ENABLE_DEBUGGER_SUPPORT
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000263 Debug* debug = fun->GetHeap()->isolate()->debug();
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000264 // Handle stepping into a getter if step into is active.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000265 if (debug->StepInActive()) {
266 debug->HandleStepIn(fun, Handle<Object>::null(), 0, false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000267 }
268#endif
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000269 bool has_pending_exception;
270 Handle<Object> result =
271 Execution::Call(fun, self, 0, NULL, &has_pending_exception);
272 // Check for pending exception and return the result.
273 if (has_pending_exception) return Failure::Exception();
274 return *result;
275}
276
277
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000278// Only deal with CALLBACKS and INTERCEPTOR
lrn@chromium.org303ada72010-10-27 09:33:13 +0000279MaybeObject* JSObject::GetPropertyWithFailedAccessCheck(
ager@chromium.org8bb60582008-12-11 12:02:20 +0000280 Object* receiver,
281 LookupResult* result,
282 String* name,
283 PropertyAttributes* attributes) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000284 if (result->IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000285 switch (result->type()) {
286 case CALLBACKS: {
287 // Only allow API accessors.
288 Object* obj = result->GetCallbackObject();
289 if (obj->IsAccessorInfo()) {
290 AccessorInfo* info = AccessorInfo::cast(obj);
291 if (info->all_can_read()) {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000292 *attributes = result->GetAttributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000293 return GetPropertyWithCallback(receiver,
294 result->GetCallbackObject(),
295 name,
296 result->holder());
297 }
298 }
299 break;
300 }
301 case NORMAL:
302 case FIELD:
303 case CONSTANT_FUNCTION: {
304 // Search ALL_CAN_READ accessors in prototype chain.
305 LookupResult r;
306 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
ager@chromium.org5c838252010-02-19 08:53:10 +0000307 if (r.IsProperty()) {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000308 return GetPropertyWithFailedAccessCheck(receiver,
309 &r,
310 name,
311 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 break;
314 }
315 case INTERCEPTOR: {
316 // If the object has an interceptor, try real named properties.
317 // No access check in GetPropertyAttributeWithInterceptor.
318 LookupResult r;
319 result->holder()->LookupRealNamedProperty(name, &r);
ager@chromium.org5c838252010-02-19 08:53:10 +0000320 if (r.IsProperty()) {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000321 return GetPropertyWithFailedAccessCheck(receiver,
322 &r,
323 name,
324 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000325 }
ager@chromium.org9ed6c322010-02-19 12:24:05 +0000326 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000327 }
ager@chromium.org5c838252010-02-19 08:53:10 +0000328 default:
329 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000330 }
331 }
332
ager@chromium.org8bb60582008-12-11 12:02:20 +0000333 // No accessible property found.
334 *attributes = ABSENT;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000335 Heap* heap = name->GetHeap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000336 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_GET);
337 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000338}
339
340
ager@chromium.org870a0b62008-11-04 11:43:05 +0000341PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
342 Object* receiver,
343 LookupResult* result,
344 String* name,
345 bool continue_search) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000346 if (result->IsProperty()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000347 switch (result->type()) {
348 case CALLBACKS: {
349 // Only allow API accessors.
350 Object* obj = result->GetCallbackObject();
351 if (obj->IsAccessorInfo()) {
352 AccessorInfo* info = AccessorInfo::cast(obj);
353 if (info->all_can_read()) {
354 return result->GetAttributes();
355 }
356 }
357 break;
358 }
359
360 case NORMAL:
361 case FIELD:
362 case CONSTANT_FUNCTION: {
363 if (!continue_search) break;
364 // Search ALL_CAN_READ accessors in prototype chain.
365 LookupResult r;
366 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
ager@chromium.org5c838252010-02-19 08:53:10 +0000367 if (r.IsProperty()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000368 return GetPropertyAttributeWithFailedAccessCheck(receiver,
369 &r,
370 name,
371 continue_search);
372 }
373 break;
374 }
375
376 case INTERCEPTOR: {
377 // If the object has an interceptor, try real named properties.
378 // No access check in GetPropertyAttributeWithInterceptor.
379 LookupResult r;
380 if (continue_search) {
381 result->holder()->LookupRealNamedProperty(name, &r);
382 } else {
383 result->holder()->LocalLookupRealNamedProperty(name, &r);
384 }
ager@chromium.org5c838252010-02-19 08:53:10 +0000385 if (r.IsProperty()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000386 return GetPropertyAttributeWithFailedAccessCheck(receiver,
387 &r,
388 name,
389 continue_search);
390 }
391 break;
392 }
393
ager@chromium.org5c838252010-02-19 08:53:10 +0000394 default:
395 UNREACHABLE();
ager@chromium.org870a0b62008-11-04 11:43:05 +0000396 }
397 }
398
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000399 GetHeap()->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000400 return ABSENT;
401}
402
403
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000404Object* JSObject::GetNormalizedProperty(LookupResult* result) {
405 ASSERT(!HasFastProperties());
406 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
407 if (IsGlobalObject()) {
408 value = JSGlobalPropertyCell::cast(value)->value();
409 }
410 ASSERT(!value->IsJSGlobalPropertyCell());
411 return value;
412}
413
414
415Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) {
416 ASSERT(!HasFastProperties());
417 if (IsGlobalObject()) {
418 JSGlobalPropertyCell* cell =
419 JSGlobalPropertyCell::cast(
420 property_dictionary()->ValueAt(result->GetDictionaryEntry()));
421 cell->set_value(value);
422 } else {
423 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
424 }
425 return value;
426}
427
428
lrn@chromium.org303ada72010-10-27 09:33:13 +0000429MaybeObject* JSObject::SetNormalizedProperty(String* name,
430 Object* value,
431 PropertyDetails details) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000432 ASSERT(!HasFastProperties());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000433 int entry = property_dictionary()->FindEntry(name);
434 if (entry == StringDictionary::kNotFound) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000435 Object* store_value = value;
436 if (IsGlobalObject()) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000437 Heap* heap = name->GetHeap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000438 MaybeObject* maybe_store_value =
439 heap->AllocateJSGlobalPropertyCell(value);
440 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000441 }
lrn@chromium.org303ada72010-10-27 09:33:13 +0000442 Object* dict;
443 { MaybeObject* maybe_dict =
444 property_dictionary()->Add(name, store_value, details);
445 if (!maybe_dict->ToObject(&dict)) return maybe_dict;
446 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000447 set_properties(StringDictionary::cast(dict));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000448 return value;
449 }
450 // Preserve enumeration index.
451 details = PropertyDetails(details.attributes(),
452 details.type(),
453 property_dictionary()->DetailsAt(entry).index());
454 if (IsGlobalObject()) {
455 JSGlobalPropertyCell* cell =
456 JSGlobalPropertyCell::cast(property_dictionary()->ValueAt(entry));
457 cell->set_value(value);
458 // Please note we have to update the property details.
459 property_dictionary()->DetailsAtPut(entry, details);
460 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000461 property_dictionary()->SetEntry(entry, name, value, details);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000462 }
463 return value;
464}
465
466
lrn@chromium.org303ada72010-10-27 09:33:13 +0000467MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000468 ASSERT(!HasFastProperties());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000469 StringDictionary* dictionary = property_dictionary();
470 int entry = dictionary->FindEntry(name);
471 if (entry != StringDictionary::kNotFound) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000472 // If we have a global object set the cell to the hole.
473 if (IsGlobalObject()) {
474 PropertyDetails details = dictionary->DetailsAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000475 if (details.IsDontDelete()) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000476 if (mode != FORCE_DELETION) return GetHeap()->false_value();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000477 // When forced to delete global properties, we have to make a
478 // map change to invalidate any ICs that think they can load
479 // from the DontDelete cell without checking if it contains
480 // the hole value.
lrn@chromium.org303ada72010-10-27 09:33:13 +0000481 Object* new_map;
482 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
483 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
484 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000485 set_map(Map::cast(new_map));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000486 }
487 JSGlobalPropertyCell* cell =
488 JSGlobalPropertyCell::cast(dictionary->ValueAt(entry));
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000489 cell->set_value(cell->heap()->the_hole_value());
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000490 dictionary->DetailsAtPut(entry, details.AsDeleted());
491 } else {
ager@chromium.org04921a82011-06-27 13:21:41 +0000492 Object* deleted = dictionary->DeleteProperty(entry, mode);
493 if (deleted == GetHeap()->true_value()) {
494 FixedArray* new_properties = NULL;
495 MaybeObject* maybe_properties = dictionary->Shrink(name);
496 if (!maybe_properties->To(&new_properties)) {
497 return maybe_properties;
498 }
499 set_properties(new_properties);
500 }
501 return deleted;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000502 }
503 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000504 return GetHeap()->true_value();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000505}
506
507
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000508bool JSObject::IsDirty() {
509 Object* cons_obj = map()->constructor();
510 if (!cons_obj->IsJSFunction())
511 return true;
512 JSFunction* fun = JSFunction::cast(cons_obj);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000513 if (!fun->shared()->IsApiFunction())
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000514 return true;
515 // If the object is fully fast case and has the same map it was
516 // created with then no changes can have been made to it.
517 return map() != fun->initial_map()
518 || !HasFastElements()
519 || !HasFastProperties();
520}
521
522
lrn@chromium.org303ada72010-10-27 09:33:13 +0000523MaybeObject* Object::GetProperty(Object* receiver,
524 LookupResult* result,
525 String* name,
526 PropertyAttributes* attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000527 // Make sure that the top context does not change when doing
528 // callbacks or interceptor calls.
529 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000530 Heap* heap = name->GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000531
532 // Traverse the prototype chain from the current object (this) to
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000533 // the holder and check for access rights. This avoids traversing the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000534 // objects more than once in case of interceptors, because the
535 // holder will always be the interceptor holder and the search may
536 // only continue with a current object just after the interceptor
537 // holder in the prototype chain.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000538 // Proxy handlers do not use the proxy's prototype, so we can skip this.
539 if (!result->IsHandler()) {
540 Object* last = result->IsProperty() ? result->holder() : heap->null_value();
541 ASSERT(this != this->GetPrototype());
542 for (Object* current = this; true; current = current->GetPrototype()) {
543 if (current->IsAccessCheckNeeded()) {
544 // Check if we're allowed to read from the current object. Note
545 // that even though we may not actually end up loading the named
546 // property from the current object, we still check that we have
547 // access to it.
548 JSObject* checked = JSObject::cast(current);
549 if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) {
550 return checked->GetPropertyWithFailedAccessCheck(receiver,
551 result,
552 name,
553 attributes);
554 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000555 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000556 // Stop traversing the chain once we reach the last object in the
557 // chain; either the holder of the result or null in case of an
558 // absent property.
559 if (current == last) break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000560 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000561 }
562
kasper.lund44510672008-07-25 07:37:58 +0000563 if (!result->IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000564 *attributes = ABSENT;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000565 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000566 }
567 *attributes = result->GetAttributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000568 Object* value;
569 JSObject* holder = result->holder();
570 switch (result->type()) {
571 case NORMAL:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000572 value = holder->GetNormalizedProperty(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000573 ASSERT(!value->IsTheHole() || result->IsReadOnly());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000574 return value->IsTheHole() ? heap->undefined_value() : value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000575 case FIELD:
ager@chromium.org7c537e22008-10-16 08:43:32 +0000576 value = holder->FastPropertyAt(result->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000577 ASSERT(!value->IsTheHole() || result->IsReadOnly());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000578 return value->IsTheHole() ? heap->undefined_value() : value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000579 case CONSTANT_FUNCTION:
580 return result->GetConstantFunction();
581 case CALLBACKS:
582 return GetPropertyWithCallback(receiver,
583 result->GetCallbackObject(),
584 name,
585 holder);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000586 case HANDLER: {
587 JSProxy* proxy = JSProxy::cast(this);
588 return GetPropertyWithHandler(receiver, name, proxy->handler());
589 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000590 case INTERCEPTOR: {
591 JSObject* recvr = JSObject::cast(receiver);
592 return holder->GetPropertyWithInterceptor(recvr, name, attributes);
593 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000594 case MAP_TRANSITION:
595 case EXTERNAL_ARRAY_TRANSITION:
596 case CONSTANT_TRANSITION:
597 case NULL_DESCRIPTOR:
598 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000599 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000600 UNREACHABLE();
601 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000602}
603
604
lrn@chromium.org303ada72010-10-27 09:33:13 +0000605MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000606 Heap* heap = IsSmi()
607 ? Isolate::Current()->heap()
608 : HeapObject::cast(this)->GetHeap();
609 Object* holder = this;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000610
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000611 // Iterate up the prototype chain until an element is found or the null
612 // prototype is encountered.
613 for (holder = this;
614 holder != heap->null_value();
615 holder = holder->GetPrototype()) {
616 if (holder->IsSmi()) {
617 Context* global_context = Isolate::Current()->context()->global_context();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000618 holder = global_context->number_function()->instance_prototype();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000619 } else {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000620 HeapObject* heap_object = HeapObject::cast(holder);
621 if (!heap_object->IsJSObject()) {
622 Isolate* isolate = heap->isolate();
623 Context* global_context = isolate->context()->global_context();
624 if (heap_object->IsString()) {
625 holder = global_context->string_function()->instance_prototype();
626 } else if (heap_object->IsHeapNumber()) {
627 holder = global_context->number_function()->instance_prototype();
628 } else if (heap_object->IsBoolean()) {
629 holder = global_context->boolean_function()->instance_prototype();
630 } else if (heap_object->IsJSProxy()) {
631 return heap->undefined_value(); // For now...
632 } else {
633 // Undefined and null have no indexed properties.
634 ASSERT(heap_object->IsUndefined() || heap_object->IsNull());
635 return heap->undefined_value();
636 }
637 }
638 }
639
640 // Inline the case for JSObjects. Doing so significantly improves the
641 // performance of fetching elements where checking the prototype chain is
642 // necessary.
643 JSObject* js_object = JSObject::cast(holder);
644
645 // Check access rights if needed.
646 if (js_object->IsAccessCheckNeeded()) {
647 Isolate* isolate = heap->isolate();
648 if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) {
649 isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_GET);
650 return heap->undefined_value();
651 }
652 }
653
654 if (js_object->HasIndexedInterceptor()) {
655 return js_object->GetElementWithInterceptor(receiver, index);
656 }
657
658 if (js_object->elements() != heap->empty_fixed_array()) {
659 MaybeObject* result = js_object->GetElementsAccessor()->GetWithReceiver(
660 js_object,
661 receiver,
662 index);
663 if (result != heap->the_hole_value()) return result;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000664 }
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000665 }
666
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000667 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000668}
669
670
671Object* Object::GetPrototype() {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000672 if (IsSmi()) {
673 Heap* heap = Isolate::Current()->heap();
674 Context* context = heap->isolate()->context()->global_context();
675 return context->number_function()->instance_prototype();
676 }
677
678 HeapObject* heap_object = HeapObject::cast(this);
679
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000680 // The object is either a number, a string, a boolean,
681 // a real JS object, or a Harmony proxy.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000682 if (heap_object->IsJSReceiver()) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000683 return heap_object->map()->prototype();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000684 }
685 Heap* heap = heap_object->GetHeap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000686 Context* context = heap->isolate()->context()->global_context();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000687
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000688 if (heap_object->IsHeapNumber()) {
689 return context->number_function()->instance_prototype();
690 }
691 if (heap_object->IsString()) {
692 return context->string_function()->instance_prototype();
693 }
694 if (heap_object->IsBoolean()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695 return context->boolean_function()->instance_prototype();
696 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000697 return heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000698 }
699}
700
701
whesse@chromium.org023421e2010-12-21 12:19:12 +0000702void Object::ShortPrint(FILE* out) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000703 HeapStringAllocator allocator;
704 StringStream accumulator(&allocator);
705 ShortPrint(&accumulator);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000706 accumulator.OutputToFile(out);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000707}
708
709
710void Object::ShortPrint(StringStream* accumulator) {
711 if (IsSmi()) {
712 Smi::cast(this)->SmiPrint(accumulator);
713 } else if (IsFailure()) {
714 Failure::cast(this)->FailurePrint(accumulator);
715 } else {
716 HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
717 }
718}
719
720
whesse@chromium.org023421e2010-12-21 12:19:12 +0000721void Smi::SmiPrint(FILE* out) {
722 PrintF(out, "%d", value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000723}
724
725
726void Smi::SmiPrint(StringStream* accumulator) {
727 accumulator->Add("%d", value());
728}
729
730
731void Failure::FailurePrint(StringStream* accumulator) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000732 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733}
734
735
whesse@chromium.org023421e2010-12-21 12:19:12 +0000736void Failure::FailurePrint(FILE* out) {
737 PrintF(out, "Failure(%p)", reinterpret_cast<void*>(value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000738}
739
740
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741// Should a word be prefixed by 'a' or 'an' in order to read naturally in
742// English? Returns false for non-ASCII or words that don't start with
743// a capital letter. The a/an rule follows pronunciation in English.
744// We don't use the BBC's overcorrect "an historic occasion" though if
745// you speak a dialect you may well say "an 'istoric occasion".
746static bool AnWord(String* str) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000747 if (str->length() == 0) return false; // A nothing.
748 int c0 = str->Get(0);
749 int c1 = str->length() > 1 ? str->Get(1) : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000750 if (c0 == 'U') {
751 if (c1 > 'Z') {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000752 return true; // An Umpire, but a UTF8String, a U.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000753 }
754 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000755 return true; // An Ape, an ABCBook.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000756 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
757 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
758 c0 == 'S' || c0 == 'X')) {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000759 return true; // An MP3File, an M.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000760 }
761 return false;
762}
763
764
lrn@chromium.org303ada72010-10-27 09:33:13 +0000765MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766#ifdef DEBUG
767 // Do not attempt to flatten in debug mode when allocation is not
768 // allowed. This is to avoid an assertion failure when allocating.
769 // Flattening strings is the only case where we always allow
770 // allocation because no GC is performed if the allocation fails.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000771 if (!HEAP->IsAllocationAllowed()) return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000772#endif
773
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000774 Heap* heap = GetHeap();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000775 switch (StringShape(this).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000776 case kConsStringTag: {
777 ConsString* cs = ConsString::cast(this);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000778 if (cs->second()->length() == 0) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000779 return cs->first();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000780 }
781 // There's little point in putting the flat string in new space if the
782 // cons string is in old space. It can never get GCed until there is
783 // an old space GC.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000784 PretenureFlag tenure = heap->InNewSpace(this) ? pretenure : TENURED;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000785 int len = length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000786 Object* object;
787 String* result;
ager@chromium.org5ec48922009-05-05 07:25:34 +0000788 if (IsAsciiRepresentation()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000789 { MaybeObject* maybe_object = heap->AllocateRawAsciiString(len, tenure);
lrn@chromium.org303ada72010-10-27 09:33:13 +0000790 if (!maybe_object->ToObject(&object)) return maybe_object;
791 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000792 result = String::cast(object);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000793 String* first = cs->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000794 int first_length = first->length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000795 char* dest = SeqAsciiString::cast(result)->GetChars();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000796 WriteToFlat(first, dest, 0, first_length);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000797 String* second = cs->second();
798 WriteToFlat(second,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000799 dest + first_length,
800 0,
801 len - first_length);
802 } else {
lrn@chromium.org303ada72010-10-27 09:33:13 +0000803 { MaybeObject* maybe_object =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000804 heap->AllocateRawTwoByteString(len, tenure);
lrn@chromium.org303ada72010-10-27 09:33:13 +0000805 if (!maybe_object->ToObject(&object)) return maybe_object;
806 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000807 result = String::cast(object);
808 uc16* dest = SeqTwoByteString::cast(result)->GetChars();
ager@chromium.org870a0b62008-11-04 11:43:05 +0000809 String* first = cs->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000810 int first_length = first->length();
811 WriteToFlat(first, dest, 0, first_length);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000812 String* second = cs->second();
813 WriteToFlat(second,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000814 dest + first_length,
815 0,
816 len - first_length);
817 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000818 cs->set_first(result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000819 cs->set_second(heap->empty_string());
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000820 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000821 }
822 default:
823 return this;
824 }
825}
826
827
ager@chromium.org6f10e412009-02-13 10:11:16 +0000828bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
lrn@chromium.org32d961d2010-06-30 09:09:34 +0000829 // Externalizing twice leaks the external resource, so it's
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000830 // prohibited by the API.
831 ASSERT(!this->IsExternalString());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000832#ifdef DEBUG
ager@chromium.org3811b432009-10-28 14:53:37 +0000833 if (FLAG_enable_slow_asserts) {
ager@chromium.org6f10e412009-02-13 10:11:16 +0000834 // Assert that the resource and the string are equivalent.
835 ASSERT(static_cast<size_t>(this->length()) == resource->length());
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000836 ScopedVector<uc16> smart_chars(this->length());
837 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
838 ASSERT(memcmp(smart_chars.start(),
ager@chromium.org6f10e412009-02-13 10:11:16 +0000839 resource->data(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000840 resource->length() * sizeof(smart_chars[0])) == 0);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000841 }
842#endif // DEBUG
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000843 Heap* heap = GetHeap();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000844 int size = this->Size(); // Byte size of the original string.
845 if (size < ExternalString::kSize) {
846 // The string is too small to fit an external String in its place. This can
847 // only happen for zero length strings.
848 return false;
849 }
850 ASSERT(size >= ExternalString::kSize);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000851 bool is_ascii = this->IsAsciiRepresentation();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000852 bool is_symbol = this->IsSymbol();
853 int length = this->length();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000854 int hash_field = this->hash_field();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000855
856 // Morph the object to an external string by adjusting the map and
857 // reinitializing the fields.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000858 this->set_map(is_ascii ?
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000859 heap->external_string_with_ascii_data_map() :
860 heap->external_string_map());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000861 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
862 self->set_length(length);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000863 self->set_hash_field(hash_field);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000864 self->set_resource(resource);
865 // Additionally make the object into an external symbol if the original string
866 // was a symbol to start with.
867 if (is_symbol) {
868 self->Hash(); // Force regeneration of the hash value.
869 // Now morph this external string into a external symbol.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000870 this->set_map(is_ascii ?
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000871 heap->external_symbol_with_ascii_data_map() :
872 heap->external_symbol_map());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000873 }
874
875 // Fill the remainder of the string with dead wood.
876 int new_size = this->Size(); // Byte size of the external String object.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000877 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000878 return true;
879}
880
881
882bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
883#ifdef DEBUG
ager@chromium.org3811b432009-10-28 14:53:37 +0000884 if (FLAG_enable_slow_asserts) {
ager@chromium.org6f10e412009-02-13 10:11:16 +0000885 // Assert that the resource and the string are equivalent.
886 ASSERT(static_cast<size_t>(this->length()) == resource->length());
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000887 ScopedVector<char> smart_chars(this->length());
888 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
889 ASSERT(memcmp(smart_chars.start(),
ager@chromium.org6f10e412009-02-13 10:11:16 +0000890 resource->data(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000891 resource->length() * sizeof(smart_chars[0])) == 0);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000892 }
893#endif // DEBUG
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000894 Heap* heap = GetHeap();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000895 int size = this->Size(); // Byte size of the original string.
896 if (size < ExternalString::kSize) {
897 // The string is too small to fit an external String in its place. This can
898 // only happen for zero length strings.
899 return false;
900 }
901 ASSERT(size >= ExternalString::kSize);
902 bool is_symbol = this->IsSymbol();
903 int length = this->length();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000904 int hash_field = this->hash_field();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000905
906 // Morph the object to an external string by adjusting the map and
907 // reinitializing the fields.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000908 this->set_map(heap->external_ascii_string_map());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000909 ExternalAsciiString* self = ExternalAsciiString::cast(this);
910 self->set_length(length);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000911 self->set_hash_field(hash_field);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000912 self->set_resource(resource);
913 // Additionally make the object into an external symbol if the original string
914 // was a symbol to start with.
915 if (is_symbol) {
916 self->Hash(); // Force regeneration of the hash value.
917 // Now morph this external string into a external symbol.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000918 this->set_map(heap->external_ascii_symbol_map());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000919 }
920
921 // Fill the remainder of the string with dead wood.
922 int new_size = this->Size(); // Byte size of the external String object.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000923 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000924 return true;
925}
926
927
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000928void String::StringShortPrint(StringStream* accumulator) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000929 int len = length();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000930 if (len > kMaxShortPrintLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000931 accumulator->Add("<Very long string[%u]>", len);
932 return;
933 }
934
935 if (!LooksValid()) {
936 accumulator->Add("<Invalid String>");
937 return;
938 }
939
940 StringInputBuffer buf(this);
941
942 bool truncated = false;
kasper.lund7276f142008-07-30 08:49:36 +0000943 if (len > kMaxShortPrintLength) {
944 len = kMaxShortPrintLength;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000945 truncated = true;
946 }
947 bool ascii = true;
948 for (int i = 0; i < len; i++) {
949 int c = buf.GetNext();
950
951 if (c < 32 || c >= 127) {
952 ascii = false;
953 }
954 }
955 buf.Reset(this);
956 if (ascii) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000957 accumulator->Add("<String[%u]: ", length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000958 for (int i = 0; i < len; i++) {
959 accumulator->Put(buf.GetNext());
960 }
961 accumulator->Put('>');
962 } else {
963 // Backslash indicates that the string contains control
964 // characters and that backslashes are therefore escaped.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000965 accumulator->Add("<String[%u]\\: ", length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000966 for (int i = 0; i < len; i++) {
967 int c = buf.GetNext();
968 if (c == '\n') {
969 accumulator->Add("\\n");
970 } else if (c == '\r') {
971 accumulator->Add("\\r");
972 } else if (c == '\\') {
973 accumulator->Add("\\\\");
974 } else if (c < 32 || c > 126) {
975 accumulator->Add("\\x%02x", c);
976 } else {
977 accumulator->Put(c);
978 }
979 }
980 if (truncated) {
981 accumulator->Put('.');
982 accumulator->Put('.');
983 accumulator->Put('.');
984 }
985 accumulator->Put('>');
986 }
987 return;
988}
989
990
991void JSObject::JSObjectShortPrint(StringStream* accumulator) {
992 switch (map()->instance_type()) {
993 case JS_ARRAY_TYPE: {
994 double length = JSArray::cast(this)->length()->Number();
995 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
996 break;
997 }
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000998 case JS_WEAK_MAP_TYPE: {
999 int elements = JSWeakMap::cast(this)->table()->NumberOfElements();
1000 accumulator->Add("<JS WeakMap[%d]>", elements);
1001 break;
1002 }
ager@chromium.org236ad962008-09-25 09:45:57 +00001003 case JS_REGEXP_TYPE: {
1004 accumulator->Add("<JS RegExp>");
1005 break;
1006 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007 case JS_FUNCTION_TYPE: {
1008 Object* fun_name = JSFunction::cast(this)->shared()->name();
1009 bool printed = false;
1010 if (fun_name->IsString()) {
1011 String* str = String::cast(fun_name);
1012 if (str->length() > 0) {
1013 accumulator->Add("<JS Function ");
1014 accumulator->Put(str);
1015 accumulator->Put('>');
1016 printed = true;
1017 }
1018 }
1019 if (!printed) {
1020 accumulator->Add("<JS Function>");
1021 }
1022 break;
1023 }
1024 // All other JSObjects are rather similar to each other (JSObject,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001025 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001026 default: {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001027 Map* map_of_this = map();
1028 Heap* heap = map_of_this->heap();
1029 Object* constructor = map_of_this->constructor();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001030 bool printed = false;
1031 if (constructor->IsHeapObject() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001032 !heap->Contains(HeapObject::cast(constructor))) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001033 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
1034 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001035 bool global_object = IsJSGlobalProxy();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001036 if (constructor->IsJSFunction()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001037 if (!heap->Contains(JSFunction::cast(constructor)->shared())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001038 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
1039 } else {
1040 Object* constructor_name =
1041 JSFunction::cast(constructor)->shared()->name();
1042 if (constructor_name->IsString()) {
1043 String* str = String::cast(constructor_name);
1044 if (str->length() > 0) {
1045 bool vowel = AnWord(str);
1046 accumulator->Add("<%sa%s ",
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001047 global_object ? "Global Object: " : "",
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001048 vowel ? "n" : "");
1049 accumulator->Put(str);
1050 accumulator->Put('>');
1051 printed = true;
1052 }
1053 }
1054 }
1055 }
1056 if (!printed) {
1057 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
1058 }
1059 }
1060 if (IsJSValue()) {
1061 accumulator->Add(" value = ");
1062 JSValue::cast(this)->value()->ShortPrint(accumulator);
1063 }
1064 accumulator->Put('>');
1065 break;
1066 }
1067 }
1068}
1069
1070
1071void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001072 // if (!HEAP->InNewSpace(this)) PrintF("*", this);
1073 Heap* heap = GetHeap();
1074 if (!heap->Contains(this)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001075 accumulator->Add("!!!INVALID POINTER!!!");
1076 return;
1077 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001078 if (!heap->Contains(map())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001079 accumulator->Add("!!!INVALID MAP!!!");
1080 return;
1081 }
1082
1083 accumulator->Add("%p ", this);
1084
1085 if (IsString()) {
1086 String::cast(this)->StringShortPrint(accumulator);
1087 return;
1088 }
1089 if (IsJSObject()) {
1090 JSObject::cast(this)->JSObjectShortPrint(accumulator);
1091 return;
1092 }
1093 switch (map()->instance_type()) {
1094 case MAP_TYPE:
1095 accumulator->Add("<Map>");
1096 break;
1097 case FIXED_ARRAY_TYPE:
1098 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
1099 break;
1100 case BYTE_ARRAY_TYPE:
1101 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
1102 break;
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001103 case EXTERNAL_PIXEL_ARRAY_TYPE:
1104 accumulator->Add("<ExternalPixelArray[%u]>",
1105 ExternalPixelArray::cast(this)->length());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001106 break;
ager@chromium.org3811b432009-10-28 14:53:37 +00001107 case EXTERNAL_BYTE_ARRAY_TYPE:
1108 accumulator->Add("<ExternalByteArray[%u]>",
1109 ExternalByteArray::cast(this)->length());
1110 break;
1111 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1112 accumulator->Add("<ExternalUnsignedByteArray[%u]>",
1113 ExternalUnsignedByteArray::cast(this)->length());
1114 break;
1115 case EXTERNAL_SHORT_ARRAY_TYPE:
1116 accumulator->Add("<ExternalShortArray[%u]>",
1117 ExternalShortArray::cast(this)->length());
1118 break;
1119 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1120 accumulator->Add("<ExternalUnsignedShortArray[%u]>",
1121 ExternalUnsignedShortArray::cast(this)->length());
1122 break;
1123 case EXTERNAL_INT_ARRAY_TYPE:
1124 accumulator->Add("<ExternalIntArray[%u]>",
1125 ExternalIntArray::cast(this)->length());
1126 break;
1127 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1128 accumulator->Add("<ExternalUnsignedIntArray[%u]>",
1129 ExternalUnsignedIntArray::cast(this)->length());
1130 break;
1131 case EXTERNAL_FLOAT_ARRAY_TYPE:
1132 accumulator->Add("<ExternalFloatArray[%u]>",
1133 ExternalFloatArray::cast(this)->length());
1134 break;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001135 case EXTERNAL_DOUBLE_ARRAY_TYPE:
1136 accumulator->Add("<ExternalDoubleArray[%u]>",
1137 ExternalDoubleArray::cast(this)->length());
1138 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001139 case SHARED_FUNCTION_INFO_TYPE:
1140 accumulator->Add("<SharedFunctionInfo>");
1141 break;
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001142 case JS_MESSAGE_OBJECT_TYPE:
1143 accumulator->Add("<JSMessageObject>");
1144 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001145#define MAKE_STRUCT_CASE(NAME, Name, name) \
1146 case NAME##_TYPE: \
ager@chromium.org381abbb2009-02-25 13:23:22 +00001147 accumulator->Put('<'); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001148 accumulator->Add(#Name); \
ager@chromium.org381abbb2009-02-25 13:23:22 +00001149 accumulator->Put('>'); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001150 break;
1151 STRUCT_LIST(MAKE_STRUCT_CASE)
1152#undef MAKE_STRUCT_CASE
1153 case CODE_TYPE:
1154 accumulator->Add("<Code>");
1155 break;
1156 case ODDBALL_TYPE: {
1157 if (IsUndefined())
1158 accumulator->Add("<undefined>");
1159 else if (IsTheHole())
1160 accumulator->Add("<the hole>");
1161 else if (IsNull())
1162 accumulator->Add("<null>");
1163 else if (IsTrue())
1164 accumulator->Add("<true>");
1165 else if (IsFalse())
1166 accumulator->Add("<false>");
1167 else
1168 accumulator->Add("<Odd Oddball>");
1169 break;
1170 }
1171 case HEAP_NUMBER_TYPE:
1172 accumulator->Add("<Number: ");
1173 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
1174 accumulator->Put('>');
1175 break;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001176 case FOREIGN_TYPE:
1177 accumulator->Add("<Foreign>");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001178 break;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001179 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1180 accumulator->Add("Cell for ");
1181 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator);
1182 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001183 default:
1184 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
1185 break;
1186 }
1187}
1188
1189
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190void HeapObject::Iterate(ObjectVisitor* v) {
1191 // Handle header
1192 IteratePointer(v, kMapOffset);
1193 // Handle object body
1194 Map* m = map();
1195 IterateBody(m->instance_type(), SizeFromMap(m), v);
1196}
1197
1198
1199void HeapObject::IterateBody(InstanceType type, int object_size,
1200 ObjectVisitor* v) {
1201 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
1202 // During GC, the map pointer field is encoded.
1203 if (type < FIRST_NONSTRING_TYPE) {
1204 switch (type & kStringRepresentationMask) {
1205 case kSeqStringTag:
1206 break;
1207 case kConsStringTag:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001208 ConsString::BodyDescriptor::IterateBody(this, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209 break;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001210 case kExternalStringTag:
1211 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1212 reinterpret_cast<ExternalAsciiString*>(this)->
1213 ExternalAsciiStringIterateBody(v);
1214 } else {
1215 reinterpret_cast<ExternalTwoByteString*>(this)->
1216 ExternalTwoByteStringIterateBody(v);
1217 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001218 break;
1219 }
1220 return;
1221 }
1222
1223 switch (type) {
1224 case FIXED_ARRAY_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001225 FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001226 break;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001227 case FIXED_DOUBLE_ARRAY_TYPE:
1228 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001229 case JS_OBJECT_TYPE:
ager@chromium.org32912102009-01-16 10:38:43 +00001230 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001231 case JS_VALUE_TYPE:
1232 case JS_ARRAY_TYPE:
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00001233 case JS_WEAK_MAP_TYPE:
ager@chromium.org236ad962008-09-25 09:45:57 +00001234 case JS_REGEXP_TYPE:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001235 case JS_GLOBAL_PROXY_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001236 case JS_GLOBAL_OBJECT_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001237 case JS_BUILTINS_OBJECT_TYPE:
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001238 case JS_MESSAGE_OBJECT_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001239 JSObject::BodyDescriptor::IterateBody(this, object_size, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001240 break;
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001241 case JS_FUNCTION_TYPE:
1242 reinterpret_cast<JSFunction*>(this)
1243 ->JSFunctionIterateBody(object_size, v);
1244 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001245 case ODDBALL_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001246 Oddball::BodyDescriptor::IterateBody(this, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001247 break;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001248 case JS_PROXY_TYPE:
1249 JSProxy::BodyDescriptor::IterateBody(this, v);
1250 break;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001251 case FOREIGN_TYPE:
1252 reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001253 break;
1254 case MAP_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001255 Map::BodyDescriptor::IterateBody(this, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001256 break;
1257 case CODE_TYPE:
1258 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1259 break;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001260 case JS_GLOBAL_PROPERTY_CELL_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001261 JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001262 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001263 case HEAP_NUMBER_TYPE:
1264 case FILLER_TYPE:
1265 case BYTE_ARRAY_TYPE:
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001266 case EXTERNAL_PIXEL_ARRAY_TYPE:
ager@chromium.org3811b432009-10-28 14:53:37 +00001267 case EXTERNAL_BYTE_ARRAY_TYPE:
1268 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1269 case EXTERNAL_SHORT_ARRAY_TYPE:
1270 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1271 case EXTERNAL_INT_ARRAY_TYPE:
1272 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1273 case EXTERNAL_FLOAT_ARRAY_TYPE:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001274 case EXTERNAL_DOUBLE_ARRAY_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001275 break;
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001276 case SHARED_FUNCTION_INFO_TYPE:
1277 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001278 break;
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001279
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001280#define MAKE_STRUCT_CASE(NAME, Name, name) \
1281 case NAME##_TYPE:
1282 STRUCT_LIST(MAKE_STRUCT_CASE)
1283#undef MAKE_STRUCT_CASE
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001284 StructBodyDescriptor::IterateBody(this, object_size, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001285 break;
1286 default:
1287 PrintF("Unknown type: %d\n", type);
1288 UNREACHABLE();
1289 }
1290}
1291
1292
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001293Object* HeapNumber::HeapNumberToBoolean() {
1294 // NaN, +0, and -0 should return the false object
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001295#if __BYTE_ORDER == __LITTLE_ENDIAN
1296 union IeeeDoubleLittleEndianArchType u;
1297#elif __BYTE_ORDER == __BIG_ENDIAN
1298 union IeeeDoubleBigEndianArchType u;
1299#endif
1300 u.d = value();
1301 if (u.bits.exp == 2047) {
1302 // Detect NaN for IEEE double precision floating point.
1303 if ((u.bits.man_low | u.bits.man_high) != 0)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001304 return GetHeap()->false_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001305 }
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001306 if (u.bits.exp == 0) {
1307 // Detect +0, and -0 for IEEE double precision floating point.
1308 if ((u.bits.man_low | u.bits.man_high) == 0)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001309 return GetHeap()->false_value();
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001310 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001311 return GetHeap()->true_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001312}
1313
1314
whesse@chromium.org023421e2010-12-21 12:19:12 +00001315void HeapNumber::HeapNumberPrint(FILE* out) {
1316 PrintF(out, "%.16g", Number());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001317}
1318
1319
1320void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1321 // The Windows version of vsnprintf can allocate when printing a %g string
1322 // into a buffer that may not be big enough. We don't want random memory
1323 // allocation when producing post-crash stack traces, so we print into a
1324 // buffer that is plenty big enough for any floating point number, then
1325 // print that using vsnprintf (which may truncate but never allocate if
1326 // there is no more space in the buffer).
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001327 EmbeddedVector<char, 100> buffer;
1328 OS::SNPrintF(buffer, "%.16g", Number());
1329 accumulator->Add("%s", buffer.start());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001330}
1331
1332
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001333String* JSReceiver::class_name() {
1334 if (IsJSFunction() && IsJSFunctionProxy()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001335 return GetHeap()->function_class_symbol();
ager@chromium.orga1645e22009-09-09 19:27:10 +00001336 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001337 if (map()->constructor()->IsJSFunction()) {
1338 JSFunction* constructor = JSFunction::cast(map()->constructor());
1339 return String::cast(constructor->shared()->instance_class_name());
1340 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001341 // If the constructor is not present, return "Object".
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001342 return GetHeap()->Object_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001343}
1344
1345
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001346String* JSReceiver::constructor_name() {
ager@chromium.orga1645e22009-09-09 19:27:10 +00001347 if (map()->constructor()->IsJSFunction()) {
1348 JSFunction* constructor = JSFunction::cast(map()->constructor());
1349 String* name = String::cast(constructor->shared()->name());
vegorov@chromium.org42841962010-10-18 11:18:59 +00001350 if (name->length() > 0) return name;
1351 String* inferred_name = constructor->shared()->inferred_name();
1352 if (inferred_name->length() > 0) return inferred_name;
1353 Object* proto = GetPrototype();
1354 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
ager@chromium.orga1645e22009-09-09 19:27:10 +00001355 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001356 // TODO(rossberg): what about proxies?
ager@chromium.orga1645e22009-09-09 19:27:10 +00001357 // If the constructor is not present, return "Object".
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001358 return GetHeap()->Object_symbol();
ager@chromium.orga1645e22009-09-09 19:27:10 +00001359}
1360
1361
lrn@chromium.org303ada72010-10-27 09:33:13 +00001362MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
1363 String* name,
1364 Object* value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001365 int index = new_map->PropertyIndexFor(name);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001366 if (map()->unused_property_fields() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001367 ASSERT(map()->unused_property_fields() == 0);
1368 int new_unused = new_map->unused_property_fields();
lrn@chromium.org303ada72010-10-27 09:33:13 +00001369 Object* values;
1370 { MaybeObject* maybe_values =
1371 properties()->CopySize(properties()->length() + new_unused + 1);
1372 if (!maybe_values->ToObject(&values)) return maybe_values;
1373 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001374 set_properties(FixedArray::cast(values));
1375 }
1376 set_map(new_map);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001377 return FastPropertyAtPut(index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001378}
1379
1380
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +00001381static bool IsIdentifier(UnicodeCache* cache,
1382 unibrow::CharacterStream* buffer) {
1383 // Checks whether the buffer contains an identifier (no escape).
1384 if (!buffer->has_more()) return false;
1385 if (!cache->IsIdentifierStart(buffer->GetNext())) {
1386 return false;
1387 }
1388 while (buffer->has_more()) {
1389 if (!cache->IsIdentifierPart(buffer->GetNext())) {
1390 return false;
1391 }
1392 }
1393 return true;
1394}
1395
1396
lrn@chromium.org303ada72010-10-27 09:33:13 +00001397MaybeObject* JSObject::AddFastProperty(String* name,
1398 Object* value,
1399 PropertyAttributes attributes) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00001400 ASSERT(!IsJSGlobalProxy());
1401
ager@chromium.org8c51fc92009-04-22 11:54:55 +00001402 // Normalize the object if the name is an actual string (not the
1403 // hidden symbols) and is not a real identifier.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001404 Isolate* isolate = GetHeap()->isolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001405 StringInputBuffer buffer(name);
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +00001406 if (!IsIdentifier(isolate->unicode_cache(), &buffer)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001407 && name != isolate->heap()->hidden_symbol()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001408 Object* obj;
1409 { MaybeObject* maybe_obj =
1410 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1411 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1412 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001413 return AddSlowProperty(name, value, attributes);
1414 }
1415
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001416 DescriptorArray* old_descriptors = map()->instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001417 // Compute the new index for new field.
1418 int index = map()->NextFreePropertyIndex();
1419
1420 // Allocate new instance descriptors with (name, index) added
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001421 FieldDescriptor new_field(name, index, attributes);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001422 Object* new_descriptors;
1423 { MaybeObject* maybe_new_descriptors =
1424 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1425 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1426 return maybe_new_descriptors;
1427 }
1428 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001429
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001430 // Only allow map transition if the object isn't the global object and there
1431 // is not a transition for the name, or there's a transition for the name but
1432 // it's unrelated to properties.
1433 int descriptor_index = old_descriptors->Search(name);
1434
1435 // External array transitions are stored in the descriptor for property "",
1436 // which is not a identifier and should have forced a switch to slow
1437 // properties above.
1438 ASSERT(descriptor_index == DescriptorArray::kNotFound ||
1439 old_descriptors->GetType(descriptor_index) != EXTERNAL_ARRAY_TRANSITION);
1440 bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound ||
1441 old_descriptors->GetType(descriptor_index) == EXTERNAL_ARRAY_TRANSITION;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001442 bool allow_map_transition =
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001443 can_insert_transition &&
1444 (isolate->context()->global_context()->object_function()->map() != map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001445
ager@chromium.org7c537e22008-10-16 08:43:32 +00001446 ASSERT(index < map()->inobject_properties() ||
1447 (index - map()->inobject_properties()) < properties()->length() ||
1448 map()->unused_property_fields() == 0);
1449 // Allocate a new map for the object.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001450 Object* r;
1451 { MaybeObject* maybe_r = map()->CopyDropDescriptors();
1452 if (!maybe_r->ToObject(&r)) return maybe_r;
1453 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001454 Map* new_map = Map::cast(r);
1455 if (allow_map_transition) {
1456 // Allocate new instance descriptors for the old map with map transition.
1457 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001458 Object* r;
1459 { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1460 if (!maybe_r->ToObject(&r)) return maybe_r;
1461 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001462 old_descriptors = DescriptorArray::cast(r);
1463 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001464
ager@chromium.org7c537e22008-10-16 08:43:32 +00001465 if (map()->unused_property_fields() == 0) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001466 if (properties()->length() > MaxFastProperties()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001467 Object* obj;
1468 { MaybeObject* maybe_obj =
1469 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1470 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1471 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001472 return AddSlowProperty(name, value, attributes);
1473 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001474 // Make room for the new value
lrn@chromium.org303ada72010-10-27 09:33:13 +00001475 Object* values;
1476 { MaybeObject* maybe_values =
1477 properties()->CopySize(properties()->length() + kFieldsAdded);
1478 if (!maybe_values->ToObject(&values)) return maybe_values;
1479 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001480 set_properties(FixedArray::cast(values));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001481 new_map->set_unused_property_fields(kFieldsAdded - 1);
1482 } else {
1483 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001484 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001485 // We have now allocated all the necessary objects.
1486 // All the changes can be applied at once, so they are atomic.
1487 map()->set_instance_descriptors(old_descriptors);
1488 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1489 set_map(new_map);
1490 return FastPropertyAtPut(index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001491}
1492
1493
lrn@chromium.org303ada72010-10-27 09:33:13 +00001494MaybeObject* JSObject::AddConstantFunctionProperty(
1495 String* name,
1496 JSFunction* function,
1497 PropertyAttributes attributes) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001498 ASSERT(!GetHeap()->InNewSpace(function));
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001499
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001500 // Allocate new instance descriptors with (name, function) added
1501 ConstantFunctionDescriptor d(name, function, attributes);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001502 Object* new_descriptors;
1503 { MaybeObject* maybe_new_descriptors =
1504 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1505 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1506 return maybe_new_descriptors;
1507 }
1508 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001509
1510 // Allocate a new map for the object.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001511 Object* new_map;
1512 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
1513 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
1514 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001515
1516 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1517 Map::cast(new_map)->set_instance_descriptors(descriptors);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001518 Map* old_map = map();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001519 set_map(Map::cast(new_map));
1520
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001521 // If the old map is the global object map (from new Object()),
1522 // then transitions are not added to it, so we are done.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001523 Heap* heap = old_map->heap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001524 if (old_map == heap->isolate()->context()->global_context()->
1525 object_function()->map()) {
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001526 return function;
1527 }
1528
1529 // Do not add CONSTANT_TRANSITIONS to global objects
1530 if (IsGlobalObject()) {
1531 return function;
1532 }
1533
1534 // Add a CONSTANT_TRANSITION descriptor to the old map,
1535 // so future assignments to this property on other objects
1536 // of the same type will create a normal field, not a constant function.
1537 // Don't do this for special properties, with non-trival attributes.
1538 if (attributes != NONE) {
1539 return function;
1540 }
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001541 ConstTransitionDescriptor mark(name, Map::cast(new_map));
lrn@chromium.org303ada72010-10-27 09:33:13 +00001542 { MaybeObject* maybe_new_descriptors =
1543 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1544 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1545 // We have accomplished the main goal, so return success.
1546 return function;
1547 }
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001548 }
1549 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1550
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001551 return function;
1552}
1553
1554
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001555// Add property in slow mode
lrn@chromium.org303ada72010-10-27 09:33:13 +00001556MaybeObject* JSObject::AddSlowProperty(String* name,
1557 Object* value,
1558 PropertyAttributes attributes) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001559 ASSERT(!HasFastProperties());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001560 StringDictionary* dict = property_dictionary();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001561 Object* store_value = value;
1562 if (IsGlobalObject()) {
1563 // In case name is an orphaned property reuse the cell.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001564 int entry = dict->FindEntry(name);
1565 if (entry != StringDictionary::kNotFound) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001566 store_value = dict->ValueAt(entry);
1567 JSGlobalPropertyCell::cast(store_value)->set_value(value);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001568 // Assign an enumeration index to the property and update
1569 // SetNextEnumerationIndex.
1570 int index = dict->NextEnumerationIndex();
1571 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1572 dict->SetNextEnumerationIndex(index + 1);
1573 dict->SetEntry(entry, name, store_value, details);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001574 return value;
1575 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001576 Heap* heap = GetHeap();
lrn@chromium.org303ada72010-10-27 09:33:13 +00001577 { MaybeObject* maybe_store_value =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001578 heap->AllocateJSGlobalPropertyCell(value);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001579 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
1580 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001581 JSGlobalPropertyCell::cast(store_value)->set_value(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001582 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001583 PropertyDetails details = PropertyDetails(attributes, NORMAL);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001584 Object* result;
1585 { MaybeObject* maybe_result = dict->Add(name, store_value, details);
1586 if (!maybe_result->ToObject(&result)) return maybe_result;
1587 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001588 if (dict != result) set_properties(StringDictionary::cast(result));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001589 return value;
1590}
1591
1592
lrn@chromium.org303ada72010-10-27 09:33:13 +00001593MaybeObject* JSObject::AddProperty(String* name,
1594 Object* value,
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001595 PropertyAttributes attributes,
1596 StrictModeFlag strict_mode) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001597 ASSERT(!IsJSGlobalProxy());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001598 Map* map_of_this = map();
1599 Heap* heap = map_of_this->heap();
1600 if (!map_of_this->is_extensible()) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001601 if (strict_mode == kNonStrictMode) {
1602 return heap->undefined_value();
1603 } else {
1604 Handle<Object> args[1] = {Handle<String>(name)};
1605 return heap->isolate()->Throw(
1606 *FACTORY->NewTypeError("object_not_extensible",
1607 HandleVector(args, 1)));
1608 }
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001609 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001610 if (HasFastProperties()) {
1611 // Ensure the descriptor array does not get too big.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001612 if (map_of_this->instance_descriptors()->number_of_descriptors() <
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001613 DescriptorArray::kMaxNumberOfDescriptors) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001614 if (value->IsJSFunction() && !heap->InNewSpace(value)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001615 return AddConstantFunctionProperty(name,
1616 JSFunction::cast(value),
1617 attributes);
1618 } else {
1619 return AddFastProperty(name, value, attributes);
1620 }
1621 } else {
1622 // Normalize the object to prevent very large instance descriptors.
1623 // This eliminates unwanted N^2 allocation and lookup behavior.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001624 Object* obj;
1625 { MaybeObject* maybe_obj =
1626 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1627 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1628 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001629 }
1630 }
1631 return AddSlowProperty(name, value, attributes);
1632}
1633
1634
lrn@chromium.org303ada72010-10-27 09:33:13 +00001635MaybeObject* JSObject::SetPropertyPostInterceptor(
1636 String* name,
1637 Object* value,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001638 PropertyAttributes attributes,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001639 StrictModeFlag strict_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001640 // Check local property, ignore interceptor.
1641 LookupResult result;
1642 LocalLookupRealNamedProperty(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00001643 if (result.IsFound()) {
1644 // An existing property, a map transition or a null descriptor was
1645 // found. Use set property to handle all these cases.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001646 return SetProperty(&result, name, value, attributes, strict_mode);
ager@chromium.org5c838252010-02-19 08:53:10 +00001647 }
1648 // Add a new real property.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001649 return AddProperty(name, value, attributes, strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001650}
1651
1652
lrn@chromium.org303ada72010-10-27 09:33:13 +00001653MaybeObject* JSObject::ReplaceSlowProperty(String* name,
1654 Object* value,
1655 PropertyAttributes attributes) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001656 StringDictionary* dictionary = property_dictionary();
1657 int old_index = dictionary->FindEntry(name);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001658 int new_enumeration_index = 0; // 0 means "Use the next available index."
1659 if (old_index != -1) {
1660 // All calls to ReplaceSlowProperty have had all transitions removed.
1661 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1662 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1663 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001664
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001665 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001666 return SetNormalizedProperty(name, value, new_details);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001667}
1668
ager@chromium.org6141cbe2009-11-20 12:14:52 +00001669
lrn@chromium.org303ada72010-10-27 09:33:13 +00001670MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
ager@chromium.org7c537e22008-10-16 08:43:32 +00001671 String* name,
1672 Object* new_value,
1673 PropertyAttributes attributes) {
1674 Map* old_map = map();
lrn@chromium.org303ada72010-10-27 09:33:13 +00001675 Object* result;
1676 { MaybeObject* maybe_result =
1677 ConvertDescriptorToField(name, new_value, attributes);
1678 if (!maybe_result->ToObject(&result)) return maybe_result;
1679 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001680 // If we get to this point we have succeeded - do not return failure
1681 // after this point. Later stuff is optional.
1682 if (!HasFastProperties()) {
1683 return result;
1684 }
1685 // Do not add transitions to the map of "new Object()".
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001686 if (map() == old_map->heap()->isolate()->context()->global_context()->
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001687 object_function()->map()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001688 return result;
1689 }
1690
1691 MapTransitionDescriptor transition(name,
1692 map(),
1693 attributes);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001694 Object* new_descriptors;
1695 { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
1696 CopyInsert(&transition, KEEP_TRANSITIONS);
1697 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1698 return result; // Yes, return _result_.
1699 }
1700 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001701 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1702 return result;
1703}
1704
1705
lrn@chromium.org303ada72010-10-27 09:33:13 +00001706MaybeObject* JSObject::ConvertDescriptorToField(String* name,
1707 Object* new_value,
1708 PropertyAttributes attributes) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001709 if (map()->unused_property_fields() == 0 &&
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001710 properties()->length() > MaxFastProperties()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001711 Object* obj;
1712 { MaybeObject* maybe_obj =
1713 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1714 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1715 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001716 return ReplaceSlowProperty(name, new_value, attributes);
1717 }
1718
1719 int index = map()->NextFreePropertyIndex();
1720 FieldDescriptor new_field(name, index, attributes);
1721 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001722 Object* descriptors_unchecked;
1723 { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
1724 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1725 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
1726 return maybe_descriptors_unchecked;
1727 }
1728 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001729 DescriptorArray* new_descriptors =
1730 DescriptorArray::cast(descriptors_unchecked);
1731
1732 // Make a new map for the object.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001733 Object* new_map_unchecked;
1734 { MaybeObject* maybe_new_map_unchecked = map()->CopyDropDescriptors();
1735 if (!maybe_new_map_unchecked->ToObject(&new_map_unchecked)) {
1736 return maybe_new_map_unchecked;
1737 }
1738 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001739 Map* new_map = Map::cast(new_map_unchecked);
1740 new_map->set_instance_descriptors(new_descriptors);
1741
1742 // Make new properties array if necessary.
1743 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1744 int new_unused_property_fields = map()->unused_property_fields() - 1;
1745 if (map()->unused_property_fields() == 0) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00001746 new_unused_property_fields = kFieldsAdded - 1;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001747 Object* new_properties_object;
1748 { MaybeObject* maybe_new_properties_object =
1749 properties()->CopySize(properties()->length() + kFieldsAdded);
1750 if (!maybe_new_properties_object->ToObject(&new_properties_object)) {
1751 return maybe_new_properties_object;
1752 }
1753 }
1754 new_properties = FixedArray::cast(new_properties_object);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001755 }
1756
1757 // Update pointers to commit changes.
1758 // Object points to the new map.
1759 new_map->set_unused_property_fields(new_unused_property_fields);
1760 set_map(new_map);
1761 if (new_properties) {
1762 set_properties(FixedArray::cast(new_properties));
1763 }
1764 return FastPropertyAtPut(index, new_value);
1765}
1766
1767
1768
lrn@chromium.org303ada72010-10-27 09:33:13 +00001769MaybeObject* JSObject::SetPropertyWithInterceptor(
1770 String* name,
1771 Object* value,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001772 PropertyAttributes attributes,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001773 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001774 Isolate* isolate = GetIsolate();
1775 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001776 Handle<JSObject> this_handle(this);
1777 Handle<String> name_handle(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001778 Handle<Object> value_handle(value, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001779 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1780 if (!interceptor->setter()->IsUndefined()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001781 LOG(isolate, ApiNamedPropertyAccess("interceptor-named-set", this, name));
1782 CustomArguments args(isolate, interceptor->data(), this, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00001783 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001784 v8::NamedPropertySetter setter =
1785 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1786 v8::Handle<v8::Value> result;
1787 {
1788 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001789 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001790 Handle<Object> value_unhole(value->IsTheHole() ?
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001791 isolate->heap()->undefined_value() :
1792 value,
1793 isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001794 result = setter(v8::Utils::ToLocal(name_handle),
1795 v8::Utils::ToLocal(value_unhole),
1796 info);
1797 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001798 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001799 if (!result.IsEmpty()) return *value_handle;
1800 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00001801 MaybeObject* raw_result =
1802 this_handle->SetPropertyPostInterceptor(*name_handle,
1803 *value_handle,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001804 attributes,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001805 strict_mode);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001806 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001807 return raw_result;
1808}
1809
1810
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001811MaybeObject* JSReceiver::SetProperty(String* name,
1812 Object* value,
1813 PropertyAttributes attributes,
1814 StrictModeFlag strict_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001815 LookupResult result;
1816 LocalLookup(name, &result);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001817 return SetProperty(&result, name, value, attributes, strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001818}
1819
1820
lrn@chromium.org303ada72010-10-27 09:33:13 +00001821MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
1822 String* name,
1823 Object* value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00001824 JSObject* holder,
1825 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001826 Isolate* isolate = GetIsolate();
1827 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001828
1829 // We should never get here to initialize a const with the hole
1830 // value since a const declaration would conflict with the setter.
1831 ASSERT(!value->IsTheHole());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001832 Handle<Object> value_handle(value, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001833
1834 // To accommodate both the old and the new api we switch on the
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001835 // data structure used to store the callbacks. Eventually foreign
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001836 // callbacks should be phased out.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001837 if (structure->IsForeign()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001838 AccessorDescriptor* callback =
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001839 reinterpret_cast<AccessorDescriptor*>(
1840 Foreign::cast(structure)->address());
lrn@chromium.org303ada72010-10-27 09:33:13 +00001841 MaybeObject* obj = (callback->setter)(this, value, callback->data);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001842 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001843 if (obj->IsFailure()) return obj;
1844 return *value_handle;
1845 }
1846
1847 if (structure->IsAccessorInfo()) {
1848 // api style callbacks
1849 AccessorInfo* data = AccessorInfo::cast(structure);
1850 Object* call_obj = data->setter();
1851 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1852 if (call_fun == NULL) return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001853 Handle<String> key(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001854 LOG(isolate, ApiNamedPropertyAccess("store", this, name));
1855 CustomArguments args(isolate, data->data(), this, JSObject::cast(holder));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00001856 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001857 {
1858 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001859 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001860 call_fun(v8::Utils::ToLocal(key),
1861 v8::Utils::ToLocal(value_handle),
1862 info);
1863 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001864 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001865 return *value_handle;
1866 }
1867
1868 if (structure->IsFixedArray()) {
1869 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1870 if (setter->IsJSFunction()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001871 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001872 } else {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00001873 if (strict_mode == kNonStrictMode) {
1874 return value;
1875 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001876 Handle<String> key(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001877 Handle<Object> holder_handle(holder, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001878 Handle<Object> args[2] = { key, holder_handle };
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001879 return isolate->Throw(
1880 *isolate->factory()->NewTypeError("no_setter_in_callback",
1881 HandleVector(args, 2)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001882 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001883 }
1884
1885 UNREACHABLE();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00001886 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001887}
1888
1889
lrn@chromium.org303ada72010-10-27 09:33:13 +00001890MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1891 Object* value) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001892 Isolate* isolate = GetIsolate();
1893 Handle<Object> value_handle(value, isolate);
1894 Handle<JSFunction> fun(JSFunction::cast(setter), isolate);
1895 Handle<JSObject> self(this, isolate);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001896#ifdef ENABLE_DEBUGGER_SUPPORT
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001897 Debug* debug = isolate->debug();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001898 // Handle stepping into a setter if step into is active.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001899 if (debug->StepInActive()) {
1900 debug->HandleStepIn(fun, Handle<Object>::null(), 0, false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001901 }
1902#endif
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001903 bool has_pending_exception;
1904 Object** argv[] = { value_handle.location() };
1905 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1906 // Check for pending exception and return the result.
1907 if (has_pending_exception) return Failure::Exception();
1908 return *value_handle;
1909}
1910
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001911
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001912void JSObject::LookupCallbackSetterInPrototypes(String* name,
1913 LookupResult* result) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001914 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001915 for (Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001916 pt != heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001917 pt = pt->GetPrototype()) {
1918 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00001919 if (result->IsProperty()) {
whesse@chromium.org030d38e2011-07-13 13:23:34 +00001920 if (result->type() == CALLBACKS && !result->IsReadOnly()) return;
1921 // Found non-callback or read-only callback, stop looking.
1922 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001923 }
1924 }
1925 result->NotFound();
1926}
1927
1928
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00001929MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes(
1930 uint32_t index,
1931 Object* value,
1932 bool* found,
1933 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001934 Heap* heap = GetHeap();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001935 for (Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001936 pt != heap->null_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001937 pt = pt->GetPrototype()) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001938 if (!JSObject::cast(pt)->HasDictionaryElements()) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00001939 continue;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001940 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001941 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1942 int entry = dictionary->FindEntry(index);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001943 if (entry != NumberDictionary::kNotFound) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001944 PropertyDetails details = dictionary->DetailsAt(entry);
1945 if (details.type() == CALLBACKS) {
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00001946 *found = true;
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00001947 return SetElementWithCallback(dictionary->ValueAt(entry),
1948 index,
1949 value,
1950 JSObject::cast(pt),
1951 strict_mode);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001952 }
1953 }
1954 }
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00001955 *found = false;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001956 return heap->the_hole_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001957}
1958
1959
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001960void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1961 DescriptorArray* descriptors = map()->instance_descriptors();
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001962 int number = descriptors->SearchWithCache(name);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001963 if (number != DescriptorArray::kNotFound) {
1964 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1965 } else {
1966 result->NotFound();
1967 }
1968}
1969
1970
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001971void Map::LookupInDescriptors(JSObject* holder,
1972 String* name,
1973 LookupResult* result) {
1974 DescriptorArray* descriptors = instance_descriptors();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001975 DescriptorLookupCache* cache = heap()->isolate()->descriptor_lookup_cache();
1976 int number = cache->Lookup(descriptors, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001977 if (number == DescriptorLookupCache::kAbsent) {
1978 number = descriptors->Search(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001979 cache->Update(descriptors, name, number);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001980 }
1981 if (number != DescriptorArray::kNotFound) {
1982 result->DescriptorResult(holder, descriptors->GetDetails(number), number);
1983 } else {
1984 result->NotFound();
1985 }
1986}
1987
1988
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001989static JSObject::ElementsKind GetElementsKindFromExternalArrayType(
1990 ExternalArrayType array_type) {
1991 switch (array_type) {
1992 case kExternalByteArray:
1993 return JSObject::EXTERNAL_BYTE_ELEMENTS;
1994 break;
1995 case kExternalUnsignedByteArray:
1996 return JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS;
1997 break;
1998 case kExternalShortArray:
1999 return JSObject::EXTERNAL_SHORT_ELEMENTS;
2000 break;
2001 case kExternalUnsignedShortArray:
2002 return JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS;
2003 break;
2004 case kExternalIntArray:
2005 return JSObject::EXTERNAL_INT_ELEMENTS;
2006 break;
2007 case kExternalUnsignedIntArray:
2008 return JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS;
2009 break;
2010 case kExternalFloatArray:
2011 return JSObject::EXTERNAL_FLOAT_ELEMENTS;
2012 break;
2013 case kExternalDoubleArray:
2014 return JSObject::EXTERNAL_DOUBLE_ELEMENTS;
2015 break;
2016 case kExternalPixelArray:
2017 return JSObject::EXTERNAL_PIXEL_ELEMENTS;
2018 break;
2019 }
2020 UNREACHABLE();
2021 return JSObject::DICTIONARY_ELEMENTS;
2022}
2023
2024
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002025MaybeObject* Map::GetExternalArrayElementsMap(ExternalArrayType array_type,
2026 bool safe_to_add_transition) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002027 Heap* current_heap = heap();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002028 DescriptorArray* descriptors = instance_descriptors();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002029 String* external_array_sentinel_name = current_heap->empty_symbol();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002030
2031 if (safe_to_add_transition) {
2032 // It's only safe to manipulate the descriptor array if it would be
2033 // safe to add a transition.
2034
2035 ASSERT(!is_shared()); // no transitions can be added to shared maps.
2036 // Check if the external array transition already exists.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002037 DescriptorLookupCache* cache =
2038 current_heap->isolate()->descriptor_lookup_cache();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002039 int index = cache->Lookup(descriptors, external_array_sentinel_name);
2040 if (index == DescriptorLookupCache::kAbsent) {
2041 index = descriptors->Search(external_array_sentinel_name);
2042 cache->Update(descriptors,
2043 external_array_sentinel_name,
2044 index);
2045 }
2046
2047 // If the transition already exists, check the type. If there is a match,
2048 // return it.
2049 if (index != DescriptorArray::kNotFound) {
2050 PropertyDetails details(PropertyDetails(descriptors->GetDetails(index)));
2051 if (details.type() == EXTERNAL_ARRAY_TRANSITION &&
2052 details.array_type() == array_type) {
2053 return descriptors->GetValue(index);
2054 } else {
2055 safe_to_add_transition = false;
2056 }
2057 }
2058 }
2059
2060 // No transition to an existing external array map. Make a new one.
2061 Object* obj;
2062 { MaybeObject* maybe_map = CopyDropTransitions();
2063 if (!maybe_map->ToObject(&obj)) return maybe_map;
2064 }
2065 Map* new_map = Map::cast(obj);
2066
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00002067 new_map->set_elements_kind(GetElementsKindFromExternalArrayType(array_type));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002068 GetIsolate()->counters()->map_to_external_array_elements()->Increment();
2069
2070 // Only remember the map transition if the object's map is NOT equal to the
2071 // global object_function's map and there is not an already existing
2072 // non-matching external array transition.
2073 bool allow_map_transition =
2074 safe_to_add_transition &&
2075 (GetIsolate()->context()->global_context()->object_function()->map() !=
2076 map());
2077 if (allow_map_transition) {
2078 // Allocate new instance descriptors for the old map with map transition.
2079 ExternalArrayTransitionDescriptor desc(external_array_sentinel_name,
2080 Map::cast(new_map),
2081 array_type);
2082 Object* new_descriptors;
2083 MaybeObject* maybe_new_descriptors = descriptors->CopyInsert(
2084 &desc,
2085 KEEP_TRANSITIONS);
2086 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
2087 return maybe_new_descriptors;
2088 }
2089 descriptors = DescriptorArray::cast(new_descriptors);
2090 set_instance_descriptors(descriptors);
2091 }
2092
2093 return new_map;
2094}
2095
2096
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002097void JSObject::LocalLookupRealNamedProperty(String* name,
2098 LookupResult* result) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002099 if (IsJSGlobalProxy()) {
2100 Object* proto = GetPrototype();
2101 if (proto->IsNull()) return result->NotFound();
2102 ASSERT(proto->IsJSGlobalObject());
2103 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
2104 }
2105
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002106 if (HasFastProperties()) {
2107 LookupInDescriptor(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00002108 if (result->IsFound()) {
2109 // A property, a map transition or a null descriptor was found.
2110 // We return all of these result types because
2111 // LocalLookupRealNamedProperty is used when setting properties
2112 // where map transitions and null descriptors are handled.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002113 ASSERT(result->holder() == this && result->type() != NORMAL);
2114 // Disallow caching for uninitialized constants. These can only
2115 // occur as fields.
2116 if (result->IsReadOnly() && result->type() == FIELD &&
ager@chromium.org7c537e22008-10-16 08:43:32 +00002117 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002118 result->DisallowCaching();
2119 }
2120 return;
2121 }
2122 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002123 int entry = property_dictionary()->FindEntry(name);
2124 if (entry != StringDictionary::kNotFound) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002125 Object* value = property_dictionary()->ValueAt(entry);
2126 if (IsGlobalObject()) {
2127 PropertyDetails d = property_dictionary()->DetailsAt(entry);
2128 if (d.IsDeleted()) {
2129 result->NotFound();
2130 return;
2131 }
2132 value = JSGlobalPropertyCell::cast(value)->value();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002133 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002134 // Make sure to disallow caching for uninitialized constants
2135 // found in the dictionary-mode objects.
2136 if (value->IsTheHole()) result->DisallowCaching();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002137 result->DictionaryResult(this, entry);
2138 return;
2139 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002140 }
2141 result->NotFound();
2142}
2143
2144
2145void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
2146 LocalLookupRealNamedProperty(name, result);
2147 if (result->IsProperty()) return;
2148
2149 LookupRealNamedPropertyInPrototypes(name, result);
2150}
2151
2152
2153void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
2154 LookupResult* result) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002155 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002156 for (Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002157 pt != heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002158 pt = JSObject::cast(pt)->GetPrototype()) {
2159 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00002160 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002161 }
2162 result->NotFound();
2163}
2164
2165
2166// We only need to deal with CALLBACKS and INTERCEPTORS
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002167MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(
2168 LookupResult* result,
2169 String* name,
2170 Object* value,
2171 bool check_prototype,
2172 StrictModeFlag strict_mode) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00002173 if (check_prototype && !result->IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002174 LookupCallbackSetterInPrototypes(name, result);
2175 }
2176
2177 if (result->IsProperty()) {
2178 if (!result->IsReadOnly()) {
2179 switch (result->type()) {
2180 case CALLBACKS: {
2181 Object* obj = result->GetCallbackObject();
2182 if (obj->IsAccessorInfo()) {
2183 AccessorInfo* info = AccessorInfo::cast(obj);
2184 if (info->all_can_write()) {
2185 return SetPropertyWithCallback(result->GetCallbackObject(),
2186 name,
2187 value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002188 result->holder(),
2189 strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002190 }
2191 }
2192 break;
2193 }
2194 case INTERCEPTOR: {
2195 // Try lookup real named properties. Note that only property can be
2196 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
2197 LookupResult r;
2198 LookupRealNamedProperty(name, &r);
2199 if (r.IsProperty()) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002200 return SetPropertyWithFailedAccessCheck(&r,
2201 name,
2202 value,
2203 check_prototype,
2204 strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002205 }
2206 break;
2207 }
2208 default: {
2209 break;
2210 }
2211 }
2212 }
2213 }
2214
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002215 Heap* heap = GetHeap();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002216 HandleScope scope(heap->isolate());
2217 Handle<Object> value_handle(value);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002218 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00002219 return *value_handle;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002220}
2221
2222
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002223MaybeObject* JSReceiver::SetProperty(LookupResult* result,
2224 String* key,
2225 Object* value,
2226 PropertyAttributes attributes,
2227 StrictModeFlag strict_mode) {
2228 if (result->IsFound() && result->type() == HANDLER) {
2229 return JSProxy::cast(this)->SetPropertyWithHandler(
2230 key, value, attributes, strict_mode);
2231 } else {
2232 return JSObject::cast(this)->SetPropertyForResult(
2233 result, key, value, attributes, strict_mode);
2234 }
2235}
2236
2237
rossberg@chromium.org717967f2011-07-20 13:44:42 +00002238bool JSProxy::HasPropertyWithHandler(String* name_raw) {
2239 Isolate* isolate = GetIsolate();
2240 HandleScope scope(isolate);
2241 Handle<Object> receiver(this);
2242 Handle<Object> name(name_raw);
2243 Handle<Object> handler(this->handler());
2244
2245 // Extract trap function.
2246 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("has");
2247 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
2248 if (trap->IsUndefined()) {
2249 trap = isolate->derived_has_trap();
2250 }
2251
2252 // Call trap function.
2253 Object** args[] = { name.location() };
2254 bool has_exception;
2255 Handle<Object> result =
2256 Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
2257 if (has_exception) return Failure::Exception();
2258
2259 return result->ToBoolean()->IsTrue();
2260}
2261
2262
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002263MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
2264 String* name_raw,
2265 Object* value_raw,
2266 PropertyAttributes attributes,
2267 StrictModeFlag strict_mode) {
2268 Isolate* isolate = GetIsolate();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002269 HandleScope scope(isolate);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002270 Handle<Object> receiver(this);
2271 Handle<Object> name(name_raw);
2272 Handle<Object> value(value_raw);
2273 Handle<Object> handler(this->handler());
2274
2275 // Extract trap function.
2276 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("set");
2277 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
2278 if (trap->IsUndefined()) {
2279 trap = isolate->derived_set_trap();
2280 }
2281
2282 // Call trap function.
2283 Object** args[] = {
2284 receiver.location(), name.location(), value.location()
2285 };
2286 bool has_exception;
rossberg@chromium.org717967f2011-07-20 13:44:42 +00002287 Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002288 if (has_exception) return Failure::Exception();
2289
ager@chromium.org04921a82011-06-27 13:21:41 +00002290 return *value;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002291}
2292
2293
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002294MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
2295 String* name_raw, DeleteMode mode) {
2296 Isolate* isolate = GetIsolate();
2297 HandleScope scope(isolate);
2298 Handle<Object> receiver(this);
2299 Handle<Object> name(name_raw);
2300 Handle<Object> handler(this->handler());
2301
2302 // Extract trap function.
2303 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete");
2304 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
2305 if (trap->IsUndefined()) {
2306 Handle<Object> args[] = { handler, trap_name };
2307 Handle<Object> error = isolate->factory()->NewTypeError(
2308 "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args)));
2309 isolate->Throw(*error);
2310 return Failure::Exception();
2311 }
2312
2313 // Call trap function.
2314 Object** args[] = { name.location() };
2315 bool has_exception;
2316 Handle<Object> result =
2317 Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
2318 if (has_exception) return Failure::Exception();
2319
2320 Object* bool_result = result->ToBoolean();
danno@chromium.orgb6451162011-08-17 14:33:23 +00002321 if (mode == STRICT_DELETION &&
2322 bool_result == isolate->heap()->false_value()) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002323 Handle<Object> args[] = { handler, trap_name };
2324 Handle<Object> error = isolate->factory()->NewTypeError(
2325 "handler_failed", HandleVector(args, ARRAY_SIZE(args)));
2326 isolate->Throw(*error);
2327 return Failure::Exception();
2328 }
2329 return bool_result;
2330}
2331
2332
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002333MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
2334 JSReceiver* receiver_raw,
2335 String* name_raw,
2336 bool* has_exception) {
2337 Isolate* isolate = GetIsolate();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002338 HandleScope scope(isolate);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002339 Handle<JSReceiver> receiver(receiver_raw);
2340 Handle<Object> name(name_raw);
2341 Handle<Object> handler(this->handler());
2342
2343 // Extract trap function.
2344 Handle<String> trap_name =
2345 isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor");
2346 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
2347 if (trap->IsUndefined()) {
2348 Handle<Object> args[] = { handler, trap_name };
2349 Handle<Object> error = isolate->factory()->NewTypeError(
2350 "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args)));
2351 isolate->Throw(*error);
2352 *has_exception = true;
2353 return NONE;
2354 }
2355
2356 // Call trap function.
2357 Object** args[] = { name.location() };
2358 Handle<Object> result =
2359 Execution::Call(trap, handler, ARRAY_SIZE(args), args, has_exception);
2360 if (has_exception) return NONE;
2361
2362 // TODO(rossberg): convert result to PropertyAttributes
2363 USE(result);
2364 return NONE;
2365}
2366
2367
rossberg@chromium.org717967f2011-07-20 13:44:42 +00002368void JSProxy::Fix() {
2369 Isolate* isolate = GetIsolate();
2370 HandleScope scope(isolate);
2371 Handle<JSProxy> self(this);
2372
2373 isolate->factory()->BecomeJSObject(self);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00002374 ASSERT(self->IsJSObject());
rossberg@chromium.org717967f2011-07-20 13:44:42 +00002375 // TODO(rossberg): recognize function proxies.
2376}
2377
2378
2379
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002380MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
whesse@chromium.org030d38e2011-07-13 13:23:34 +00002381 String* name,
2382 Object* value,
2383 PropertyAttributes attributes,
2384 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002385 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002386 // Make sure that the top context does not change when doing callbacks or
2387 // interceptor calls.
2388 AssertNoContextChange ncc;
2389
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002390 // Optimization for 2-byte strings often used as keys in a decompression
2391 // dictionary. We make these short keys into symbols to avoid constantly
2392 // reallocating them.
2393 if (!name->IsSymbol() && name->length() <= 2) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00002394 Object* symbol_version;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002395 { MaybeObject* maybe_symbol_version = heap->LookupSymbol(name);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002396 if (maybe_symbol_version->ToObject(&symbol_version)) {
2397 name = String::cast(symbol_version);
2398 }
2399 }
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002400 }
2401
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002402 // Check access rights if needed.
2403 if (IsAccessCheckNeeded()
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002404 && !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002405 return SetPropertyWithFailedAccessCheck(result,
2406 name,
2407 value,
2408 true,
2409 strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002411
2412 if (IsJSGlobalProxy()) {
2413 Object* proto = GetPrototype();
2414 if (proto->IsNull()) return value;
2415 ASSERT(proto->IsJSGlobalObject());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002416 return JSObject::cast(proto)->SetProperty(
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002417 result, name, value, attributes, strict_mode);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002418 }
2419
ager@chromium.org32912102009-01-16 10:38:43 +00002420 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002421 // We could not find a local property so let's check whether there is an
2422 // accessor that wants to handle the property.
2423 LookupResult accessor_result;
2424 LookupCallbackSetterInPrototypes(name, &accessor_result);
ager@chromium.org5c838252010-02-19 08:53:10 +00002425 if (accessor_result.IsProperty()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002426 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
2427 name,
2428 value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002429 accessor_result.holder(),
2430 strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002431 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002432 }
ager@chromium.org5c838252010-02-19 08:53:10 +00002433 if (!result->IsFound()) {
2434 // Neither properties nor transitions found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002435 return AddProperty(name, value, attributes, strict_mode);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002436 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002437 if (result->IsReadOnly() && result->IsProperty()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002438 if (strict_mode == kStrictMode) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002439 HandleScope scope(heap->isolate());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002440 Handle<String> key(name);
2441 Handle<Object> holder(this);
2442 Handle<Object> args[2] = { key, holder };
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002443 return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError(
2444 "strict_read_only_property", HandleVector(args, 2)));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002445 } else {
2446 return value;
2447 }
2448 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002449 // This is a real property that is not read-only, or it is a
2450 // transition or null descriptor and there are no setters in the prototypes.
2451 switch (result->type()) {
2452 case NORMAL:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002453 return SetNormalizedProperty(result, value);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002454 case FIELD:
ager@chromium.org7c537e22008-10-16 08:43:32 +00002455 return FastPropertyAtPut(result->GetFieldIndex(), value);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002456 case MAP_TRANSITION:
2457 if (attributes == result->GetAttributes()) {
2458 // Only use map transition if the attributes match.
2459 return AddFastPropertyUsingMap(result->GetTransitionMap(),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002460 name,
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002461 value);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002462 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002463 return ConvertDescriptorToField(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002464 case CONSTANT_FUNCTION:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002465 // Only replace the function if necessary.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002466 if (value == result->GetConstantFunction()) return value;
2467 // Preserve the attributes of this existing property.
2468 attributes = result->GetAttributes();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002469 return ConvertDescriptorToField(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002470 case CALLBACKS:
2471 return SetPropertyWithCallback(result->GetCallbackObject(),
2472 name,
2473 value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002474 result->holder(),
2475 strict_mode);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002476 case INTERCEPTOR:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002477 return SetPropertyWithInterceptor(name, value, attributes, strict_mode);
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00002478 case CONSTANT_TRANSITION: {
2479 // If the same constant function is being added we can simply
2480 // transition to the target map.
2481 Map* target_map = result->GetTransitionMap();
2482 DescriptorArray* target_descriptors = target_map->instance_descriptors();
2483 int number = target_descriptors->SearchWithCache(name);
2484 ASSERT(number != DescriptorArray::kNotFound);
2485 ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
2486 JSFunction* function =
2487 JSFunction::cast(target_descriptors->GetValue(number));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002488 ASSERT(!HEAP->InNewSpace(function));
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00002489 if (value == function) {
2490 set_map(target_map);
2491 return value;
2492 }
2493 // Otherwise, replace with a MAP_TRANSITION to a new map with a
2494 // FIELD, even if the value is a constant function.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002495 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00002496 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002497 case NULL_DESCRIPTOR:
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002498 case EXTERNAL_ARRAY_TRANSITION:
ager@chromium.org7c537e22008-10-16 08:43:32 +00002499 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002500 default:
2501 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002502 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002503 UNREACHABLE();
2504 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002505}
2506
2507
2508// Set a real local property, even if it is READ_ONLY. If the property is not
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002509// present, add it with attributes NONE. This code is an exact clone of
2510// SetProperty, with the check for IsReadOnly and the check for a
2511// callback setter removed. The two lines looking up the LookupResult
2512// result are also added. If one of the functions is changed, the other
2513// should be.
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002514// Note that this method cannot be used to set the prototype of a function
2515// because ConvertDescriptorToField() which is called in "case CALLBACKS:"
2516// doesn't handle function prototypes correctly.
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00002517MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002518 String* name,
2519 Object* value,
2520 PropertyAttributes attributes) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002521
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002522 // Make sure that the top context does not change when doing callbacks or
2523 // interceptor calls.
2524 AssertNoContextChange ncc;
ager@chromium.org5c838252010-02-19 08:53:10 +00002525 LookupResult result;
2526 LocalLookup(name, &result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002527 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002528 if (IsAccessCheckNeeded()) {
2529 Heap* heap = GetHeap();
2530 if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002531 return SetPropertyWithFailedAccessCheck(&result,
2532 name,
2533 value,
2534 false,
2535 kNonStrictMode);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002536 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002537 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002538
2539 if (IsJSGlobalProxy()) {
2540 Object* proto = GetPrototype();
2541 if (proto->IsNull()) return value;
2542 ASSERT(proto->IsJSGlobalObject());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00002543 return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes(
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002544 name,
2545 value,
2546 attributes);
2547 }
2548
ager@chromium.org7c537e22008-10-16 08:43:32 +00002549 // Check for accessor in prototype chain removed here in clone.
ager@chromium.org5c838252010-02-19 08:53:10 +00002550 if (!result.IsFound()) {
2551 // Neither properties nor transitions found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002552 return AddProperty(name, value, attributes, kNonStrictMode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002553 }
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002554
ager@chromium.org5c838252010-02-19 08:53:10 +00002555 PropertyDetails details = PropertyDetails(attributes, NORMAL);
2556
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002557 // Check of IsReadOnly removed from here in clone.
ager@chromium.org5c838252010-02-19 08:53:10 +00002558 switch (result.type()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002559 case NORMAL:
ager@chromium.org5c838252010-02-19 08:53:10 +00002560 return SetNormalizedProperty(name, value, details);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002561 case FIELD:
ager@chromium.org5c838252010-02-19 08:53:10 +00002562 return FastPropertyAtPut(result.GetFieldIndex(), value);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002563 case MAP_TRANSITION:
ager@chromium.org5c838252010-02-19 08:53:10 +00002564 if (attributes == result.GetAttributes()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002565 // Only use map transition if the attributes match.
ager@chromium.org5c838252010-02-19 08:53:10 +00002566 return AddFastPropertyUsingMap(result.GetTransitionMap(),
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002567 name,
2568 value);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002569 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002570 return ConvertDescriptorToField(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002571 case CONSTANT_FUNCTION:
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002572 // Only replace the function if necessary.
ager@chromium.org5c838252010-02-19 08:53:10 +00002573 if (value == result.GetConstantFunction()) return value;
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002574 // Preserve the attributes of this existing property.
ager@chromium.org5c838252010-02-19 08:53:10 +00002575 attributes = result.GetAttributes();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002576 return ConvertDescriptorToField(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002577 case CALLBACKS:
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002578 case INTERCEPTOR:
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002579 // Override callback in clone
2580 return ConvertDescriptorToField(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002581 case CONSTANT_TRANSITION:
2582 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
2583 // if the value is a function.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002584 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002585 case NULL_DESCRIPTOR:
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002586 case EXTERNAL_ARRAY_TRANSITION:
ager@chromium.org7c537e22008-10-16 08:43:32 +00002587 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002588 default:
2589 UNREACHABLE();
2590 }
2591 UNREACHABLE();
2592 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002593}
2594
2595
2596PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
2597 JSObject* receiver,
2598 String* name,
2599 bool continue_search) {
2600 // Check local property, ignore interceptor.
2601 LookupResult result;
2602 LocalLookupRealNamedProperty(name, &result);
kasper.lund44510672008-07-25 07:37:58 +00002603 if (result.IsProperty()) return result.GetAttributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002604
2605 if (continue_search) {
2606 // Continue searching via the prototype chain.
2607 Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002608 if (!pt->IsNull()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002609 return JSObject::cast(pt)->
2610 GetPropertyAttributeWithReceiver(receiver, name);
2611 }
2612 }
2613 return ABSENT;
2614}
2615
2616
2617PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
2618 JSObject* receiver,
2619 String* name,
2620 bool continue_search) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002621 Isolate* isolate = GetIsolate();
2622
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002623 // Make sure that the top context does not change when doing
2624 // callbacks or interceptor calls.
2625 AssertNoContextChange ncc;
2626
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002627 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002628 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2629 Handle<JSObject> receiver_handle(receiver);
2630 Handle<JSObject> holder_handle(this);
2631 Handle<String> name_handle(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002632 CustomArguments args(isolate, interceptor->data(), receiver, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002633 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002634 if (!interceptor->query()->IsUndefined()) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00002635 v8::NamedPropertyQuery query =
2636 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002637 LOG(isolate,
2638 ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00002639 v8::Handle<v8::Integer> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002640 {
2641 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002642 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002643 result = query(v8::Utils::ToLocal(name_handle), info);
2644 }
2645 if (!result.IsEmpty()) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00002646 ASSERT(result->IsInt32());
2647 return static_cast<PropertyAttributes>(result->Int32Value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002648 }
2649 } else if (!interceptor->getter()->IsUndefined()) {
2650 v8::NamedPropertyGetter getter =
2651 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002652 LOG(isolate,
2653 ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002654 v8::Handle<v8::Value> result;
2655 {
2656 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002657 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002658 result = getter(v8::Utils::ToLocal(name_handle), info);
2659 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002660 if (!result.IsEmpty()) return DONT_ENUM;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002661 }
2662 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2663 *name_handle,
2664 continue_search);
2665}
2666
2667
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002668PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver(
2669 JSReceiver* receiver,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002670 String* key) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002671 uint32_t index = 0;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002672 if (IsJSObject() && key->AsArrayIndex(&index)) {
2673 if (JSObject::cast(this)->HasElementWithReceiver(receiver, index))
2674 return NONE;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002675 return ABSENT;
2676 }
2677 // Named property.
2678 LookupResult result;
2679 Lookup(key, &result);
2680 return GetPropertyAttribute(receiver, &result, key, true);
2681}
2682
2683
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002684PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
2685 LookupResult* result,
2686 String* name,
2687 bool continue_search) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002688 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002689 if (IsAccessCheckNeeded()) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002690 JSObject* this_obj = JSObject::cast(this);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002691 Heap* heap = GetHeap();
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002692 if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) {
2693 return this_obj->GetPropertyAttributeWithFailedAccessCheck(
2694 receiver, result, name, continue_search);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002695 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002696 }
ager@chromium.org5c838252010-02-19 08:53:10 +00002697 if (result->IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698 switch (result->type()) {
2699 case NORMAL: // fall through
2700 case FIELD:
2701 case CONSTANT_FUNCTION:
2702 case CALLBACKS:
2703 return result->GetAttributes();
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002704 case HANDLER: {
2705 // TODO(rossberg): propagate exceptions properly.
2706 bool has_exception = false;
2707 return JSProxy::cast(this)->GetPropertyAttributeWithHandler(
2708 receiver, name, &has_exception);
2709 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002710 case INTERCEPTOR:
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002711 return result->holder()->GetPropertyAttributeWithInterceptor(
2712 JSObject::cast(receiver), name, continue_search);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002713 default:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002714 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002715 }
2716 }
2717 return ABSENT;
2718}
2719
2720
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002721PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002722 // Check whether the name is an array index.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002723 uint32_t index = 0;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002724 if (IsJSObject() && name->AsArrayIndex(&index)) {
2725 if (JSObject::cast(this)->HasLocalElement(index)) return NONE;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002726 return ABSENT;
2727 }
2728 // Named property.
2729 LookupResult result;
2730 LocalLookup(name, &result);
2731 return GetPropertyAttribute(this, &result, name, false);
2732}
2733
2734
lrn@chromium.org303ada72010-10-27 09:33:13 +00002735MaybeObject* NormalizedMapCache::Get(JSObject* obj,
2736 PropertyNormalizationMode mode) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002737 Isolate* isolate = obj->GetIsolate();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002738 Map* fast = obj->map();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00002739 int index = fast->Hash() % kEntries;
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002740 Object* result = get(index);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00002741 if (result->IsMap() &&
2742 Map::cast(result)->EquivalentToForNormalization(fast, mode)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002743#ifdef DEBUG
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00002744 Map::cast(result)->SharedMapVerify();
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002745 if (FLAG_enable_slow_asserts) {
2746 // The cached map should match newly created normalized map bit-by-bit.
lrn@chromium.org303ada72010-10-27 09:33:13 +00002747 Object* fresh;
2748 { MaybeObject* maybe_fresh =
2749 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2750 if (maybe_fresh->ToObject(&fresh)) {
2751 ASSERT(memcmp(Map::cast(fresh)->address(),
2752 Map::cast(result)->address(),
2753 Map::kSize) == 0);
2754 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002755 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002756 }
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002757#endif
2758 return result;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002759 }
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002760
lrn@chromium.org303ada72010-10-27 09:33:13 +00002761 { MaybeObject* maybe_result =
2762 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2763 if (!maybe_result->ToObject(&result)) return maybe_result;
2764 }
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002765 set(index, result);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002766 isolate->counters()->normalized_maps()->Increment();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002767
2768 return result;
2769}
2770
2771
ricow@chromium.org65fae842010-08-25 15:26:24 +00002772void NormalizedMapCache::Clear() {
2773 int entries = length();
2774 for (int i = 0; i != entries; i++) {
2775 set_undefined(i);
2776 }
2777}
2778
2779
lrn@chromium.org303ada72010-10-27 09:33:13 +00002780MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002781 if (map()->is_shared()) {
2782 // Fast case maps are never marked as shared.
2783 ASSERT(!HasFastProperties());
2784 // Replace the map with an identical copy that can be safely modified.
lrn@chromium.org303ada72010-10-27 09:33:13 +00002785 Object* obj;
2786 { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES,
2787 UNIQUE_NORMALIZED_MAP);
2788 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2789 }
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002790 GetIsolate()->counters()->normalized_maps()->Increment();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002791
2792 set_map(Map::cast(obj));
2793 }
2794 return map()->UpdateCodeCache(name, code);
2795}
2796
2797
lrn@chromium.org303ada72010-10-27 09:33:13 +00002798MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2799 int expected_additional_properties) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002800 if (!HasFastProperties()) return this;
2801
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002802 // The global object is always normalized.
2803 ASSERT(!IsGlobalObject());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00002804 // JSGlobalProxy must never be normalized
2805 ASSERT(!IsJSGlobalProxy());
2806
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002807 Map* map_of_this = map();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002808
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002809 // Allocate new content.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002810 int property_count = map_of_this->NumberOfDescribedProperties();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002811 if (expected_additional_properties > 0) {
2812 property_count += expected_additional_properties;
2813 } else {
2814 property_count += 2; // Make space for two more properties.
2815 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00002816 Object* obj;
2817 { MaybeObject* maybe_obj =
2818 StringDictionary::Allocate(property_count);
2819 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2820 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002821 StringDictionary* dictionary = StringDictionary::cast(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002822
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002823 DescriptorArray* descs = map_of_this->instance_descriptors();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002824 for (int i = 0; i < descs->number_of_descriptors(); i++) {
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00002825 PropertyDetails details(descs->GetDetails(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002826 switch (details.type()) {
2827 case CONSTANT_FUNCTION: {
2828 PropertyDetails d =
2829 PropertyDetails(details.attributes(), NORMAL, details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002830 Object* value = descs->GetConstantFunction(i);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002831 Object* result;
2832 { MaybeObject* maybe_result =
2833 dictionary->Add(descs->GetKey(i), value, d);
2834 if (!maybe_result->ToObject(&result)) return maybe_result;
2835 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002836 dictionary = StringDictionary::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002837 break;
2838 }
2839 case FIELD: {
2840 PropertyDetails d =
2841 PropertyDetails(details.attributes(), NORMAL, details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002842 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
lrn@chromium.org303ada72010-10-27 09:33:13 +00002843 Object* result;
2844 { MaybeObject* maybe_result =
2845 dictionary->Add(descs->GetKey(i), value, d);
2846 if (!maybe_result->ToObject(&result)) return maybe_result;
2847 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002848 dictionary = StringDictionary::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002849 break;
2850 }
2851 case CALLBACKS: {
2852 PropertyDetails d =
2853 PropertyDetails(details.attributes(), CALLBACKS, details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002854 Object* value = descs->GetCallbacksObject(i);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002855 Object* result;
2856 { MaybeObject* maybe_result =
2857 dictionary->Add(descs->GetKey(i), value, d);
2858 if (!maybe_result->ToObject(&result)) return maybe_result;
2859 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002860 dictionary = StringDictionary::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002861 break;
2862 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002863 case MAP_TRANSITION:
2864 case CONSTANT_TRANSITION:
2865 case NULL_DESCRIPTOR:
2866 case INTERCEPTOR:
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002867 case EXTERNAL_ARRAY_TRANSITION:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002868 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002869 default:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002870 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002871 }
2872 }
2873
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002874 Heap* current_heap = map_of_this->heap();
2875
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002876 // Copy the next enumeration index from instance descriptor.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002877 int index = map_of_this->instance_descriptors()->NextEnumerationIndex();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002878 dictionary->SetNextEnumerationIndex(index);
2879
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002880 { MaybeObject* maybe_obj =
2881 current_heap->isolate()->context()->global_context()->
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002882 normalized_map_cache()->Get(this, mode);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002883 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2884 }
ager@chromium.org32912102009-01-16 10:38:43 +00002885 Map* new_map = Map::cast(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002886
ager@chromium.org32912102009-01-16 10:38:43 +00002887 // We have now successfully allocated all the necessary objects.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002888 // Changes can now be made with the guarantee that all of them take effect.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002889
2890 // Resize the object in the heap if necessary.
2891 int new_instance_size = new_map->instance_size();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002892 int instance_size_delta = map_of_this->instance_size() - new_instance_size;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002893 ASSERT(instance_size_delta >= 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002894 current_heap->CreateFillerObjectAt(this->address() + new_instance_size,
2895 instance_size_delta);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002896
ager@chromium.org32912102009-01-16 10:38:43 +00002897 set_map(new_map);
danno@chromium.org40cb8782011-05-25 07:58:50 +00002898 new_map->clear_instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002899
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002900 set_properties(dictionary);
2901
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002902 current_heap->isolate()->counters()->props_to_dictionary()->Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002903
2904#ifdef DEBUG
2905 if (FLAG_trace_normalization) {
2906 PrintF("Object properties have been normalized:\n");
2907 Print();
2908 }
2909#endif
2910 return this;
2911}
2912
2913
lrn@chromium.org303ada72010-10-27 09:33:13 +00002914MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915 if (HasFastProperties()) return this;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002916 ASSERT(!IsGlobalObject());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002917 return property_dictionary()->
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002918 TransformPropertiesToFastFor(this, unused_property_fields);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002919}
2920
2921
lrn@chromium.org303ada72010-10-27 09:33:13 +00002922MaybeObject* JSObject::NormalizeElements() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002923 ASSERT(!HasExternalArrayElements());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002924
whesse@chromium.org7b260152011-06-20 15:33:18 +00002925 // Find the backing store.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002926 FixedArrayBase* array = FixedArrayBase::cast(elements());
whesse@chromium.org7b260152011-06-20 15:33:18 +00002927 Map* old_map = array->map();
2928 bool is_arguments =
2929 (old_map == old_map->heap()->non_strict_arguments_elements_map());
2930 if (is_arguments) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002931 array = FixedArrayBase::cast(FixedArray::cast(array)->get(1));
lrn@chromium.org303ada72010-10-27 09:33:13 +00002932 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002933 if (array->IsDictionary()) return array;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002934
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002935 ASSERT(HasFastElements() ||
2936 HasFastDoubleElements() ||
2937 HasFastArgumentsElements());
whesse@chromium.org7b260152011-06-20 15:33:18 +00002938 // Compute the effective length and allocate a new backing store.
2939 int length = IsJSArray()
2940 ? Smi::cast(JSArray::cast(this)->length())->value()
2941 : array->length();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002942 int old_capacity = 0;
2943 int used_elements = 0;
2944 GetElementsCapacityAndUsage(&old_capacity, &used_elements);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002945 NumberDictionary* dictionary = NULL;
2946 { Object* object;
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002947 MaybeObject* maybe = NumberDictionary::Allocate(used_elements);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002948 if (!maybe->ToObject(&object)) return maybe;
2949 dictionary = NumberDictionary::cast(object);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002950 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002951
2952 // Copy the elements to the new backing store.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002953 bool has_double_elements = array->IsFixedDoubleArray();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002954 for (int i = 0; i < length; i++) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002955 Object* value = NULL;
2956 if (has_double_elements) {
2957 FixedDoubleArray* double_array = FixedDoubleArray::cast(array);
2958 if (double_array->is_the_hole(i)) {
2959 value = GetIsolate()->heap()->the_hole_value();
2960 } else {
2961 // Objects must be allocated in the old object space, since the
2962 // overall number of HeapNumbers needed for the conversion might
2963 // exceed the capacity of new space, and we would fail repeatedly
2964 // trying to convert the FixedDoubleArray.
2965 MaybeObject* maybe_value_object =
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00002966 GetHeap()->AllocateHeapNumber(double_array->get_scalar(i), TENURED);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002967 if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
lrn@chromium.org303ada72010-10-27 09:33:13 +00002968 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002969 } else {
2970 ASSERT(old_map->has_fast_elements());
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002971 value = FixedArray::cast(array)->get(i);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002972 }
2973 PropertyDetails details = PropertyDetails(NONE, NORMAL);
2974 if (!value->IsTheHole()) {
2975 Object* result;
2976 MaybeObject* maybe_result =
2977 dictionary->AddNumberEntry(i, value, details);
2978 if (!maybe_result->ToObject(&result)) return maybe_result;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002979 dictionary = NumberDictionary::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002980 }
2981 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002982
whesse@chromium.org7b260152011-06-20 15:33:18 +00002983 // Switch to using the dictionary as the backing storage for elements.
2984 if (is_arguments) {
2985 FixedArray::cast(elements())->set(1, dictionary);
2986 } else {
2987 // Set the new map first to satify the elements type assert in
2988 // set_elements().
2989 Object* new_map;
2990 MaybeObject* maybe = map()->GetSlowElementsMap();
2991 if (!maybe->ToObject(&new_map)) return maybe;
2992 set_map(Map::cast(new_map));
2993 set_elements(dictionary);
2994 }
2995
2996 old_map->isolate()->counters()->elements_to_dictionary()->Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002997
2998#ifdef DEBUG
2999 if (FLAG_trace_normalization) {
3000 PrintF("Object elements have been normalized:\n");
3001 Print();
3002 }
3003#endif
3004
whesse@chromium.org7b260152011-06-20 15:33:18 +00003005 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
3006 return dictionary;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003007}
3008
3009
vegorov@chromium.org7943d462011-08-01 11:41:52 +00003010MaybeObject* JSObject::GetHiddenProperties(HiddenPropertiesFlag flag) {
3011 Isolate* isolate = GetIsolate();
3012 Heap* heap = isolate->heap();
3013 Object* holder = BypassGlobalProxy();
3014 if (holder->IsUndefined()) return heap->undefined_value();
3015 JSObject* obj = JSObject::cast(holder);
3016 if (obj->HasFastProperties()) {
3017 // If the object has fast properties, check whether the first slot
3018 // in the descriptor array matches the hidden symbol. Since the
3019 // hidden symbols hash code is zero (and no other string has hash
3020 // code zero) it will always occupy the first entry if present.
3021 DescriptorArray* descriptors = obj->map()->instance_descriptors();
3022 if ((descriptors->number_of_descriptors() > 0) &&
3023 (descriptors->GetKey(0) == heap->hidden_symbol()) &&
3024 descriptors->IsProperty(0)) {
3025 ASSERT(descriptors->GetType(0) == FIELD);
3026 return obj->FastPropertyAt(descriptors->GetFieldIndex(0));
3027 }
3028 }
3029
3030 // Only attempt to find the hidden properties in the local object and not
3031 // in the prototype chain.
3032 if (!obj->HasHiddenPropertiesObject()) {
3033 // Hidden properties object not found. Allocate a new hidden properties
3034 // object if requested. Otherwise return the undefined value.
3035 if (flag == ALLOW_CREATION) {
3036 Object* hidden_obj;
3037 { MaybeObject* maybe_obj = heap->AllocateJSObject(
3038 isolate->context()->global_context()->object_function());
3039 if (!maybe_obj->ToObject(&hidden_obj)) return maybe_obj;
3040 }
3041 return obj->SetHiddenPropertiesObject(hidden_obj);
3042 } else {
3043 return heap->undefined_value();
3044 }
3045 }
3046 return obj->GetHiddenPropertiesObject();
3047}
3048
3049
3050MaybeObject* JSObject::GetIdentityHash(HiddenPropertiesFlag flag) {
3051 Isolate* isolate = GetIsolate();
3052 Object* hidden_props_obj;
3053 { MaybeObject* maybe_obj = GetHiddenProperties(flag);
3054 if (!maybe_obj->ToObject(&hidden_props_obj)) return maybe_obj;
3055 }
3056 if (!hidden_props_obj->IsJSObject()) {
3057 // We failed to create hidden properties. That's a detached
3058 // global proxy.
3059 ASSERT(hidden_props_obj->IsUndefined());
3060 return Smi::FromInt(0);
3061 }
3062 JSObject* hidden_props = JSObject::cast(hidden_props_obj);
3063 String* hash_symbol = isolate->heap()->identity_hash_symbol();
3064 {
3065 // Note that HasLocalProperty() can cause a GC in the general case in the
3066 // presence of interceptors.
3067 AssertNoAllocation no_alloc;
3068 if (hidden_props->HasLocalProperty(hash_symbol)) {
3069 MaybeObject* hash = hidden_props->GetProperty(hash_symbol);
3070 return Smi::cast(hash->ToObjectChecked());
3071 }
3072 }
3073
3074 int hash_value;
3075 int attempts = 0;
3076 do {
3077 // Generate a random 32-bit hash value but limit range to fit
3078 // within a smi.
3079 hash_value = V8::Random(isolate) & Smi::kMaxValue;
3080 attempts++;
3081 } while (hash_value == 0 && attempts < 30);
3082 hash_value = hash_value != 0 ? hash_value : 1; // never return 0
3083
3084 Smi* hash = Smi::FromInt(hash_value);
3085 { MaybeObject* result = hidden_props->SetLocalPropertyIgnoreAttributes(
3086 hash_symbol,
3087 hash,
3088 static_cast<PropertyAttributes>(None));
3089 if (result->IsFailure()) return result;
3090 }
3091 return hash;
3092}
3093
3094
lrn@chromium.org303ada72010-10-27 09:33:13 +00003095MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
3096 DeleteMode mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003097 // Check local property, ignore interceptor.
3098 LookupResult result;
3099 LocalLookupRealNamedProperty(name, &result);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003100 if (!result.IsProperty()) return GetHeap()->true_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003101
3102 // Normalize object if needed.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003103 Object* obj;
3104 { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3105 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3106 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003107
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003108 return DeleteNormalizedProperty(name, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003109}
3110
3111
lrn@chromium.org303ada72010-10-27 09:33:13 +00003112MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003113 Isolate* isolate = GetIsolate();
3114 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003115 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
3116 Handle<String> name_handle(name);
3117 Handle<JSObject> this_handle(this);
3118 if (!interceptor->deleter()->IsUndefined()) {
3119 v8::NamedPropertyDeleter deleter =
3120 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003121 LOG(isolate,
3122 ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
3123 CustomArguments args(isolate, interceptor->data(), this, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003124 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003125 v8::Handle<v8::Boolean> result;
3126 {
3127 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003128 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003129 result = deleter(v8::Utils::ToLocal(name_handle), info);
3130 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003131 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003132 if (!result.IsEmpty()) {
3133 ASSERT(result->IsBoolean());
3134 return *v8::Utils::OpenHandle(*result);
3135 }
3136 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00003137 MaybeObject* raw_result =
ager@chromium.orge2902be2009-06-08 12:21:35 +00003138 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003139 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003140 return raw_result;
3141}
3142
3143
lrn@chromium.org303ada72010-10-27 09:33:13 +00003144MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003145 Isolate* isolate = GetIsolate();
3146 Heap* heap = isolate->heap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003147 // Make sure that the top context does not change when doing
3148 // callbacks or interceptor calls.
3149 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003150 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003151 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003152 if (interceptor->deleter()->IsUndefined()) return heap->false_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003153 v8::IndexedPropertyDeleter deleter =
3154 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
3155 Handle<JSObject> this_handle(this);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003156 LOG(isolate,
3157 ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
3158 CustomArguments args(isolate, interceptor->data(), this, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003159 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003160 v8::Handle<v8::Boolean> result;
3161 {
3162 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003163 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003164 result = deleter(index, info);
3165 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003166 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003167 if (!result.IsEmpty()) {
3168 ASSERT(result->IsBoolean());
3169 return *v8::Utils::OpenHandle(*result);
3170 }
danno@chromium.orgb6451162011-08-17 14:33:23 +00003171 MaybeObject* raw_result = this_handle->GetElementsAccessor()->Delete(
3172 *this_handle,
3173 index,
3174 NORMAL_DELETION);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003175 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003176 return raw_result;
3177}
3178
3179
lrn@chromium.org303ada72010-10-27 09:33:13 +00003180MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003181 Isolate* isolate = GetIsolate();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003182 // Check access rights if needed.
3183 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003184 !isolate->MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
3185 isolate->ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
3186 return isolate->heap()->false_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003187 }
3188
3189 if (IsJSGlobalProxy()) {
3190 Object* proto = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003191 if (proto->IsNull()) return isolate->heap()->false_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003192 ASSERT(proto->IsJSGlobalObject());
ager@chromium.orge2902be2009-06-08 12:21:35 +00003193 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003194 }
3195
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003196 if (HasIndexedInterceptor()) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00003197 // Skip interceptor if forcing deletion.
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00003198 if (mode != FORCE_DELETION) {
3199 return DeleteElementWithInterceptor(index);
3200 }
3201 mode = JSReceiver::FORCE_DELETION;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003202 }
3203
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00003204 return GetElementsAccessor()->Delete(this, index, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003205}
3206
3207
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00003208MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) {
3209 if (IsJSProxy()) {
3210 return JSProxy::cast(this)->DeletePropertyWithHandler(name, mode);
3211 } else {
3212 return JSObject::cast(this)->DeleteProperty(name, mode);
3213 }
3214}
3215
3216
lrn@chromium.org303ada72010-10-27 09:33:13 +00003217MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003218 Isolate* isolate = GetIsolate();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003219 // ECMA-262, 3rd, 8.6.2.5
3220 ASSERT(name->IsString());
3221
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003222 // Check access rights if needed.
3223 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003224 !isolate->MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
3225 isolate->ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
3226 return isolate->heap()->false_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003227 }
3228
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003229 if (IsJSGlobalProxy()) {
3230 Object* proto = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003231 if (proto->IsNull()) return isolate->heap()->false_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003232 ASSERT(proto->IsJSGlobalObject());
ager@chromium.orge2902be2009-06-08 12:21:35 +00003233 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003234 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003235
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003236 uint32_t index = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003237 if (name->AsArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00003238 return DeleteElement(index, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003239 } else {
3240 LookupResult result;
3241 LocalLookup(name, &result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003242 if (!result.IsProperty()) return isolate->heap()->true_value();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003243 // Ignore attributes if forcing a deletion.
3244 if (result.IsDontDelete() && mode != FORCE_DELETION) {
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003245 if (mode == STRICT_DELETION) {
3246 // Deleting a non-configurable property in strict mode.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003247 HandleScope scope(isolate);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003248 Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) };
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003249 return isolate->Throw(*isolate->factory()->NewTypeError(
3250 "strict_delete_property", HandleVector(args, 2)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003251 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003252 return isolate->heap()->false_value();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003253 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003254 // Check for interceptor.
3255 if (result.type() == INTERCEPTOR) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00003256 // Skip interceptor if forcing a deletion.
3257 if (mode == FORCE_DELETION) {
3258 return DeletePropertyPostInterceptor(name, mode);
3259 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003260 return DeletePropertyWithInterceptor(name);
3261 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003262 // Normalize object if needed.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003263 Object* obj;
3264 { MaybeObject* maybe_obj =
3265 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3266 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3267 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003268 // Make sure the properties are normalized before removing the entry.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003269 return DeleteNormalizedProperty(name, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003270 }
3271}
3272
3273
whesse@chromium.org7b260152011-06-20 15:33:18 +00003274bool JSObject::ReferencesObjectFromElements(FixedArray* elements,
3275 ElementsKind kind,
3276 Object* object) {
3277 ASSERT(kind == FAST_ELEMENTS || kind == DICTIONARY_ELEMENTS);
3278 if (kind == FAST_ELEMENTS) {
3279 int length = IsJSArray()
3280 ? Smi::cast(JSArray::cast(this)->length())->value()
3281 : elements->length();
3282 for (int i = 0; i < length; ++i) {
3283 Object* element = elements->get(i);
3284 if (!element->IsTheHole() && element == object) return true;
3285 }
3286 } else {
3287 Object* key = NumberDictionary::cast(elements)->SlowReverseLookup(object);
3288 if (!key->IsUndefined()) return true;
3289 }
3290 return false;
3291}
3292
3293
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003294// Check whether this object references another object.
3295bool JSObject::ReferencesObject(Object* obj) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003296 Map* map_of_this = map();
3297 Heap* heap = map_of_this->heap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003298 AssertNoAllocation no_alloc;
3299
3300 // Is the object the constructor for this object?
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003301 if (map_of_this->constructor() == obj) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003302 return true;
3303 }
3304
3305 // Is the object the prototype for this object?
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003306 if (map_of_this->prototype() == obj) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003307 return true;
3308 }
3309
3310 // Check if the object is among the named properties.
3311 Object* key = SlowReverseLookup(obj);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003312 if (!key->IsUndefined()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003313 return true;
3314 }
3315
3316 // Check if the object is among the indexed properties.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003317 ElementsKind kind = GetElementsKind();
3318 switch (kind) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003319 case EXTERNAL_PIXEL_ELEMENTS:
ager@chromium.org3811b432009-10-28 14:53:37 +00003320 case EXTERNAL_BYTE_ELEMENTS:
3321 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3322 case EXTERNAL_SHORT_ELEMENTS:
3323 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3324 case EXTERNAL_INT_ELEMENTS:
3325 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3326 case EXTERNAL_FLOAT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003327 case EXTERNAL_DOUBLE_ELEMENTS:
whesse@chromium.org7b260152011-06-20 15:33:18 +00003328 case FAST_DOUBLE_ELEMENTS:
ager@chromium.org3811b432009-10-28 14:53:37 +00003329 // Raw pixels and external arrays do not reference other
3330 // objects.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003331 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003332 case FAST_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003333 case DICTIONARY_ELEMENTS: {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003334 FixedArray* elements = FixedArray::cast(this->elements());
3335 if (ReferencesObjectFromElements(elements, kind, obj)) return true;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003336 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003337 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00003338 case NON_STRICT_ARGUMENTS_ELEMENTS: {
3339 FixedArray* parameter_map = FixedArray::cast(elements());
3340 // Check the mapped parameters.
3341 int length = parameter_map->length();
3342 for (int i = 2; i < length; ++i) {
3343 Object* value = parameter_map->get(i);
3344 if (!value->IsTheHole() && value == obj) return true;
3345 }
3346 // Check the arguments.
3347 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
3348 kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : FAST_ELEMENTS;
3349 if (ReferencesObjectFromElements(arguments, kind, obj)) return true;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003350 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003351 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003352 }
3353
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00003354 // For functions check the context.
3355 if (IsJSFunction()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003356 // Get the constructor function for arguments array.
3357 JSObject* arguments_boilerplate =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003358 heap->isolate()->context()->global_context()->
3359 arguments_boilerplate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003360 JSFunction* arguments_function =
3361 JSFunction::cast(arguments_boilerplate->map()->constructor());
3362
3363 // Get the context and don't check if it is the global context.
3364 JSFunction* f = JSFunction::cast(this);
3365 Context* context = f->context();
3366 if (context->IsGlobalContext()) {
3367 return false;
3368 }
3369
3370 // Check the non-special context slots.
3371 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
3372 // Only check JS objects.
3373 if (context->get(i)->IsJSObject()) {
3374 JSObject* ctxobj = JSObject::cast(context->get(i));
3375 // If it is an arguments array check the content.
3376 if (ctxobj->map()->constructor() == arguments_function) {
3377 if (ctxobj->ReferencesObject(obj)) {
3378 return true;
3379 }
3380 } else if (ctxobj == obj) {
3381 return true;
3382 }
3383 }
3384 }
3385
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003386 // Check the context extension (if any) if it can have references.
3387 if (context->has_extension() && !context->IsCatchContext()) {
3388 return JSObject::cast(context->extension())->ReferencesObject(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003389 }
3390 }
3391
3392 // No references to object.
3393 return false;
3394}
3395
3396
lrn@chromium.org303ada72010-10-27 09:33:13 +00003397MaybeObject* JSObject::PreventExtensions() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003398 Isolate* isolate = GetIsolate();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003399 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003400 !isolate->MayNamedAccess(this,
3401 isolate->heap()->undefined_value(),
3402 v8::ACCESS_KEYS)) {
3403 isolate->ReportFailedAccessCheck(this, v8::ACCESS_KEYS);
3404 return isolate->heap()->false_value();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003405 }
3406
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00003407 if (IsJSGlobalProxy()) {
3408 Object* proto = GetPrototype();
3409 if (proto->IsNull()) return this;
3410 ASSERT(proto->IsJSGlobalObject());
3411 return JSObject::cast(proto)->PreventExtensions();
3412 }
3413
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003414 // If there are fast elements we normalize.
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003415 NumberDictionary* dictionary = NULL;
3416 { MaybeObject* maybe = NormalizeElements();
3417 if (!maybe->To<NumberDictionary>(&dictionary)) return maybe;
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003418 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003419 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003420 // Make sure that we never go back to fast case.
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003421 dictionary->set_requires_slow_elements();
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003422
3423 // Do a map transition, other objects with this map may still
3424 // be extensible.
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003425 Map* new_map;
3426 { MaybeObject* maybe = map()->CopyDropTransitions();
3427 if (!maybe->To<Map>(&new_map)) return maybe;
lrn@chromium.org303ada72010-10-27 09:33:13 +00003428 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003429 new_map->set_is_extensible(false);
3430 set_map(new_map);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003431 ASSERT(!map()->is_extensible());
3432 return new_map;
3433}
3434
3435
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003436// Tests for the fast common case for property enumeration:
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003437// - This object and all prototypes has an enum cache (which means that it has
3438// no interceptors and needs no access checks).
3439// - This object has no elements.
3440// - No prototype has enumerable properties/elements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003441bool JSObject::IsSimpleEnum() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003442 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003443 for (Object* o = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003444 o != heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003445 o = JSObject::cast(o)->GetPrototype()) {
3446 JSObject* curr = JSObject::cast(o);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003447 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003448 ASSERT(!curr->HasNamedInterceptor());
3449 ASSERT(!curr->HasIndexedInterceptor());
3450 ASSERT(!curr->IsAccessCheckNeeded());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003451 if (curr->NumberOfEnumElements() > 0) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003452 if (curr != this) {
3453 FixedArray* curr_fixed_array =
3454 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003455 if (curr_fixed_array->length() > 0) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003456 }
3457 }
3458 return true;
3459}
3460
3461
3462int Map::NumberOfDescribedProperties() {
3463 int result = 0;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003464 DescriptorArray* descs = instance_descriptors();
3465 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3466 if (descs->IsProperty(i)) result++;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003467 }
3468 return result;
3469}
3470
3471
3472int Map::PropertyIndexFor(String* name) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003473 DescriptorArray* descs = instance_descriptors();
3474 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3475 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
3476 return descs->GetFieldIndex(i);
3477 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003478 }
3479 return -1;
3480}
3481
3482
3483int Map::NextFreePropertyIndex() {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003484 int max_index = -1;
3485 DescriptorArray* descs = instance_descriptors();
3486 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3487 if (descs->GetType(i) == FIELD) {
3488 int current_index = descs->GetFieldIndex(i);
3489 if (current_index > max_index) max_index = current_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003490 }
3491 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003492 return max_index + 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003493}
3494
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003495
3496AccessorDescriptor* Map::FindAccessor(String* name) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003497 DescriptorArray* descs = instance_descriptors();
3498 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3499 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
3500 return descs->GetCallbacks(i);
3501 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003502 }
3503 return NULL;
3504}
3505
3506
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003507void JSReceiver::LocalLookup(String* name, LookupResult* result) {
3508 if (IsJSProxy()) {
3509 result->HandlerResult();
3510 } else {
3511 JSObject::cast(this)->LocalLookup(name, result);
3512 }
3513}
3514
3515
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003516void JSObject::LocalLookup(String* name, LookupResult* result) {
3517 ASSERT(name->IsString());
3518
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003519 Heap* heap = GetHeap();
3520
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003521 if (IsJSGlobalProxy()) {
3522 Object* proto = GetPrototype();
3523 if (proto->IsNull()) return result->NotFound();
3524 ASSERT(proto->IsJSGlobalObject());
3525 return JSObject::cast(proto)->LocalLookup(name, result);
3526 }
3527
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003528 // Do not use inline caching if the object is a non-global object
3529 // that requires access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003530 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003531 result->DisallowCaching();
3532 }
3533
3534 // Check __proto__ before interceptor.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00003535 if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003536 result->ConstantResult(this);
3537 return;
3538 }
3539
3540 // Check for lookup interceptor except when bootstrapping.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003541 if (HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003542 result->InterceptorResult(this);
3543 return;
3544 }
3545
3546 LocalLookupRealNamedProperty(name, result);
3547}
3548
3549
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003550void JSReceiver::Lookup(String* name, LookupResult* result) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003551 // Ecma-262 3rd 8.6.2.4
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003552 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003553 for (Object* current = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003554 current != heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003555 current = JSObject::cast(current)->GetPrototype()) {
3556 JSObject::cast(current)->LocalLookup(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00003557 if (result->IsProperty()) return;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003558 }
3559 result->NotFound();
3560}
3561
3562
3563// Search object and it's prototype chain for callback properties.
3564void JSObject::LookupCallback(String* name, LookupResult* result) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003565 Heap* heap = GetHeap();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003566 for (Object* current = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003567 current != heap->null_value();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003568 current = JSObject::cast(current)->GetPrototype()) {
3569 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00003570 if (result->IsProperty() && result->type() == CALLBACKS) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003571 }
3572 result->NotFound();
3573}
3574
3575
whesse@chromium.org7b260152011-06-20 15:33:18 +00003576// Search for a getter or setter in an elements dictionary. Returns either
3577// undefined if the element is read-only, or the getter/setter pair (fixed
3578// array) if there is an existing one, or the hole value if the element does
3579// not exist or is a normal non-getter/setter data element.
3580static Object* FindGetterSetterInDictionary(NumberDictionary* dictionary,
3581 uint32_t index,
3582 Heap* heap) {
3583 int entry = dictionary->FindEntry(index);
3584 if (entry != NumberDictionary::kNotFound) {
3585 Object* result = dictionary->ValueAt(entry);
3586 PropertyDetails details = dictionary->DetailsAt(entry);
3587 if (details.IsReadOnly()) return heap->undefined_value();
3588 if (details.type() == CALLBACKS && result->IsFixedArray()) return result;
3589 }
3590 return heap->the_hole_value();
3591}
3592
3593
lrn@chromium.org303ada72010-10-27 09:33:13 +00003594MaybeObject* JSObject::DefineGetterSetter(String* name,
3595 PropertyAttributes attributes) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003596 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003597 // Make sure that the top context does not change when doing callbacks or
3598 // interceptor calls.
3599 AssertNoContextChange ncc;
3600
ager@chromium.orgddb913d2009-01-27 10:01:48 +00003601 // Try to flatten before operating on the string.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003602 name->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003603
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003604 if (!CanSetCallback(name)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003605 return heap->undefined_value();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003606 }
3607
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003608 uint32_t index = 0;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003609 bool is_element = name->AsArrayIndex(&index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003610
3611 if (is_element) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003612 switch (GetElementsKind()) {
3613 case FAST_ELEMENTS:
ricow@chromium.org9fa09672011-07-25 11:05:35 +00003614 case FAST_DOUBLE_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003615 break;
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003616 case EXTERNAL_PIXEL_ELEMENTS:
ager@chromium.org3811b432009-10-28 14:53:37 +00003617 case EXTERNAL_BYTE_ELEMENTS:
3618 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3619 case EXTERNAL_SHORT_ELEMENTS:
3620 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3621 case EXTERNAL_INT_ELEMENTS:
3622 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3623 case EXTERNAL_FLOAT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003624 case EXTERNAL_DOUBLE_ELEMENTS:
ager@chromium.org3811b432009-10-28 14:53:37 +00003625 // Ignore getters and setters on pixel and external array
3626 // elements.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003627 return heap->undefined_value();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003628 case DICTIONARY_ELEMENTS: {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003629 Object* probe =
3630 FindGetterSetterInDictionary(element_dictionary(), index, heap);
3631 if (!probe->IsTheHole()) return probe;
3632 // Otherwise allow to override it.
3633 break;
3634 }
3635 case NON_STRICT_ARGUMENTS_ELEMENTS: {
3636 // Ascertain whether we have read-only properties or an existing
3637 // getter/setter pair in an arguments elements dictionary backing
3638 // store.
3639 FixedArray* parameter_map = FixedArray::cast(elements());
3640 uint32_t length = parameter_map->length();
3641 Object* probe =
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00003642 index < (length - 2) ? parameter_map->get(index + 2) : NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003643 if (probe == NULL || probe->IsTheHole()) {
3644 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
3645 if (arguments->IsDictionary()) {
3646 NumberDictionary* dictionary = NumberDictionary::cast(arguments);
3647 probe = FindGetterSetterInDictionary(dictionary, index, heap);
3648 if (!probe->IsTheHole()) return probe;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003649 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003650 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003651 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003652 }
3653 }
3654 } else {
3655 // Lookup the name.
3656 LookupResult result;
3657 LocalLookup(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00003658 if (result.IsProperty()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003659 if (result.IsReadOnly()) return heap->undefined_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003660 if (result.type() == CALLBACKS) {
3661 Object* obj = result.GetCallbackObject();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003662 // Need to preserve old getters/setters.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003663 if (obj->IsFixedArray()) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003664 // Use set to update attributes.
3665 return SetPropertyCallback(name, obj, attributes);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003666 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003667 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003668 }
3669 }
3670
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003671 // Allocate the fixed array to hold getter and setter.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003672 Object* structure;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003673 { MaybeObject* maybe_structure = heap->AllocateFixedArray(2, TENURED);
lrn@chromium.org303ada72010-10-27 09:33:13 +00003674 if (!maybe_structure->ToObject(&structure)) return maybe_structure;
3675 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003676
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003677 if (is_element) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003678 return SetElementCallback(index, structure, attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003679 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003680 return SetPropertyCallback(name, structure, attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003681 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003682}
3683
3684
3685bool JSObject::CanSetCallback(String* name) {
3686 ASSERT(!IsAccessCheckNeeded()
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003687 || Isolate::Current()->MayNamedAccess(this, name, v8::ACCESS_SET));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003688
3689 // Check if there is an API defined callback object which prohibits
3690 // callback overwriting in this object or it's prototype chain.
3691 // This mechanism is needed for instance in a browser setting, where
3692 // certain accessors such as window.location should not be allowed
3693 // to be overwritten because allowing overwriting could potentially
3694 // cause security problems.
3695 LookupResult callback_result;
3696 LookupCallback(name, &callback_result);
3697 if (callback_result.IsProperty()) {
3698 Object* obj = callback_result.GetCallbackObject();
3699 if (obj->IsAccessorInfo() &&
3700 AccessorInfo::cast(obj)->prohibits_overwriting()) {
3701 return false;
3702 }
3703 }
3704
3705 return true;
3706}
3707
3708
lrn@chromium.org303ada72010-10-27 09:33:13 +00003709MaybeObject* JSObject::SetElementCallback(uint32_t index,
3710 Object* structure,
3711 PropertyAttributes attributes) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003712 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3713
3714 // Normalize elements to make this operation simple.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003715 NumberDictionary* dictionary = NULL;
3716 { Object* result;
3717 MaybeObject* maybe = NormalizeElements();
3718 if (!maybe->ToObject(&result)) return maybe;
3719 dictionary = NumberDictionary::cast(result);
lrn@chromium.org303ada72010-10-27 09:33:13 +00003720 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00003721 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003722
3723 // Update the dictionary with the new CALLBACKS property.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003724 { Object* result;
3725 MaybeObject* maybe = dictionary->Set(index, structure, details);
3726 if (!maybe->ToObject(&result)) return maybe;
3727 dictionary = NumberDictionary::cast(result);
lrn@chromium.org303ada72010-10-27 09:33:13 +00003728 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003729
whesse@chromium.org7b260152011-06-20 15:33:18 +00003730 dictionary->set_requires_slow_elements();
3731 // Update the dictionary backing store on the object.
3732 if (elements()->map() == GetHeap()->non_strict_arguments_elements_map()) {
3733 // Also delete any parameter alias.
3734 //
3735 // TODO(kmillikin): when deleting the last parameter alias we could
3736 // switch to a direct backing store without the parameter map. This
3737 // would allow GC of the context.
3738 FixedArray* parameter_map = FixedArray::cast(elements());
3739 uint32_t length = parameter_map->length();
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00003740 if (index < length - 2) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003741 parameter_map->set(index + 2, GetHeap()->the_hole_value());
3742 }
3743 parameter_map->set(1, dictionary);
3744 } else {
3745 set_elements(dictionary);
3746 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003747
3748 return structure;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003749}
3750
3751
lrn@chromium.org303ada72010-10-27 09:33:13 +00003752MaybeObject* JSObject::SetPropertyCallback(String* name,
3753 Object* structure,
3754 PropertyAttributes attributes) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003755 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3756
3757 bool convert_back_to_fast = HasFastProperties() &&
3758 (map()->instance_descriptors()->number_of_descriptors()
3759 < DescriptorArray::kMaxNumberOfDescriptors);
3760
3761 // Normalize object to make this operation simple.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003762 Object* ok;
3763 { MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3764 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3765 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003766
3767 // For the global object allocate a new map to invalidate the global inline
3768 // caches which have a global property cell reference directly in the code.
3769 if (IsGlobalObject()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00003770 Object* new_map;
3771 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
3772 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3773 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003774 set_map(Map::cast(new_map));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003775 // When running crankshaft, changing the map is not enough. We
3776 // need to deoptimize all functions that rely on this global
3777 // object.
3778 Deoptimizer::DeoptimizeGlobalObject(this);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003779 }
3780
3781 // Update the dictionary with the new CALLBACKS property.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003782 Object* result;
3783 { MaybeObject* maybe_result = SetNormalizedProperty(name, structure, details);
3784 if (!maybe_result->ToObject(&result)) return maybe_result;
3785 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003786
3787 if (convert_back_to_fast) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00003788 { MaybeObject* maybe_ok = TransformToFastProperties(0);
3789 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3790 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003791 }
3792 return result;
3793}
3794
lrn@chromium.org303ada72010-10-27 09:33:13 +00003795MaybeObject* JSObject::DefineAccessor(String* name,
3796 bool is_getter,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003797 Object* fun,
lrn@chromium.org303ada72010-10-27 09:33:13 +00003798 PropertyAttributes attributes) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003799 ASSERT(fun->IsJSFunction() || fun->IsUndefined());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003800 Isolate* isolate = GetIsolate();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003801 // Check access rights if needed.
3802 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003803 !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
3804 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
3805 return isolate->heap()->undefined_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003806 }
3807
3808 if (IsJSGlobalProxy()) {
3809 Object* proto = GetPrototype();
3810 if (proto->IsNull()) return this;
3811 ASSERT(proto->IsJSGlobalObject());
3812 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
3813 fun, attributes);
3814 }
3815
lrn@chromium.org303ada72010-10-27 09:33:13 +00003816 Object* array;
3817 { MaybeObject* maybe_array = DefineGetterSetter(name, attributes);
3818 if (!maybe_array->ToObject(&array)) return maybe_array;
3819 }
3820 if (array->IsUndefined()) return array;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003821 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
3822 return this;
3823}
3824
3825
lrn@chromium.org303ada72010-10-27 09:33:13 +00003826MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003827 Isolate* isolate = GetIsolate();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003828 String* name = String::cast(info->name());
3829 // Check access rights if needed.
3830 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003831 !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
3832 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
3833 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003834 }
3835
3836 if (IsJSGlobalProxy()) {
3837 Object* proto = GetPrototype();
3838 if (proto->IsNull()) return this;
3839 ASSERT(proto->IsJSGlobalObject());
3840 return JSObject::cast(proto)->DefineAccessor(info);
3841 }
3842
3843 // Make sure that the top context does not change when doing callbacks or
3844 // interceptor calls.
3845 AssertNoContextChange ncc;
3846
3847 // Try to flatten before operating on the string.
3848 name->TryFlatten();
3849
3850 if (!CanSetCallback(name)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003851 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003852 }
3853
3854 uint32_t index = 0;
3855 bool is_element = name->AsArrayIndex(&index);
3856
3857 if (is_element) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003858 if (IsJSArray()) return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003859
3860 // Accessors overwrite previous callbacks (cf. with getters/setters).
3861 switch (GetElementsKind()) {
3862 case FAST_ELEMENTS:
ricow@chromium.org9fa09672011-07-25 11:05:35 +00003863 case FAST_DOUBLE_ELEMENTS:
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003864 break;
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003865 case EXTERNAL_PIXEL_ELEMENTS:
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003866 case EXTERNAL_BYTE_ELEMENTS:
3867 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3868 case EXTERNAL_SHORT_ELEMENTS:
3869 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3870 case EXTERNAL_INT_ELEMENTS:
3871 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3872 case EXTERNAL_FLOAT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003873 case EXTERNAL_DOUBLE_ELEMENTS:
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003874 // Ignore getters and setters on pixel and external array
3875 // elements.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003876 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003877 case DICTIONARY_ELEMENTS:
3878 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003879 case NON_STRICT_ARGUMENTS_ELEMENTS:
3880 UNIMPLEMENTED();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003881 break;
3882 }
3883
lrn@chromium.org303ada72010-10-27 09:33:13 +00003884 Object* ok;
3885 { MaybeObject* maybe_ok =
3886 SetElementCallback(index, info, info->property_attributes());
3887 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3888 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003889 } else {
3890 // Lookup the name.
3891 LookupResult result;
3892 LocalLookup(name, &result);
3893 // ES5 forbids turning a property into an accessor if it's not
3894 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
3895 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003896 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003897 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00003898 Object* ok;
3899 { MaybeObject* maybe_ok =
3900 SetPropertyCallback(name, info, info->property_attributes());
3901 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3902 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003903 }
3904
3905 return this;
3906}
3907
3908
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003909Object* JSObject::LookupAccessor(String* name, bool is_getter) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003910 Heap* heap = GetHeap();
3911
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003912 // Make sure that the top context does not change when doing callbacks or
3913 // interceptor calls.
3914 AssertNoContextChange ncc;
3915
3916 // Check access rights if needed.
3917 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003918 !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_HAS)) {
3919 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
3920 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003921 }
3922
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003923 // Make the lookup and include prototypes.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003924 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003925 uint32_t index = 0;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003926 if (name->AsArrayIndex(&index)) {
3927 for (Object* obj = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003928 obj != heap->null_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003929 obj = JSObject::cast(obj)->GetPrototype()) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003930 JSObject* js_object = JSObject::cast(obj);
3931 if (js_object->HasDictionaryElements()) {
3932 NumberDictionary* dictionary = js_object->element_dictionary();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003933 int entry = dictionary->FindEntry(index);
3934 if (entry != NumberDictionary::kNotFound) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003935 Object* element = dictionary->ValueAt(entry);
3936 PropertyDetails details = dictionary->DetailsAt(entry);
3937 if (details.type() == CALLBACKS) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003938 if (element->IsFixedArray()) {
3939 return FixedArray::cast(element)->get(accessor_index);
3940 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003941 }
3942 }
3943 }
3944 }
3945 } else {
3946 for (Object* obj = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003947 obj != heap->null_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003948 obj = JSObject::cast(obj)->GetPrototype()) {
3949 LookupResult result;
3950 JSObject::cast(obj)->LocalLookup(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00003951 if (result.IsProperty()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003952 if (result.IsReadOnly()) return heap->undefined_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003953 if (result.type() == CALLBACKS) {
3954 Object* obj = result.GetCallbackObject();
3955 if (obj->IsFixedArray()) {
3956 return FixedArray::cast(obj)->get(accessor_index);
3957 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003958 }
3959 }
3960 }
3961 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003962 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003963}
3964
3965
3966Object* JSObject::SlowReverseLookup(Object* value) {
3967 if (HasFastProperties()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003968 DescriptorArray* descs = map()->instance_descriptors();
3969 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3970 if (descs->GetType(i) == FIELD) {
3971 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3972 return descs->GetKey(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003973 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003974 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3975 if (descs->GetConstantFunction(i) == value) {
3976 return descs->GetKey(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003977 }
3978 }
3979 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003980 return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003981 } else {
3982 return property_dictionary()->SlowReverseLookup(value);
3983 }
3984}
3985
3986
lrn@chromium.org303ada72010-10-27 09:33:13 +00003987MaybeObject* Map::CopyDropDescriptors() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003988 Heap* heap = GetHeap();
lrn@chromium.org303ada72010-10-27 09:33:13 +00003989 Object* result;
3990 { MaybeObject* maybe_result =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003991 heap->AllocateMap(instance_type(), instance_size());
lrn@chromium.org303ada72010-10-27 09:33:13 +00003992 if (!maybe_result->ToObject(&result)) return maybe_result;
3993 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003994 Map::cast(result)->set_prototype(prototype());
3995 Map::cast(result)->set_constructor(constructor());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003996 // Don't copy descriptors, so map transitions always remain a forest.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00003997 // If we retained the same descriptors we would have two maps
3998 // pointing to the same transition which is bad because the garbage
3999 // collector relies on being able to reverse pointers from transitions
4000 // to maps. If properties need to be retained use CopyDropTransitions.
danno@chromium.org40cb8782011-05-25 07:58:50 +00004001 Map::cast(result)->clear_instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004002 // Please note instance_type and instance_size are set when allocated.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004003 Map::cast(result)->set_inobject_properties(inobject_properties());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004004 Map::cast(result)->set_unused_property_fields(unused_property_fields());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004005
4006 // If the map has pre-allocated properties always start out with a descriptor
4007 // array describing these properties.
4008 if (pre_allocated_property_fields() > 0) {
4009 ASSERT(constructor()->IsJSFunction());
4010 JSFunction* ctor = JSFunction::cast(constructor());
lrn@chromium.org303ada72010-10-27 09:33:13 +00004011 Object* descriptors;
4012 { MaybeObject* maybe_descriptors =
4013 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
4014 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
4015 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004016 Map::cast(result)->set_instance_descriptors(
4017 DescriptorArray::cast(descriptors));
4018 Map::cast(result)->set_pre_allocated_property_fields(
4019 pre_allocated_property_fields());
4020 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004021 Map::cast(result)->set_bit_field(bit_field());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004022 Map::cast(result)->set_bit_field2(bit_field2());
danno@chromium.org40cb8782011-05-25 07:58:50 +00004023 Map::cast(result)->set_bit_field3(bit_field3());
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004024 Map::cast(result)->set_is_shared(false);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004025 Map::cast(result)->ClearCodeCache(heap);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004026 return result;
4027}
4028
4029
lrn@chromium.org303ada72010-10-27 09:33:13 +00004030MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
4031 NormalizedMapSharingMode sharing) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004032 int new_instance_size = instance_size();
4033 if (mode == CLEAR_INOBJECT_PROPERTIES) {
4034 new_instance_size -= inobject_properties() * kPointerSize;
4035 }
4036
lrn@chromium.org303ada72010-10-27 09:33:13 +00004037 Object* result;
4038 { MaybeObject* maybe_result =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004039 GetHeap()->AllocateMap(instance_type(), new_instance_size);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004040 if (!maybe_result->ToObject(&result)) return maybe_result;
4041 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004042
4043 if (mode != CLEAR_INOBJECT_PROPERTIES) {
4044 Map::cast(result)->set_inobject_properties(inobject_properties());
4045 }
4046
4047 Map::cast(result)->set_prototype(prototype());
4048 Map::cast(result)->set_constructor(constructor());
4049
4050 Map::cast(result)->set_bit_field(bit_field());
4051 Map::cast(result)->set_bit_field2(bit_field2());
danno@chromium.org40cb8782011-05-25 07:58:50 +00004052 Map::cast(result)->set_bit_field3(bit_field3());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004053
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004054 Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
4055
ricow@chromium.org65fae842010-08-25 15:26:24 +00004056#ifdef DEBUG
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004057 if (Map::cast(result)->is_shared()) {
4058 Map::cast(result)->SharedMapVerify();
4059 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004060#endif
4061
4062 return result;
4063}
4064
4065
lrn@chromium.org303ada72010-10-27 09:33:13 +00004066MaybeObject* Map::CopyDropTransitions() {
4067 Object* new_map;
4068 { MaybeObject* maybe_new_map = CopyDropDescriptors();
4069 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
4070 }
4071 Object* descriptors;
4072 { MaybeObject* maybe_descriptors =
4073 instance_descriptors()->RemoveTransitions();
4074 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
4075 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004076 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004077 return new_map;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004078}
4079
4080
lrn@chromium.org303ada72010-10-27 09:33:13 +00004081MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004082 // Allocate the code cache if not present.
4083 if (code_cache()->IsFixedArray()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00004084 Object* result;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004085 { MaybeObject* maybe_result = code->heap()->AllocateCodeCache();
lrn@chromium.org303ada72010-10-27 09:33:13 +00004086 if (!maybe_result->ToObject(&result)) return maybe_result;
4087 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004088 set_code_cache(result);
4089 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004090
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004091 // Update the code cache.
4092 return CodeCache::cast(code_cache())->Update(name, code);
4093}
4094
4095
4096Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
4097 // Do a lookup if a code cache exists.
4098 if (!code_cache()->IsFixedArray()) {
4099 return CodeCache::cast(code_cache())->Lookup(name, flags);
4100 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004101 return GetHeap()->undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004102 }
4103}
4104
4105
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004106int Map::IndexInCodeCache(Object* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004107 // Get the internal index if a code cache exists.
4108 if (!code_cache()->IsFixedArray()) {
4109 return CodeCache::cast(code_cache())->GetIndex(name, code);
4110 }
4111 return -1;
4112}
4113
4114
4115void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
4116 // No GC is supposed to happen between a call to IndexInCodeCache and
4117 // RemoveFromCodeCache so the code cache must be there.
4118 ASSERT(!code_cache()->IsFixedArray());
4119 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
4120}
4121
4122
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004123void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004124 // Traverse the transition tree without using a stack. We do this by
4125 // reversing the pointers in the maps and descriptor arrays.
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004126 Map* current = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004127 Map* meta_map = heap()->meta_map();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004128 Object** map_or_index_field = NULL;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004129 while (current != meta_map) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004130 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
danno@chromium.org40cb8782011-05-25 07:58:50 +00004131 *RawField(current, Map::kInstanceDescriptorsOrBitField3Offset));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004132 if (!d->IsEmpty()) {
4133 FixedArray* contents = reinterpret_cast<FixedArray*>(
4134 d->get(DescriptorArray::kContentArrayIndex));
4135 map_or_index_field = RawField(contents, HeapObject::kMapOffset);
4136 Object* map_or_index = *map_or_index_field;
4137 bool map_done = true; // Controls a nested continue statement.
4138 for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0;
4139 i < contents->length();
4140 i += 2) {
4141 PropertyDetails details(Smi::cast(contents->get(i + 1)));
4142 if (details.IsTransition()) {
4143 // Found a map in the transition array. We record our progress in
4144 // the transition array by recording the current map in the map field
4145 // of the next map and recording the index in the transition array in
4146 // the map field of the array.
4147 Map* next = Map::cast(contents->get(i));
4148 next->set_map(current);
4149 *map_or_index_field = Smi::FromInt(i + 2);
4150 current = next;
4151 map_done = false;
4152 break;
4153 }
4154 }
4155 if (!map_done) continue;
whesse@chromium.org030d38e2011-07-13 13:23:34 +00004156 } else {
4157 map_or_index_field = NULL;
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004158 }
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004159 // That was the regular transitions, now for the prototype transitions.
4160 FixedArray* prototype_transitions =
4161 current->unchecked_prototype_transitions();
4162 Object** proto_map_or_index_field =
4163 RawField(prototype_transitions, HeapObject::kMapOffset);
4164 Object* map_or_index = *proto_map_or_index_field;
4165 const int start = kProtoTransitionHeaderSize + kProtoTransitionMapOffset;
4166 int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : start;
4167 if (i < prototype_transitions->length()) {
4168 // Found a map in the prototype transition array. Record progress in
4169 // an analogous way to the regular transitions array above.
4170 Object* perhaps_map = prototype_transitions->get(i);
4171 if (perhaps_map->IsMap()) {
4172 Map* next = Map::cast(perhaps_map);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004173 next->set_map(current);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004174 *proto_map_or_index_field =
4175 Smi::FromInt(i + kProtoTransitionElementsPerEntry);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004176 current = next;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004177 continue;
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004178 }
4179 }
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004180 *proto_map_or_index_field = heap()->fixed_array_map();
4181 if (map_or_index_field != NULL) {
4182 *map_or_index_field = heap()->fixed_array_map();
4183 }
4184
4185 // The callback expects a map to have a real map as its map, so we save
4186 // the map field, which is being used to track the traversal and put the
4187 // correct map (the meta_map) in place while we do the callback.
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004188 Map* prev = current->map();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004189 current->set_map(meta_map);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004190 callback(current, data);
4191 current = prev;
4192 }
4193}
4194
4195
lrn@chromium.org303ada72010-10-27 09:33:13 +00004196MaybeObject* CodeCache::Update(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004197 // The number of monomorphic stubs for normal load/store/call IC's can grow to
4198 // a large number and therefore they need to go into a hash table. They are
4199 // used to load global properties from cells.
4200 if (code->type() == NORMAL) {
4201 // Make sure that a hash table is allocated for the normal load code cache.
4202 if (normal_type_cache()->IsUndefined()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00004203 Object* result;
4204 { MaybeObject* maybe_result =
4205 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
4206 if (!maybe_result->ToObject(&result)) return maybe_result;
4207 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004208 set_normal_type_cache(result);
4209 }
4210 return UpdateNormalTypeCache(name, code);
4211 } else {
4212 ASSERT(default_cache()->IsFixedArray());
4213 return UpdateDefaultCache(name, code);
4214 }
4215}
4216
4217
lrn@chromium.org303ada72010-10-27 09:33:13 +00004218MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004219 // When updating the default code cache we disregard the type encoded in the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004220 // flags. This allows call constant stubs to overwrite call field
4221 // stubs, etc.
4222 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
4223
4224 // First check whether we can update existing code cache without
4225 // extending it.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004226 FixedArray* cache = default_cache();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004227 int length = cache->length();
ager@chromium.org236ad962008-09-25 09:45:57 +00004228 int deleted_index = -1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004229 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004230 Object* key = cache->get(i);
ager@chromium.org236ad962008-09-25 09:45:57 +00004231 if (key->IsNull()) {
4232 if (deleted_index < 0) deleted_index = i;
4233 continue;
4234 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004235 if (key->IsUndefined()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004236 if (deleted_index >= 0) i = deleted_index;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004237 cache->set(i + kCodeCacheEntryNameOffset, name);
4238 cache->set(i + kCodeCacheEntryCodeOffset, code);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004239 return this;
4240 }
4241 if (name->Equals(String::cast(key))) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004242 Code::Flags found =
4243 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004244 if (Code::RemoveTypeFromFlags(found) == flags) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004245 cache->set(i + kCodeCacheEntryCodeOffset, code);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004246 return this;
4247 }
4248 }
4249 }
4250
ager@chromium.org236ad962008-09-25 09:45:57 +00004251 // Reached the end of the code cache. If there were deleted
4252 // elements, reuse the space for the first of them.
4253 if (deleted_index >= 0) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004254 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
4255 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
ager@chromium.org236ad962008-09-25 09:45:57 +00004256 return this;
4257 }
4258
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004259 // Extend the code cache with some new entries (at least one). Must be a
4260 // multiple of the entry size.
4261 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
4262 new_length = new_length - new_length % kCodeCacheEntrySize;
4263 ASSERT((new_length % kCodeCacheEntrySize) == 0);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004264 Object* result;
4265 { MaybeObject* maybe_result = cache->CopySize(new_length);
4266 if (!maybe_result->ToObject(&result)) return maybe_result;
4267 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004268
4269 // Add the (name, code) pair to the new cache.
4270 cache = FixedArray::cast(result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004271 cache->set(length + kCodeCacheEntryNameOffset, name);
4272 cache->set(length + kCodeCacheEntryCodeOffset, code);
4273 set_default_cache(cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004274 return this;
4275}
4276
4277
lrn@chromium.org303ada72010-10-27 09:33:13 +00004278MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004279 // Adding a new entry can cause a new cache to be allocated.
4280 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
lrn@chromium.org303ada72010-10-27 09:33:13 +00004281 Object* new_cache;
4282 { MaybeObject* maybe_new_cache = cache->Put(name, code);
4283 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
4284 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004285 set_normal_type_cache(new_cache);
4286 return this;
4287}
4288
4289
4290Object* CodeCache::Lookup(String* name, Code::Flags flags) {
4291 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
4292 return LookupNormalTypeCache(name, flags);
4293 } else {
4294 return LookupDefaultCache(name, flags);
4295 }
4296}
4297
4298
4299Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
4300 FixedArray* cache = default_cache();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004301 int length = cache->length();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004302 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
4303 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
ager@chromium.org236ad962008-09-25 09:45:57 +00004304 // Skip deleted elements.
4305 if (key->IsNull()) continue;
4306 if (key->IsUndefined()) return key;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004307 if (name->Equals(String::cast(key))) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004308 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
4309 if (code->flags() == flags) {
4310 return code;
4311 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004312 }
4313 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004314 return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004315}
4316
4317
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004318Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
4319 if (!normal_type_cache()->IsUndefined()) {
4320 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
4321 return cache->Lookup(name, flags);
4322 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004323 return GetHeap()->undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004324 }
4325}
4326
4327
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004328int CodeCache::GetIndex(Object* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004329 if (code->type() == NORMAL) {
4330 if (normal_type_cache()->IsUndefined()) return -1;
4331 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004332 return cache->GetIndex(String::cast(name), code->flags());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004333 }
4334
4335 FixedArray* array = default_cache();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004336 int len = array->length();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004337 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
4338 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004339 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004340 return -1;
4341}
4342
4343
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004344void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004345 if (code->type() == NORMAL) {
4346 ASSERT(!normal_type_cache()->IsUndefined());
4347 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004348 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004349 cache->RemoveByIndex(index);
4350 } else {
4351 FixedArray* array = default_cache();
4352 ASSERT(array->length() >= index && array->get(index)->IsCode());
4353 // Use null instead of undefined for deleted elements to distinguish
4354 // deleted elements from unused elements. This distinction is used
4355 // when looking up in the cache and when updating the cache.
4356 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
4357 array->set_null(index - 1); // Name.
4358 array->set_null(index); // Code.
4359 }
4360}
4361
4362
4363// The key in the code cache hash table consists of the property name and the
4364// code object. The actual match is on the name and the code flags. If a key
4365// is created using the flags and not a code object it can only be used for
4366// lookup not to create a new entry.
4367class CodeCacheHashTableKey : public HashTableKey {
4368 public:
4369 CodeCacheHashTableKey(String* name, Code::Flags flags)
4370 : name_(name), flags_(flags), code_(NULL) { }
4371
4372 CodeCacheHashTableKey(String* name, Code* code)
4373 : name_(name),
4374 flags_(code->flags()),
4375 code_(code) { }
4376
4377
4378 bool IsMatch(Object* other) {
4379 if (!other->IsFixedArray()) return false;
4380 FixedArray* pair = FixedArray::cast(other);
4381 String* name = String::cast(pair->get(0));
4382 Code::Flags flags = Code::cast(pair->get(1))->flags();
4383 if (flags != flags_) {
4384 return false;
4385 }
4386 return name_->Equals(name);
4387 }
4388
4389 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
4390 return name->Hash() ^ flags;
4391 }
4392
4393 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
4394
4395 uint32_t HashForObject(Object* obj) {
4396 FixedArray* pair = FixedArray::cast(obj);
4397 String* name = String::cast(pair->get(0));
4398 Code* code = Code::cast(pair->get(1));
4399 return NameFlagsHashHelper(name, code->flags());
4400 }
4401
lrn@chromium.org303ada72010-10-27 09:33:13 +00004402 MUST_USE_RESULT MaybeObject* AsObject() {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004403 ASSERT(code_ != NULL);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004404 Object* obj;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004405 { MaybeObject* maybe_obj = code_->heap()->AllocateFixedArray(2);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004406 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4407 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004408 FixedArray* pair = FixedArray::cast(obj);
4409 pair->set(0, name_);
4410 pair->set(1, code_);
4411 return pair;
4412 }
4413
4414 private:
4415 String* name_;
4416 Code::Flags flags_;
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004417 // TODO(jkummerow): We should be able to get by without this.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004418 Code* code_;
4419};
4420
4421
4422Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
4423 CodeCacheHashTableKey key(name, flags);
4424 int entry = FindEntry(&key);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004425 if (entry == kNotFound) return GetHeap()->undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004426 return get(EntryToIndex(entry) + 1);
4427}
4428
4429
lrn@chromium.org303ada72010-10-27 09:33:13 +00004430MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004431 CodeCacheHashTableKey key(name, code);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004432 Object* obj;
4433 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
4434 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4435 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004436
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004437 // Don't use |this|, as the table might have grown.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004438 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
4439
4440 int entry = cache->FindInsertionEntry(key.Hash());
lrn@chromium.org303ada72010-10-27 09:33:13 +00004441 Object* k;
4442 { MaybeObject* maybe_k = key.AsObject();
4443 if (!maybe_k->ToObject(&k)) return maybe_k;
4444 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004445
4446 cache->set(EntryToIndex(entry), k);
4447 cache->set(EntryToIndex(entry) + 1, code);
4448 cache->ElementAdded();
4449 return cache;
4450}
4451
4452
4453int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
4454 CodeCacheHashTableKey key(name, flags);
4455 int entry = FindEntry(&key);
4456 return (entry == kNotFound) ? -1 : entry;
4457}
4458
4459
4460void CodeCacheHashTable::RemoveByIndex(int index) {
4461 ASSERT(index >= 0);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004462 Heap* heap = GetHeap();
4463 set(EntryToIndex(index), heap->null_value());
4464 set(EntryToIndex(index) + 1, heap->null_value());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004465 ElementRemoved();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004466}
4467
4468
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004469static bool HasKey(FixedArray* array, Object* key) {
4470 int len0 = array->length();
4471 for (int i = 0; i < len0; i++) {
4472 Object* element = array->get(i);
4473 if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
4474 if (element->IsString() &&
4475 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
4476 return true;
4477 }
4478 }
4479 return false;
4480}
4481
4482
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004483MaybeObject* PolymorphicCodeCache::Update(MapList* maps,
4484 Code::Flags flags,
4485 Code* code) {
4486 // Initialize cache if necessary.
4487 if (cache()->IsUndefined()) {
4488 Object* result;
4489 { MaybeObject* maybe_result =
4490 PolymorphicCodeCacheHashTable::Allocate(
4491 PolymorphicCodeCacheHashTable::kInitialSize);
4492 if (!maybe_result->ToObject(&result)) return maybe_result;
4493 }
4494 set_cache(result);
4495 } else {
4496 // This entry shouldn't be contained in the cache yet.
4497 ASSERT(PolymorphicCodeCacheHashTable::cast(cache())
4498 ->Lookup(maps, flags)->IsUndefined());
4499 }
4500 PolymorphicCodeCacheHashTable* hash_table =
4501 PolymorphicCodeCacheHashTable::cast(cache());
4502 Object* new_cache;
4503 { MaybeObject* maybe_new_cache = hash_table->Put(maps, flags, code);
4504 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
4505 }
4506 set_cache(new_cache);
4507 return this;
4508}
4509
4510
4511Object* PolymorphicCodeCache::Lookup(MapList* maps, Code::Flags flags) {
4512 if (!cache()->IsUndefined()) {
4513 PolymorphicCodeCacheHashTable* hash_table =
4514 PolymorphicCodeCacheHashTable::cast(cache());
4515 return hash_table->Lookup(maps, flags);
4516 } else {
4517 return GetHeap()->undefined_value();
4518 }
4519}
4520
4521
4522// Despite their name, object of this class are not stored in the actual
4523// hash table; instead they're temporarily used for lookups. It is therefore
4524// safe to have a weak (non-owning) pointer to a MapList as a member field.
4525class PolymorphicCodeCacheHashTableKey : public HashTableKey {
4526 public:
4527 // Callers must ensure that |maps| outlives the newly constructed object.
4528 PolymorphicCodeCacheHashTableKey(MapList* maps, int code_flags)
4529 : maps_(maps),
4530 code_flags_(code_flags) {}
4531
4532 bool IsMatch(Object* other) {
4533 MapList other_maps(kDefaultListAllocationSize);
4534 int other_flags;
4535 FromObject(other, &other_flags, &other_maps);
4536 if (code_flags_ != other_flags) return false;
4537 if (maps_->length() != other_maps.length()) return false;
4538 // Compare just the hashes first because it's faster.
4539 int this_hash = MapsHashHelper(maps_, code_flags_);
4540 int other_hash = MapsHashHelper(&other_maps, other_flags);
4541 if (this_hash != other_hash) return false;
4542
4543 // Full comparison: for each map in maps_, look for an equivalent map in
4544 // other_maps. This implementation is slow, but probably good enough for
4545 // now because the lists are short (<= 4 elements currently).
4546 for (int i = 0; i < maps_->length(); ++i) {
4547 bool match_found = false;
4548 for (int j = 0; j < other_maps.length(); ++j) {
4549 if (maps_->at(i)->EquivalentTo(other_maps.at(j))) {
4550 match_found = true;
4551 break;
4552 }
4553 }
4554 if (!match_found) return false;
4555 }
4556 return true;
4557 }
4558
4559 static uint32_t MapsHashHelper(MapList* maps, int code_flags) {
4560 uint32_t hash = code_flags;
4561 for (int i = 0; i < maps->length(); ++i) {
4562 hash ^= maps->at(i)->Hash();
4563 }
4564 return hash;
4565 }
4566
4567 uint32_t Hash() {
4568 return MapsHashHelper(maps_, code_flags_);
4569 }
4570
4571 uint32_t HashForObject(Object* obj) {
4572 MapList other_maps(kDefaultListAllocationSize);
4573 int other_flags;
4574 FromObject(obj, &other_flags, &other_maps);
4575 return MapsHashHelper(&other_maps, other_flags);
4576 }
4577
4578 MUST_USE_RESULT MaybeObject* AsObject() {
4579 Object* obj;
4580 // The maps in |maps_| must be copied to a newly allocated FixedArray,
4581 // both because the referenced MapList is short-lived, and because C++
4582 // objects can't be stored in the heap anyway.
4583 { MaybeObject* maybe_obj =
4584 HEAP->AllocateUninitializedFixedArray(maps_->length() + 1);
4585 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4586 }
4587 FixedArray* list = FixedArray::cast(obj);
4588 list->set(0, Smi::FromInt(code_flags_));
4589 for (int i = 0; i < maps_->length(); ++i) {
4590 list->set(i + 1, maps_->at(i));
4591 }
4592 return list;
4593 }
4594
4595 private:
4596 static MapList* FromObject(Object* obj, int* code_flags, MapList* maps) {
4597 FixedArray* list = FixedArray::cast(obj);
4598 maps->Rewind(0);
4599 *code_flags = Smi::cast(list->get(0))->value();
4600 for (int i = 1; i < list->length(); ++i) {
4601 maps->Add(Map::cast(list->get(i)));
4602 }
4603 return maps;
4604 }
4605
4606 MapList* maps_; // weak.
4607 int code_flags_;
whesse@chromium.org7b260152011-06-20 15:33:18 +00004608 static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1;
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004609};
4610
4611
4612Object* PolymorphicCodeCacheHashTable::Lookup(MapList* maps, int code_flags) {
4613 PolymorphicCodeCacheHashTableKey key(maps, code_flags);
4614 int entry = FindEntry(&key);
4615 if (entry == kNotFound) return GetHeap()->undefined_value();
4616 return get(EntryToIndex(entry) + 1);
4617}
4618
4619
4620MaybeObject* PolymorphicCodeCacheHashTable::Put(MapList* maps,
4621 int code_flags,
4622 Code* code) {
4623 PolymorphicCodeCacheHashTableKey key(maps, code_flags);
4624 Object* obj;
4625 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
4626 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4627 }
4628 PolymorphicCodeCacheHashTable* cache =
4629 reinterpret_cast<PolymorphicCodeCacheHashTable*>(obj);
4630 int entry = cache->FindInsertionEntry(key.Hash());
4631 { MaybeObject* maybe_obj = key.AsObject();
4632 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4633 }
4634 cache->set(EntryToIndex(entry), obj);
4635 cache->set(EntryToIndex(entry) + 1, code);
4636 cache->ElementAdded();
4637 return cache;
4638}
4639
4640
lrn@chromium.org303ada72010-10-27 09:33:13 +00004641MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004642 ElementsAccessor* accessor = array->GetElementsAccessor();
4643 MaybeObject* maybe_result =
4644 accessor->AddElementsToFixedArray(array->elements(), this);
4645 FixedArray* result;
4646 if (!maybe_result->To<FixedArray>(&result)) return maybe_result;
4647#ifdef DEBUG
4648 if (FLAG_enable_slow_asserts) {
4649 for (int i = 0; i < result->length(); i++) {
4650 Object* current = result->get(i);
4651 ASSERT(current->IsNumber() || current->IsString());
4652 }
4653 }
4654#endif
4655 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004656}
4657
4658
lrn@chromium.org303ada72010-10-27 09:33:13 +00004659MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004660 int len0 = length();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +00004661#ifdef DEBUG
4662 if (FLAG_enable_slow_asserts) {
4663 for (int i = 0; i < len0; i++) {
4664 ASSERT(get(i)->IsString() || get(i)->IsNumber());
4665 }
4666 }
4667#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004668 int len1 = other->length();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +00004669 // Optimize if 'other' is empty.
4670 // We cannot optimize if 'this' is empty, as other may have holes
4671 // or non keys.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004672 if (len1 == 0) return this;
4673
4674 // Compute how many elements are not in this.
4675 int extra = 0;
4676 for (int y = 0; y < len1; y++) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004677 Object* value = other->get(y);
4678 if (!value->IsTheHole() && !HasKey(this, value)) extra++;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004679 }
4680
ager@chromium.org5ec48922009-05-05 07:25:34 +00004681 if (extra == 0) return this;
4682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004683 // Allocate the result
lrn@chromium.org303ada72010-10-27 09:33:13 +00004684 Object* obj;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004685 { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(len0 + extra);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004686 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4687 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004688 // Fill in the content
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004689 AssertNoAllocation no_gc;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004690 FixedArray* result = FixedArray::cast(obj);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004691 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004692 for (int i = 0; i < len0; i++) {
ager@chromium.orgb61a0d12010-10-13 08:35:23 +00004693 Object* e = get(i);
4694 ASSERT(e->IsString() || e->IsNumber());
4695 result->set(i, e, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004696 }
4697 // Fill in the extra keys.
4698 int index = 0;
4699 for (int y = 0; y < len1; y++) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004700 Object* value = other->get(y);
4701 if (!value->IsTheHole() && !HasKey(this, value)) {
ager@chromium.orgb61a0d12010-10-13 08:35:23 +00004702 Object* e = other->get(y);
4703 ASSERT(e->IsString() || e->IsNumber());
4704 result->set(len0 + index, e, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004705 index++;
4706 }
4707 }
4708 ASSERT(extra == index);
4709 return result;
4710}
4711
4712
lrn@chromium.org303ada72010-10-27 09:33:13 +00004713MaybeObject* FixedArray::CopySize(int new_length) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004714 Heap* heap = GetHeap();
4715 if (new_length == 0) return heap->empty_fixed_array();
lrn@chromium.org303ada72010-10-27 09:33:13 +00004716 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004717 { MaybeObject* maybe_obj = heap->AllocateFixedArray(new_length);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004718 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4719 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004720 FixedArray* result = FixedArray::cast(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004721 // Copy the content
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004722 AssertNoAllocation no_gc;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004723 int len = length();
4724 if (new_length < len) len = new_length;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004725 result->set_map(map());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004726 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004727 for (int i = 0; i < len; i++) {
4728 result->set(i, get(i), mode);
4729 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004730 return result;
4731}
4732
4733
4734void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004735 AssertNoAllocation no_gc;
4736 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004737 for (int index = 0; index < len; index++) {
4738 dest->set(dest_pos+index, get(pos+index), mode);
4739 }
4740}
4741
4742
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004743#ifdef DEBUG
4744bool FixedArray::IsEqualTo(FixedArray* other) {
4745 if (length() != other->length()) return false;
4746 for (int i = 0 ; i < length(); ++i) {
4747 if (get(i) != other->get(i)) return false;
4748 }
4749 return true;
4750}
4751#endif
4752
4753
lrn@chromium.org303ada72010-10-27 09:33:13 +00004754MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004755 Heap* heap = Isolate::Current()->heap();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004756 if (number_of_descriptors == 0) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004757 return heap->empty_descriptor_array();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004758 }
4759 // Allocate the array of keys.
lrn@chromium.org303ada72010-10-27 09:33:13 +00004760 Object* array;
4761 { MaybeObject* maybe_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004762 heap->AllocateFixedArray(ToKeyIndex(number_of_descriptors));
lrn@chromium.org303ada72010-10-27 09:33:13 +00004763 if (!maybe_array->ToObject(&array)) return maybe_array;
4764 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004765 // Do not use DescriptorArray::cast on incomplete object.
4766 FixedArray* result = FixedArray::cast(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004767
4768 // Allocate the content array and set it in the descriptor array.
lrn@chromium.org303ada72010-10-27 09:33:13 +00004769 { MaybeObject* maybe_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004770 heap->AllocateFixedArray(number_of_descriptors << 1);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004771 if (!maybe_array->ToObject(&array)) return maybe_array;
4772 }
danno@chromium.org40cb8782011-05-25 07:58:50 +00004773 result->set(kBitField3StorageIndex, Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004774 result->set(kContentArrayIndex, array);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004775 result->set(kEnumerationIndexIndex,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004776 Smi::FromInt(PropertyDetails::kInitialIndex));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777 return result;
4778}
4779
4780
4781void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
4782 FixedArray* new_cache) {
4783 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
4784 if (HasEnumCache()) {
4785 FixedArray::cast(get(kEnumerationIndexIndex))->
4786 set(kEnumCacheBridgeCacheIndex, new_cache);
4787 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004788 if (IsEmpty()) return; // Do nothing for empty descriptor array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004789 FixedArray::cast(bridge_storage)->
4790 set(kEnumCacheBridgeCacheIndex, new_cache);
4791 fast_set(FixedArray::cast(bridge_storage),
4792 kEnumCacheBridgeEnumIndex,
4793 get(kEnumerationIndexIndex));
4794 set(kEnumerationIndexIndex, bridge_storage);
4795 }
4796}
4797
4798
lrn@chromium.org303ada72010-10-27 09:33:13 +00004799MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
4800 TransitionFlag transition_flag) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004801 // Transitions are only kept when inserting another transition.
4802 // This precondition is not required by this function's implementation, but
4803 // is currently required by the semantics of maps, so we check it.
4804 // Conversely, we filter after replacing, so replacing a transition and
4805 // removing all other transitions is not supported.
4806 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
4807 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
4808 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004809
4810 // Ensure the key is a symbol.
lrn@chromium.org303ada72010-10-27 09:33:13 +00004811 Object* result;
4812 { MaybeObject* maybe_result = descriptor->KeyToSymbol();
4813 if (!maybe_result->ToObject(&result)) return maybe_result;
4814 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004815
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004816 int transitions = 0;
4817 int null_descriptors = 0;
4818 if (remove_transitions) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004819 for (int i = 0; i < number_of_descriptors(); i++) {
4820 if (IsTransition(i)) transitions++;
4821 if (IsNullDescriptor(i)) null_descriptors++;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004822 }
4823 } else {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004824 for (int i = 0; i < number_of_descriptors(); i++) {
4825 if (IsNullDescriptor(i)) null_descriptors++;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004826 }
4827 }
4828 int new_size = number_of_descriptors() - transitions - null_descriptors;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004830 // If key is in descriptor, we replace it in-place when filtering.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004831 // Count a null descriptor for key as inserted, not replaced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004832 int index = Search(descriptor->GetKey());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004833 const bool inserting = (index == kNotFound);
4834 const bool replacing = !inserting;
4835 bool keep_enumeration_index = false;
4836 if (inserting) {
4837 ++new_size;
4838 }
4839 if (replacing) {
4840 // We are replacing an existing descriptor. We keep the enumeration
4841 // index of a visible property.
4842 PropertyType t = PropertyDetails(GetDetails(index)).type();
4843 if (t == CONSTANT_FUNCTION ||
4844 t == FIELD ||
4845 t == CALLBACKS ||
4846 t == INTERCEPTOR) {
4847 keep_enumeration_index = true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004848 } else if (remove_transitions) {
4849 // Replaced descriptor has been counted as removed if it is
4850 // a transition that will be replaced. Adjust count in this case.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004851 ++new_size;
4852 }
4853 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00004854 { MaybeObject* maybe_result = Allocate(new_size);
4855 if (!maybe_result->ToObject(&result)) return maybe_result;
4856 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004857 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004858 // Set the enumeration index in the descriptors and set the enumeration index
4859 // in the result.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004860 int enumeration_index = NextEnumerationIndex();
4861 if (!descriptor->GetDetails().IsTransition()) {
4862 if (keep_enumeration_index) {
4863 descriptor->SetEnumerationIndex(
4864 PropertyDetails(GetDetails(index)).index());
4865 } else {
4866 descriptor->SetEnumerationIndex(enumeration_index);
4867 ++enumeration_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004868 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004869 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004870 new_descriptors->SetNextEnumerationIndex(enumeration_index);
4871
4872 // Copy the descriptors, filtering out transitions and null descriptors,
4873 // and inserting or replacing a descriptor.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004874 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004875 int from_index = 0;
4876 int to_index = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004877
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004878 for (; from_index < number_of_descriptors(); from_index++) {
4879 String* key = GetKey(from_index);
4880 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
4881 break;
4882 }
4883 if (IsNullDescriptor(from_index)) continue;
4884 if (remove_transitions && IsTransition(from_index)) continue;
4885 new_descriptors->CopyFrom(to_index++, this, from_index);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004886 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004887
4888 new_descriptors->Set(to_index++, descriptor);
4889 if (replacing) from_index++;
4890
4891 for (; from_index < number_of_descriptors(); from_index++) {
4892 if (IsNullDescriptor(from_index)) continue;
4893 if (remove_transitions && IsTransition(from_index)) continue;
4894 new_descriptors->CopyFrom(to_index++, this, from_index);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004895 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004896
4897 ASSERT(to_index == new_descriptors->number_of_descriptors());
4898 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004900 return new_descriptors;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004901}
4902
4903
lrn@chromium.org303ada72010-10-27 09:33:13 +00004904MaybeObject* DescriptorArray::RemoveTransitions() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004905 // Remove all transitions and null descriptors. Return a copy of the array
4906 // with all transitions removed, or a Failure object if the new array could
4907 // not be allocated.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004908
4909 // Compute the size of the map transition entries to be removed.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004910 int num_removed = 0;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004911 for (int i = 0; i < number_of_descriptors(); i++) {
4912 if (!IsProperty(i)) num_removed++;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004913 }
4914
4915 // Allocate the new descriptor array.
lrn@chromium.org303ada72010-10-27 09:33:13 +00004916 Object* result;
4917 { MaybeObject* maybe_result = Allocate(number_of_descriptors() - num_removed);
4918 if (!maybe_result->ToObject(&result)) return maybe_result;
4919 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004920 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
4921
4922 // Copy the content.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004923 int next_descriptor = 0;
4924 for (int i = 0; i < number_of_descriptors(); i++) {
4925 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004926 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004927 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004928
4929 return new_descriptors;
4930}
4931
4932
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +00004933void DescriptorArray::SortUnchecked() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004934 // In-place heap sort.
4935 int len = number_of_descriptors();
4936
4937 // Bottom-up max-heap construction.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004938 // Index of the last node with children
4939 const int max_parent_index = (len / 2) - 1;
4940 for (int i = max_parent_index; i >= 0; --i) {
4941 int parent_index = i;
4942 const uint32_t parent_hash = GetKey(i)->Hash();
4943 while (parent_index <= max_parent_index) {
4944 int child_index = 2 * parent_index + 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004945 uint32_t child_hash = GetKey(child_index)->Hash();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004946 if (child_index + 1 < len) {
4947 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4948 if (right_child_hash > child_hash) {
4949 child_index++;
4950 child_hash = right_child_hash;
4951 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004952 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004953 if (child_hash <= parent_hash) break;
4954 Swap(parent_index, child_index);
4955 // Now element at child_index could be < its children.
4956 parent_index = child_index; // parent_hash remains correct.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004957 }
4958 }
4959
4960 // Extract elements and create sorted array.
4961 for (int i = len - 1; i > 0; --i) {
4962 // Put max element at the back of the array.
4963 Swap(0, i);
4964 // Sift down the new top element.
4965 int parent_index = 0;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004966 const uint32_t parent_hash = GetKey(parent_index)->Hash();
4967 const int max_parent_index = (i / 2) - 1;
4968 while (parent_index <= max_parent_index) {
4969 int child_index = parent_index * 2 + 1;
4970 uint32_t child_hash = GetKey(child_index)->Hash();
4971 if (child_index + 1 < i) {
4972 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4973 if (right_child_hash > child_hash) {
4974 child_index++;
4975 child_hash = right_child_hash;
4976 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004977 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004978 if (child_hash <= parent_hash) break;
4979 Swap(parent_index, child_index);
4980 parent_index = child_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004981 }
4982 }
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +00004983}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004984
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +00004985
4986void DescriptorArray::Sort() {
4987 SortUnchecked();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004988 SLOW_ASSERT(IsSortedNoDuplicates());
4989}
4990
4991
4992int DescriptorArray::BinarySearch(String* name, int low, int high) {
4993 uint32_t hash = name->Hash();
4994
4995 while (low <= high) {
4996 int mid = (low + high) / 2;
4997 String* mid_name = GetKey(mid);
4998 uint32_t mid_hash = mid_name->Hash();
4999
5000 if (mid_hash > hash) {
5001 high = mid - 1;
5002 continue;
5003 }
5004 if (mid_hash < hash) {
5005 low = mid + 1;
5006 continue;
5007 }
5008 // Found an element with the same hash-code.
5009 ASSERT(hash == mid_hash);
5010 // There might be more, so we find the first one and
5011 // check them all to see if we have a match.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005012 if (name == mid_name && !is_null_descriptor(mid)) return mid;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005013 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
5014 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005015 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005016 }
5017 break;
5018 }
5019 return kNotFound;
5020}
5021
5022
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005023int DescriptorArray::LinearSearch(String* name, int len) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00005024 uint32_t hash = name->Hash();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005025 for (int number = 0; number < len; number++) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00005026 String* entry = GetKey(number);
5027 if ((entry->Hash() == hash) &&
5028 name->Equals(entry) &&
5029 !is_null_descriptor(number)) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005030 return number;
5031 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005032 }
5033 return kNotFound;
5034}
5035
5036
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005037MaybeObject* DeoptimizationInputData::Allocate(int deopt_entry_count,
5038 PretenureFlag pretenure) {
5039 ASSERT(deopt_entry_count > 0);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005040 return HEAP->AllocateFixedArray(LengthFor(deopt_entry_count),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005041 pretenure);
5042}
5043
5044
5045MaybeObject* DeoptimizationOutputData::Allocate(int number_of_deopt_points,
5046 PretenureFlag pretenure) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005047 if (number_of_deopt_points == 0) return HEAP->empty_fixed_array();
5048 return HEAP->AllocateFixedArray(LengthOfFixedArray(number_of_deopt_points),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005049 pretenure);
5050}
5051
5052
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005053#ifdef DEBUG
5054bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
5055 if (IsEmpty()) return other->IsEmpty();
5056 if (other->IsEmpty()) return false;
5057 if (length() != other->length()) return false;
5058 for (int i = 0; i < length(); ++i) {
5059 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
5060 }
5061 return GetContentArray()->IsEqualTo(other->GetContentArray());
5062}
5063#endif
5064
5065
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005066bool String::LooksValid() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005067 if (!Isolate::Current()->heap()->Contains(this)) return false;
ager@chromium.org870a0b62008-11-04 11:43:05 +00005068 return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005069}
5070
5071
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005072int String::Utf8Length() {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005073 if (IsAsciiRepresentation()) return length();
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005074 // Attempt to flatten before accessing the string. It probably
5075 // doesn't make Utf8Length faster, but it is very likely that
5076 // the string will be accessed later (for example by WriteUtf8)
5077 // so it's still a good idea.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005078 Heap* heap = GetHeap();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005079 TryFlatten();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005080 Access<StringInputBuffer> buffer(
5081 heap->isolate()->objects_string_input_buffer());
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005082 buffer->Reset(0, this);
5083 int result = 0;
5084 while (buffer->has_more())
5085 result += unibrow::Utf8::Length(buffer->GetNext());
5086 return result;
5087}
5088
5089
ager@chromium.org7c537e22008-10-16 08:43:32 +00005090Vector<const char> String::ToAsciiVector() {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005091 ASSERT(IsAsciiRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005092 ASSERT(IsFlat());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005093
5094 int offset = 0;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005095 int length = this->length();
5096 StringRepresentationTag string_tag = StringShape(this).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005097 String* string = this;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005098 if (string_tag == kConsStringTag) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005099 ConsString* cons = ConsString::cast(string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005100 ASSERT(cons->second()->length() == 0);
ager@chromium.org870a0b62008-11-04 11:43:05 +00005101 string = cons->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005102 string_tag = StringShape(string).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005103 }
5104 if (string_tag == kSeqStringTag) {
5105 SeqAsciiString* seq = SeqAsciiString::cast(string);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005106 char* start = seq->GetChars();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005107 return Vector<const char>(start + offset, length);
5108 }
5109 ASSERT(string_tag == kExternalStringTag);
5110 ExternalAsciiString* ext = ExternalAsciiString::cast(string);
5111 const char* start = ext->resource()->data();
5112 return Vector<const char>(start + offset, length);
5113}
5114
5115
5116Vector<const uc16> String::ToUC16Vector() {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005117 ASSERT(IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005118 ASSERT(IsFlat());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005119
5120 int offset = 0;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005121 int length = this->length();
5122 StringRepresentationTag string_tag = StringShape(this).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005123 String* string = this;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005124 if (string_tag == kConsStringTag) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005125 ConsString* cons = ConsString::cast(string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005126 ASSERT(cons->second()->length() == 0);
ager@chromium.org870a0b62008-11-04 11:43:05 +00005127 string = cons->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005128 string_tag = StringShape(string).representation_tag();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005129 }
5130 if (string_tag == kSeqStringTag) {
5131 SeqTwoByteString* seq = SeqTwoByteString::cast(string);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005132 return Vector<const uc16>(seq->GetChars() + offset, length);
ager@chromium.org7c537e22008-10-16 08:43:32 +00005133 }
5134 ASSERT(string_tag == kExternalStringTag);
5135 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
5136 const uc16* start =
5137 reinterpret_cast<const uc16*>(ext->resource()->data());
5138 return Vector<const uc16>(start + offset, length);
5139}
5140
5141
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005142SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
5143 RobustnessFlag robust_flag,
5144 int offset,
5145 int length,
5146 int* length_return) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005147 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
5148 return SmartPointer<char>(NULL);
5149 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005150 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005151
5152 // Negative length means the to the end of the string.
5153 if (length < 0) length = kMaxInt - offset;
5154
5155 // Compute the size of the UTF-8 string. Start at the specified offset.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005156 Access<StringInputBuffer> buffer(
5157 heap->isolate()->objects_string_input_buffer());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005158 buffer->Reset(offset, this);
5159 int character_position = offset;
5160 int utf8_bytes = 0;
5161 while (buffer->has_more()) {
5162 uint16_t character = buffer->GetNext();
5163 if (character_position < offset + length) {
5164 utf8_bytes += unibrow::Utf8::Length(character);
5165 }
5166 character_position++;
5167 }
5168
5169 if (length_return) {
5170 *length_return = utf8_bytes;
5171 }
5172
5173 char* result = NewArray<char>(utf8_bytes + 1);
5174
5175 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
5176 buffer->Rewind();
5177 buffer->Seek(offset);
5178 character_position = offset;
5179 int utf8_byte_position = 0;
5180 while (buffer->has_more()) {
5181 uint16_t character = buffer->GetNext();
5182 if (character_position < offset + length) {
5183 if (allow_nulls == DISALLOW_NULLS && character == 0) {
5184 character = ' ';
5185 }
5186 utf8_byte_position +=
5187 unibrow::Utf8::Encode(result + utf8_byte_position, character);
5188 }
5189 character_position++;
5190 }
5191 result[utf8_byte_position] = 0;
5192 return SmartPointer<char>(result);
5193}
5194
5195
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00005196SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
5197 RobustnessFlag robust_flag,
5198 int* length_return) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005199 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
5200}
5201
5202
5203const uc16* String::GetTwoByteData() {
5204 return GetTwoByteData(0);
5205}
5206
5207
5208const uc16* String::GetTwoByteData(unsigned start) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005209 ASSERT(!IsAsciiRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005210 switch (StringShape(this).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005211 case kSeqStringTag:
ager@chromium.org7c537e22008-10-16 08:43:32 +00005212 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005213 case kExternalStringTag:
5214 return ExternalTwoByteString::cast(this)->
5215 ExternalTwoByteStringGetData(start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005216 case kConsStringTag:
5217 UNREACHABLE();
5218 return NULL;
5219 }
5220 UNREACHABLE();
5221 return NULL;
5222}
5223
5224
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005225SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005226 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005227 return SmartPointer<uc16>();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005228 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005229 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005230
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005231 Access<StringInputBuffer> buffer(
5232 heap->isolate()->objects_string_input_buffer());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005233 buffer->Reset(this);
5234
5235 uc16* result = NewArray<uc16>(length() + 1);
5236
5237 int i = 0;
5238 while (buffer->has_more()) {
5239 uint16_t character = buffer->GetNext();
5240 result[i++] = character;
5241 }
5242 result[i] = 0;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005243 return SmartPointer<uc16>(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005244}
5245
5246
ager@chromium.org7c537e22008-10-16 08:43:32 +00005247const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005248 return reinterpret_cast<uc16*>(
5249 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
5250}
5251
5252
ager@chromium.org7c537e22008-10-16 08:43:32 +00005253void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00005254 unsigned* offset_ptr,
5255 unsigned max_chars) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005256 unsigned chars_read = 0;
5257 unsigned offset = *offset_ptr;
5258 while (chars_read < max_chars) {
5259 uint16_t c = *reinterpret_cast<uint16_t*>(
5260 reinterpret_cast<char*>(this) -
5261 kHeapObjectTag + kHeaderSize + offset * kShortSize);
5262 if (c <= kMaxAsciiCharCode) {
5263 // Fast case for ASCII characters. Cursor is an input output argument.
5264 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
5265 rbb->util_buffer,
5266 rbb->capacity,
5267 rbb->cursor)) {
5268 break;
5269 }
5270 } else {
5271 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
5272 rbb->util_buffer,
5273 rbb->capacity,
5274 rbb->cursor)) {
5275 break;
5276 }
5277 }
5278 offset++;
5279 chars_read++;
5280 }
5281 *offset_ptr = offset;
5282 rbb->remaining += chars_read;
5283}
5284
5285
ager@chromium.org7c537e22008-10-16 08:43:32 +00005286const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
5287 unsigned* remaining,
5288 unsigned* offset_ptr,
5289 unsigned max_chars) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005290 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
5291 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
5292 *remaining = max_chars;
5293 *offset_ptr += max_chars;
5294 return b;
5295}
5296
5297
5298// This will iterate unless the block of string data spans two 'halves' of
5299// a ConsString, in which case it will recurse. Since the block of string
5300// data to be read has a maximum size this limits the maximum recursion
5301// depth to something sane. Since C++ does not have tail call recursion
5302// elimination, the iteration must be explicit. Since this is not an
5303// -IntoBuffer method it can delegate to one of the efficient
5304// *AsciiStringReadBlock routines.
5305const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
5306 unsigned* offset_ptr,
5307 unsigned max_chars) {
5308 ConsString* current = this;
5309 unsigned offset = *offset_ptr;
5310 int offset_correction = 0;
5311
5312 while (true) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00005313 String* left = current->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005314 unsigned left_length = (unsigned)left->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005315 if (left_length > offset &&
5316 (max_chars <= left_length - offset ||
5317 (rbb->capacity <= left_length - offset &&
5318 (max_chars = left_length - offset, true)))) { // comma operator!
5319 // Left hand side only - iterate unless we have reached the bottom of
5320 // the cons tree. The assignment on the left of the comma operator is
5321 // in order to make use of the fact that the -IntoBuffer routines can
5322 // produce at most 'capacity' characters. This enables us to postpone
5323 // the point where we switch to the -IntoBuffer routines (below) in order
5324 // to maximize the chances of delegating a big chunk of work to the
5325 // efficient *AsciiStringReadBlock routines.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005326 if (StringShape(left).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005327 current = ConsString::cast(left);
5328 continue;
5329 } else {
5330 const unibrow::byte* answer =
5331 String::ReadBlock(left, rbb, &offset, max_chars);
5332 *offset_ptr = offset + offset_correction;
5333 return answer;
5334 }
5335 } else if (left_length <= offset) {
5336 // Right hand side only - iterate unless we have reached the bottom of
5337 // the cons tree.
ager@chromium.org870a0b62008-11-04 11:43:05 +00005338 String* right = current->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005339 offset -= left_length;
5340 offset_correction += left_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00005341 if (StringShape(right).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005342 current = ConsString::cast(right);
5343 continue;
5344 } else {
5345 const unibrow::byte* answer =
5346 String::ReadBlock(right, rbb, &offset, max_chars);
5347 *offset_ptr = offset + offset_correction;
5348 return answer;
5349 }
5350 } else {
5351 // The block to be read spans two sides of the ConsString, so we call the
5352 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
5353 // are able to assemble data from several part strings because they use
5354 // the util_buffer to store their data and never return direct pointers
5355 // to their storage. We don't try to read more than the buffer capacity
5356 // here or we can get too much recursion.
5357 ASSERT(rbb->remaining == 0);
5358 ASSERT(rbb->cursor == 0);
5359 current->ConsStringReadBlockIntoBuffer(
5360 rbb,
5361 &offset,
5362 max_chars > rbb->capacity ? rbb->capacity : max_chars);
5363 *offset_ptr = offset + offset_correction;
5364 return rbb->util_buffer;
5365 }
5366 }
5367}
5368
5369
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005370uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
5371 ASSERT(index >= 0 && index < length());
5372 return resource()->data()[index];
5373}
5374
5375
5376const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
5377 unsigned* remaining,
5378 unsigned* offset_ptr,
5379 unsigned max_chars) {
5380 // Cast const char* to unibrow::byte* (signedness difference).
5381 const unibrow::byte* b =
5382 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
5383 *remaining = max_chars;
5384 *offset_ptr += max_chars;
5385 return b;
5386}
5387
5388
5389const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
5390 unsigned start) {
5391 return resource()->data() + start;
5392}
5393
5394
5395uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
5396 ASSERT(index >= 0 && index < length());
5397 return resource()->data()[index];
5398}
5399
5400
5401void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
5402 ReadBlockBuffer* rbb,
5403 unsigned* offset_ptr,
5404 unsigned max_chars) {
5405 unsigned chars_read = 0;
5406 unsigned offset = *offset_ptr;
5407 const uint16_t* data = resource()->data();
5408 while (chars_read < max_chars) {
5409 uint16_t c = data[offset];
5410 if (c <= kMaxAsciiCharCode) {
ager@chromium.org80787b72009-04-17 10:24:24 +00005411 // Fast case for ASCII characters. Cursor is an input output argument.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005412 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
5413 rbb->util_buffer,
5414 rbb->capacity,
5415 rbb->cursor))
5416 break;
5417 } else {
5418 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
5419 rbb->util_buffer,
5420 rbb->capacity,
5421 rbb->cursor))
5422 break;
5423 }
5424 offset++;
5425 chars_read++;
5426 }
5427 *offset_ptr = offset;
5428 rbb->remaining += chars_read;
5429}
5430
5431
ager@chromium.org7c537e22008-10-16 08:43:32 +00005432void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005433 unsigned* offset_ptr,
5434 unsigned max_chars) {
5435 unsigned capacity = rbb->capacity - rbb->cursor;
5436 if (max_chars > capacity) max_chars = capacity;
5437 memcpy(rbb->util_buffer + rbb->cursor,
5438 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
5439 *offset_ptr * kCharSize,
5440 max_chars);
5441 rbb->remaining += max_chars;
5442 *offset_ptr += max_chars;
5443 rbb->cursor += max_chars;
5444}
5445
5446
5447void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
5448 ReadBlockBuffer* rbb,
5449 unsigned* offset_ptr,
5450 unsigned max_chars) {
5451 unsigned capacity = rbb->capacity - rbb->cursor;
5452 if (max_chars > capacity) max_chars = capacity;
5453 memcpy(rbb->util_buffer + rbb->cursor,
5454 resource()->data() + *offset_ptr,
5455 max_chars);
5456 rbb->remaining += max_chars;
5457 *offset_ptr += max_chars;
5458 rbb->cursor += max_chars;
5459}
5460
5461
5462// This method determines the type of string involved and then copies
5463// a whole chunk of characters into a buffer, or returns a pointer to a buffer
5464// where they can be found. The pointer is not necessarily valid across a GC
5465// (see AsciiStringReadBlock).
5466const unibrow::byte* String::ReadBlock(String* input,
5467 ReadBlockBuffer* rbb,
5468 unsigned* offset_ptr,
5469 unsigned max_chars) {
5470 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
5471 if (max_chars == 0) {
5472 rbb->remaining = 0;
5473 return NULL;
5474 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005475 switch (StringShape(input).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005476 case kSeqStringTag:
ager@chromium.org5ec48922009-05-05 07:25:34 +00005477 if (input->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005478 SeqAsciiString* str = SeqAsciiString::cast(input);
5479 return str->SeqAsciiStringReadBlock(&rbb->remaining,
5480 offset_ptr,
5481 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005482 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005483 SeqTwoByteString* str = SeqTwoByteString::cast(input);
5484 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
5485 offset_ptr,
5486 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005487 return rbb->util_buffer;
5488 }
5489 case kConsStringTag:
5490 return ConsString::cast(input)->ConsStringReadBlock(rbb,
5491 offset_ptr,
5492 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005493 case kExternalStringTag:
ager@chromium.org5ec48922009-05-05 07:25:34 +00005494 if (input->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
5496 &rbb->remaining,
5497 offset_ptr,
5498 max_chars);
5499 } else {
5500 ExternalTwoByteString::cast(input)->
5501 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
5502 offset_ptr,
5503 max_chars);
5504 return rbb->util_buffer;
5505 }
5506 default:
5507 break;
5508 }
5509
5510 UNREACHABLE();
5511 return 0;
5512}
5513
5514
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005515void Relocatable::PostGarbageCollectionProcessing() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005516 Isolate* isolate = Isolate::Current();
5517 Relocatable* current = isolate->relocatable_top();
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005518 while (current != NULL) {
5519 current->PostGarbageCollection();
5520 current = current->prev_;
5521 }
5522}
5523
5524
5525// Reserve space for statics needing saving and restoring.
5526int Relocatable::ArchiveSpacePerThread() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005527 return sizeof(Isolate::Current()->relocatable_top());
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005528}
5529
5530
5531// Archive statics that are thread local.
lrn@chromium.org1c092762011-05-09 09:42:16 +00005532char* Relocatable::ArchiveState(Isolate* isolate, char* to) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005533 *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top();
5534 isolate->set_relocatable_top(NULL);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005535 return to + ArchiveSpacePerThread();
5536}
5537
5538
5539// Restore statics that are thread local.
lrn@chromium.org1c092762011-05-09 09:42:16 +00005540char* Relocatable::RestoreState(Isolate* isolate, char* from) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005541 isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005542 return from + ArchiveSpacePerThread();
5543}
5544
5545
5546char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
5547 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
5548 Iterate(v, top);
5549 return thread_storage + ArchiveSpacePerThread();
5550}
5551
5552
5553void Relocatable::Iterate(ObjectVisitor* v) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005554 Isolate* isolate = Isolate::Current();
5555 Iterate(v, isolate->relocatable_top());
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005556}
5557
5558
5559void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
5560 Relocatable* current = top;
5561 while (current != NULL) {
5562 current->IterateInstance(v);
5563 current = current->prev_;
5564 }
5565}
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005566
5567
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005568FlatStringReader::FlatStringReader(Isolate* isolate, Handle<String> str)
5569 : Relocatable(isolate),
5570 str_(str.location()),
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005571 length_(str->length()) {
5572 PostGarbageCollection();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005573}
5574
5575
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005576FlatStringReader::FlatStringReader(Isolate* isolate, Vector<const char> input)
5577 : Relocatable(isolate),
5578 str_(0),
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005579 is_ascii_(true),
5580 length_(input.length()),
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005581 start_(input.start()) { }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005582
5583
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005584void FlatStringReader::PostGarbageCollection() {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005585 if (str_ == NULL) return;
5586 Handle<String> str(str_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005587 ASSERT(str->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00005588 is_ascii_ = str->IsAsciiRepresentation();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005589 if (is_ascii_) {
5590 start_ = str->ToAsciiVector().start();
5591 } else {
5592 start_ = str->ToUC16Vector().start();
5593 }
5594}
5595
5596
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005597void StringInputBuffer::Seek(unsigned pos) {
5598 Reset(pos, input_);
5599}
5600
5601
5602void SafeStringInputBuffer::Seek(unsigned pos) {
5603 Reset(pos, input_);
5604}
5605
5606
5607// This method determines the type of string involved and then copies
5608// a whole chunk of characters into a buffer. It can be used with strings
5609// that have been glued together to form a ConsString and which must cooperate
5610// to fill up a buffer.
5611void String::ReadBlockIntoBuffer(String* input,
5612 ReadBlockBuffer* rbb,
5613 unsigned* offset_ptr,
5614 unsigned max_chars) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005615 ASSERT(*offset_ptr <= (unsigned)input->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005616 if (max_chars == 0) return;
5617
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005618 switch (StringShape(input).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005619 case kSeqStringTag:
ager@chromium.org5ec48922009-05-05 07:25:34 +00005620 if (input->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005621 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005622 offset_ptr,
5623 max_chars);
5624 return;
5625 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005626 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627 offset_ptr,
5628 max_chars);
5629 return;
5630 }
5631 case kConsStringTag:
5632 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
5633 offset_ptr,
5634 max_chars);
5635 return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005636 case kExternalStringTag:
ager@chromium.org5ec48922009-05-05 07:25:34 +00005637 if (input->IsAsciiRepresentation()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005638 ExternalAsciiString::cast(input)->
5639 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
5640 } else {
5641 ExternalTwoByteString::cast(input)->
5642 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
5643 offset_ptr,
5644 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005645 }
5646 return;
5647 default:
5648 break;
5649 }
5650
5651 UNREACHABLE();
5652 return;
5653}
5654
5655
5656const unibrow::byte* String::ReadBlock(String* input,
5657 unibrow::byte* util_buffer,
5658 unsigned capacity,
5659 unsigned* remaining,
5660 unsigned* offset_ptr) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005661 ASSERT(*offset_ptr <= (unsigned)input->length());
5662 unsigned chars = input->length() - *offset_ptr;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005663 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
5664 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005665 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005666 *remaining = rbb.remaining;
5667 return answer;
5668}
5669
5670
5671const unibrow::byte* String::ReadBlock(String** raw_input,
5672 unibrow::byte* util_buffer,
5673 unsigned capacity,
5674 unsigned* remaining,
5675 unsigned* offset_ptr) {
5676 Handle<String> input(raw_input);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005677 ASSERT(*offset_ptr <= (unsigned)input->length());
5678 unsigned chars = input->length() - *offset_ptr;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005679 if (chars > capacity) chars = capacity;
5680 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
5681 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005682 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005683 *remaining = rbb.remaining;
5684 return rbb.util_buffer;
5685}
5686
5687
5688// This will iterate unless the block of string data spans two 'halves' of
5689// a ConsString, in which case it will recurse. Since the block of string
5690// data to be read has a maximum size this limits the maximum recursion
5691// depth to something sane. Since C++ does not have tail call recursion
5692// elimination, the iteration must be explicit.
5693void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
5694 unsigned* offset_ptr,
5695 unsigned max_chars) {
5696 ConsString* current = this;
5697 unsigned offset = *offset_ptr;
5698 int offset_correction = 0;
5699
5700 while (true) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00005701 String* left = current->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005702 unsigned left_length = (unsigned)left->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005703 if (left_length > offset &&
5704 max_chars <= left_length - offset) {
5705 // Left hand side only - iterate unless we have reached the bottom of
5706 // the cons tree.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005707 if (StringShape(left).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005708 current = ConsString::cast(left);
5709 continue;
5710 } else {
5711 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
5712 *offset_ptr = offset + offset_correction;
5713 return;
5714 }
5715 } else if (left_length <= offset) {
5716 // Right hand side only - iterate unless we have reached the bottom of
5717 // the cons tree.
5718 offset -= left_length;
5719 offset_correction += left_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00005720 String* right = current->second();
5721 if (StringShape(right).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005722 current = ConsString::cast(right);
5723 continue;
5724 } else {
5725 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
5726 *offset_ptr = offset + offset_correction;
5727 return;
5728 }
5729 } else {
5730 // The block to be read spans two sides of the ConsString, so we recurse.
5731 // First recurse on the left.
5732 max_chars -= left_length - offset;
5733 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
5734 // We may have reached the max or there may not have been enough space
5735 // in the buffer for the characters in the left hand side.
5736 if (offset == left_length) {
5737 // Recurse on the right.
5738 String* right = String::cast(current->second());
5739 offset -= left_length;
5740 offset_correction += left_length;
5741 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
5742 }
5743 *offset_ptr = offset + offset_correction;
5744 return;
5745 }
5746 }
5747}
5748
5749
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005750uint16_t ConsString::ConsStringGet(int index) {
5751 ASSERT(index >= 0 && index < this->length());
5752
5753 // Check for a flattened cons string
ager@chromium.org870a0b62008-11-04 11:43:05 +00005754 if (second()->length() == 0) {
5755 String* left = first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005756 return left->Get(index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005757 }
5758
5759 String* string = String::cast(this);
5760
5761 while (true) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005762 if (StringShape(string).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005763 ConsString* cons_string = ConsString::cast(string);
ager@chromium.org870a0b62008-11-04 11:43:05 +00005764 String* left = cons_string->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005765 if (left->length() > index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005766 string = left;
5767 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005768 index -= left->length();
ager@chromium.org870a0b62008-11-04 11:43:05 +00005769 string = cons_string->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005770 }
5771 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005772 return string->Get(index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005773 }
5774 }
5775
5776 UNREACHABLE();
5777 return 0;
5778}
5779
5780
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005781template <typename sinkchar>
5782void String::WriteToFlat(String* src,
5783 sinkchar* sink,
5784 int f,
5785 int t) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005786 String* source = src;
5787 int from = f;
5788 int to = t;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005789 while (true) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005790 ASSERT(0 <= from && from <= to && to <= source->length());
5791 switch (StringShape(source).full_representation_tag()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005792 case kAsciiStringTag | kExternalStringTag: {
5793 CopyChars(sink,
5794 ExternalAsciiString::cast(source)->resource()->data() + from,
5795 to - from);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005796 return;
5797 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005798 case kTwoByteStringTag | kExternalStringTag: {
5799 const uc16* data =
5800 ExternalTwoByteString::cast(source)->resource()->data();
5801 CopyChars(sink,
5802 data + from,
5803 to - from);
5804 return;
5805 }
5806 case kAsciiStringTag | kSeqStringTag: {
5807 CopyChars(sink,
5808 SeqAsciiString::cast(source)->GetChars() + from,
5809 to - from);
5810 return;
5811 }
5812 case kTwoByteStringTag | kSeqStringTag: {
5813 CopyChars(sink,
5814 SeqTwoByteString::cast(source)->GetChars() + from,
5815 to - from);
5816 return;
5817 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005818 case kAsciiStringTag | kConsStringTag:
5819 case kTwoByteStringTag | kConsStringTag: {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005820 ConsString* cons_string = ConsString::cast(source);
ager@chromium.org870a0b62008-11-04 11:43:05 +00005821 String* first = cons_string->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005822 int boundary = first->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005823 if (to - boundary >= boundary - from) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005824 // Right hand side is longer. Recurse over left.
5825 if (from < boundary) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005826 WriteToFlat(first, sink, from, boundary);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005827 sink += boundary - from;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005828 from = 0;
5829 } else {
5830 from -= boundary;
5831 }
5832 to -= boundary;
ager@chromium.org870a0b62008-11-04 11:43:05 +00005833 source = cons_string->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005834 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005835 // Left hand side is longer. Recurse over right.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005836 if (to > boundary) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00005837 String* second = cons_string->second();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005838 WriteToFlat(second,
5839 sink + boundary - from,
5840 0,
5841 to - boundary);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005842 to = boundary;
5843 }
5844 source = first;
5845 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005846 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005847 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005848 }
5849 }
5850}
5851
5852
ager@chromium.org7c537e22008-10-16 08:43:32 +00005853template <typename IteratorA, typename IteratorB>
5854static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
5855 // General slow case check. We know that the ia and ib iterators
5856 // have the same length.
5857 while (ia->has_more()) {
5858 uc32 ca = ia->GetNext();
5859 uc32 cb = ib->GetNext();
5860 if (ca != cb)
5861 return false;
5862 }
5863 return true;
5864}
5865
5866
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005867// Compares the contents of two strings by reading and comparing
5868// int-sized blocks of characters.
5869template <typename Char>
5870static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
5871 int length = a.length();
5872 ASSERT_EQ(length, b.length());
5873 const Char* pa = a.start();
5874 const Char* pb = b.start();
5875 int i = 0;
ager@chromium.org9085a012009-05-11 19:22:57 +00005876#ifndef V8_HOST_CAN_READ_UNALIGNED
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005877 // If this architecture isn't comfortable reading unaligned ints
5878 // then we have to check that the strings are aligned before
5879 // comparing them blockwise.
5880 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
5881 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
5882 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005883 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005884#endif
5885 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
5886 int endpoint = length - kStepSize;
5887 // Compare blocks until we reach near the end of the string.
5888 for (; i <= endpoint; i += kStepSize) {
5889 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
5890 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
5891 if (wa != wb) {
5892 return false;
5893 }
5894 }
ager@chromium.org9085a012009-05-11 19:22:57 +00005895#ifndef V8_HOST_CAN_READ_UNALIGNED
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005896 }
5897#endif
5898 // Compare the remaining characters that didn't fit into a block.
5899 for (; i < length; i++) {
5900 if (a[i] != b[i]) {
5901 return false;
5902 }
5903 }
5904 return true;
5905}
5906
5907
ager@chromium.org7c537e22008-10-16 08:43:32 +00005908template <typename IteratorA>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005909static inline bool CompareStringContentsPartial(Isolate* isolate,
5910 IteratorA* ia,
5911 String* b) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005912 if (b->IsFlat()) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005913 if (b->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005914 VectorIterator<char> ib(b->ToAsciiVector());
5915 return CompareStringContents(ia, &ib);
5916 } else {
ager@chromium.org9085a012009-05-11 19:22:57 +00005917 VectorIterator<uc16> ib(b->ToUC16Vector());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005918 return CompareStringContents(ia, &ib);
5919 }
5920 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005921 isolate->objects_string_compare_buffer_b()->Reset(0, b);
5922 return CompareStringContents(ia,
5923 isolate->objects_string_compare_buffer_b());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005924 }
5925}
5926
5927
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005928bool String::SlowEquals(String* other) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005929 // Fast check: negative check with lengths.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005930 int len = length();
5931 if (len != other->length()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005932 if (len == 0) return true;
5933
5934 // Fast check: if hash code is computed for both strings
5935 // a fast negative check can be performed.
5936 if (HasHashCode() && other->HasHashCode()) {
5937 if (Hash() != other->Hash()) return false;
5938 }
5939
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005940 // We know the strings are both non-empty. Compare the first chars
5941 // before we try to flatten the strings.
5942 if (this->Get(0) != other->Get(0)) return false;
5943
5944 String* lhs = this->TryFlattenGetString();
5945 String* rhs = other->TryFlattenGetString();
5946
5947 if (StringShape(lhs).IsSequentialAscii() &&
5948 StringShape(rhs).IsSequentialAscii()) {
5949 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
5950 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005951 return CompareRawStringContents(Vector<const char>(str1, len),
5952 Vector<const char>(str2, len));
5953 }
5954
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005955 Isolate* isolate = GetIsolate();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005956 if (lhs->IsFlat()) {
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00005957 if (lhs->IsAsciiRepresentation()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005958 Vector<const char> vec1 = lhs->ToAsciiVector();
5959 if (rhs->IsFlat()) {
5960 if (rhs->IsAsciiRepresentation()) {
5961 Vector<const char> vec2 = rhs->ToAsciiVector();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005962 return CompareRawStringContents(vec1, vec2);
5963 } else {
5964 VectorIterator<char> buf1(vec1);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005965 VectorIterator<uc16> ib(rhs->ToUC16Vector());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005966 return CompareStringContents(&buf1, &ib);
5967 }
5968 } else {
5969 VectorIterator<char> buf1(vec1);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005970 isolate->objects_string_compare_buffer_b()->Reset(0, rhs);
5971 return CompareStringContents(&buf1,
5972 isolate->objects_string_compare_buffer_b());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005973 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00005974 } else {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005975 Vector<const uc16> vec1 = lhs->ToUC16Vector();
5976 if (rhs->IsFlat()) {
5977 if (rhs->IsAsciiRepresentation()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005978 VectorIterator<uc16> buf1(vec1);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005979 VectorIterator<char> ib(rhs->ToAsciiVector());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005980 return CompareStringContents(&buf1, &ib);
5981 } else {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005982 Vector<const uc16> vec2(rhs->ToUC16Vector());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005983 return CompareRawStringContents(vec1, vec2);
5984 }
5985 } else {
5986 VectorIterator<uc16> buf1(vec1);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005987 isolate->objects_string_compare_buffer_b()->Reset(0, rhs);
5988 return CompareStringContents(&buf1,
5989 isolate->objects_string_compare_buffer_b());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005990 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005991 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00005992 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005993 isolate->objects_string_compare_buffer_a()->Reset(0, lhs);
5994 return CompareStringContentsPartial(isolate,
5995 isolate->objects_string_compare_buffer_a(), rhs);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005996 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005997}
5998
5999
6000bool String::MarkAsUndetectable() {
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00006001 if (StringShape(this).IsSymbol()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006002
6003 Map* map = this->map();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006004 Heap* heap = map->heap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006005 if (map == heap->string_map()) {
6006 this->set_map(heap->undetectable_string_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006007 return true;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006008 } else if (map == heap->ascii_string_map()) {
6009 this->set_map(heap->undetectable_ascii_string_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006010 return true;
6011 }
6012 // Rest cannot be marked as undetectable
6013 return false;
6014}
6015
6016
6017bool String::IsEqualTo(Vector<const char> str) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006018 Isolate* isolate = GetIsolate();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006019 int slen = length();
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +00006020 Access<UnicodeCache::Utf8Decoder>
6021 decoder(isolate->unicode_cache()->utf8_decoder());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006022 decoder->Reset(str.start(), str.length());
6023 int i;
6024 for (i = 0; i < slen && decoder->has_more(); i++) {
6025 uc32 r = decoder->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006026 if (Get(i) != r) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006027 }
6028 return i == slen && !decoder->has_more();
6029}
6030
6031
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00006032bool String::IsAsciiEqualTo(Vector<const char> str) {
6033 int slen = length();
6034 if (str.length() != slen) return false;
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00006035 if (IsFlat() && IsAsciiRepresentation()) {
6036 return CompareChars(ToAsciiVector().start(), str.start(), slen) == 0;
6037 }
6038 for (int i = 0; i < slen; i++) {
6039 if (Get(i) != static_cast<uint16_t>(str[i])) return false;
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00006040 }
6041 return true;
6042}
6043
6044
6045bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
6046 int slen = length();
6047 if (str.length() != slen) return false;
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00006048 if (IsFlat() && IsTwoByteRepresentation()) {
6049 return CompareChars(ToUC16Vector().start(), str.start(), slen) == 0;
6050 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00006051 for (int i = 0; i < slen; i++) {
6052 if (Get(i) != str[i]) return false;
6053 }
6054 return true;
6055}
6056
6057
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006058uint32_t String::ComputeAndSetHash() {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006059 // Should only be called if hash code has not yet been computed.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006060 ASSERT(!HasHashCode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006061
ricow@chromium.orgaa1b6162010-03-29 07:44:58 +00006062 const int len = length();
6063
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006064 // Compute the hash code.
ricow@chromium.orgaa1b6162010-03-29 07:44:58 +00006065 uint32_t field = 0;
6066 if (StringShape(this).IsSequentialAscii()) {
6067 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
6068 } else if (StringShape(this).IsSequentialTwoByte()) {
6069 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
6070 } else {
6071 StringInputBuffer buffer(this);
6072 field = ComputeHashField(&buffer, len);
6073 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006074
6075 // Store the hash code in the object.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006076 set_hash_field(field);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006077
6078 // Check the hash code is there.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006079 ASSERT(HasHashCode());
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006080 uint32_t result = field >> kHashShift;
6081 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
6082 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006083}
6084
6085
6086bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
6087 uint32_t* index,
6088 int length) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006089 if (length == 0 || length > kMaxArrayIndexSize) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006090 uc32 ch = buffer->GetNext();
6091
6092 // If the string begins with a '0' character, it must only consist
6093 // of it to be a legal array index.
6094 if (ch == '0') {
6095 *index = 0;
6096 return length == 1;
6097 }
6098
6099 // Convert string to uint32 array index; character by character.
6100 int d = ch - '0';
6101 if (d < 0 || d > 9) return false;
6102 uint32_t result = d;
6103 while (buffer->has_more()) {
6104 d = buffer->GetNext() - '0';
6105 if (d < 0 || d > 9) return false;
6106 // Check that the new result is below the 32 bit limit.
6107 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
6108 result = (result * 10) + d;
6109 }
6110
6111 *index = result;
6112 return true;
6113}
6114
6115
6116bool String::SlowAsArrayIndex(uint32_t* index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006117 if (length() <= kMaxCachedArrayIndexLength) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006118 Hash(); // force computation of hash code
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006119 uint32_t field = hash_field();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006120 if ((field & kIsNotArrayIndexMask) != 0) return false;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006121 // Isolate the array index form the full hash field.
6122 *index = (kArrayIndexHashMask & field) >> kHashShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006123 return true;
6124 } else {
6125 StringInputBuffer buffer(this);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006126 return ComputeArrayIndex(&buffer, index, length());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006127 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006128}
6129
6130
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00006131uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006132 // For array indexes mix the length into the hash as an array index could
6133 // be zero.
6134 ASSERT(length > 0);
6135 ASSERT(length <= String::kMaxArrayIndexSize);
6136 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
6137 (1 << String::kArrayIndexValueBits));
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00006138
6139 value <<= String::kHashShift;
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006140 value |= length << String::kArrayIndexHashLengthShift;
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00006141
6142 ASSERT((value & String::kIsNotArrayIndexMask) == 0);
6143 ASSERT((length > String::kMaxCachedArrayIndexLength) ||
6144 (value & String::kContainsCachedArrayIndexMask) == 0);
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006145 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006146}
6147
6148
ager@chromium.org7c537e22008-10-16 08:43:32 +00006149uint32_t StringHasher::GetHashField() {
6150 ASSERT(is_valid());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006151 if (length_ <= String::kMaxHashCalcLength) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006152 if (is_array_index()) {
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00006153 return MakeArrayIndexHash(array_index(), length_);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006154 }
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006155 return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask;
ager@chromium.org7c537e22008-10-16 08:43:32 +00006156 } else {
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006157 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006158 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006159}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006160
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006161
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006162uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
6163 int length) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006164 StringHasher hasher(length);
6165
6166 // Very long strings have a trivial hash that doesn't inspect the
6167 // string contents.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006168 if (hasher.has_trivial_hash()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006169 return hasher.GetHashField();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006170 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006171
6172 // Do the iterative array index computation as long as there is a
6173 // chance this is an array index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006174 while (buffer->has_more() && hasher.is_array_index()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006175 hasher.AddCharacter(buffer->GetNext());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006176 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006177
6178 // Process the remaining characters without updating the array
6179 // index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006180 while (buffer->has_more()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006181 hasher.AddCharacterNoIndex(buffer->GetNext());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006182 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006183
6184 return hasher.GetHashField();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006185}
6186
6187
lrn@chromium.org303ada72010-10-27 09:33:13 +00006188MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006189 Heap* heap = GetHeap();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006190 if (start == 0 && end == length()) return this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006191 MaybeObject* result = heap->AllocateSubString(this, start, end, pretenure);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006192 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006193}
6194
6195
6196void String::PrintOn(FILE* file) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006197 int length = this->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006198 for (int i = 0; i < length; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006199 fprintf(file, "%c", Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006200 }
6201}
6202
6203
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006204void Map::CreateBackPointers() {
6205 DescriptorArray* descriptors = instance_descriptors();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006206 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00006207 if (descriptors->GetType(i) == MAP_TRANSITION ||
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006208 descriptors->GetType(i) == EXTERNAL_ARRAY_TRANSITION ||
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00006209 descriptors->GetType(i) == CONSTANT_TRANSITION) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006210 // Get target.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006211 Map* target = Map::cast(descriptors->GetValue(i));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006212#ifdef DEBUG
6213 // Verify target.
6214 Object* source_prototype = prototype();
6215 Object* target_prototype = target->prototype();
6216 ASSERT(source_prototype->IsJSObject() ||
6217 source_prototype->IsMap() ||
6218 source_prototype->IsNull());
6219 ASSERT(target_prototype->IsJSObject() ||
6220 target_prototype->IsNull());
6221 ASSERT(source_prototype->IsMap() ||
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006222 source_prototype == target_prototype);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006223#endif
6224 // Point target back to source. set_prototype() will not let us set
6225 // the prototype to a map, as we do here.
6226 *RawField(target, kPrototypeOffset) = this;
6227 }
6228 }
6229}
6230
6231
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006232void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006233 // Live DescriptorArray objects will be marked, so we must use
6234 // low-level accessors to get and modify their data.
6235 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
danno@chromium.org40cb8782011-05-25 07:58:50 +00006236 *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset));
6237 if (d->IsEmpty()) return;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006238 Smi* NullDescriptorDetails =
6239 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
6240 FixedArray* contents = reinterpret_cast<FixedArray*>(
6241 d->get(DescriptorArray::kContentArrayIndex));
6242 ASSERT(contents->length() >= 2);
6243 for (int i = 0; i < contents->length(); i += 2) {
6244 // If the pair (value, details) is a map transition,
6245 // check if the target is live. If not, null the descriptor.
6246 // Also drop the back pointer for that map transition, so that this
6247 // map is not reached again by following a back pointer from a
6248 // non-live object.
6249 PropertyDetails details(Smi::cast(contents->get(i + 1)));
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00006250 if (details.type() == MAP_TRANSITION ||
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006251 details.type() == EXTERNAL_ARRAY_TRANSITION ||
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00006252 details.type() == CONSTANT_TRANSITION) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006253 Map* target = reinterpret_cast<Map*>(contents->get(i));
6254 ASSERT(target->IsHeapObject());
6255 if (!target->IsMarked()) {
6256 ASSERT(target->IsMap());
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00006257 contents->set_unchecked(i + 1, NullDescriptorDetails);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006258 contents->set_null_unchecked(heap, i);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006259 ASSERT(target->prototype() == this ||
6260 target->prototype() == real_prototype);
6261 // Getter prototype() is read-only, set_prototype() has side effects.
6262 *RawField(target, Map::kPrototypeOffset) = real_prototype;
6263 }
6264 }
6265 }
6266}
6267
6268
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006269int Map::Hash() {
6270 // For performance reasons we only hash the 3 most variable fields of a map:
6271 // constructor, prototype and bit_field2.
6272
6273 // Shift away the tag.
6274 int hash = (static_cast<uint32_t>(
6275 reinterpret_cast<uintptr_t>(constructor())) >> 2);
6276
6277 // XOR-ing the prototype and constructor directly yields too many zero bits
6278 // when the two pointers are close (which is fairly common).
6279 // To avoid this we shift the prototype 4 bits relatively to the constructor.
6280 hash ^= (static_cast<uint32_t>(
6281 reinterpret_cast<uintptr_t>(prototype())) << 2);
6282
6283 return hash ^ (hash >> 16) ^ bit_field2();
6284}
6285
6286
6287bool Map::EquivalentToForNormalization(Map* other,
6288 PropertyNormalizationMode mode) {
6289 return
6290 constructor() == other->constructor() &&
6291 prototype() == other->prototype() &&
6292 inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ?
6293 0 :
6294 other->inobject_properties()) &&
6295 instance_type() == other->instance_type() &&
6296 bit_field() == other->bit_field() &&
6297 bit_field2() == other->bit_field2() &&
6298 (bit_field3() & ~(1<<Map::kIsShared)) ==
6299 (other->bit_field3() & ~(1<<Map::kIsShared));
6300}
6301
6302
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00006303void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
6304 // Iterate over all fields in the body but take care in dealing with
6305 // the code entry.
6306 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
6307 v->VisitCodeEntry(this->address() + kCodeEntryOffset);
6308 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
6309}
6310
6311
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006312void JSFunction::MarkForLazyRecompilation() {
6313 ASSERT(is_compiled() && !IsOptimized());
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006314 ASSERT(shared()->allows_lazy_compilation() ||
6315 code()->optimizable());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006316 Builtins* builtins = GetIsolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006317 ReplaceCode(builtins->builtin(Builtins::kLazyRecompile));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006318}
6319
6320
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006321bool JSFunction::IsInlineable() {
6322 if (IsBuiltin()) return false;
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006323 SharedFunctionInfo* shared_info = shared();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006324 // Check that the function has a script associated with it.
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006325 if (!shared_info->script()->IsScript()) return false;
6326 if (shared_info->optimization_disabled()) return false;
6327 Code* code = shared_info->code();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006328 if (code->kind() == Code::OPTIMIZED_FUNCTION) return true;
6329 // If we never ran this (unlikely) then lets try to optimize it.
6330 if (code->kind() != Code::FUNCTION) return true;
6331 return code->optimizable();
6332}
6333
6334
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006335Object* JSFunction::SetInstancePrototype(Object* value) {
6336 ASSERT(value->IsJSObject());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006337 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006338 if (has_initial_map()) {
6339 initial_map()->set_prototype(value);
6340 } else {
6341 // Put the value in the initial map field until an initial map is
6342 // needed. At that point, a new initial map is created and the
6343 // prototype is put into the initial map where it belongs.
6344 set_prototype_or_initial_map(value);
6345 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006346 heap->ClearInstanceofCache();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006347 return value;
6348}
6349
6350
lrn@chromium.org303ada72010-10-27 09:33:13 +00006351MaybeObject* JSFunction::SetPrototype(Object* value) {
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006352 ASSERT(should_have_prototype());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006353 Object* construct_prototype = value;
6354
6355 // If the value is not a JSObject, store the value in the map's
6356 // constructor field so it can be accessed. Also, set the prototype
6357 // used for constructing objects to the original object prototype.
6358 // See ECMA-262 13.2.2.
6359 if (!value->IsJSObject()) {
6360 // Copy the map so this does not affect unrelated functions.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006361 // Remove map transitions because they point to maps with a
6362 // different prototype.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006363 Object* new_object;
lrn@chromium.org303ada72010-10-27 09:33:13 +00006364 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006365 if (!maybe_new_map->ToObject(&new_object)) return maybe_new_map;
lrn@chromium.org303ada72010-10-27 09:33:13 +00006366 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006367 Map* new_map = Map::cast(new_object);
6368 Heap* heap = new_map->heap();
6369 set_map(new_map);
6370 new_map->set_constructor(value);
6371 new_map->set_non_instance_prototype(true);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006372 construct_prototype =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006373 heap->isolate()->context()->global_context()->
6374 initial_object_prototype();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006375 } else {
6376 map()->set_non_instance_prototype(false);
6377 }
6378
6379 return SetInstancePrototype(construct_prototype);
6380}
6381
6382
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006383Object* JSFunction::RemovePrototype() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006384 Context* global_context = context()->global_context();
6385 Map* no_prototype_map = shared()->strict_mode()
6386 ? global_context->strict_mode_function_without_prototype_map()
6387 : global_context->function_without_prototype_map();
6388
6389 if (map() == no_prototype_map) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006390 // Be idempotent.
6391 return this;
6392 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006393
6394 ASSERT(!shared()->strict_mode() ||
6395 map() == global_context->strict_mode_function_map());
6396 ASSERT(shared()->strict_mode() || map() == global_context->function_map());
6397
6398 set_map(no_prototype_map);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006399 set_prototype_or_initial_map(no_prototype_map->heap()->the_hole_value());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006400 return this;
6401}
6402
6403
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006404Object* JSFunction::SetInstanceClassName(String* name) {
6405 shared()->set_instance_class_name(name);
6406 return this;
6407}
6408
6409
whesse@chromium.org023421e2010-12-21 12:19:12 +00006410void JSFunction::PrintName(FILE* out) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006411 SmartPointer<char> name = shared()->DebugName()->ToCString();
whesse@chromium.org023421e2010-12-21 12:19:12 +00006412 PrintF(out, "%s", *name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006413}
6414
6415
ager@chromium.org236ad962008-09-25 09:45:57 +00006416Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
6417 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
6418}
6419
6420
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006421MaybeObject* Oddball::Initialize(const char* to_string,
6422 Object* to_number,
6423 byte kind) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00006424 Object* symbol;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006425 { MaybeObject* maybe_symbol =
6426 Isolate::Current()->heap()->LookupAsciiSymbol(to_string);
lrn@chromium.org303ada72010-10-27 09:33:13 +00006427 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
6428 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006429 set_to_string(String::cast(symbol));
6430 set_to_number(to_number);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006431 set_kind(kind);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006432 return this;
6433}
6434
6435
ager@chromium.orgb61a0d12010-10-13 08:35:23 +00006436String* SharedFunctionInfo::DebugName() {
6437 Object* n = name();
6438 if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
6439 return String::cast(n);
6440}
6441
6442
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006443bool SharedFunctionInfo::HasSourceCode() {
6444 return !script()->IsUndefined() &&
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00006445 !reinterpret_cast<Script*>(script())->source()->IsUndefined();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006446}
6447
6448
6449Object* SharedFunctionInfo::GetSourceCode() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006450 Isolate* isolate = GetIsolate();
6451 if (!HasSourceCode()) return isolate->heap()->undefined_value();
6452 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006453 Object* source = Script::cast(script())->source();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006454 return *SubString(Handle<String>(String::cast(source), isolate),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006455 start_position(), end_position());
6456}
6457
6458
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006459int SharedFunctionInfo::SourceSize() {
6460 return end_position() - start_position();
6461}
6462
6463
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006464int SharedFunctionInfo::CalculateInstanceSize() {
6465 int instance_size =
6466 JSObject::kHeaderSize +
6467 expected_nof_properties() * kPointerSize;
6468 if (instance_size > JSObject::kMaxInstanceSize) {
6469 instance_size = JSObject::kMaxInstanceSize;
6470 }
6471 return instance_size;
6472}
6473
6474
6475int SharedFunctionInfo::CalculateInObjectProperties() {
6476 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
6477}
6478
6479
ager@chromium.org5c838252010-02-19 08:53:10 +00006480bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
6481 // Check the basic conditions for generating inline constructor code.
6482 if (!FLAG_inline_new
6483 || !has_only_simple_this_property_assignments()
6484 || this_property_assignments_count() == 0) {
6485 return false;
6486 }
6487
6488 // If the prototype is null inline constructors cause no problems.
6489 if (!prototype->IsJSObject()) {
6490 ASSERT(prototype->IsNull());
6491 return true;
6492 }
6493
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006494 Heap* heap = GetHeap();
6495
ager@chromium.org5c838252010-02-19 08:53:10 +00006496 // Traverse the proposed prototype chain looking for setters for properties of
6497 // the same names as are set by the inline constructor.
6498 for (Object* obj = prototype;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006499 obj != heap->null_value();
ager@chromium.org5c838252010-02-19 08:53:10 +00006500 obj = obj->GetPrototype()) {
6501 JSObject* js_object = JSObject::cast(obj);
6502 for (int i = 0; i < this_property_assignments_count(); i++) {
6503 LookupResult result;
6504 String* name = GetThisPropertyAssignmentName(i);
6505 js_object->LocalLookupRealNamedProperty(name, &result);
6506 if (result.IsProperty() && result.type() == CALLBACKS) {
6507 return false;
6508 }
6509 }
6510 }
6511
6512 return true;
6513}
6514
6515
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +00006516void SharedFunctionInfo::ForbidInlineConstructor() {
6517 set_compiler_hints(BooleanBit::set(compiler_hints(),
6518 kHasOnlySimpleThisPropertyAssignments,
6519 false));
6520}
6521
6522
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006523void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006524 bool only_simple_this_property_assignments,
6525 FixedArray* assignments) {
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006526 set_compiler_hints(BooleanBit::set(compiler_hints(),
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006527 kHasOnlySimpleThisPropertyAssignments,
6528 only_simple_this_property_assignments));
6529 set_this_property_assignments(assignments);
6530 set_this_property_assignments_count(assignments->length() / 3);
6531}
6532
6533
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006534void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006535 Heap* heap = GetHeap();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006536 set_compiler_hints(BooleanBit::set(compiler_hints(),
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006537 kHasOnlySimpleThisPropertyAssignments,
6538 false));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006539 set_this_property_assignments(heap->undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006540 set_this_property_assignments_count(0);
6541}
6542
6543
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006544String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
6545 Object* obj = this_property_assignments();
6546 ASSERT(obj->IsFixedArray());
6547 ASSERT(index < this_property_assignments_count());
6548 obj = FixedArray::cast(obj)->get(index * 3);
6549 ASSERT(obj->IsString());
6550 return String::cast(obj);
6551}
6552
6553
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006554bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
6555 Object* obj = this_property_assignments();
6556 ASSERT(obj->IsFixedArray());
6557 ASSERT(index < this_property_assignments_count());
6558 obj = FixedArray::cast(obj)->get(index * 3 + 1);
6559 return Smi::cast(obj)->value() != -1;
6560}
6561
6562
6563int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
6564 ASSERT(IsThisPropertyAssignmentArgument(index));
6565 Object* obj =
6566 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
6567 return Smi::cast(obj)->value();
6568}
6569
6570
6571Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
6572 ASSERT(!IsThisPropertyAssignmentArgument(index));
6573 Object* obj =
6574 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
6575 return obj;
6576}
6577
6578
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006579// Support function for printing the source code to a StringStream
6580// without any allocation in the heap.
6581void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
6582 int max_length) {
6583 // For some native functions there is no source.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006584 if (!HasSourceCode()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006585 accumulator->Add("<No Source>");
6586 return;
6587 }
6588
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006589 // Get the source for the script which this function came from.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006590 // Don't use String::cast because we don't want more assertion errors while
6591 // we are already creating a stack dump.
6592 String* script_source =
6593 reinterpret_cast<String*>(Script::cast(script())->source());
6594
6595 if (!script_source->LooksValid()) {
6596 accumulator->Add("<Invalid Source>");
6597 return;
6598 }
6599
6600 if (!is_toplevel()) {
6601 accumulator->Add("function ");
6602 Object* name = this->name();
6603 if (name->IsString() && String::cast(name)->length() > 0) {
6604 accumulator->PrintName(name);
6605 }
6606 }
6607
6608 int len = end_position() - start_position();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006609 if (len <= max_length || max_length < 0) {
6610 accumulator->Put(script_source, start_position(), end_position());
6611 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006612 accumulator->Put(script_source,
6613 start_position(),
6614 start_position() + max_length);
6615 accumulator->Add("...\n");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006616 }
6617}
6618
6619
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006620static bool IsCodeEquivalent(Code* code, Code* recompiled) {
6621 if (code->instruction_size() != recompiled->instruction_size()) return false;
6622 ByteArray* code_relocation = code->relocation_info();
6623 ByteArray* recompiled_relocation = recompiled->relocation_info();
6624 int length = code_relocation->length();
6625 if (length != recompiled_relocation->length()) return false;
6626 int compare = memcmp(code_relocation->GetDataStartAddress(),
6627 recompiled_relocation->GetDataStartAddress(),
6628 length);
6629 return compare == 0;
6630}
6631
6632
6633void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
6634 ASSERT(!has_deoptimization_support());
6635 AssertNoAllocation no_allocation;
6636 Code* code = this->code();
6637 if (IsCodeEquivalent(code, recompiled)) {
6638 // Copy the deoptimization data from the recompiled code.
6639 code->set_deoptimization_data(recompiled->deoptimization_data());
6640 code->set_has_deoptimization_support(true);
6641 } else {
6642 // TODO(3025757): In case the recompiled isn't equivalent to the
6643 // old code, we have to replace it. We should try to avoid this
6644 // altogether because it flushes valuable type feedback by
6645 // effectively resetting all IC state.
6646 set_code(recompiled);
6647 }
6648 ASSERT(has_deoptimization_support());
6649}
6650
6651
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006652void SharedFunctionInfo::DisableOptimization(JSFunction* function) {
6653 // Disable optimization for the shared function info and mark the
6654 // code as non-optimizable. The marker on the shared function info
6655 // is there because we flush non-optimized code thereby loosing the
6656 // non-optimizable information for the code. When the code is
6657 // regenerated and set on the shared function info it is marked as
6658 // non-optimizable if optimization is disabled for the shared
6659 // function info.
6660 set_optimization_disabled(true);
6661 // Code should be the lazy compilation stub or else unoptimized. If the
6662 // latter, disable optimization for the code too.
6663 ASSERT(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN);
6664 if (code()->kind() == Code::FUNCTION) {
6665 code()->set_optimizable(false);
6666 }
6667 if (FLAG_trace_opt) {
6668 PrintF("[disabled optimization for: ");
6669 function->PrintName();
6670 PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
6671 }
6672}
6673
6674
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006675bool SharedFunctionInfo::VerifyBailoutId(int id) {
6676 // TODO(srdjan): debugging ARM crashes in hydrogen. OK to disable while
6677 // we are always bailing out on ARM.
6678
6679 ASSERT(id != AstNode::kNoNumber);
6680 Code* unoptimized = code();
6681 DeoptimizationOutputData* data =
6682 DeoptimizationOutputData::cast(unoptimized->deoptimization_data());
6683 unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this);
6684 USE(ignore);
6685 return true; // Return true if there was no ASSERT.
6686}
6687
6688
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006689void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) {
6690 ASSERT(!IsInobjectSlackTrackingInProgress());
6691
6692 // Only initiate the tracking the first time.
6693 if (live_objects_may_exist()) return;
6694 set_live_objects_may_exist(true);
6695
6696 // No tracking during the snapshot construction phase.
6697 if (Serializer::enabled()) return;
6698
6699 if (map->unused_property_fields() == 0) return;
6700
6701 // Nonzero counter is a leftover from the previous attempt interrupted
6702 // by GC, keep it.
6703 if (construction_count() == 0) {
6704 set_construction_count(kGenerousAllocationCount);
6705 }
6706 set_initial_map(map);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006707 Builtins* builtins = map->heap()->isolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006708 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric),
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006709 construct_stub());
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006710 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown));
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006711}
6712
6713
6714// Called from GC, hence reinterpret_cast and unchecked accessors.
6715void SharedFunctionInfo::DetachInitialMap() {
6716 Map* map = reinterpret_cast<Map*>(initial_map());
6717
6718 // Make the map remember to restore the link if it survives the GC.
6719 map->set_bit_field2(
6720 map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo));
6721
6722 // Undo state changes made by StartInobjectTracking (except the
6723 // construction_count). This way if the initial map does not survive the GC
6724 // then StartInobjectTracking will be called again the next time the
6725 // constructor is called. The countdown will continue and (possibly after
6726 // several more GCs) CompleteInobjectSlackTracking will eventually be called.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006727 set_initial_map(map->heap()->raw_unchecked_undefined_value());
6728 Builtins* builtins = map->heap()->isolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006729 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown),
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006730 *RawField(this, kConstructStubOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006731 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric));
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006732 // It is safe to clear the flag: it will be set again if the map is live.
6733 set_live_objects_may_exist(false);
6734}
6735
6736
6737// Called from GC, hence reinterpret_cast and unchecked accessors.
6738void SharedFunctionInfo::AttachInitialMap(Map* map) {
6739 map->set_bit_field2(
6740 map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo));
6741
6742 // Resume inobject slack tracking.
6743 set_initial_map(map);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006744 Builtins* builtins = map->heap()->isolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006745 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric),
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006746 *RawField(this, kConstructStubOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006747 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown));
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006748 // The map survived the gc, so there may be objects referencing it.
6749 set_live_objects_may_exist(true);
6750}
6751
6752
6753static void GetMinInobjectSlack(Map* map, void* data) {
6754 int slack = map->unused_property_fields();
6755 if (*reinterpret_cast<int*>(data) > slack) {
6756 *reinterpret_cast<int*>(data) = slack;
6757 }
6758}
6759
6760
6761static void ShrinkInstanceSize(Map* map, void* data) {
6762 int slack = *reinterpret_cast<int*>(data);
6763 map->set_inobject_properties(map->inobject_properties() - slack);
6764 map->set_unused_property_fields(map->unused_property_fields() - slack);
6765 map->set_instance_size(map->instance_size() - slack * kPointerSize);
6766
6767 // Visitor id might depend on the instance size, recalculate it.
6768 map->set_visitor_id(StaticVisitorBase::GetVisitorId(map));
6769}
6770
6771
6772void SharedFunctionInfo::CompleteInobjectSlackTracking() {
6773 ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress());
6774 Map* map = Map::cast(initial_map());
6775
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006776 Heap* heap = map->heap();
6777 set_initial_map(heap->undefined_value());
6778 Builtins* builtins = heap->isolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006779 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown),
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006780 construct_stub());
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006781 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric));
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006782
6783 int slack = map->unused_property_fields();
6784 map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
6785 if (slack != 0) {
6786 // Resize the initial map and all maps in its transition tree.
6787 map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006788
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006789 // Give the correct expected_nof_properties to initial maps created later.
6790 ASSERT(expected_nof_properties() >= slack);
6791 set_expected_nof_properties(expected_nof_properties() - slack);
6792 }
6793}
6794
6795
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006796void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006797 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006798 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
6799 Object* old_target = target;
6800 VisitPointer(&target);
6801 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006802}
6803
6804
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00006805void ObjectVisitor::VisitCodeEntry(Address entry_address) {
6806 Object* code = Code::GetObjectFromEntryAddress(entry_address);
6807 Object* old_code = code;
6808 VisitPointer(&code);
6809 if (code != old_code) {
6810 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry();
6811 }
6812}
6813
6814
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006815void ObjectVisitor::VisitGlobalPropertyCell(RelocInfo* rinfo) {
6816 ASSERT(rinfo->rmode() == RelocInfo::GLOBAL_PROPERTY_CELL);
6817 Object* cell = rinfo->target_cell();
6818 Object* old_cell = cell;
6819 VisitPointer(&cell);
6820 if (cell != old_cell) {
6821 rinfo->set_target_cell(reinterpret_cast<JSGlobalPropertyCell*>(cell));
6822 }
6823}
6824
6825
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006826void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +00006827 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
6828 rinfo->IsPatchedReturnSequence()) ||
6829 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
6830 rinfo->IsPatchedDebugBreakSlotSequence()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006831 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
6832 Object* old_target = target;
6833 VisitPointer(&target);
6834 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006835}
6836
6837
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006838void Code::InvalidateRelocation() {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006839 set_relocation_info(heap()->empty_byte_array());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006840}
6841
6842
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006843void Code::Relocate(intptr_t delta) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006844 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
6845 it.rinfo()->apply(delta);
6846 }
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006847 CPU::FlushICache(instruction_start(), instruction_size());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006848}
6849
6850
6851void Code::CopyFrom(const CodeDesc& desc) {
6852 // copy code
6853 memmove(instruction_start(), desc.buffer, desc.instr_size);
6854
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006855 // copy reloc info
6856 memmove(relocation_start(),
6857 desc.buffer + desc.buffer_size - desc.reloc_size,
6858 desc.reloc_size);
6859
6860 // unbox handles and relocate
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006861 intptr_t delta = instruction_start() - desc.buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006862 int mode_mask = RelocInfo::kCodeTargetMask |
ager@chromium.org236ad962008-09-25 09:45:57 +00006863 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006864 RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) |
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006865 RelocInfo::kApplyMask;
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00006866 Assembler* origin = desc.origin; // Needed to find target_object on X64.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006867 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006868 RelocInfo::Mode mode = it.rinfo()->rmode();
6869 if (mode == RelocInfo::EMBEDDED_OBJECT) {
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00006870 Handle<Object> p = it.rinfo()->target_object_handle(origin);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006871 it.rinfo()->set_target_object(*p);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006872 } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006873 Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006874 it.rinfo()->set_target_cell(*cell);
ager@chromium.org236ad962008-09-25 09:45:57 +00006875 } else if (RelocInfo::IsCodeTarget(mode)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006876 // rewrite code handles in inline cache targets to direct
6877 // pointers to the first instruction in the code object
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00006878 Handle<Object> p = it.rinfo()->target_object_handle(origin);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006879 Code* code = Code::cast(*p);
6880 it.rinfo()->set_target_address(code->instruction_start());
6881 } else {
6882 it.rinfo()->apply(delta);
6883 }
6884 }
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006885 CPU::FlushICache(instruction_start(), instruction_size());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006886}
6887
6888
6889// Locate the source position which is closest to the address in the code. This
6890// is using the source position information embedded in the relocation info.
6891// The position returned is relative to the beginning of the script where the
6892// source for this function is found.
6893int Code::SourcePosition(Address pc) {
6894 int distance = kMaxInt;
ager@chromium.org236ad962008-09-25 09:45:57 +00006895 int position = RelocInfo::kNoPosition; // Initially no position found.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006896 // Run through all the relocation info to find the best matching source
6897 // position. All the code needs to be considered as the sequence of the
6898 // instructions in the code does not necessarily follow the same order as the
6899 // source.
6900 RelocIterator it(this, RelocInfo::kPositionMask);
6901 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006902 // Only look at positions after the current pc.
6903 if (it.rinfo()->pc() < pc) {
6904 // Get position and distance.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006905
6906 int dist = static_cast<int>(pc - it.rinfo()->pc());
6907 int pos = static_cast<int>(it.rinfo()->data());
ager@chromium.org236ad962008-09-25 09:45:57 +00006908 // If this position is closer than the current candidate or if it has the
6909 // same distance as the current candidate and the position is higher then
6910 // this position is the new candidate.
6911 if ((dist < distance) ||
6912 (dist == distance && pos > position)) {
6913 position = pos;
6914 distance = dist;
6915 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006916 }
6917 it.next();
6918 }
6919 return position;
6920}
6921
6922
6923// Same as Code::SourcePosition above except it only looks for statement
6924// positions.
6925int Code::SourceStatementPosition(Address pc) {
6926 // First find the position as close as possible using all position
6927 // information.
6928 int position = SourcePosition(pc);
6929 // Now find the closest statement position before the position.
6930 int statement_position = 0;
6931 RelocIterator it(this, RelocInfo::kPositionMask);
6932 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006933 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006934 int p = static_cast<int>(it.rinfo()->data());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006935 if (statement_position < p && p <= position) {
6936 statement_position = p;
6937 }
6938 }
6939 it.next();
6940 }
6941 return statement_position;
6942}
6943
6944
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006945SafepointEntry Code::GetSafepointEntry(Address pc) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006946 SafepointTable table(this);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006947 return table.FindEntry(pc);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006948}
6949
6950
6951void Code::SetNoStackCheckTable() {
6952 // Indicate the absence of a stack-check table by a table start after the
6953 // end of the instructions. Table start must be aligned, so round up.
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006954 set_stack_check_table_offset(RoundUp(instruction_size(), kIntSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006955}
6956
6957
6958Map* Code::FindFirstMap() {
6959 ASSERT(is_inline_cache_stub());
6960 AssertNoAllocation no_allocation;
6961 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
6962 for (RelocIterator it(this, mask); !it.done(); it.next()) {
6963 RelocInfo* info = it.rinfo();
6964 Object* object = info->target_object();
6965 if (object->IsMap()) return Map::cast(object);
6966 }
6967 return NULL;
6968}
6969
6970
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006971#ifdef ENABLE_DISASSEMBLER
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006972
whesse@chromium.org023421e2010-12-21 12:19:12 +00006973void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006974 disasm::NameConverter converter;
6975 int deopt_count = DeoptCount();
whesse@chromium.org023421e2010-12-21 12:19:12 +00006976 PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006977 if (0 == deopt_count) return;
6978
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00006979 PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc",
6980 FLAG_print_code_verbose ? "commands" : "");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006981 for (int i = 0; i < deopt_count; i++) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00006982 PrintF(out, "%6d %6d %6d",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006983 i, AstId(i)->value(), ArgumentsStackHeight(i)->value());
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00006984
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00006985 if (!FLAG_print_code_verbose) {
6986 PrintF(out, "\n");
6987 continue;
6988 }
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00006989 // Print details of the frame translation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006990 int translation_index = TranslationIndex(i)->value();
6991 TranslationIterator iterator(TranslationByteArray(), translation_index);
6992 Translation::Opcode opcode =
6993 static_cast<Translation::Opcode>(iterator.Next());
6994 ASSERT(Translation::BEGIN == opcode);
6995 int frame_count = iterator.Next();
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00006996 PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode),
6997 frame_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006998
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00006999 while (iterator.HasNext() &&
7000 Translation::BEGIN !=
7001 (opcode = static_cast<Translation::Opcode>(iterator.Next()))) {
7002 PrintF(out, "%24s %s ", "", Translation::StringFor(opcode));
7003
7004 switch (opcode) {
7005 case Translation::BEGIN:
7006 UNREACHABLE();
7007 break;
7008
7009 case Translation::FRAME: {
7010 int ast_id = iterator.Next();
7011 int function_id = iterator.Next();
7012 JSFunction* function =
7013 JSFunction::cast(LiteralArray()->get(function_id));
7014 unsigned height = iterator.Next();
7015 PrintF(out, "{ast_id=%d, \nfunction=", ast_id);
7016 function->PrintName(out);
7017 PrintF(out, ", height=%u}", height);
7018 break;
7019 }
7020
7021 case Translation::DUPLICATE:
7022 break;
7023
7024 case Translation::REGISTER: {
7025 int reg_code = iterator.Next();
7026 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
7027 break;
7028 }
7029
7030 case Translation::INT32_REGISTER: {
7031 int reg_code = iterator.Next();
7032 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
7033 break;
7034 }
7035
7036 case Translation::DOUBLE_REGISTER: {
7037 int reg_code = iterator.Next();
7038 PrintF(out, "{input=%s}",
7039 DoubleRegister::AllocationIndexToString(reg_code));
7040 break;
7041 }
7042
7043 case Translation::STACK_SLOT: {
7044 int input_slot_index = iterator.Next();
7045 PrintF(out, "{input=%d}", input_slot_index);
7046 break;
7047 }
7048
7049 case Translation::INT32_STACK_SLOT: {
7050 int input_slot_index = iterator.Next();
7051 PrintF(out, "{input=%d}", input_slot_index);
7052 break;
7053 }
7054
7055 case Translation::DOUBLE_STACK_SLOT: {
7056 int input_slot_index = iterator.Next();
7057 PrintF(out, "{input=%d}", input_slot_index);
7058 break;
7059 }
7060
7061 case Translation::LITERAL: {
7062 unsigned literal_index = iterator.Next();
7063 PrintF(out, "{literal_id=%u}", literal_index);
7064 break;
7065 }
7066
7067 case Translation::ARGUMENTS_OBJECT:
7068 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007069 }
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00007070 PrintF(out, "\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007071 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007072 }
7073}
7074
7075
whesse@chromium.org023421e2010-12-21 12:19:12 +00007076void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) {
7077 PrintF(out, "Deoptimization Output Data (deopt points = %d)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007078 this->DeoptPoints());
7079 if (this->DeoptPoints() == 0) return;
7080
7081 PrintF("%6s %8s %s\n", "ast id", "pc", "state");
7082 for (int i = 0; i < this->DeoptPoints(); i++) {
7083 int pc_and_state = this->PcAndState(i)->value();
7084 PrintF("%6d %8d %s\n",
7085 this->AstId(i)->value(),
7086 FullCodeGenerator::PcField::decode(pc_and_state),
7087 FullCodeGenerator::State2String(
7088 FullCodeGenerator::StateField::decode(pc_and_state)));
7089 }
7090}
7091
whesse@chromium.org7b260152011-06-20 15:33:18 +00007092
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007093// Identify kind of code.
7094const char* Code::Kind2String(Kind kind) {
7095 switch (kind) {
7096 case FUNCTION: return "FUNCTION";
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007097 case OPTIMIZED_FUNCTION: return "OPTIMIZED_FUNCTION";
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007098 case STUB: return "STUB";
7099 case BUILTIN: return "BUILTIN";
7100 case LOAD_IC: return "LOAD_IC";
7101 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
7102 case STORE_IC: return "STORE_IC";
7103 case KEYED_STORE_IC: return "KEYED_STORE_IC";
7104 case CALL_IC: return "CALL_IC";
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007105 case KEYED_CALL_IC: return "KEYED_CALL_IC";
danno@chromium.org40cb8782011-05-25 07:58:50 +00007106 case UNARY_OP_IC: return "UNARY_OP_IC";
7107 case BINARY_OP_IC: return "BINARY_OP_IC";
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007108 case COMPARE_IC: return "COMPARE_IC";
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007109 case TO_BOOLEAN_IC: return "TO_BOOLEAN_IC";
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007110 }
7111 UNREACHABLE();
7112 return NULL;
7113}
mads.s.ager31e71382008-08-13 09:32:07 +00007114
7115
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007116const char* Code::ICState2String(InlineCacheState state) {
7117 switch (state) {
7118 case UNINITIALIZED: return "UNINITIALIZED";
7119 case PREMONOMORPHIC: return "PREMONOMORPHIC";
7120 case MONOMORPHIC: return "MONOMORPHIC";
7121 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
7122 case MEGAMORPHIC: return "MEGAMORPHIC";
7123 case DEBUG_BREAK: return "DEBUG_BREAK";
7124 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
7125 }
7126 UNREACHABLE();
7127 return NULL;
7128}
7129
7130
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007131const char* Code::PropertyType2String(PropertyType type) {
7132 switch (type) {
7133 case NORMAL: return "NORMAL";
7134 case FIELD: return "FIELD";
7135 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
7136 case CALLBACKS: return "CALLBACKS";
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007137 case HANDLER: return "HANDLER";
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007138 case INTERCEPTOR: return "INTERCEPTOR";
7139 case MAP_TRANSITION: return "MAP_TRANSITION";
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00007140 case EXTERNAL_ARRAY_TRANSITION: return "EXTERNAL_ARRAY_TRANSITION";
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007141 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
7142 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
7143 }
7144 UNREACHABLE();
7145 return NULL;
7146}
7147
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007148
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007149void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) {
7150 const char* name = NULL;
7151 switch (kind) {
7152 case CALL_IC:
7153 if (extra == STRING_INDEX_OUT_OF_BOUNDS) {
7154 name = "STRING_INDEX_OUT_OF_BOUNDS";
7155 }
7156 break;
7157 case STORE_IC:
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00007158 case KEYED_STORE_IC:
7159 if (extra == kStrictMode) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007160 name = "STRICT";
7161 }
7162 break;
7163 default:
7164 break;
7165 }
7166 if (name != NULL) {
7167 PrintF(out, "extra_ic_state = %s\n", name);
7168 } else {
whesse@chromium.org7b260152011-06-20 15:33:18 +00007169 PrintF(out, "extra_ic_state = %d\n", extra);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007170 }
7171}
7172
7173
whesse@chromium.org023421e2010-12-21 12:19:12 +00007174void Code::Disassemble(const char* name, FILE* out) {
7175 PrintF(out, "kind = %s\n", Kind2String(kind()));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007176 if (is_inline_cache_stub()) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007177 PrintF(out, "ic_state = %s\n", ICState2String(ic_state()));
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007178 PrintExtraICState(out, kind(), extra_ic_state());
whesse@chromium.org023421e2010-12-21 12:19:12 +00007179 PrintF(out, "ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007180 if (ic_state() == MONOMORPHIC) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007181 PrintF(out, "type = %s\n", PropertyType2String(type()));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007182 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007183 if (is_call_stub() || is_keyed_call_stub()) {
7184 PrintF(out, "argc = %d\n", arguments_count());
7185 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007186 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007187 if ((name != NULL) && (name[0] != '\0')) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007188 PrintF(out, "name = %s\n", name);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007189 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007190 if (kind() == OPTIMIZED_FUNCTION) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007191 PrintF(out, "stack_slots = %d\n", stack_slots());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007192 }
mads.s.ager31e71382008-08-13 09:32:07 +00007193
whesse@chromium.org023421e2010-12-21 12:19:12 +00007194 PrintF(out, "Instructions (size = %d)\n", instruction_size());
7195 Disassembler::Decode(out, this);
7196 PrintF(out, "\n");
mads.s.ager31e71382008-08-13 09:32:07 +00007197
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007198 if (kind() == FUNCTION) {
7199 DeoptimizationOutputData* data =
7200 DeoptimizationOutputData::cast(this->deoptimization_data());
whesse@chromium.org023421e2010-12-21 12:19:12 +00007201 data->DeoptimizationOutputDataPrint(out);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007202 } else if (kind() == OPTIMIZED_FUNCTION) {
7203 DeoptimizationInputData* data =
7204 DeoptimizationInputData::cast(this->deoptimization_data());
whesse@chromium.org023421e2010-12-21 12:19:12 +00007205 data->DeoptimizationInputDataPrint(out);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007206 }
7207 PrintF("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007208
7209 if (kind() == OPTIMIZED_FUNCTION) {
7210 SafepointTable table(this);
whesse@chromium.org023421e2010-12-21 12:19:12 +00007211 PrintF(out, "Safepoints (size = %u)\n", table.size());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007212 for (unsigned i = 0; i < table.length(); i++) {
7213 unsigned pc_offset = table.GetPcOffset(i);
whesse@chromium.org023421e2010-12-21 12:19:12 +00007214 PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007215 table.PrintEntry(i);
whesse@chromium.org023421e2010-12-21 12:19:12 +00007216 PrintF(out, " (sp -> fp)");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007217 SafepointEntry entry = table.GetEntry(i);
7218 if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
7219 PrintF(out, " %6d", entry.deoptimization_index());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007220 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007221 PrintF(out, " <none>");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007222 }
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007223 if (entry.argument_count() > 0) {
7224 PrintF(out, " argc: %d", entry.argument_count());
7225 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00007226 PrintF(out, "\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007227 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00007228 PrintF(out, "\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007229 } else if (kind() == FUNCTION) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007230 unsigned offset = stack_check_table_offset();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007231 // If there is no stack check table, the "table start" will at or after
7232 // (due to alignment) the end of the instruction stream.
7233 if (static_cast<int>(offset) < instruction_size()) {
7234 unsigned* address =
7235 reinterpret_cast<unsigned*>(instruction_start() + offset);
7236 unsigned length = address[0];
whesse@chromium.org023421e2010-12-21 12:19:12 +00007237 PrintF(out, "Stack checks (size = %u)\n", length);
7238 PrintF(out, "ast_id pc_offset\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007239 for (unsigned i = 0; i < length; ++i) {
7240 unsigned index = (2 * i) + 1;
whesse@chromium.org023421e2010-12-21 12:19:12 +00007241 PrintF(out, "%6u %9u\n", address[index], address[index + 1]);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007242 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00007243 PrintF(out, "\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007244 }
7245 }
7246
mads.s.ager31e71382008-08-13 09:32:07 +00007247 PrintF("RelocInfo (size = %d)\n", relocation_size());
whesse@chromium.org023421e2010-12-21 12:19:12 +00007248 for (RelocIterator it(this); !it.done(); it.next()) it.rinfo()->Print(out);
7249 PrintF(out, "\n");
mads.s.ager31e71382008-08-13 09:32:07 +00007250}
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007251#endif // ENABLE_DISASSEMBLER
7252
7253
whesse@chromium.org7b260152011-06-20 15:33:18 +00007254static void CopyFastElementsToFast(FixedArray* source,
7255 FixedArray* destination,
7256 WriteBarrierMode mode) {
7257 uint32_t count = static_cast<uint32_t>(source->length());
7258 for (uint32_t i = 0; i < count; ++i) {
7259 destination->set(i, source->get(i), mode);
7260 }
7261}
7262
7263
7264static void CopySlowElementsToFast(NumberDictionary* source,
7265 FixedArray* destination,
7266 WriteBarrierMode mode) {
7267 for (int i = 0; i < source->Capacity(); ++i) {
7268 Object* key = source->KeyAt(i);
7269 if (key->IsNumber()) {
7270 uint32_t entry = static_cast<uint32_t>(key->Number());
7271 destination->set(entry, source->ValueAt(i), mode);
7272 }
7273 }
7274}
7275
7276
lrn@chromium.org303ada72010-10-27 09:33:13 +00007277MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
7278 int length) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007279 Heap* heap = GetHeap();
ager@chromium.org3811b432009-10-28 14:53:37 +00007280 // We should never end in here with a pixel or external array.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007281 ASSERT(!HasExternalArrayElements());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007282
whesse@chromium.org7b260152011-06-20 15:33:18 +00007283 // Allocate a new fast elements backing store.
7284 FixedArray* new_elements = NULL;
7285 { Object* object;
7286 MaybeObject* maybe = heap->AllocateFixedArrayWithHoles(capacity);
7287 if (!maybe->ToObject(&object)) return maybe;
7288 new_elements = FixedArray::cast(object);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007289 }
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007290
whesse@chromium.org7b260152011-06-20 15:33:18 +00007291 // Find the new map to use for this object if there is a map change.
7292 Map* new_map = NULL;
7293 if (elements()->map() != heap->non_strict_arguments_elements_map()) {
7294 Object* object;
7295 MaybeObject* maybe = map()->GetFastElementsMap();
7296 if (!maybe->ToObject(&object)) return maybe;
7297 new_map = Map::cast(object);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007298 }
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007299
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007300 switch (GetElementsKind()) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007301 case FAST_ELEMENTS: {
7302 AssertNoAllocation no_gc;
7303 WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007304 CopyFastElementsToFast(FixedArray::cast(elements()), new_elements, mode);
7305 set_map(new_map);
7306 set_elements(new_elements);
7307 break;
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007308 }
7309 case DICTIONARY_ELEMENTS: {
7310 AssertNoAllocation no_gc;
7311 WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007312 CopySlowElementsToFast(NumberDictionary::cast(elements()),
7313 new_elements,
7314 mode);
7315 set_map(new_map);
7316 set_elements(new_elements);
7317 break;
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007318 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007319 case NON_STRICT_ARGUMENTS_ELEMENTS: {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007320 AssertNoAllocation no_gc;
7321 WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007322 // The object's map and the parameter map are unchanged, the unaliased
7323 // arguments are copied to the new backing store.
7324 FixedArray* parameter_map = FixedArray::cast(elements());
7325 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
7326 if (arguments->IsDictionary()) {
7327 CopySlowElementsToFast(NumberDictionary::cast(arguments),
7328 new_elements,
7329 mode);
7330 } else {
7331 CopyFastElementsToFast(arguments, new_elements, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007332 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007333 parameter_map->set(1, new_elements);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007334 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007335 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007336 case FAST_DOUBLE_ELEMENTS: {
7337 FixedDoubleArray* old_elements = FixedDoubleArray::cast(elements());
7338 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
7339 // Fill out the new array with this content and array holes.
7340 for (uint32_t i = 0; i < old_length; i++) {
7341 if (!old_elements->is_the_hole(i)) {
7342 Object* obj;
7343 // Objects must be allocated in the old object space, since the
7344 // overall number of HeapNumbers needed for the conversion might
7345 // exceed the capacity of new space, and we would fail repeatedly
7346 // trying to convert the FixedDoubleArray.
7347 MaybeObject* maybe_value_object =
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00007348 GetHeap()->AllocateHeapNumber(old_elements->get_scalar(i),
7349 TENURED);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007350 if (!maybe_value_object->ToObject(&obj)) return maybe_value_object;
7351 // Force write barrier. It's not worth trying to exploit
7352 // elems->GetWriteBarrierMode(), since it requires an
7353 // AssertNoAllocation stack object that would have to be positioned
7354 // after the HeapNumber allocation anyway.
whesse@chromium.org7b260152011-06-20 15:33:18 +00007355 new_elements->set(i, obj, UPDATE_WRITE_BARRIER);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007356 }
7357 }
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007358 set_map(new_map);
7359 set_elements(new_elements);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007360 break;
7361 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007362 case EXTERNAL_BYTE_ELEMENTS:
7363 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7364 case EXTERNAL_SHORT_ELEMENTS:
7365 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7366 case EXTERNAL_INT_ELEMENTS:
7367 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7368 case EXTERNAL_FLOAT_ELEMENTS:
7369 case EXTERNAL_DOUBLE_ELEMENTS:
7370 case EXTERNAL_PIXEL_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007371 UNREACHABLE();
7372 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007373 }
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007374
whesse@chromium.org7b260152011-06-20 15:33:18 +00007375 // Update the length if necessary.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007376 if (IsJSArray()) {
7377 JSArray::cast(this)->set_length(Smi::FromInt(length));
7378 }
7379
whesse@chromium.org7b260152011-06-20 15:33:18 +00007380 return new_elements;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007381}
7382
7383
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007384MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
7385 int capacity,
7386 int length) {
7387 Heap* heap = GetHeap();
7388 // We should never end in here with a pixel or external array.
7389 ASSERT(!HasExternalArrayElements());
7390
7391 Object* obj;
7392 { MaybeObject* maybe_obj =
7393 heap->AllocateUninitializedFixedDoubleArray(capacity);
7394 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7395 }
7396 FixedDoubleArray* elems = FixedDoubleArray::cast(obj);
7397
7398 { MaybeObject* maybe_obj = map()->GetFastDoubleElementsMap();
7399 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7400 }
7401 Map* new_map = Map::cast(obj);
7402
7403 AssertNoAllocation no_gc;
7404 switch (GetElementsKind()) {
7405 case FAST_ELEMENTS: {
7406 elems->Initialize(FixedArray::cast(elements()));
7407 break;
7408 }
7409 case FAST_DOUBLE_ELEMENTS: {
7410 elems->Initialize(FixedDoubleArray::cast(elements()));
7411 break;
7412 }
7413 case DICTIONARY_ELEMENTS: {
7414 elems->Initialize(NumberDictionary::cast(elements()));
7415 break;
7416 }
7417 default:
7418 UNREACHABLE();
7419 break;
7420 }
7421
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007422 ASSERT(new_map->has_fast_double_elements());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007423 set_map(new_map);
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007424 ASSERT(elems->IsFixedDoubleArray());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007425 set_elements(elems);
7426
7427 if (IsJSArray()) {
7428 JSArray::cast(this)->set_length(Smi::FromInt(length));
7429 }
7430
7431 return this;
7432}
7433
7434
lrn@chromium.org303ada72010-10-27 09:33:13 +00007435MaybeObject* JSObject::SetSlowElements(Object* len) {
ager@chromium.org3811b432009-10-28 14:53:37 +00007436 // We should never end in here with a pixel or external array.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007437 ASSERT(!HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007438
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007439 uint32_t new_length = static_cast<uint32_t>(len->Number());
7440
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007441 switch (GetElementsKind()) {
7442 case FAST_ELEMENTS: {
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007443 case FAST_DOUBLE_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007444 // Make sure we never try to shrink dense arrays into sparse arrays.
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007445 ASSERT(static_cast<uint32_t>(
7446 FixedArrayBase::cast(elements())->length()) <= new_length);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007447 MaybeObject* result = NormalizeElements();
7448 if (result->IsFailure()) return result;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007449
7450 // Update length for JSArrays.
7451 if (IsJSArray()) JSArray::cast(this)->set_length(len);
7452 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007453 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007454 case DICTIONARY_ELEMENTS: {
7455 if (IsJSArray()) {
7456 uint32_t old_length =
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007457 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007458 element_dictionary()->RemoveNumberEntries(new_length, old_length),
7459 JSArray::cast(this)->set_length(len);
7460 }
7461 break;
7462 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007463 case NON_STRICT_ARGUMENTS_ELEMENTS:
7464 UNIMPLEMENTED();
7465 break;
7466 case EXTERNAL_BYTE_ELEMENTS:
7467 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7468 case EXTERNAL_SHORT_ELEMENTS:
7469 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7470 case EXTERNAL_INT_ELEMENTS:
7471 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7472 case EXTERNAL_FLOAT_ELEMENTS:
7473 case EXTERNAL_DOUBLE_ELEMENTS:
7474 case EXTERNAL_PIXEL_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007475 UNREACHABLE();
7476 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007477 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007478 return this;
7479}
7480
7481
lrn@chromium.org303ada72010-10-27 09:33:13 +00007482MaybeObject* JSArray::Initialize(int capacity) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007483 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007484 ASSERT(capacity >= 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007485 set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007486 FixedArray* new_elements;
7487 if (capacity == 0) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007488 new_elements = heap->empty_fixed_array();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007489 } else {
lrn@chromium.org303ada72010-10-27 09:33:13 +00007490 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007491 { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(capacity);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007492 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7493 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007494 new_elements = FixedArray::cast(obj);
7495 }
7496 set_elements(new_elements);
7497 return this;
7498}
7499
7500
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007501void JSArray::Expand(int required_size) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007502 Handle<JSArray> self(this);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007503 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007504 int old_size = old_backing->length();
ager@chromium.org6141cbe2009-11-20 12:14:52 +00007505 int new_size = required_size > old_size ? required_size : old_size;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007506 Handle<FixedArray> new_backing = FACTORY->NewFixedArray(new_size);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007507 // Can't use this any more now because we may have had a GC!
7508 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
7509 self->SetContent(*new_backing);
7510}
7511
7512
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007513static Failure* ArrayLengthRangeError(Heap* heap) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007514 HandleScope scope(heap->isolate());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007515 return heap->isolate()->Throw(
7516 *FACTORY->NewRangeError("invalid_array_length",
7517 HandleVector<Object>(NULL, 0)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007518}
7519
7520
lrn@chromium.org303ada72010-10-27 09:33:13 +00007521MaybeObject* JSObject::SetElementsLength(Object* len) {
ager@chromium.org3811b432009-10-28 14:53:37 +00007522 // We should never end in here with a pixel or external array.
ager@chromium.org5c838252010-02-19 08:53:10 +00007523 ASSERT(AllowsSetElementsLength());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007524
lrn@chromium.org303ada72010-10-27 09:33:13 +00007525 MaybeObject* maybe_smi_length = len->ToSmi();
7526 Object* smi_length = Smi::FromInt(0);
7527 if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007528 const int value = Smi::cast(smi_length)->value();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007529 if (value < 0) return ArrayLengthRangeError(GetHeap());
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007530 JSObject::ElementsKind elements_kind = GetElementsKind();
7531 switch (elements_kind) {
7532 case FAST_ELEMENTS:
7533 case FAST_DOUBLE_ELEMENTS: {
7534 int old_capacity = FixedArrayBase::cast(elements())->length();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007535 if (value <= old_capacity) {
7536 if (IsJSArray()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00007537 Object* obj;
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007538 if (elements_kind == FAST_ELEMENTS) {
7539 MaybeObject* maybe_obj = EnsureWritableFastElements();
lrn@chromium.org303ada72010-10-27 09:33:13 +00007540 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7541 }
ager@chromium.org04921a82011-06-27 13:21:41 +00007542 if (2 * value <= old_capacity) {
7543 // If more than half the elements won't be used, trim the array.
7544 if (value == 0) {
7545 initialize_elements();
7546 } else {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007547 Address filler_start;
7548 int filler_size;
7549 if (GetElementsKind() == FAST_ELEMENTS) {
7550 FixedArray* fast_elements = FixedArray::cast(elements());
7551 fast_elements->set_length(value);
7552 filler_start = fast_elements->address() +
7553 FixedArray::OffsetOfElementAt(value);
7554 filler_size = (old_capacity - value) * kPointerSize;
7555 } else {
7556 ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
7557 FixedDoubleArray* fast_double_elements =
7558 FixedDoubleArray::cast(elements());
7559 fast_double_elements->set_length(value);
7560 filler_start = fast_double_elements->address() +
7561 FixedDoubleArray::OffsetOfElementAt(value);
7562 filler_size = (old_capacity - value) * kDoubleSize;
7563 }
ager@chromium.org04921a82011-06-27 13:21:41 +00007564 GetHeap()->CreateFillerObjectAt(filler_start, filler_size);
7565 }
7566 } else {
7567 // Otherwise, fill the unused tail with holes.
7568 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007569 if (GetElementsKind() == FAST_ELEMENTS) {
7570 FixedArray* fast_elements = FixedArray::cast(elements());
7571 for (int i = value; i < old_length; i++) {
7572 fast_elements->set_the_hole(i);
7573 }
7574 } else {
7575 ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
7576 FixedDoubleArray* fast_double_elements =
7577 FixedDoubleArray::cast(elements());
7578 for (int i = value; i < old_length; i++) {
7579 fast_double_elements->set_the_hole(i);
7580 }
ager@chromium.org04921a82011-06-27 13:21:41 +00007581 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007582 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007583 JSArray::cast(this)->set_length(Smi::cast(smi_length));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007584 }
7585 return this;
7586 }
7587 int min = NewElementsCapacity(old_capacity);
7588 int new_capacity = value > min ? value : min;
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007589 if (!ShouldConvertToSlowElements(new_capacity)) {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007590 MaybeObject* result;
7591 if (GetElementsKind() == FAST_ELEMENTS) {
7592 result = SetFastElementsCapacityAndLength(new_capacity, value);
7593 } else {
7594 ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
7595 result = SetFastDoubleElementsCapacityAndLength(new_capacity,
7596 value);
7597 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007598 if (result->IsFailure()) return result;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007599 return this;
7600 }
7601 break;
7602 }
7603 case DICTIONARY_ELEMENTS: {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007604 if (IsJSArray()) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007605 if (value == 0) {
7606 // If the length of a slow array is reset to zero, we clear
7607 // the array and flush backing storage. This has the added
7608 // benefit that the array returns to fast mode.
lrn@chromium.org303ada72010-10-27 09:33:13 +00007609 Object* obj;
7610 { MaybeObject* maybe_obj = ResetElements();
7611 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7612 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007613 } else {
7614 // Remove deleted elements.
7615 uint32_t old_length =
7616 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
7617 element_dictionary()->RemoveNumberEntries(value, old_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007618 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007619 JSArray::cast(this)->set_length(Smi::cast(smi_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007620 }
7621 return this;
7622 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007623 case NON_STRICT_ARGUMENTS_ELEMENTS:
7624 case EXTERNAL_BYTE_ELEMENTS:
7625 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7626 case EXTERNAL_SHORT_ELEMENTS:
7627 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7628 case EXTERNAL_INT_ELEMENTS:
7629 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7630 case EXTERNAL_FLOAT_ELEMENTS:
7631 case EXTERNAL_DOUBLE_ELEMENTS:
7632 case EXTERNAL_PIXEL_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007633 UNREACHABLE();
7634 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007635 }
7636 }
7637
7638 // General slow case.
7639 if (len->IsNumber()) {
7640 uint32_t length;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007641 if (len->ToArrayIndex(&length)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007642 return SetSlowElements(len);
7643 } else {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007644 return ArrayLengthRangeError(GetHeap());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007645 }
7646 }
7647
7648 // len is not a number so make the array size one and
7649 // set only element to len.
lrn@chromium.org303ada72010-10-27 09:33:13 +00007650 Object* obj;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007651 { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007652 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7653 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007654 FixedArray::cast(obj)->set(0, len);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007655 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007656 set_elements(FixedArray::cast(obj));
7657 return this;
7658}
7659
7660
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007661Object* Map::GetPrototypeTransition(Object* prototype) {
7662 FixedArray* cache = prototype_transitions();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007663 int number_of_transitions = NumberOfProtoTransitions();
7664 const int proto_offset =
7665 kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset;
7666 const int map_offset = kProtoTransitionHeaderSize + kProtoTransitionMapOffset;
7667 const int step = kProtoTransitionElementsPerEntry;
7668 for (int i = 0; i < number_of_transitions; i++) {
7669 if (cache->get(proto_offset + i * step) == prototype) {
7670 Object* map = cache->get(map_offset + i * step);
7671 ASSERT(map->IsMap());
7672 return map;
7673 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007674 }
7675 return NULL;
7676}
7677
7678
7679MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007680 ASSERT(map->IsMap());
7681 ASSERT(HeapObject::cast(prototype)->map()->IsMap());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007682 // Don't cache prototype transition if this map is shared.
7683 if (is_shared() || !FLAG_cache_prototype_transitions) return this;
7684
7685 FixedArray* cache = prototype_transitions();
7686
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007687 const int step = kProtoTransitionElementsPerEntry;
7688 const int header = kProtoTransitionHeaderSize;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007689
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007690 int capacity = (cache->length() - header) / step;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007691
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007692 int transitions = NumberOfProtoTransitions() + 1;
7693
7694 if (transitions > capacity) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007695 if (capacity > kMaxCachedPrototypeTransitions) return this;
7696
7697 FixedArray* new_cache;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007698 // Grow array by factor 2 over and above what we need.
7699 { MaybeObject* maybe_cache =
7700 heap()->AllocateFixedArray(transitions * 2 * step + header);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007701 if (!maybe_cache->To<FixedArray>(&new_cache)) return maybe_cache;
7702 }
7703
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007704 for (int i = 0; i < capacity * step; i++) {
7705 new_cache->set(i + header, cache->get(i + header));
7706 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007707 cache = new_cache;
7708 set_prototype_transitions(cache);
7709 }
7710
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007711 int last = transitions - 1;
7712
7713 cache->set(header + last * step + kProtoTransitionPrototypeOffset, prototype);
7714 cache->set(header + last * step + kProtoTransitionMapOffset, map);
7715 SetNumberOfProtoTransitions(transitions);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007716
7717 return cache;
7718}
7719
7720
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007721MaybeObject* JSReceiver::SetPrototype(Object* value,
7722 bool skip_hidden_prototypes) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007723#ifdef DEBUG
7724 int size = Size();
7725#endif
7726
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007727 Heap* heap = GetHeap();
ager@chromium.org5c838252010-02-19 08:53:10 +00007728 // Silently ignore the change if value is not a JSObject or null.
7729 // SpiderMonkey behaves this way.
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007730 if (!value->IsJSReceiver() && !value->IsNull()) return value;
ager@chromium.org5c838252010-02-19 08:53:10 +00007731
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007732 // From 8.6.2 Object Internal Methods
7733 // ...
7734 // In addition, if [[Extensible]] is false the value of the [[Class]] and
7735 // [[Prototype]] internal properties of the object may not be modified.
7736 // ...
7737 // Implementation specific extensions that modify [[Class]], [[Prototype]]
7738 // or [[Extensible]] must not violate the invariants defined in the preceding
7739 // paragraph.
7740 if (!this->map()->is_extensible()) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007741 HandleScope scope(heap->isolate());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007742 Handle<Object> handle(this, heap->isolate());
7743 return heap->isolate()->Throw(
7744 *FACTORY->NewTypeError("non_extensible_proto",
7745 HandleVector<Object>(&handle, 1)));
7746 }
7747
ager@chromium.org5c838252010-02-19 08:53:10 +00007748 // Before we can set the prototype we need to be sure
7749 // prototype cycles are prevented.
7750 // It is sufficient to validate that the receiver is not in the new prototype
7751 // chain.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007752 for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00007753 if (JSObject::cast(pt) == this) {
7754 // Cycle detected.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007755 HandleScope scope(heap->isolate());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007756 return heap->isolate()->Throw(
7757 *FACTORY->NewError("cyclic_proto", HandleVector<Object>(NULL, 0)));
ager@chromium.org5c838252010-02-19 08:53:10 +00007758 }
7759 }
7760
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007761 JSReceiver* real_receiver = this;
ager@chromium.org5c838252010-02-19 08:53:10 +00007762
7763 if (skip_hidden_prototypes) {
7764 // Find the first object in the chain whose prototype object is not
7765 // hidden and set the new prototype on that object.
7766 Object* current_proto = real_receiver->GetPrototype();
7767 while (current_proto->IsJSObject() &&
7768 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
7769 real_receiver = JSObject::cast(current_proto);
7770 current_proto = current_proto->GetPrototype();
7771 }
7772 }
7773
7774 // Set the new prototype of the object.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007775 Map* map = real_receiver->map();
7776
7777 // Nothing to do if prototype is already set.
7778 if (map->prototype() == value) return value;
7779
7780 Object* new_map = map->GetPrototypeTransition(value);
7781 if (new_map == NULL) {
7782 { MaybeObject* maybe_new_map = map->CopyDropTransitions();
7783 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
7784 }
7785
7786 { MaybeObject* maybe_new_cache =
7787 map->PutPrototypeTransition(value, Map::cast(new_map));
7788 if (maybe_new_cache->IsFailure()) return maybe_new_cache;
7789 }
7790
7791 Map::cast(new_map)->set_prototype(value);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007792 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007793 ASSERT(Map::cast(new_map)->prototype() == value);
ager@chromium.org5c838252010-02-19 08:53:10 +00007794 real_receiver->set_map(Map::cast(new_map));
7795
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007796 heap->ClearInstanceofCache();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007797 ASSERT(size == Size());
ager@chromium.org5c838252010-02-19 08:53:10 +00007798 return value;
7799}
7800
7801
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007802bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007803 switch (GetElementsKind()) {
7804 case FAST_ELEMENTS: {
7805 uint32_t length = IsJSArray() ?
7806 static_cast<uint32_t>
7807 (Smi::cast(JSArray::cast(this)->length())->value()) :
7808 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7809 if ((index < length) &&
7810 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
7811 return true;
7812 }
7813 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007814 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007815 case FAST_DOUBLE_ELEMENTS: {
7816 uint32_t length = IsJSArray() ?
7817 static_cast<uint32_t>
7818 (Smi::cast(JSArray::cast(this)->length())->value()) :
7819 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
7820 if ((index < length) &&
7821 !FixedDoubleArray::cast(elements())->is_the_hole(index)) {
7822 return true;
7823 }
7824 break;
7825 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007826 case EXTERNAL_PIXEL_ELEMENTS: {
7827 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007828 if (index < static_cast<uint32_t>(pixels->length())) {
7829 return true;
7830 }
7831 break;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007832 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007833 case EXTERNAL_BYTE_ELEMENTS:
7834 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7835 case EXTERNAL_SHORT_ELEMENTS:
7836 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7837 case EXTERNAL_INT_ELEMENTS:
7838 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007839 case EXTERNAL_FLOAT_ELEMENTS:
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007840 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00007841 ExternalArray* array = ExternalArray::cast(elements());
7842 if (index < static_cast<uint32_t>(array->length())) {
7843 return true;
7844 }
7845 break;
7846 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007847 case DICTIONARY_ELEMENTS: {
7848 if (element_dictionary()->FindEntry(index)
7849 != NumberDictionary::kNotFound) {
7850 return true;
7851 }
7852 break;
7853 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007854 case NON_STRICT_ARGUMENTS_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007855 UNREACHABLE();
7856 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007857 }
7858
7859 // Handle [] on String objects.
7860 if (this->IsStringObjectWithCharacterAt(index)) return true;
7861
7862 Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007863 if (pt->IsNull()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007864 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
7865}
7866
7867
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007868bool JSObject::HasElementWithInterceptor(JSReceiver* receiver, uint32_t index) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007869 Isolate* isolate = GetIsolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007870 // Make sure that the top context does not change when doing
7871 // callbacks or interceptor calls.
7872 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007873 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007874 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007875 Handle<JSReceiver> receiver_handle(receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007876 Handle<JSObject> holder_handle(this);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007877 CustomArguments args(isolate, interceptor->data(), receiver, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007878 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007879 if (!interceptor->query()->IsUndefined()) {
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00007880 v8::IndexedPropertyQuery query =
7881 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007882 LOG(isolate,
7883 ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00007884 v8::Handle<v8::Integer> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007885 {
7886 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007887 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007888 result = query(index, info);
7889 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00007890 if (!result.IsEmpty()) {
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00007891 ASSERT(result->IsInt32());
7892 return true; // absence of property is signaled by empty handle.
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00007893 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007894 } else if (!interceptor->getter()->IsUndefined()) {
7895 v8::IndexedPropertyGetter getter =
7896 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007897 LOG(isolate,
7898 ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007899 v8::Handle<v8::Value> result;
7900 {
7901 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007902 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007903 result = getter(index, info);
7904 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007905 if (!result.IsEmpty()) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007906 }
7907 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
7908}
7909
7910
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007911JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007912 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007913 if (IsAccessCheckNeeded()) {
7914 Heap* heap = GetHeap();
7915 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
7916 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7917 return UNDEFINED_ELEMENT;
7918 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007919 }
7920
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007921 if (IsJSGlobalProxy()) {
7922 Object* proto = GetPrototype();
7923 if (proto->IsNull()) return UNDEFINED_ELEMENT;
7924 ASSERT(proto->IsJSGlobalObject());
7925 return JSObject::cast(proto)->HasLocalElement(index);
7926 }
7927
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007928 // Check for lookup interceptor
7929 if (HasIndexedInterceptor()) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007930 return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
7931 : UNDEFINED_ELEMENT;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007932 }
7933
7934 // Handle [] on String objects.
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007935 if (this->IsStringObjectWithCharacterAt(index)) {
7936 return STRING_CHARACTER_ELEMENT;
7937 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007938
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007939 switch (GetElementsKind()) {
7940 case FAST_ELEMENTS: {
7941 uint32_t length = IsJSArray() ?
7942 static_cast<uint32_t>
7943 (Smi::cast(JSArray::cast(this)->length())->value()) :
7944 static_cast<uint32_t>(FixedArray::cast(elements())->length());
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007945 if ((index < length) &&
7946 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
7947 return FAST_ELEMENT;
7948 }
7949 break;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007950 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007951 case FAST_DOUBLE_ELEMENTS: {
7952 uint32_t length = IsJSArray() ?
7953 static_cast<uint32_t>
7954 (Smi::cast(JSArray::cast(this)->length())->value()) :
7955 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
7956 if ((index < length) &&
7957 !FixedDoubleArray::cast(elements())->is_the_hole(index)) {
7958 return FAST_ELEMENT;
7959 }
7960 break;
7961 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007962 case EXTERNAL_PIXEL_ELEMENTS: {
7963 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007964 if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT;
7965 break;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007966 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007967 case EXTERNAL_BYTE_ELEMENTS:
7968 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7969 case EXTERNAL_SHORT_ELEMENTS:
7970 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7971 case EXTERNAL_INT_ELEMENTS:
7972 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007973 case EXTERNAL_FLOAT_ELEMENTS:
7974 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00007975 ExternalArray* array = ExternalArray::cast(elements());
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007976 if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
7977 break;
ager@chromium.org3811b432009-10-28 14:53:37 +00007978 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007979 case DICTIONARY_ELEMENTS: {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007980 if (element_dictionary()->FindEntry(index) !=
whesse@chromium.org7b260152011-06-20 15:33:18 +00007981 NumberDictionary::kNotFound) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007982 return DICTIONARY_ELEMENT;
7983 }
7984 break;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007985 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007986 case NON_STRICT_ARGUMENTS_ELEMENTS: {
7987 // Aliased parameters and non-aliased elements in a fast backing store
7988 // behave as FAST_ELEMENT. Non-aliased elements in a dictionary
7989 // backing store behave as DICTIONARY_ELEMENT.
7990 FixedArray* parameter_map = FixedArray::cast(elements());
7991 uint32_t length = parameter_map->length();
7992 Object* probe =
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00007993 index < (length - 2) ? parameter_map->get(index + 2) : NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00007994 if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT;
7995 // If not aliased, check the arguments.
7996 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
7997 if (arguments->IsDictionary()) {
7998 NumberDictionary* dictionary = NumberDictionary::cast(arguments);
7999 if (dictionary->FindEntry(index) != NumberDictionary::kNotFound) {
8000 return DICTIONARY_ELEMENT;
8001 }
8002 } else {
8003 length = arguments->length();
8004 probe = (index < length) ? arguments->get(index) : NULL;
8005 if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT;
8006 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008007 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008008 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008009 }
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00008010
8011 return UNDEFINED_ELEMENT;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008012}
8013
8014
whesse@chromium.org7b260152011-06-20 15:33:18 +00008015bool JSObject::HasElementInElements(FixedArray* elements,
8016 ElementsKind kind,
8017 uint32_t index) {
8018 ASSERT(kind == FAST_ELEMENTS || kind == DICTIONARY_ELEMENTS);
8019 if (kind == FAST_ELEMENTS) {
8020 int length = IsJSArray()
8021 ? Smi::cast(JSArray::cast(this)->length())->value()
8022 : elements->length();
8023 if (index < static_cast<uint32_t>(length) &&
8024 !elements->get(index)->IsTheHole()) {
8025 return true;
8026 }
8027 } else {
8028 if (NumberDictionary::cast(elements)->FindEntry(index) !=
8029 NumberDictionary::kNotFound) {
8030 return true;
8031 }
8032 }
8033 return false;
8034}
8035
8036
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008037bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008038 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008039 if (IsAccessCheckNeeded()) {
8040 Heap* heap = GetHeap();
8041 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
8042 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
8043 return false;
8044 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008045 }
8046
8047 // Check for lookup interceptor
8048 if (HasIndexedInterceptor()) {
8049 return HasElementWithInterceptor(receiver, index);
8050 }
8051
whesse@chromium.org7b260152011-06-20 15:33:18 +00008052 ElementsKind kind = GetElementsKind();
8053 switch (kind) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008054 case FAST_ELEMENTS: {
8055 uint32_t length = IsJSArray() ?
8056 static_cast<uint32_t>
8057 (Smi::cast(JSArray::cast(this)->length())->value()) :
8058 static_cast<uint32_t>(FixedArray::cast(elements())->length());
8059 if ((index < length) &&
8060 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
8061 break;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008062 }
rossberg@chromium.org717967f2011-07-20 13:44:42 +00008063 case FAST_DOUBLE_ELEMENTS: {
8064 uint32_t length = IsJSArray() ?
8065 static_cast<uint32_t>
8066 (Smi::cast(JSArray::cast(this)->length())->value()) :
8067 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
8068 if ((index < length) &&
8069 !FixedDoubleArray::cast(elements())->is_the_hole(index)) return true;
8070 break;
8071 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008072 case EXTERNAL_PIXEL_ELEMENTS: {
8073 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008074 if (index < static_cast<uint32_t>(pixels->length())) {
8075 return true;
8076 }
8077 break;
8078 }
ager@chromium.org3811b432009-10-28 14:53:37 +00008079 case EXTERNAL_BYTE_ELEMENTS:
8080 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
8081 case EXTERNAL_SHORT_ELEMENTS:
8082 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
8083 case EXTERNAL_INT_ELEMENTS:
8084 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00008085 case EXTERNAL_FLOAT_ELEMENTS:
8086 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00008087 ExternalArray* array = ExternalArray::cast(elements());
8088 if (index < static_cast<uint32_t>(array->length())) {
8089 return true;
8090 }
8091 break;
8092 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008093 case DICTIONARY_ELEMENTS: {
8094 if (element_dictionary()->FindEntry(index)
8095 != NumberDictionary::kNotFound) {
8096 return true;
8097 }
8098 break;
8099 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008100 case NON_STRICT_ARGUMENTS_ELEMENTS: {
8101 FixedArray* parameter_map = FixedArray::cast(elements());
8102 uint32_t length = parameter_map->length();
8103 Object* probe =
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00008104 (index < length - 2) ? parameter_map->get(index + 2) : NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008105 if (probe != NULL && !probe->IsTheHole()) return true;
8106
8107 // Not a mapped parameter, check the arguments.
8108 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
8109 kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : FAST_ELEMENTS;
8110 if (HasElementInElements(arguments, kind, index)) return true;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008111 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008112 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008113 }
8114
8115 // Handle [] on String objects.
8116 if (this->IsStringObjectWithCharacterAt(index)) return true;
8117
8118 Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008119 if (pt->IsNull()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008120 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
8121}
8122
8123
lrn@chromium.org303ada72010-10-27 09:33:13 +00008124MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008125 Object* value,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008126 StrictModeFlag strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008127 bool check_prototype) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008128 Isolate* isolate = GetIsolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008129 // Make sure that the top context does not change when doing
8130 // callbacks or interceptor calls.
8131 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008132 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008133 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
8134 Handle<JSObject> this_handle(this);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008135 Handle<Object> value_handle(value, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008136 if (!interceptor->setter()->IsUndefined()) {
8137 v8::IndexedPropertySetter setter =
8138 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008139 LOG(isolate,
8140 ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
8141 CustomArguments args(isolate, interceptor->data(), this, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00008142 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008143 v8::Handle<v8::Value> result;
8144 {
8145 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008146 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008147 result = setter(index, v8::Utils::ToLocal(value_handle), info);
8148 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008149 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008150 if (!result.IsEmpty()) return *value_handle;
8151 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00008152 MaybeObject* raw_result =
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008153 this_handle->SetElementWithoutInterceptor(index,
8154 *value_handle,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008155 strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008156 check_prototype);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008157 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008158 return raw_result;
8159}
8160
8161
lrn@chromium.org303ada72010-10-27 09:33:13 +00008162MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
8163 Object* structure,
8164 uint32_t index,
8165 Object* holder) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008166 Isolate* isolate = GetIsolate();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008167 ASSERT(!structure->IsForeign());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008168
8169 // api style callbacks.
8170 if (structure->IsAccessorInfo()) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008171 Handle<AccessorInfo> data(AccessorInfo::cast(structure));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008172 Object* fun_obj = data->getter();
8173 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008174 HandleScope scope(isolate);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008175 Handle<JSObject> self(JSObject::cast(receiver));
8176 Handle<JSObject> holder_handle(JSObject::cast(holder));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008177 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008178 Handle<String> key = isolate->factory()->NumberToString(number);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008179 LOG(isolate, ApiNamedPropertyAccess("load", *self, *key));
8180 CustomArguments args(isolate, data->data(), *self, *holder_handle);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008181 v8::AccessorInfo info(args.end());
8182 v8::Handle<v8::Value> result;
8183 {
8184 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008185 VMState state(isolate, EXTERNAL);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008186 result = call_fun(v8::Utils::ToLocal(key), info);
8187 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008188 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
8189 if (result.IsEmpty()) return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008190 return *v8::Utils::OpenHandle(*result);
8191 }
8192
8193 // __defineGetter__ callback
8194 if (structure->IsFixedArray()) {
8195 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
8196 if (getter->IsJSFunction()) {
8197 return Object::GetPropertyWithDefinedGetter(receiver,
8198 JSFunction::cast(getter));
8199 }
8200 // Getter is not a function.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008201 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008202 }
8203
8204 UNREACHABLE();
8205 return NULL;
8206}
8207
8208
lrn@chromium.org303ada72010-10-27 09:33:13 +00008209MaybeObject* JSObject::SetElementWithCallback(Object* structure,
8210 uint32_t index,
8211 Object* value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008212 JSObject* holder,
8213 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008214 Isolate* isolate = GetIsolate();
8215 HandleScope scope(isolate);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008216
8217 // We should never get here to initialize a const with the hole
8218 // value since a const declaration would conflict with the setter.
8219 ASSERT(!value->IsTheHole());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008220 Handle<Object> value_handle(value, isolate);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008221
8222 // To accommodate both the old and the new api we switch on the
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008223 // data structure used to store the callbacks. Eventually foreign
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008224 // callbacks should be phased out.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008225 ASSERT(!structure->IsForeign());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008226
8227 if (structure->IsAccessorInfo()) {
8228 // api style callbacks
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008229 Handle<JSObject> self(this);
8230 Handle<JSObject> holder_handle(JSObject::cast(holder));
8231 Handle<AccessorInfo> data(AccessorInfo::cast(structure));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008232 Object* call_obj = data->setter();
8233 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
8234 if (call_fun == NULL) return value;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008235 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
8236 Handle<String> key(isolate->factory()->NumberToString(number));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008237 LOG(isolate, ApiNamedPropertyAccess("store", *self, *key));
8238 CustomArguments args(isolate, data->data(), *self, *holder_handle);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008239 v8::AccessorInfo info(args.end());
8240 {
8241 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008242 VMState state(isolate, EXTERNAL);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008243 call_fun(v8::Utils::ToLocal(key),
8244 v8::Utils::ToLocal(value_handle),
8245 info);
8246 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008247 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008248 return *value_handle;
8249 }
8250
8251 if (structure->IsFixedArray()) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008252 Handle<Object> setter(FixedArray::cast(structure)->get(kSetterIndex));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008253 if (setter->IsJSFunction()) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008254 return SetPropertyWithDefinedSetter(JSFunction::cast(*setter), value);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008255 } else {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008256 if (strict_mode == kNonStrictMode) {
8257 return value;
8258 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008259 Handle<Object> holder_handle(holder, isolate);
8260 Handle<Object> key(isolate->factory()->NewNumberFromUint(index));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008261 Handle<Object> args[2] = { key, holder_handle };
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008262 return isolate->Throw(
8263 *isolate->factory()->NewTypeError("no_setter_in_callback",
8264 HandleVector(args, 2)));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008265 }
8266 }
8267
8268 UNREACHABLE();
8269 return NULL;
8270}
8271
8272
whesse@chromium.org7b260152011-06-20 15:33:18 +00008273bool JSObject::HasFastArgumentsElements() {
8274 Heap* heap = GetHeap();
8275 if (!elements()->IsFixedArray()) return false;
8276 FixedArray* elements = FixedArray::cast(this->elements());
8277 if (elements->map() != heap->non_strict_arguments_elements_map()) {
8278 return false;
8279 }
8280 FixedArray* arguments = FixedArray::cast(elements->get(1));
8281 return !arguments->IsDictionary();
8282}
8283
8284
8285bool JSObject::HasDictionaryArgumentsElements() {
8286 Heap* heap = GetHeap();
8287 if (!elements()->IsFixedArray()) return false;
8288 FixedArray* elements = FixedArray::cast(this->elements());
8289 if (elements->map() != heap->non_strict_arguments_elements_map()) {
8290 return false;
8291 }
8292 FixedArray* arguments = FixedArray::cast(elements->get(1));
8293 return arguments->IsDictionary();
8294}
8295
8296
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008297// Adding n elements in fast case is O(n*n).
8298// Note: revisit design to have dual undefined values to capture absent
8299// elements.
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008300MaybeObject* JSObject::SetFastElement(uint32_t index,
8301 Object* value,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008302 StrictModeFlag strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008303 bool check_prototype) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008304 ASSERT(HasFastElements() || HasFastArgumentsElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008305
whesse@chromium.org7b260152011-06-20 15:33:18 +00008306 FixedArray* backing_store = FixedArray::cast(elements());
8307 if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) {
8308 backing_store = FixedArray::cast(backing_store->get(1));
8309 } else {
8310 Object* writable;
8311 MaybeObject* maybe = EnsureWritableFastElements();
8312 if (!maybe->ToObject(&writable)) return maybe;
8313 backing_store = FixedArray::cast(writable);
lrn@chromium.org303ada72010-10-27 09:33:13 +00008314 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008315 uint32_t length = static_cast<uint32_t>(backing_store->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008316
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008317 if (check_prototype &&
whesse@chromium.org7b260152011-06-20 15:33:18 +00008318 (index >= length || backing_store->get(index)->IsTheHole())) {
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00008319 bool found;
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008320 MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
8321 value,
8322 &found,
8323 strict_mode);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00008324 if (found) return result;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008325 }
8326
ager@chromium.org04921a82011-06-27 13:21:41 +00008327 // Check whether there is extra space in fixed array.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008328 if (index < length) {
8329 backing_store->set(index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008330 if (IsJSArray()) {
8331 // Update the length of the array if needed.
8332 uint32_t array_length = 0;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008333 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008334 if (index >= array_length) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008335 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008336 }
8337 }
ager@chromium.orgbdf2f942008-10-17 07:23:00 +00008338 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008339 }
8340
8341 // Allow gap in fast case.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008342 if ((index - length) < kMaxGap) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008343 // Try allocating extra space.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008344 int new_capacity = NewElementsCapacity(index + 1);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00008345 if (!ShouldConvertToSlowElements(new_capacity)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008346 ASSERT(static_cast<uint32_t>(new_capacity) > index);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008347 Object* new_elements;
8348 MaybeObject* maybe =
8349 SetFastElementsCapacityAndLength(new_capacity, index + 1);
8350 if (!maybe->ToObject(&new_elements)) return maybe;
8351 FixedArray::cast(new_elements)->set(index, value);
ager@chromium.orgbdf2f942008-10-17 07:23:00 +00008352 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008353 }
8354 }
8355
8356 // Otherwise default to slow case.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008357 MaybeObject* result = NormalizeElements();
8358 if (result->IsFailure()) return result;
8359 return SetDictionaryElement(index, value, strict_mode, check_prototype);
8360}
8361
8362
8363MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
8364 Object* value,
8365 StrictModeFlag strict_mode,
8366 bool check_prototype) {
8367 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
8368 Isolate* isolate = GetIsolate();
8369 Heap* heap = isolate->heap();
8370
8371 // Insert element in the dictionary.
8372 FixedArray* elements = FixedArray::cast(this->elements());
8373 bool is_arguments =
8374 (elements->map() == heap->non_strict_arguments_elements_map());
8375 NumberDictionary* dictionary = NULL;
8376 if (is_arguments) {
8377 dictionary = NumberDictionary::cast(elements->get(1));
8378 } else {
8379 dictionary = NumberDictionary::cast(elements);
lrn@chromium.org303ada72010-10-27 09:33:13 +00008380 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008381
8382 int entry = dictionary->FindEntry(index);
8383 if (entry != NumberDictionary::kNotFound) {
8384 Object* element = dictionary->ValueAt(entry);
8385 PropertyDetails details = dictionary->DetailsAt(entry);
8386 if (details.type() == CALLBACKS) {
8387 return SetElementWithCallback(element, index, value, this, strict_mode);
8388 } else {
8389 dictionary->UpdateMaxNumberKey(index);
8390 // If put fails in strict mode, throw an exception.
8391 if (!dictionary->ValueAtPut(entry, value) && strict_mode == kStrictMode) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008392 Handle<Object> holder(this);
ager@chromium.org04921a82011-06-27 13:21:41 +00008393 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008394 Handle<Object> args[2] = { number, holder };
8395 Handle<Object> error =
8396 isolate->factory()->NewTypeError("strict_read_only_property",
8397 HandleVector(args, 2));
8398 return isolate->Throw(*error);
8399 }
8400 }
8401 } else {
8402 // Index not already used. Look for an accessor in the prototype chain.
8403 if (check_prototype) {
8404 bool found;
8405 MaybeObject* result =
8406 SetElementWithCallbackSetterInPrototypes(
8407 index, value, &found, strict_mode);
8408 if (found) return result;
8409 }
8410 // When we set the is_extensible flag to false we always force the
8411 // element into dictionary mode (and force them to stay there).
8412 if (!map()->is_extensible()) {
8413 if (strict_mode == kNonStrictMode) {
8414 return isolate->heap()->undefined_value();
8415 } else {
8416 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
8417 Handle<String> name = isolate->factory()->NumberToString(number);
8418 Handle<Object> args[1] = { name };
8419 Handle<Object> error =
8420 isolate->factory()->NewTypeError("object_not_extensible",
8421 HandleVector(args, 1));
8422 return isolate->Throw(*error);
8423 }
8424 }
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008425 FixedArrayBase* new_dictionary;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008426 MaybeObject* maybe = dictionary->AtNumberPut(index, value);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008427 if (!maybe->To<FixedArrayBase>(&new_dictionary)) return maybe;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008428 if (dictionary != NumberDictionary::cast(new_dictionary)) {
8429 if (is_arguments) {
8430 elements->set(1, new_dictionary);
8431 } else {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008432 set_elements(new_dictionary);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008433 }
8434 dictionary = NumberDictionary::cast(new_dictionary);
8435 }
8436 }
8437
8438 // Update the array length if this JSObject is an array.
8439 if (IsJSArray()) {
8440 MaybeObject* result =
8441 JSArray::cast(this)->JSArrayUpdateLengthFromIndex(index, value);
8442 if (result->IsFailure()) return result;
8443 }
8444
8445 // Attempt to put this object back in fast case.
8446 if (ShouldConvertToFastElements()) {
8447 uint32_t new_length = 0;
8448 if (IsJSArray()) {
8449 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
8450 } else {
8451 new_length = dictionary->max_number_key() + 1;
8452 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008453 MaybeObject* result = CanConvertToFastDoubleElements()
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008454 ? SetFastDoubleElementsCapacityAndLength(new_length, new_length)
8455 : SetFastElementsCapacityAndLength(new_length, new_length);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008456 if (result->IsFailure()) return result;
8457#ifdef DEBUG
8458 if (FLAG_trace_normalization) {
8459 PrintF("Object elements are fast case again:\n");
8460 Print();
8461 }
8462#endif
8463 }
8464 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008465}
8466
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00008467
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008468MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement(
8469 uint32_t index,
8470 Object* value,
8471 StrictModeFlag strict_mode,
8472 bool check_prototype) {
8473 ASSERT(HasFastDoubleElements());
8474
8475 FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
8476 uint32_t elms_length = static_cast<uint32_t>(elms->length());
8477
8478 // If storing to an element that isn't in the array, pass the store request
8479 // up the prototype chain before storing in the receiver's elements.
8480 if (check_prototype &&
8481 (index >= elms_length || elms->is_the_hole(index))) {
8482 bool found;
8483 MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
8484 value,
8485 &found,
8486 strict_mode);
8487 if (found) return result;
8488 }
8489
8490 // If the value object is not a heap number, switch to fast elements and try
8491 // again.
8492 bool value_is_smi = value->IsSmi();
8493 if (!value->IsNumber()) {
8494 Object* obj;
8495 uint32_t length = elms_length;
8496 if (IsJSArray()) {
8497 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
8498 }
8499 MaybeObject* maybe_obj =
8500 SetFastElementsCapacityAndLength(elms_length, length);
8501 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8502 return SetFastElement(index, value, strict_mode, check_prototype);
8503 }
8504
8505 double double_value = value_is_smi
8506 ? static_cast<double>(Smi::cast(value)->value())
8507 : HeapNumber::cast(value)->value();
8508
8509 // Check whether there is extra space in the fixed array.
8510 if (index < elms_length) {
8511 elms->set(index, double_value);
8512 if (IsJSArray()) {
8513 // Update the length of the array if needed.
8514 uint32_t array_length = 0;
8515 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
8516 if (index >= array_length) {
8517 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
8518 }
8519 }
8520 return value;
8521 }
8522
8523 // Allow gap in fast case.
8524 if ((index - elms_length) < kMaxGap) {
8525 // Try allocating extra space.
8526 int new_capacity = NewElementsCapacity(index+1);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00008527 if (!ShouldConvertToSlowElements(new_capacity)) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008528 ASSERT(static_cast<uint32_t>(new_capacity) > index);
8529 Object* obj;
8530 { MaybeObject* maybe_obj =
8531 SetFastDoubleElementsCapacityAndLength(new_capacity,
8532 index + 1);
8533 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8534 }
8535 FixedDoubleArray::cast(elements())->set(index, double_value);
8536 return value;
8537 }
8538 }
8539
8540 // Otherwise default to slow case.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008541 ASSERT(HasFastDoubleElements());
8542 ASSERT(map()->has_fast_double_elements());
8543 ASSERT(elements()->IsFixedDoubleArray());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008544 Object* obj;
8545 { MaybeObject* maybe_obj = NormalizeElements();
8546 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8547 }
8548 ASSERT(HasDictionaryElements());
8549 return SetElement(index, value, strict_mode, check_prototype);
8550}
8551
8552
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008553MaybeObject* JSObject::SetElement(uint32_t index,
8554 Object* value,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008555 StrictModeFlag strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008556 bool check_prototype) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008557 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008558 if (IsAccessCheckNeeded()) {
8559 Heap* heap = GetHeap();
8560 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008561 HandleScope scope(heap->isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008562 Handle<Object> value_handle(value);
8563 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
8564 return *value_handle;
8565 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008566 }
8567
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008568 if (IsJSGlobalProxy()) {
8569 Object* proto = GetPrototype();
8570 if (proto->IsNull()) return value;
8571 ASSERT(proto->IsJSGlobalObject());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008572 return JSObject::cast(proto)->SetElement(index,
8573 value,
8574 strict_mode,
8575 check_prototype);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008576 }
8577
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008578 // Check for lookup interceptor
8579 if (HasIndexedInterceptor()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008580 return SetElementWithInterceptor(index,
8581 value,
8582 strict_mode,
8583 check_prototype);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008584 }
8585
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008586 return SetElementWithoutInterceptor(index,
8587 value,
8588 strict_mode,
8589 check_prototype);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008590}
8591
8592
lrn@chromium.org303ada72010-10-27 09:33:13 +00008593MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008594 Object* value,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008595 StrictModeFlag strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008596 bool check_prototype) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008597 Isolate* isolate = GetIsolate();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008598 switch (GetElementsKind()) {
8599 case FAST_ELEMENTS:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008600 return SetFastElement(index, value, strict_mode, check_prototype);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008601 case FAST_DOUBLE_ELEMENTS:
8602 return SetFastDoubleElement(index, value, strict_mode, check_prototype);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008603 case EXTERNAL_PIXEL_ELEMENTS: {
8604 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008605 return pixels->SetValue(index, value);
8606 }
ager@chromium.org3811b432009-10-28 14:53:37 +00008607 case EXTERNAL_BYTE_ELEMENTS: {
8608 ExternalByteArray* array = ExternalByteArray::cast(elements());
8609 return array->SetValue(index, value);
8610 }
8611 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
8612 ExternalUnsignedByteArray* array =
8613 ExternalUnsignedByteArray::cast(elements());
8614 return array->SetValue(index, value);
8615 }
8616 case EXTERNAL_SHORT_ELEMENTS: {
8617 ExternalShortArray* array = ExternalShortArray::cast(elements());
8618 return array->SetValue(index, value);
8619 }
8620 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
8621 ExternalUnsignedShortArray* array =
8622 ExternalUnsignedShortArray::cast(elements());
8623 return array->SetValue(index, value);
8624 }
8625 case EXTERNAL_INT_ELEMENTS: {
8626 ExternalIntArray* array = ExternalIntArray::cast(elements());
8627 return array->SetValue(index, value);
8628 }
8629 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
8630 ExternalUnsignedIntArray* array =
8631 ExternalUnsignedIntArray::cast(elements());
8632 return array->SetValue(index, value);
8633 }
8634 case EXTERNAL_FLOAT_ELEMENTS: {
8635 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
8636 return array->SetValue(index, value);
8637 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00008638 case EXTERNAL_DOUBLE_ELEMENTS: {
8639 ExternalDoubleArray* array = ExternalDoubleArray::cast(elements());
8640 return array->SetValue(index, value);
8641 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008642 case DICTIONARY_ELEMENTS:
8643 return SetDictionaryElement(index, value, strict_mode, check_prototype);
8644 case NON_STRICT_ARGUMENTS_ELEMENTS: {
8645 FixedArray* parameter_map = FixedArray::cast(elements());
8646 uint32_t length = parameter_map->length();
8647 Object* probe =
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00008648 (index < length - 2) ? parameter_map->get(index + 2) : NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008649 if (probe != NULL && !probe->IsTheHole()) {
8650 Context* context = Context::cast(parameter_map->get(0));
8651 int context_index = Smi::cast(probe)->value();
8652 ASSERT(!context->get(context_index)->IsTheHole());
8653 context->set(context_index, value);
8654 return value;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008655 } else {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008656 // Object is not mapped, defer to the arguments.
8657 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
8658 if (arguments->IsDictionary()) {
8659 return SetDictionaryElement(index, value, strict_mode,
8660 check_prototype);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008661 } else {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008662 return SetFastElement(index, value, strict_mode, check_prototype);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008663 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008664 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008665 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008666 }
8667 // All possible cases have been handled above. Add a return to avoid the
8668 // complaints from the compiler.
8669 UNREACHABLE();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008670 return isolate->heap()->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008671}
8672
8673
lrn@chromium.org303ada72010-10-27 09:33:13 +00008674MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
8675 Object* value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008676 uint32_t old_len = 0;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008677 CHECK(length()->ToArrayIndex(&old_len));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008678 // Check to see if we need to update the length. For now, we make
8679 // sure that the length stays within 32-bits (unsigned).
8680 if (index >= old_len && index != 0xffffffff) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00008681 Object* len;
8682 { MaybeObject* maybe_len =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008683 GetHeap()->NumberFromDouble(static_cast<double>(index) + 1);
lrn@chromium.org303ada72010-10-27 09:33:13 +00008684 if (!maybe_len->ToObject(&len)) return maybe_len;
8685 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008686 set_length(len);
8687 }
8688 return value;
8689}
8690
8691
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00008692MaybeObject* JSObject::GetElementWithInterceptor(Object* receiver,
lrn@chromium.org303ada72010-10-27 09:33:13 +00008693 uint32_t index) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008694 Isolate* isolate = GetIsolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008695 // Make sure that the top context does not change when doing
8696 // callbacks or interceptor calls.
8697 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008698 HandleScope scope(isolate);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00008699 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor(), isolate);
8700 Handle<Object> this_handle(receiver, isolate);
8701 Handle<JSObject> holder_handle(this, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008702 if (!interceptor->getter()->IsUndefined()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008703 v8::IndexedPropertyGetter getter =
8704 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008705 LOG(isolate,
8706 ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
8707 CustomArguments args(isolate, interceptor->data(), receiver, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00008708 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008709 v8::Handle<v8::Value> result;
8710 {
8711 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008712 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008713 result = getter(index, info);
8714 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008715 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008716 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
8717 }
8718
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00008719 Heap* heap = holder_handle->GetHeap();
8720 ElementsAccessor* handler = holder_handle->GetElementsAccessor();
8721 MaybeObject* raw_result = handler->GetWithReceiver(*holder_handle,
8722 *this_handle,
8723 index);
8724 if (raw_result != heap->the_hole_value()) return raw_result;
8725
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008726 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008727
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00008728 Object* pt = holder_handle->GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008729 if (pt == heap->null_value()) return heap->undefined_value();
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00008730 return pt->GetElementWithReceiver(*this_handle, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008731}
8732
8733
8734bool JSObject::HasDenseElements() {
8735 int capacity = 0;
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008736 int used = 0;
8737 GetElementsCapacityAndUsage(&capacity, &used);
8738 return (capacity == 0) || (used > (capacity / 2));
8739}
8740
8741
8742void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
8743 *capacity = 0;
8744 *used = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008745
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008746 FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements());
8747 FixedArray* backing_store = NULL;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008748 switch (GetElementsKind()) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008749 case NON_STRICT_ARGUMENTS_ELEMENTS:
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008750 backing_store_base =
8751 FixedArray::cast(FixedArray::cast(backing_store_base)->get(1));
8752 backing_store = FixedArray::cast(backing_store_base);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008753 if (backing_store->IsDictionary()) {
8754 NumberDictionary* dictionary = NumberDictionary::cast(backing_store);
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008755 *capacity = dictionary->Capacity();
8756 *used = dictionary->NumberOfElements();
whesse@chromium.org7b260152011-06-20 15:33:18 +00008757 break;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008758 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008759 // Fall through.
8760 case FAST_ELEMENTS:
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008761 backing_store = FixedArray::cast(backing_store_base);
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008762 *capacity = backing_store->length();
8763 for (int i = 0; i < *capacity; ++i) {
8764 if (!backing_store->get(i)->IsTheHole()) ++(*used);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008765 }
8766 break;
8767 case DICTIONARY_ELEMENTS: {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008768 NumberDictionary* dictionary =
8769 NumberDictionary::cast(FixedArray::cast(elements()));
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008770 *capacity = dictionary->Capacity();
8771 *used = dictionary->NumberOfElements();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008772 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008773 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008774 case FAST_DOUBLE_ELEMENTS: {
8775 FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008776 *capacity = elms->length();
8777 for (int i = 0; i < *capacity; i++) {
8778 if (!elms->is_the_hole(i)) ++(*used);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008779 }
8780 break;
8781 }
ager@chromium.org3811b432009-10-28 14:53:37 +00008782 case EXTERNAL_BYTE_ELEMENTS:
8783 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
8784 case EXTERNAL_SHORT_ELEMENTS:
8785 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
8786 case EXTERNAL_INT_ELEMENTS:
8787 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00008788 case EXTERNAL_FLOAT_ELEMENTS:
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008789 case EXTERNAL_DOUBLE_ELEMENTS:
8790 case EXTERNAL_PIXEL_ELEMENTS:
8791 // External arrays are considered 100% used.
8792 ExternalArray* external_array = ExternalArray::cast(elements());
8793 *capacity = external_array->length();
8794 *used = external_array->length();
8795 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008796 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008797}
8798
8799
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008800bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008801 STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
8802 kMaxUncheckedFastElementsLength);
8803 if (new_capacity <= kMaxUncheckedOldFastElementsLength ||
8804 (new_capacity <= kMaxUncheckedFastElementsLength &&
8805 GetHeap()->InNewSpace(this))) {
8806 return false;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008807 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008808 // If the fast-case backing storage takes up roughly three times as
8809 // much space (in machine words) as a dictionary backing storage
8810 // would, the object should have slow elements.
8811 int old_capacity = 0;
8812 int used_elements = 0;
8813 GetElementsCapacityAndUsage(&old_capacity, &used_elements);
8814 int dictionary_size = NumberDictionary::ComputeCapacity(used_elements) *
8815 NumberDictionary::kEntrySize;
8816 return 3 * dictionary_size <= new_capacity;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008817}
8818
8819
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008820bool JSObject::ShouldConvertToFastElements() {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008821 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008822 // If the elements are sparse, we should not go back to fast case.
8823 if (!HasDenseElements()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008824 // An object requiring access checks is never allowed to have fast
8825 // elements. If it had fast elements we would skip security checks.
8826 if (IsAccessCheckNeeded()) return false;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008827
8828 FixedArray* elements = FixedArray::cast(this->elements());
8829 NumberDictionary* dictionary = NULL;
8830 if (elements->map() == GetHeap()->non_strict_arguments_elements_map()) {
8831 dictionary = NumberDictionary::cast(elements->get(1));
8832 } else {
8833 dictionary = NumberDictionary::cast(elements);
8834 }
8835 // If an element has been added at a very high index in the elements
8836 // dictionary, we cannot go back to fast case.
8837 if (dictionary->requires_slow_elements()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008838 // If the dictionary backing storage takes up roughly half as much
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008839 // space (in machine words) as a fast-case backing storage would,
8840 // the object should have fast elements.
8841 uint32_t array_size = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008842 if (IsJSArray()) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008843 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008844 } else {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008845 array_size = dictionary->max_number_key();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008846 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008847 uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
8848 NumberDictionary::kEntrySize;
8849 return 2 * dictionary_size >= array_size;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008850}
8851
8852
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008853bool JSObject::CanConvertToFastDoubleElements() {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008854 if (FLAG_unbox_double_arrays) {
8855 ASSERT(HasDictionaryElements());
8856 NumberDictionary* dictionary = NumberDictionary::cast(elements());
8857 for (int i = 0; i < dictionary->Capacity(); i++) {
8858 Object* key = dictionary->KeyAt(i);
8859 if (key->IsNumber()) {
8860 if (!dictionary->ValueAt(i)->IsNumber()) return false;
8861 }
8862 }
8863 return true;
8864 } else {
8865 return false;
8866 }
8867}
8868
8869
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008870// Certain compilers request function template instantiation when they
8871// see the definition of the other template functions in the
8872// class. This requires us to have the template functions put
8873// together, so even though this function belongs in objects-debug.cc,
8874// we keep it here instead to satisfy certain compilers.
whesse@chromium.org023421e2010-12-21 12:19:12 +00008875#ifdef OBJECT_PRINT
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008876template<typename Shape, typename Key>
whesse@chromium.org023421e2010-12-21 12:19:12 +00008877void Dictionary<Shape, Key>::Print(FILE* out) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008878 int capacity = HashTable<Shape, Key>::Capacity();
8879 for (int i = 0; i < capacity; i++) {
8880 Object* k = HashTable<Shape, Key>::KeyAt(i);
8881 if (HashTable<Shape, Key>::IsKey(k)) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00008882 PrintF(out, " ");
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008883 if (k->IsString()) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00008884 String::cast(k)->StringPrint(out);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008885 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +00008886 k->ShortPrint(out);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008887 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00008888 PrintF(out, ": ");
8889 ValueAt(i)->ShortPrint(out);
8890 PrintF(out, "\n");
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008891 }
8892 }
8893}
8894#endif
8895
8896
8897template<typename Shape, typename Key>
8898void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008899 int pos = 0;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008900 int capacity = HashTable<Shape, Key>::Capacity();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008901 AssertNoAllocation no_gc;
8902 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008903 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008904 Object* k = Dictionary<Shape, Key>::KeyAt(i);
8905 if (Dictionary<Shape, Key>::IsKey(k)) {
8906 elements->set(pos++, ValueAt(i), mode);
8907 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008908 }
8909 ASSERT(pos == elements->length());
8910}
8911
8912
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008913InterceptorInfo* JSObject::GetNamedInterceptor() {
8914 ASSERT(map()->has_named_interceptor());
8915 JSFunction* constructor = JSFunction::cast(map()->constructor());
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008916 ASSERT(constructor->shared()->IsApiFunction());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008917 Object* result =
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008918 constructor->shared()->get_api_func_data()->named_property_handler();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008919 return InterceptorInfo::cast(result);
8920}
8921
8922
8923InterceptorInfo* JSObject::GetIndexedInterceptor() {
8924 ASSERT(map()->has_indexed_interceptor());
8925 JSFunction* constructor = JSFunction::cast(map()->constructor());
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008926 ASSERT(constructor->shared()->IsApiFunction());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008927 Object* result =
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008928 constructor->shared()->get_api_func_data()->indexed_property_handler();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008929 return InterceptorInfo::cast(result);
8930}
8931
8932
lrn@chromium.org303ada72010-10-27 09:33:13 +00008933MaybeObject* JSObject::GetPropertyPostInterceptor(
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008934 JSReceiver* receiver,
lrn@chromium.org303ada72010-10-27 09:33:13 +00008935 String* name,
8936 PropertyAttributes* attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008937 // Check local property in holder, ignore interceptor.
8938 LookupResult result;
8939 LocalLookupRealNamedProperty(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00008940 if (result.IsProperty()) {
8941 return GetProperty(receiver, &result, name, attributes);
8942 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008943 // Continue searching via the prototype chain.
8944 Object* pt = GetPrototype();
8945 *attributes = ABSENT;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008946 if (pt->IsNull()) return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008947 return pt->GetPropertyWithReceiver(receiver, name, attributes);
8948}
8949
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008950
lrn@chromium.org303ada72010-10-27 09:33:13 +00008951MaybeObject* JSObject::GetLocalPropertyPostInterceptor(
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008952 JSReceiver* receiver,
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008953 String* name,
8954 PropertyAttributes* attributes) {
8955 // Check local property in holder, ignore interceptor.
8956 LookupResult result;
8957 LocalLookupRealNamedProperty(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00008958 if (result.IsProperty()) {
8959 return GetProperty(receiver, &result, name, attributes);
8960 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008961 return GetHeap()->undefined_value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008962}
8963
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008964
lrn@chromium.org303ada72010-10-27 09:33:13 +00008965MaybeObject* JSObject::GetPropertyWithInterceptor(
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008966 JSReceiver* receiver,
ager@chromium.orge2902be2009-06-08 12:21:35 +00008967 String* name,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00008968 PropertyAttributes* attributes) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008969 Isolate* isolate = GetIsolate();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008970 InterceptorInfo* interceptor = GetNamedInterceptor();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008971 HandleScope scope(isolate);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008972 Handle<JSReceiver> receiver_handle(receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008973 Handle<JSObject> holder_handle(this);
8974 Handle<String> name_handle(name);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008975
8976 if (!interceptor->getter()->IsUndefined()) {
8977 v8::NamedPropertyGetter getter =
8978 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008979 LOG(isolate,
8980 ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
8981 CustomArguments args(isolate, interceptor->data(), receiver, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00008982 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008983 v8::Handle<v8::Value> result;
8984 {
8985 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008986 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008987 result = getter(v8::Utils::ToLocal(name_handle), info);
8988 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008989 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008990 if (!result.IsEmpty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008991 *attributes = NONE;
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00008992 return *v8::Utils::OpenHandle(*result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008993 }
8994 }
8995
lrn@chromium.org303ada72010-10-27 09:33:13 +00008996 MaybeObject* result = holder_handle->GetPropertyPostInterceptor(
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008997 *receiver_handle,
8998 *name_handle,
8999 attributes);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009000 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
ager@chromium.orge2902be2009-06-08 12:21:35 +00009001 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009002}
9003
9004
9005bool JSObject::HasRealNamedProperty(String* key) {
9006 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009007 if (IsAccessCheckNeeded()) {
9008 Heap* heap = GetHeap();
9009 if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
9010 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
9011 return false;
9012 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009013 }
9014
9015 LookupResult result;
9016 LocalLookupRealNamedProperty(key, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00009017 return result.IsProperty() && (result.type() != INTERCEPTOR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009018}
9019
9020
9021bool JSObject::HasRealElementProperty(uint32_t index) {
9022 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009023 if (IsAccessCheckNeeded()) {
9024 Heap* heap = GetHeap();
9025 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
9026 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
9027 return false;
9028 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009029 }
9030
9031 // Handle [] on String objects.
9032 if (this->IsStringObjectWithCharacterAt(index)) return true;
9033
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009034 switch (GetElementsKind()) {
9035 case FAST_ELEMENTS: {
9036 uint32_t length = IsJSArray() ?
9037 static_cast<uint32_t>(
9038 Smi::cast(JSArray::cast(this)->length())->value()) :
9039 static_cast<uint32_t>(FixedArray::cast(elements())->length());
9040 return (index < length) &&
9041 !FixedArray::cast(elements())->get(index)->IsTheHole();
9042 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00009043 case FAST_DOUBLE_ELEMENTS: {
9044 uint32_t length = IsJSArray() ?
9045 static_cast<uint32_t>(
9046 Smi::cast(JSArray::cast(this)->length())->value()) :
9047 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
9048 return (index < length) &&
9049 !FixedDoubleArray::cast(elements())->is_the_hole(index);
9050 break;
9051 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00009052 case EXTERNAL_PIXEL_ELEMENTS: {
9053 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009054 return index < static_cast<uint32_t>(pixels->length());
9055 }
ager@chromium.org3811b432009-10-28 14:53:37 +00009056 case EXTERNAL_BYTE_ELEMENTS:
9057 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
9058 case EXTERNAL_SHORT_ELEMENTS:
9059 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
9060 case EXTERNAL_INT_ELEMENTS:
9061 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00009062 case EXTERNAL_FLOAT_ELEMENTS:
9063 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00009064 ExternalArray* array = ExternalArray::cast(elements());
9065 return index < static_cast<uint32_t>(array->length());
9066 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009067 case DICTIONARY_ELEMENTS: {
9068 return element_dictionary()->FindEntry(index)
9069 != NumberDictionary::kNotFound;
9070 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009071 case NON_STRICT_ARGUMENTS_ELEMENTS:
9072 UNIMPLEMENTED();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009073 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009074 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009075 // All possibilities have been handled above already.
9076 UNREACHABLE();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009077 return GetHeap()->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009078}
9079
9080
9081bool JSObject::HasRealNamedCallbackProperty(String* key) {
9082 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009083 if (IsAccessCheckNeeded()) {
9084 Heap* heap = GetHeap();
9085 if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
9086 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
9087 return false;
9088 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009089 }
9090
9091 LookupResult result;
9092 LocalLookupRealNamedProperty(key, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00009093 return result.IsProperty() && (result.type() == CALLBACKS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009094}
9095
9096
9097int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
9098 if (HasFastProperties()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009099 DescriptorArray* descs = map()->instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009100 int result = 0;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009101 for (int i = 0; i < descs->number_of_descriptors(); i++) {
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00009102 PropertyDetails details(descs->GetDetails(i));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009103 if (details.IsProperty() && (details.attributes() & filter) == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009104 result++;
9105 }
9106 }
9107 return result;
9108 } else {
9109 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
9110 }
9111}
9112
9113
9114int JSObject::NumberOfEnumProperties() {
9115 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
9116}
9117
9118
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009119void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009120 Object* temp = get(i);
9121 set(i, get(j));
9122 set(j, temp);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009123 if (this != numbers) {
9124 temp = numbers->get(i);
9125 numbers->set(i, numbers->get(j));
9126 numbers->set(j, temp);
9127 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009128}
9129
9130
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009131static void InsertionSortPairs(FixedArray* content,
9132 FixedArray* numbers,
9133 int len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009134 for (int i = 1; i < len; i++) {
9135 int j = i;
9136 while (j > 0 &&
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009137 (NumberToUint32(numbers->get(j - 1)) >
9138 NumberToUint32(numbers->get(j)))) {
9139 content->SwapPairs(numbers, j - 1, j);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009140 j--;
9141 }
9142 }
9143}
9144
9145
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009146void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009147 // In-place heap sort.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009148 ASSERT(content->length() == numbers->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009149
9150 // Bottom-up max-heap construction.
9151 for (int i = 1; i < len; ++i) {
9152 int child_index = i;
9153 while (child_index > 0) {
9154 int parent_index = ((child_index + 1) >> 1) - 1;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009155 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
9156 uint32_t child_value = NumberToUint32(numbers->get(child_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009157 if (parent_value < child_value) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009158 content->SwapPairs(numbers, parent_index, child_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009159 } else {
9160 break;
9161 }
9162 child_index = parent_index;
9163 }
9164 }
9165
9166 // Extract elements and create sorted array.
9167 for (int i = len - 1; i > 0; --i) {
9168 // Put max element at the back of the array.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009169 content->SwapPairs(numbers, 0, i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009170 // Sift down the new top element.
9171 int parent_index = 0;
9172 while (true) {
9173 int child_index = ((parent_index + 1) << 1) - 1;
9174 if (child_index >= i) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009175 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
9176 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
9177 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009178 if (child_index + 1 >= i || child1_value > child2_value) {
9179 if (parent_value > child1_value) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009180 content->SwapPairs(numbers, parent_index, child_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009181 parent_index = child_index;
9182 } else {
9183 if (parent_value > child2_value) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009184 content->SwapPairs(numbers, parent_index, child_index + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009185 parent_index = child_index + 1;
9186 }
9187 }
9188 }
9189}
9190
9191
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009192// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
9193void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
9194 ASSERT(this->length() == numbers->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009195 // For small arrays, simply use insertion sort.
9196 if (len <= 10) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009197 InsertionSortPairs(this, numbers, len);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009198 return;
9199 }
9200 // Check the range of indices.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009201 uint32_t min_index = NumberToUint32(numbers->get(0));
9202 uint32_t max_index = min_index;
9203 uint32_t i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009204 for (i = 1; i < len; i++) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009205 if (NumberToUint32(numbers->get(i)) < min_index) {
9206 min_index = NumberToUint32(numbers->get(i));
9207 } else if (NumberToUint32(numbers->get(i)) > max_index) {
9208 max_index = NumberToUint32(numbers->get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009209 }
9210 }
9211 if (max_index - min_index + 1 == len) {
9212 // Indices form a contiguous range, unless there are duplicates.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009213 // Do an in-place linear time sort assuming distinct numbers, but
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009214 // avoid hanging in case they are not.
9215 for (i = 0; i < len; i++) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009216 uint32_t p;
9217 uint32_t j = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009218 // While the current element at i is not at its correct position p,
9219 // swap the elements at these two positions.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009220 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009221 j++ < len) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009222 SwapPairs(numbers, i, p);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009223 }
9224 }
9225 } else {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009226 HeapSortPairs(this, numbers, len);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009227 return;
9228 }
9229}
9230
9231
9232// Fill in the names of local properties into the supplied storage. The main
9233// purpose of this function is to provide reflection information for the object
9234// mirrors.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009235void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009236 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009237 if (HasFastProperties()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009238 DescriptorArray* descs = map()->instance_descriptors();
9239 for (int i = 0; i < descs->number_of_descriptors(); i++) {
9240 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009241 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009242 ASSERT(storage->length() >= index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009243 } else {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00009244 property_dictionary()->CopyKeysTo(storage,
9245 index,
9246 StringDictionary::UNSORTED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009247 }
9248}
9249
9250
9251int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
9252 return GetLocalElementKeys(NULL, filter);
9253}
9254
9255
9256int JSObject::NumberOfEnumElements() {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009257 // Fast case for objects with no elements.
9258 if (!IsJSValue() && HasFastElements()) {
9259 uint32_t length = IsJSArray() ?
9260 static_cast<uint32_t>(
9261 Smi::cast(JSArray::cast(this)->length())->value()) :
9262 static_cast<uint32_t>(FixedArray::cast(elements())->length());
9263 if (length == 0) return 0;
9264 }
9265 // Compute the number of enumerable elements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009266 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
9267}
9268
9269
9270int JSObject::GetLocalElementKeys(FixedArray* storage,
9271 PropertyAttributes filter) {
9272 int counter = 0;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009273 switch (GetElementsKind()) {
9274 case FAST_ELEMENTS: {
9275 int length = IsJSArray() ?
9276 Smi::cast(JSArray::cast(this)->length())->value() :
9277 FixedArray::cast(elements())->length();
9278 for (int i = 0; i < length; i++) {
9279 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
9280 if (storage != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009281 storage->set(counter, Smi::FromInt(i));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009282 }
9283 counter++;
9284 }
9285 }
9286 ASSERT(!storage || storage->length() >= counter);
9287 break;
9288 }
rossberg@chromium.org717967f2011-07-20 13:44:42 +00009289 case FAST_DOUBLE_ELEMENTS: {
9290 int length = IsJSArray() ?
9291 Smi::cast(JSArray::cast(this)->length())->value() :
9292 FixedDoubleArray::cast(elements())->length();
9293 for (int i = 0; i < length; i++) {
9294 if (!FixedDoubleArray::cast(elements())->is_the_hole(i)) {
9295 if (storage != NULL) {
9296 storage->set(counter, Smi::FromInt(i));
9297 }
9298 counter++;
9299 }
9300 }
9301 ASSERT(!storage || storage->length() >= counter);
9302 break;
9303 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00009304 case EXTERNAL_PIXEL_ELEMENTS: {
9305 int length = ExternalPixelArray::cast(elements())->length();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009306 while (counter < length) {
9307 if (storage != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009308 storage->set(counter, Smi::FromInt(counter));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009309 }
9310 counter++;
9311 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009312 ASSERT(!storage || storage->length() >= counter);
9313 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009314 }
ager@chromium.org3811b432009-10-28 14:53:37 +00009315 case EXTERNAL_BYTE_ELEMENTS:
9316 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
9317 case EXTERNAL_SHORT_ELEMENTS:
9318 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
9319 case EXTERNAL_INT_ELEMENTS:
9320 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00009321 case EXTERNAL_FLOAT_ELEMENTS:
9322 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00009323 int length = ExternalArray::cast(elements())->length();
9324 while (counter < length) {
9325 if (storage != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009326 storage->set(counter, Smi::FromInt(counter));
ager@chromium.org3811b432009-10-28 14:53:37 +00009327 }
9328 counter++;
9329 }
9330 ASSERT(!storage || storage->length() >= counter);
9331 break;
9332 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009333 case DICTIONARY_ELEMENTS: {
9334 if (storage != NULL) {
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009335 element_dictionary()->CopyKeysTo(storage,
9336 filter,
9337 NumberDictionary::SORTED);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009338 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009339 counter += element_dictionary()->NumberOfElementsFilterAttributes(filter);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009340 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009341 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009342 case NON_STRICT_ARGUMENTS_ELEMENTS: {
9343 FixedArray* parameter_map = FixedArray::cast(elements());
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009344 int mapped_length = parameter_map->length() - 2;
whesse@chromium.org7b260152011-06-20 15:33:18 +00009345 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
9346 if (arguments->IsDictionary()) {
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009347 // Copy the keys from arguments first, because Dictionary::CopyKeysTo
9348 // will insert in storage starting at index 0.
whesse@chromium.org7b260152011-06-20 15:33:18 +00009349 NumberDictionary* dictionary = NumberDictionary::cast(arguments);
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009350 if (storage != NULL) {
9351 dictionary->CopyKeysTo(storage, filter, NumberDictionary::UNSORTED);
9352 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009353 counter += dictionary->NumberOfElementsFilterAttributes(filter);
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009354 for (int i = 0; i < mapped_length; ++i) {
9355 if (!parameter_map->get(i + 2)->IsTheHole()) {
9356 if (storage != NULL) storage->set(counter, Smi::FromInt(i));
whesse@chromium.org7b260152011-06-20 15:33:18 +00009357 ++counter;
9358 }
9359 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009360 if (storage != NULL) storage->SortPairs(storage, counter);
9361
9362 } else {
9363 int backing_length = arguments->length();
9364 int i = 0;
9365 for (; i < mapped_length; ++i) {
9366 if (!parameter_map->get(i + 2)->IsTheHole()) {
9367 if (storage != NULL) storage->set(counter, Smi::FromInt(i));
9368 ++counter;
9369 } else if (i < backing_length && !arguments->get(i)->IsTheHole()) {
9370 if (storage != NULL) storage->set(counter, Smi::FromInt(i));
9371 ++counter;
9372 }
9373 }
9374 for (; i < backing_length; ++i) {
9375 if (storage != NULL) storage->set(counter, Smi::FromInt(i));
9376 ++counter;
9377 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009378 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009379 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00009380 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009381 }
9382
9383 if (this->IsJSValue()) {
9384 Object* val = JSValue::cast(this)->value();
9385 if (val->IsString()) {
9386 String* str = String::cast(val);
9387 if (storage) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009388 for (int i = 0; i < str->length(); i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009389 storage->set(counter + i, Smi::FromInt(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009390 }
9391 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009392 counter += str->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009393 }
9394 }
9395 ASSERT(!storage || storage->length() == counter);
9396 return counter;
9397}
9398
9399
9400int JSObject::GetEnumElementKeys(FixedArray* storage) {
9401 return GetLocalElementKeys(storage,
9402 static_cast<PropertyAttributes>(DONT_ENUM));
9403}
9404
9405
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009406// StringKey simply carries a string object as key.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009407class StringKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009408 public:
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009409 explicit StringKey(String* string) :
9410 string_(string),
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009411 hash_(HashForObject(string)) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009412
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009413 bool IsMatch(Object* string) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009414 // We know that all entries in a hash table had their hash keys created.
9415 // Use that knowledge to have fast failure.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009416 if (hash_ != HashForObject(string)) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009417 return false;
9418 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009419 return string_->Equals(String::cast(string));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009420 }
9421
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009422 uint32_t Hash() { return hash_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009423
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009424 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009425
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009426 Object* AsObject() { return string_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009427
9428 String* string_;
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009429 uint32_t hash_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009430};
9431
ager@chromium.org381abbb2009-02-25 13:23:22 +00009432
9433// StringSharedKeys are used as keys in the eval cache.
9434class StringSharedKey : public HashTableKey {
9435 public:
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009436 StringSharedKey(String* source,
9437 SharedFunctionInfo* shared,
9438 StrictModeFlag strict_mode)
9439 : source_(source),
9440 shared_(shared),
9441 strict_mode_(strict_mode) { }
ager@chromium.org381abbb2009-02-25 13:23:22 +00009442
9443 bool IsMatch(Object* other) {
9444 if (!other->IsFixedArray()) return false;
9445 FixedArray* pair = FixedArray::cast(other);
9446 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
9447 if (shared != shared_) return false;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009448 StrictModeFlag strict_mode = static_cast<StrictModeFlag>(
9449 Smi::cast(pair->get(2))->value());
9450 if (strict_mode != strict_mode_) return false;
ager@chromium.org381abbb2009-02-25 13:23:22 +00009451 String* source = String::cast(pair->get(1));
9452 return source->Equals(source_);
9453 }
9454
ager@chromium.org381abbb2009-02-25 13:23:22 +00009455 static uint32_t StringSharedHashHelper(String* source,
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009456 SharedFunctionInfo* shared,
9457 StrictModeFlag strict_mode) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00009458 uint32_t hash = source->Hash();
9459 if (shared->HasSourceCode()) {
9460 // Instead of using the SharedFunctionInfo pointer in the hash
9461 // code computation, we use a combination of the hash of the
9462 // script source code and the start and end positions. We do
9463 // this to ensure that the cache entries can survive garbage
9464 // collection.
9465 Script* script = Script::cast(shared->script());
9466 hash ^= String::cast(script->source())->Hash();
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009467 if (strict_mode == kStrictMode) hash ^= 0x8000;
ager@chromium.org381abbb2009-02-25 13:23:22 +00009468 hash += shared->start_position();
9469 }
9470 return hash;
9471 }
9472
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009473 uint32_t Hash() {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009474 return StringSharedHashHelper(source_, shared_, strict_mode_);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009475 }
9476
9477 uint32_t HashForObject(Object* obj) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00009478 FixedArray* pair = FixedArray::cast(obj);
9479 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
9480 String* source = String::cast(pair->get(1));
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009481 StrictModeFlag strict_mode = static_cast<StrictModeFlag>(
9482 Smi::cast(pair->get(2))->value());
9483 return StringSharedHashHelper(source, shared, strict_mode);
ager@chromium.org381abbb2009-02-25 13:23:22 +00009484 }
9485
lrn@chromium.org303ada72010-10-27 09:33:13 +00009486 MUST_USE_RESULT MaybeObject* AsObject() {
9487 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009488 { MaybeObject* maybe_obj = source_->GetHeap()->AllocateFixedArray(3);
lrn@chromium.org303ada72010-10-27 09:33:13 +00009489 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9490 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00009491 FixedArray* pair = FixedArray::cast(obj);
9492 pair->set(0, shared_);
9493 pair->set(1, source_);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009494 pair->set(2, Smi::FromInt(strict_mode_));
ager@chromium.org381abbb2009-02-25 13:23:22 +00009495 return pair;
9496 }
9497
ager@chromium.org381abbb2009-02-25 13:23:22 +00009498 private:
9499 String* source_;
9500 SharedFunctionInfo* shared_;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009501 StrictModeFlag strict_mode_;
ager@chromium.org381abbb2009-02-25 13:23:22 +00009502};
9503
9504
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009505// RegExpKey carries the source and flags of a regular expression as key.
9506class RegExpKey : public HashTableKey {
9507 public:
9508 RegExpKey(String* string, JSRegExp::Flags flags)
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009509 : string_(string),
9510 flags_(Smi::FromInt(flags.value())) { }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009511
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009512 // Rather than storing the key in the hash table, a pointer to the
9513 // stored value is stored where the key should be. IsMatch then
9514 // compares the search key to the found object, rather than comparing
9515 // a key to a key.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009516 bool IsMatch(Object* obj) {
9517 FixedArray* val = FixedArray::cast(obj);
9518 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
9519 && (flags_ == val->get(JSRegExp::kFlagsIndex));
9520 }
9521
9522 uint32_t Hash() { return RegExpHash(string_, flags_); }
9523
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009524 Object* AsObject() {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009525 // Plain hash maps, which is where regexp keys are used, don't
9526 // use this function.
9527 UNREACHABLE();
9528 return NULL;
9529 }
9530
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009531 uint32_t HashForObject(Object* obj) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009532 FixedArray* val = FixedArray::cast(obj);
9533 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
9534 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
9535 }
9536
9537 static uint32_t RegExpHash(String* string, Smi* flags) {
9538 return string->Hash() + flags->value();
9539 }
9540
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009541 String* string_;
9542 Smi* flags_;
9543};
9544
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009545// Utf8SymbolKey carries a vector of chars as key.
9546class Utf8SymbolKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009547 public:
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009548 explicit Utf8SymbolKey(Vector<const char> string)
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009549 : string_(string), hash_field_(0) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009550
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009551 bool IsMatch(Object* string) {
9552 return String::cast(string)->IsEqualTo(string_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009553 }
9554
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009555 uint32_t Hash() {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009556 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009557 unibrow::Utf8InputBuffer<> buffer(string_.start(),
9558 static_cast<unsigned>(string_.length()));
9559 chars_ = buffer.Length();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009560 hash_field_ = String::ComputeHashField(&buffer, chars_);
9561 uint32_t result = hash_field_ >> String::kHashShift;
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009562 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
9563 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009564 }
9565
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009566 uint32_t HashForObject(Object* other) {
9567 return String::cast(other)->Hash();
9568 }
9569
lrn@chromium.org303ada72010-10-27 09:33:13 +00009570 MaybeObject* AsObject() {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009571 if (hash_field_ == 0) Hash();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009572 return Isolate::Current()->heap()->AllocateSymbol(
9573 string_, chars_, hash_field_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009574 }
9575
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009576 Vector<const char> string_;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009577 uint32_t hash_field_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009578 int chars_; // Caches the number of characters when computing the hash code.
9579};
9580
9581
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00009582template <typename Char>
9583class SequentialSymbolKey : public HashTableKey {
9584 public:
9585 explicit SequentialSymbolKey(Vector<const Char> string)
9586 : string_(string), hash_field_(0) { }
9587
9588 uint32_t Hash() {
9589 StringHasher hasher(string_.length());
9590
9591 // Very long strings have a trivial hash that doesn't inspect the
9592 // string contents.
9593 if (hasher.has_trivial_hash()) {
9594 hash_field_ = hasher.GetHashField();
9595 } else {
9596 int i = 0;
9597 // Do the iterative array index computation as long as there is a
9598 // chance this is an array index.
9599 while (i < string_.length() && hasher.is_array_index()) {
9600 hasher.AddCharacter(static_cast<uc32>(string_[i]));
9601 i++;
9602 }
9603
9604 // Process the remaining characters without updating the array
9605 // index.
9606 while (i < string_.length()) {
9607 hasher.AddCharacterNoIndex(static_cast<uc32>(string_[i]));
9608 i++;
9609 }
9610 hash_field_ = hasher.GetHashField();
9611 }
9612
9613 uint32_t result = hash_field_ >> String::kHashShift;
9614 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
9615 return result;
9616 }
9617
9618
9619 uint32_t HashForObject(Object* other) {
9620 return String::cast(other)->Hash();
9621 }
9622
9623 Vector<const Char> string_;
9624 uint32_t hash_field_;
9625};
9626
9627
9628
9629class AsciiSymbolKey : public SequentialSymbolKey<char> {
9630 public:
9631 explicit AsciiSymbolKey(Vector<const char> str)
9632 : SequentialSymbolKey<char>(str) { }
9633
9634 bool IsMatch(Object* string) {
9635 return String::cast(string)->IsAsciiEqualTo(string_);
9636 }
9637
9638 MaybeObject* AsObject() {
9639 if (hash_field_ == 0) Hash();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009640 return HEAP->AllocateAsciiSymbol(string_, hash_field_);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00009641 }
9642};
9643
9644
danno@chromium.org40cb8782011-05-25 07:58:50 +00009645class SubStringAsciiSymbolKey : public HashTableKey {
9646 public:
9647 explicit SubStringAsciiSymbolKey(Handle<SeqAsciiString> string,
9648 int from,
9649 int length)
9650 : string_(string), from_(from), length_(length) { }
9651
9652 uint32_t Hash() {
9653 ASSERT(length_ >= 0);
9654 ASSERT(from_ + length_ <= string_->length());
9655 StringHasher hasher(length_);
9656
9657 // Very long strings have a trivial hash that doesn't inspect the
9658 // string contents.
9659 if (hasher.has_trivial_hash()) {
9660 hash_field_ = hasher.GetHashField();
9661 } else {
9662 int i = 0;
9663 // Do the iterative array index computation as long as there is a
9664 // chance this is an array index.
9665 while (i < length_ && hasher.is_array_index()) {
9666 hasher.AddCharacter(static_cast<uc32>(
9667 string_->SeqAsciiStringGet(i + from_)));
9668 i++;
9669 }
9670
9671 // Process the remaining characters without updating the array
9672 // index.
9673 while (i < length_) {
9674 hasher.AddCharacterNoIndex(static_cast<uc32>(
9675 string_->SeqAsciiStringGet(i + from_)));
9676 i++;
9677 }
9678 hash_field_ = hasher.GetHashField();
9679 }
9680
9681 uint32_t result = hash_field_ >> String::kHashShift;
9682 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
9683 return result;
9684 }
9685
9686
9687 uint32_t HashForObject(Object* other) {
9688 return String::cast(other)->Hash();
9689 }
9690
9691 bool IsMatch(Object* string) {
9692 Vector<const char> chars(string_->GetChars() + from_, length_);
9693 return String::cast(string)->IsAsciiEqualTo(chars);
9694 }
9695
9696 MaybeObject* AsObject() {
9697 if (hash_field_ == 0) Hash();
9698 Vector<const char> chars(string_->GetChars() + from_, length_);
9699 return HEAP->AllocateAsciiSymbol(chars, hash_field_);
9700 }
9701
9702 private:
9703 Handle<SeqAsciiString> string_;
9704 int from_;
9705 int length_;
9706 uint32_t hash_field_;
9707};
9708
9709
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00009710class TwoByteSymbolKey : public SequentialSymbolKey<uc16> {
9711 public:
9712 explicit TwoByteSymbolKey(Vector<const uc16> str)
9713 : SequentialSymbolKey<uc16>(str) { }
9714
9715 bool IsMatch(Object* string) {
9716 return String::cast(string)->IsTwoByteEqualTo(string_);
9717 }
9718
9719 MaybeObject* AsObject() {
9720 if (hash_field_ == 0) Hash();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009721 return HEAP->AllocateTwoByteSymbol(string_, hash_field_);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00009722 }
9723};
9724
9725
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009726// SymbolKey carries a string/symbol object as key.
9727class SymbolKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009728 public:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009729 explicit SymbolKey(String* string)
9730 : string_(string) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009731
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009732 bool IsMatch(Object* string) {
9733 return String::cast(string)->Equals(string_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009734 }
9735
9736 uint32_t Hash() { return string_->Hash(); }
9737
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009738 uint32_t HashForObject(Object* other) {
9739 return String::cast(other)->Hash();
9740 }
9741
lrn@chromium.org303ada72010-10-27 09:33:13 +00009742 MaybeObject* AsObject() {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00009743 // Attempt to flatten the string, so that symbols will most often
9744 // be flat strings.
9745 string_ = string_->TryFlattenGetString();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009746 Heap* heap = string_->GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009747 // Transform string to symbol if possible.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009748 Map* map = heap->SymbolMapForString(string_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009749 if (map != NULL) {
9750 string_->set_map(map);
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009751 ASSERT(string_->IsSymbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009752 return string_;
9753 }
9754 // Otherwise allocate a new symbol.
9755 StringInputBuffer buffer(string_);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009756 return heap->AllocateInternalSymbol(&buffer,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009757 string_->length(),
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009758 string_->hash_field());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009759 }
9760
9761 static uint32_t StringHash(Object* obj) {
9762 return String::cast(obj)->Hash();
9763 }
9764
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009765 String* string_;
9766};
9767
9768
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009769template<typename Shape, typename Key>
9770void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009771 IteratePointers(v, 0, kElementsStartOffset);
9772}
9773
9774
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009775template<typename Shape, typename Key>
9776void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009777 IteratePointers(v,
9778 kElementsStartOffset,
9779 kHeaderSize + length() * kPointerSize);
9780}
9781
9782
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009783template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +00009784MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
9785 PretenureFlag pretenure) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00009786 int capacity = ComputeCapacity(at_least_space_for);
9787 if (capacity > HashTable::kMaxCapacity) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009788 return Failure::OutOfMemoryException();
9789 }
9790
lrn@chromium.org303ada72010-10-27 09:33:13 +00009791 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009792 { MaybeObject* maybe_obj = Isolate::Current()->heap()->
9793 AllocateHashTable(EntryToIndex(capacity), pretenure);
lrn@chromium.org303ada72010-10-27 09:33:13 +00009794 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009795 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00009796 HashTable::cast(obj)->SetNumberOfElements(0);
9797 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
9798 HashTable::cast(obj)->SetCapacity(capacity);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009799 return obj;
9800}
9801
9802
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009803// Find entry for key otherwise return kNotFound.
ricow@chromium.org4980dff2010-07-19 08:33:45 +00009804int StringDictionary::FindEntry(String* key) {
9805 if (!key->IsSymbol()) {
9806 return HashTable<StringDictionaryShape, String*>::FindEntry(key);
9807 }
9808
9809 // Optimized for symbol key. Knowledge of the key type allows:
9810 // 1. Move the check if the key is a symbol out of the loop.
9811 // 2. Avoid comparing hash codes in symbol to symbol comparision.
9812 // 3. Detect a case when a dictionary key is not a symbol but the key is.
9813 // In case of positive result the dictionary key may be replaced by
9814 // the symbol with minimal performance penalty. It gives a chance to
9815 // perform further lookups in code stubs (and significant performance boost
9816 // a certain style of code).
9817
9818 // EnsureCapacity will guarantee the hash table is never full.
9819 uint32_t capacity = Capacity();
9820 uint32_t entry = FirstProbe(key->Hash(), capacity);
9821 uint32_t count = 1;
9822
9823 while (true) {
9824 int index = EntryToIndex(entry);
9825 Object* element = get(index);
9826 if (element->IsUndefined()) break; // Empty entry.
9827 if (key == element) return entry;
9828 if (!element->IsSymbol() &&
9829 !element->IsNull() &&
9830 String::cast(element)->Equals(key)) {
9831 // Replace a non-symbol key by the equivalent symbol for faster further
9832 // lookups.
9833 set(index, key);
9834 return entry;
9835 }
9836 ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
9837 entry = NextProbe(entry, count++, capacity);
9838 }
9839 return kNotFound;
9840}
9841
9842
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009843template<typename Shape, typename Key>
ager@chromium.org04921a82011-06-27 13:21:41 +00009844MaybeObject* HashTable<Shape, Key>::Rehash(HashTable* new_table, Key key) {
9845 ASSERT(NumberOfElements() < new_table->Capacity());
9846
9847 AssertNoAllocation no_gc;
9848 WriteBarrierMode mode = new_table->GetWriteBarrierMode(no_gc);
9849
9850 // Copy prefix to new array.
9851 for (int i = kPrefixStartIndex;
9852 i < kPrefixStartIndex + Shape::kPrefixSize;
9853 i++) {
9854 new_table->set(i, get(i), mode);
9855 }
9856
9857 // Rehash the elements.
9858 int capacity = Capacity();
9859 for (int i = 0; i < capacity; i++) {
9860 uint32_t from_index = EntryToIndex(i);
9861 Object* k = get(from_index);
9862 if (IsKey(k)) {
9863 uint32_t hash = Shape::HashForObject(key, k);
9864 uint32_t insertion_index =
9865 EntryToIndex(new_table->FindInsertionEntry(hash));
9866 for (int j = 0; j < Shape::kEntrySize; j++) {
9867 new_table->set(insertion_index + j, get(from_index + j), mode);
9868 }
9869 }
9870 }
9871 new_table->SetNumberOfElements(NumberOfElements());
9872 new_table->SetNumberOfDeletedElements(0);
9873 return new_table;
9874}
9875
9876
9877template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +00009878MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009879 int capacity = Capacity();
9880 int nof = NumberOfElements() + n;
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00009881 int nod = NumberOfDeletedElements();
9882 // Return if:
9883 // 50% is still free after adding n elements and
9884 // at most 50% of the free elements are deleted elements.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009885 if (nod <= (capacity - nof) >> 1) {
9886 int needed_free = nof >> 1;
9887 if (nof + needed_free <= capacity) return this;
9888 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009889
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009890 const int kMinCapacityForPretenure = 256;
9891 bool pretenure =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009892 (capacity > kMinCapacityForPretenure) && !GetHeap()->InNewSpace(this);
lrn@chromium.org303ada72010-10-27 09:33:13 +00009893 Object* obj;
9894 { MaybeObject* maybe_obj =
9895 Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
9896 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9897 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009898
ager@chromium.org04921a82011-06-27 13:21:41 +00009899 return Rehash(HashTable::cast(obj), key);
9900}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009901
ager@chromium.org04921a82011-06-27 13:21:41 +00009902
9903template<typename Shape, typename Key>
9904MaybeObject* HashTable<Shape, Key>::Shrink(Key key) {
9905 int capacity = Capacity();
9906 int nof = NumberOfElements();
9907
9908 // Shrink to fit the number of elements if only a quarter of the
9909 // capacity is filled with elements.
9910 if (nof > (capacity >> 2)) return this;
9911 // Allocate a new dictionary with room for at least the current
9912 // number of elements. The allocation method will make sure that
9913 // there is extra room in the dictionary for additions. Don't go
9914 // lower than room for 16 elements.
9915 int at_least_room_for = nof;
9916 if (at_least_room_for < 16) return this;
9917
9918 const int kMinCapacityForPretenure = 256;
9919 bool pretenure =
9920 (at_least_room_for > kMinCapacityForPretenure) &&
9921 !GetHeap()->InNewSpace(this);
9922 Object* obj;
9923 { MaybeObject* maybe_obj =
9924 Allocate(at_least_room_for, pretenure ? TENURED : NOT_TENURED);
9925 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009926 }
ager@chromium.org04921a82011-06-27 13:21:41 +00009927
9928 return Rehash(HashTable::cast(obj), key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009929}
9930
9931
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009932template<typename Shape, typename Key>
9933uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009934 uint32_t capacity = Capacity();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009935 uint32_t entry = FirstProbe(hash, capacity);
9936 uint32_t count = 1;
9937 // EnsureCapacity will guarantee the hash table is never full.
9938 while (true) {
9939 Object* element = KeyAt(entry);
9940 if (element->IsUndefined() || element->IsNull()) break;
9941 entry = NextProbe(entry, count++, capacity);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009942 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009943 return entry;
9944}
9945
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009946// Force instantiation of template instances class.
9947// Please note this list is compiler dependent.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009948
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009949template class HashTable<SymbolTableShape, HashTableKey*>;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009950
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009951template class HashTable<CompilationCacheShape, HashTableKey*>;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009952
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009953template class HashTable<MapCacheShape, HashTableKey*>;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009954
vegorov@chromium.org7943d462011-08-01 11:41:52 +00009955template class HashTable<ObjectHashTableShape, JSObject*>;
9956
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009957template class Dictionary<StringDictionaryShape, String*>;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009958
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009959template class Dictionary<NumberDictionaryShape, uint32_t>;
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009960
lrn@chromium.org303ada72010-10-27 09:33:13 +00009961template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009962 int);
9963
lrn@chromium.org303ada72010-10-27 09:33:13 +00009964template MaybeObject* Dictionary<StringDictionaryShape, String*>::Allocate(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009965 int);
9966
lrn@chromium.org303ada72010-10-27 09:33:13 +00009967template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009968 uint32_t, Object*);
9969
9970template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
9971 Object*);
9972
9973template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
9974 Object*);
9975
9976template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009977 FixedArray*,
9978 PropertyAttributes,
9979 Dictionary<NumberDictionaryShape, uint32_t>::SortMode);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009980
9981template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
9982 int, JSObject::DeleteMode);
9983
9984template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
9985 int, JSObject::DeleteMode);
9986
ager@chromium.org04921a82011-06-27 13:21:41 +00009987template MaybeObject* Dictionary<StringDictionaryShape, String*>::Shrink(
9988 String*);
9989
9990template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Shrink(
9991 uint32_t);
9992
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009993template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009994 FixedArray*,
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00009995 int,
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009996 Dictionary<StringDictionaryShape, String*>::SortMode);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009997
9998template int
9999Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
10000 PropertyAttributes);
10001
lrn@chromium.org303ada72010-10-27 09:33:13 +000010002template MaybeObject* Dictionary<StringDictionaryShape, String*>::Add(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010003 String*, Object*, PropertyDetails);
10004
lrn@chromium.org303ada72010-10-27 09:33:13 +000010005template MaybeObject*
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010006Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
10007
10008template int
10009Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
10010 PropertyAttributes);
10011
lrn@chromium.org303ada72010-10-27 09:33:13 +000010012template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Add(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010013 uint32_t, Object*, PropertyDetails);
10014
lrn@chromium.org303ada72010-10-27 09:33:13 +000010015template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::
10016 EnsureCapacity(int, uint32_t);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010017
lrn@chromium.org303ada72010-10-27 09:33:13 +000010018template MaybeObject* Dictionary<StringDictionaryShape, String*>::
10019 EnsureCapacity(int, String*);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010020
lrn@chromium.org303ada72010-10-27 09:33:13 +000010021template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010022 uint32_t, Object*, PropertyDetails, uint32_t);
10023
lrn@chromium.org303ada72010-10-27 09:33:13 +000010024template MaybeObject* Dictionary<StringDictionaryShape, String*>::AddEntry(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010025 String*, Object*, PropertyDetails, uint32_t);
10026
10027template
10028int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
10029
10030template
10031int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010032
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010033template
10034int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
10035
10036
ager@chromium.org5ec48922009-05-05 07:25:34 +000010037// Collates undefined and unexisting elements below limit from position
10038// zero of the elements. The object stays in Dictionary mode.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010039MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010040 ASSERT(HasDictionaryElements());
ager@chromium.org5ec48922009-05-05 07:25:34 +000010041 // Must stay in dictionary mode, either because of requires_slow_elements,
10042 // or because we are not going to sort (and therefore compact) all of the
10043 // elements.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010044 NumberDictionary* dict = element_dictionary();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010045 HeapNumber* result_double = NULL;
10046 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
10047 // Allocate space for result before we start mutating the object.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010048 Object* new_double;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010049 { MaybeObject* maybe_new_double = GetHeap()->AllocateHeapNumber(0.0);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010050 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
10051 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010052 result_double = HeapNumber::cast(new_double);
10053 }
10054
lrn@chromium.org303ada72010-10-27 09:33:13 +000010055 Object* obj;
10056 { MaybeObject* maybe_obj =
10057 NumberDictionary::Allocate(dict->NumberOfElements());
10058 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10059 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010060 NumberDictionary* new_dict = NumberDictionary::cast(obj);
ager@chromium.org5ec48922009-05-05 07:25:34 +000010061
10062 AssertNoAllocation no_alloc;
10063
ager@chromium.org5ec48922009-05-05 07:25:34 +000010064 uint32_t pos = 0;
10065 uint32_t undefs = 0;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010066 int capacity = dict->Capacity();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010067 for (int i = 0; i < capacity; i++) {
10068 Object* k = dict->KeyAt(i);
10069 if (dict->IsKey(k)) {
10070 ASSERT(k->IsNumber());
10071 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
10072 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
10073 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
10074 Object* value = dict->ValueAt(i);
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010075 PropertyDetails details = dict->DetailsAt(i);
10076 if (details.type() == CALLBACKS) {
10077 // Bail out and do the sorting of undefineds and array holes in JS.
10078 return Smi::FromInt(-1);
10079 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010080 uint32_t key = NumberToUint32(k);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010081 // In the following we assert that adding the entry to the new dictionary
10082 // does not cause GC. This is the case because we made sure to allocate
10083 // the dictionary big enough above, so it need not grow.
ager@chromium.org5ec48922009-05-05 07:25:34 +000010084 if (key < limit) {
10085 if (value->IsUndefined()) {
10086 undefs++;
10087 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000010088 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
10089 // Adding an entry with the key beyond smi-range requires
10090 // allocation. Bailout.
10091 return Smi::FromInt(-1);
10092 }
lrn@chromium.org303ada72010-10-27 09:33:13 +000010093 new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010094 pos++;
10095 }
10096 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000010097 if (key > static_cast<uint32_t>(Smi::kMaxValue)) {
10098 // Adding an entry with the key beyond smi-range requires
10099 // allocation. Bailout.
10100 return Smi::FromInt(-1);
10101 }
lrn@chromium.org303ada72010-10-27 09:33:13 +000010102 new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010103 }
10104 }
10105 }
10106
10107 uint32_t result = pos;
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010108 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010109 Heap* heap = GetHeap();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010110 while (undefs > 0) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000010111 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
10112 // Adding an entry with the key beyond smi-range requires
10113 // allocation. Bailout.
10114 return Smi::FromInt(-1);
10115 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010116 new_dict->AddNumberEntry(pos, heap->undefined_value(), no_details)->
lrn@chromium.org303ada72010-10-27 09:33:13 +000010117 ToObjectUnchecked();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010118 pos++;
10119 undefs--;
10120 }
10121
10122 set_elements(new_dict);
10123
10124 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
10125 return Smi::FromInt(static_cast<int>(result));
10126 }
10127
10128 ASSERT_NE(NULL, result_double);
10129 result_double->set_value(static_cast<double>(result));
10130 return result_double;
10131}
10132
10133
10134// Collects all defined (non-hole) and non-undefined (array) elements at
10135// the start of the elements array.
10136// If the object is in dictionary mode, it is converted to fast elements
10137// mode.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010138MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000010139 ASSERT(!HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010140
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010141 Heap* heap = GetHeap();
10142
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010143 if (HasDictionaryElements()) {
ager@chromium.org5ec48922009-05-05 07:25:34 +000010144 // Convert to fast elements containing only the existing properties.
10145 // Ordering is irrelevant, since we are going to sort anyway.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010146 NumberDictionary* dict = element_dictionary();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010147 if (IsJSArray() || dict->requires_slow_elements() ||
10148 dict->max_number_key() >= limit) {
10149 return PrepareSlowElementsForSort(limit);
10150 }
10151 // Convert to fast elements.
10152
lrn@chromium.org303ada72010-10-27 09:33:13 +000010153 Object* obj;
10154 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
10155 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10156 }
fschneider@chromium.org40b9da32010-06-28 11:29:21 +000010157 Map* new_map = Map::cast(obj);
10158
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010159 PretenureFlag tenure = heap->InNewSpace(this) ? NOT_TENURED: TENURED;
lrn@chromium.org303ada72010-10-27 09:33:13 +000010160 Object* new_array;
10161 { MaybeObject* maybe_new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010162 heap->AllocateFixedArray(dict->NumberOfElements(), tenure);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010163 if (!maybe_new_array->ToObject(&new_array)) return maybe_new_array;
10164 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010165 FixedArray* fast_elements = FixedArray::cast(new_array);
10166 dict->CopyValuesTo(fast_elements);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +000010167
10168 set_map(new_map);
ager@chromium.org5ec48922009-05-05 07:25:34 +000010169 set_elements(fast_elements);
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010170 } else if (!HasFastDoubleElements()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +000010171 Object* obj;
10172 { MaybeObject* maybe_obj = EnsureWritableFastElements();
10173 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10174 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010175 }
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010176 ASSERT(HasFastElements() || HasFastDoubleElements());
ager@chromium.org5ec48922009-05-05 07:25:34 +000010177
10178 // Collect holes at the end, undefined before that and the rest at the
10179 // start, and return the number of non-hole, non-undefined values.
10180
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010181 FixedArrayBase* elements_base = FixedArrayBase::cast(this->elements());
10182 uint32_t elements_length = static_cast<uint32_t>(elements_base->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +000010183 if (limit > elements_length) {
10184 limit = elements_length ;
10185 }
10186 if (limit == 0) {
10187 return Smi::FromInt(0);
10188 }
10189
10190 HeapNumber* result_double = NULL;
10191 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
10192 // Pessimistically allocate space for return value before
10193 // we start mutating the array.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010194 Object* new_double;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010195 { MaybeObject* maybe_new_double = heap->AllocateHeapNumber(0.0);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010196 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
10197 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010198 result_double = HeapNumber::cast(new_double);
10199 }
10200
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010201 uint32_t result = 0;
10202 if (elements_base->map() == heap->fixed_double_array_map()) {
10203 FixedDoubleArray* elements = FixedDoubleArray::cast(elements_base);
10204 // Split elements into defined and the_hole, in that order.
10205 unsigned int holes = limit;
10206 // Assume most arrays contain no holes and undefined values, so minimize the
10207 // number of stores of non-undefined, non-the-hole values.
10208 for (unsigned int i = 0; i < holes; i++) {
10209 if (elements->is_the_hole(i)) {
10210 holes--;
10211 } else {
10212 continue;
10213 }
10214 // Position i needs to be filled.
10215 while (holes > i) {
10216 if (elements->is_the_hole(holes)) {
10217 holes--;
10218 } else {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000010219 elements->set(i, elements->get_scalar(holes));
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010220 break;
10221 }
10222 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010223 }
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010224 result = holes;
10225 while (holes < limit) {
10226 elements->set_the_hole(holes);
10227 holes++;
10228 }
10229 } else {
10230 FixedArray* elements = FixedArray::cast(elements_base);
10231 AssertNoAllocation no_alloc;
10232
10233 // Split elements into defined, undefined and the_hole, in that order. Only
10234 // count locations for undefined and the hole, and fill them afterwards.
10235 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
10236 unsigned int undefs = limit;
10237 unsigned int holes = limit;
10238 // Assume most arrays contain no holes and undefined values, so minimize the
10239 // number of stores of non-undefined, non-the-hole values.
10240 for (unsigned int i = 0; i < undefs; i++) {
10241 Object* current = elements->get(i);
ager@chromium.org5ec48922009-05-05 07:25:34 +000010242 if (current->IsTheHole()) {
10243 holes--;
10244 undefs--;
10245 } else if (current->IsUndefined()) {
10246 undefs--;
10247 } else {
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010248 continue;
10249 }
10250 // Position i needs to be filled.
10251 while (undefs > i) {
10252 current = elements->get(undefs);
10253 if (current->IsTheHole()) {
10254 holes--;
10255 undefs--;
10256 } else if (current->IsUndefined()) {
10257 undefs--;
10258 } else {
10259 elements->set(i, current, write_barrier);
10260 break;
10261 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010262 }
10263 }
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010264 result = undefs;
10265 while (undefs < holes) {
10266 elements->set_undefined(undefs);
10267 undefs++;
10268 }
10269 while (holes < limit) {
10270 elements->set_the_hole(holes);
10271 holes++;
10272 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010273 }
10274
10275 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
10276 return Smi::FromInt(static_cast<int>(result));
10277 }
10278 ASSERT_NE(NULL, result_double);
10279 result_double->set_value(static_cast<double>(result));
10280 return result_double;
10281}
10282
10283
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000010284Object* ExternalPixelArray::SetValue(uint32_t index, Object* value) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010285 uint8_t clamped_value = 0;
10286 if (index < static_cast<uint32_t>(length())) {
10287 if (value->IsSmi()) {
10288 int int_value = Smi::cast(value)->value();
10289 if (int_value < 0) {
10290 clamped_value = 0;
10291 } else if (int_value > 255) {
10292 clamped_value = 255;
10293 } else {
10294 clamped_value = static_cast<uint8_t>(int_value);
10295 }
10296 } else if (value->IsHeapNumber()) {
10297 double double_value = HeapNumber::cast(value)->value();
10298 if (!(double_value > 0)) {
10299 // NaN and less than zero clamp to zero.
10300 clamped_value = 0;
10301 } else if (double_value > 255) {
10302 // Greater than 255 clamp to 255.
10303 clamped_value = 255;
10304 } else {
10305 // Other doubles are rounded to the nearest integer.
10306 clamped_value = static_cast<uint8_t>(double_value + 0.5);
10307 }
10308 } else {
10309 // Clamp undefined to zero (default). All other types have been
10310 // converted to a number type further up in the call chain.
10311 ASSERT(value->IsUndefined());
10312 }
10313 set(index, clamped_value);
10314 }
10315 return Smi::FromInt(clamped_value);
10316}
10317
10318
ager@chromium.org3811b432009-10-28 14:53:37 +000010319template<typename ExternalArrayClass, typename ValueType>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010320static MaybeObject* ExternalArrayIntSetter(Heap* heap,
10321 ExternalArrayClass* receiver,
lrn@chromium.org303ada72010-10-27 09:33:13 +000010322 uint32_t index,
10323 Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010324 ValueType cast_value = 0;
10325 if (index < static_cast<uint32_t>(receiver->length())) {
10326 if (value->IsSmi()) {
10327 int int_value = Smi::cast(value)->value();
10328 cast_value = static_cast<ValueType>(int_value);
10329 } else if (value->IsHeapNumber()) {
10330 double double_value = HeapNumber::cast(value)->value();
10331 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
10332 } else {
10333 // Clamp undefined to zero (default). All other types have been
10334 // converted to a number type further up in the call chain.
10335 ASSERT(value->IsUndefined());
10336 }
10337 receiver->set(index, cast_value);
10338 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010339 return heap->NumberFromInt32(cast_value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010340}
10341
10342
lrn@chromium.org303ada72010-10-27 09:33:13 +000010343MaybeObject* ExternalByteArray::SetValue(uint32_t index, Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010344 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010345 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010346}
10347
10348
lrn@chromium.org303ada72010-10-27 09:33:13 +000010349MaybeObject* ExternalUnsignedByteArray::SetValue(uint32_t index,
10350 Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010351 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010352 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010353}
10354
10355
lrn@chromium.org303ada72010-10-27 09:33:13 +000010356MaybeObject* ExternalShortArray::SetValue(uint32_t index,
10357 Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010358 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010359 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010360}
10361
10362
lrn@chromium.org303ada72010-10-27 09:33:13 +000010363MaybeObject* ExternalUnsignedShortArray::SetValue(uint32_t index,
10364 Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010365 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010366 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010367}
10368
10369
lrn@chromium.org303ada72010-10-27 09:33:13 +000010370MaybeObject* ExternalIntArray::SetValue(uint32_t index, Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010371 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010372 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010373}
10374
10375
lrn@chromium.org303ada72010-10-27 09:33:13 +000010376MaybeObject* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010377 uint32_t cast_value = 0;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010378 Heap* heap = GetHeap();
ager@chromium.org3811b432009-10-28 14:53:37 +000010379 if (index < static_cast<uint32_t>(length())) {
10380 if (value->IsSmi()) {
10381 int int_value = Smi::cast(value)->value();
10382 cast_value = static_cast<uint32_t>(int_value);
10383 } else if (value->IsHeapNumber()) {
10384 double double_value = HeapNumber::cast(value)->value();
10385 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
10386 } else {
10387 // Clamp undefined to zero (default). All other types have been
10388 // converted to a number type further up in the call chain.
10389 ASSERT(value->IsUndefined());
10390 }
10391 set(index, cast_value);
10392 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010393 return heap->NumberFromUint32(cast_value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010394}
10395
10396
lrn@chromium.org303ada72010-10-27 09:33:13 +000010397MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010398 float cast_value = 0;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010399 Heap* heap = GetHeap();
ager@chromium.org3811b432009-10-28 14:53:37 +000010400 if (index < static_cast<uint32_t>(length())) {
10401 if (value->IsSmi()) {
10402 int int_value = Smi::cast(value)->value();
10403 cast_value = static_cast<float>(int_value);
10404 } else if (value->IsHeapNumber()) {
10405 double double_value = HeapNumber::cast(value)->value();
10406 cast_value = static_cast<float>(double_value);
10407 } else {
10408 // Clamp undefined to zero (default). All other types have been
10409 // converted to a number type further up in the call chain.
10410 ASSERT(value->IsUndefined());
10411 }
10412 set(index, cast_value);
10413 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010414 return heap->AllocateHeapNumber(cast_value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010415}
10416
10417
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +000010418MaybeObject* ExternalDoubleArray::SetValue(uint32_t index, Object* value) {
10419 double double_value = 0;
10420 Heap* heap = GetHeap();
10421 if (index < static_cast<uint32_t>(length())) {
10422 if (value->IsSmi()) {
10423 int int_value = Smi::cast(value)->value();
10424 double_value = static_cast<double>(int_value);
10425 } else if (value->IsHeapNumber()) {
10426 double_value = HeapNumber::cast(value)->value();
10427 } else {
10428 // Clamp undefined to zero (default). All other types have been
10429 // converted to a number type further up in the call chain.
10430 ASSERT(value->IsUndefined());
10431 }
10432 set(index, double_value);
10433 }
10434 return heap->AllocateHeapNumber(double_value);
10435}
10436
10437
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010438JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010439 ASSERT(!HasFastProperties());
10440 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010441 return JSGlobalPropertyCell::cast(value);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010442}
10443
10444
lrn@chromium.org303ada72010-10-27 09:33:13 +000010445MaybeObject* GlobalObject::EnsurePropertyCell(String* name) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000010446 ASSERT(!HasFastProperties());
10447 int entry = property_dictionary()->FindEntry(name);
10448 if (entry == StringDictionary::kNotFound) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010449 Heap* heap = GetHeap();
lrn@chromium.org303ada72010-10-27 09:33:13 +000010450 Object* cell;
10451 { MaybeObject* maybe_cell =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010452 heap->AllocateJSGlobalPropertyCell(heap->the_hole_value());
lrn@chromium.org303ada72010-10-27 09:33:13 +000010453 if (!maybe_cell->ToObject(&cell)) return maybe_cell;
10454 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000010455 PropertyDetails details(NONE, NORMAL);
10456 details = details.AsDeleted();
lrn@chromium.org303ada72010-10-27 09:33:13 +000010457 Object* dictionary;
10458 { MaybeObject* maybe_dictionary =
10459 property_dictionary()->Add(name, cell, details);
10460 if (!maybe_dictionary->ToObject(&dictionary)) return maybe_dictionary;
10461 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000010462 set_properties(StringDictionary::cast(dictionary));
10463 return cell;
10464 } else {
10465 Object* value = property_dictionary()->ValueAt(entry);
10466 ASSERT(value->IsJSGlobalPropertyCell());
10467 return value;
10468 }
10469}
10470
10471
lrn@chromium.org303ada72010-10-27 09:33:13 +000010472MaybeObject* SymbolTable::LookupString(String* string, Object** s) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010473 SymbolKey key(string);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010474 return LookupKey(&key, s);
10475}
10476
10477
ager@chromium.org6141cbe2009-11-20 12:14:52 +000010478// This class is used for looking up two character strings in the symbol table.
10479// If we don't have a hit we don't want to waste much time so we unroll the
10480// string hash calculation loop here for speed. Doesn't work if the two
10481// characters form a decimal integer, since such strings have a different hash
10482// algorithm.
10483class TwoCharHashTableKey : public HashTableKey {
10484 public:
10485 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
10486 : c1_(c1), c2_(c2) {
10487 // Char 1.
10488 uint32_t hash = c1 + (c1 << 10);
10489 hash ^= hash >> 6;
10490 // Char 2.
10491 hash += c2;
10492 hash += hash << 10;
10493 hash ^= hash >> 6;
10494 // GetHash.
10495 hash += hash << 3;
10496 hash ^= hash >> 11;
10497 hash += hash << 15;
10498 if (hash == 0) hash = 27;
10499#ifdef DEBUG
10500 StringHasher hasher(2);
10501 hasher.AddCharacter(c1);
10502 hasher.AddCharacter(c2);
10503 // If this assert fails then we failed to reproduce the two-character
10504 // version of the string hashing algorithm above. One reason could be
10505 // that we were passed two digits as characters, since the hash
10506 // algorithm is different in that case.
10507 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
10508#endif
10509 hash_ = hash;
10510 }
10511
10512 bool IsMatch(Object* o) {
10513 if (!o->IsString()) return false;
10514 String* other = String::cast(o);
10515 if (other->length() != 2) return false;
10516 if (other->Get(0) != c1_) return false;
10517 return other->Get(1) == c2_;
10518 }
10519
10520 uint32_t Hash() { return hash_; }
10521 uint32_t HashForObject(Object* key) {
10522 if (!key->IsString()) return 0;
10523 return String::cast(key)->Hash();
10524 }
10525
10526 Object* AsObject() {
10527 // The TwoCharHashTableKey is only used for looking in the symbol
10528 // table, not for adding to it.
10529 UNREACHABLE();
10530 return NULL;
10531 }
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010532
ager@chromium.org6141cbe2009-11-20 12:14:52 +000010533 private:
10534 uint32_t c1_;
10535 uint32_t c2_;
10536 uint32_t hash_;
10537};
10538
10539
ager@chromium.org7c537e22008-10-16 08:43:32 +000010540bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
10541 SymbolKey key(string);
10542 int entry = FindEntry(&key);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010543 if (entry == kNotFound) {
ager@chromium.org7c537e22008-10-16 08:43:32 +000010544 return false;
10545 } else {
10546 String* result = String::cast(KeyAt(entry));
ager@chromium.org870a0b62008-11-04 11:43:05 +000010547 ASSERT(StringShape(result).IsSymbol());
ager@chromium.org7c537e22008-10-16 08:43:32 +000010548 *symbol = result;
10549 return true;
10550 }
10551}
10552
10553
ager@chromium.org6141cbe2009-11-20 12:14:52 +000010554bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
10555 uint32_t c2,
10556 String** symbol) {
10557 TwoCharHashTableKey key(c1, c2);
10558 int entry = FindEntry(&key);
10559 if (entry == kNotFound) {
10560 return false;
10561 } else {
10562 String* result = String::cast(KeyAt(entry));
10563 ASSERT(StringShape(result).IsSymbol());
10564 *symbol = result;
10565 return true;
10566 }
10567}
10568
10569
lrn@chromium.org303ada72010-10-27 09:33:13 +000010570MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010571 Utf8SymbolKey key(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010572 return LookupKey(&key, s);
10573}
10574
10575
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +000010576MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str,
10577 Object** s) {
10578 AsciiSymbolKey key(str);
10579 return LookupKey(&key, s);
10580}
10581
10582
danno@chromium.org40cb8782011-05-25 07:58:50 +000010583MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle<SeqAsciiString> str,
10584 int from,
10585 int length,
10586 Object** s) {
10587 SubStringAsciiSymbolKey key(str, from, length);
10588 return LookupKey(&key, s);
10589}
10590
10591
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +000010592MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str,
10593 Object** s) {
10594 TwoByteSymbolKey key(str);
10595 return LookupKey(&key, s);
10596}
10597
lrn@chromium.org303ada72010-10-27 09:33:13 +000010598MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010599 int entry = FindEntry(key);
10600
10601 // Symbol already in table.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010602 if (entry != kNotFound) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010603 *s = KeyAt(entry);
10604 return this;
10605 }
10606
10607 // Adding new symbol. Grow table if needed.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010608 Object* obj;
10609 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
10610 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10611 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010612
10613 // Create symbol object.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010614 Object* symbol;
10615 { MaybeObject* maybe_symbol = key->AsObject();
10616 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
10617 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010618
10619 // If the symbol table grew as part of EnsureCapacity, obj is not
10620 // the current symbol table and therefore we cannot use
10621 // SymbolTable::cast here.
10622 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
10623
10624 // Add the new symbol and return it along with the symbol table.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010625 entry = table->FindInsertionEntry(key->Hash());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010626 table->set(EntryToIndex(entry), symbol);
10627 table->ElementAdded();
10628 *s = symbol;
10629 return table;
10630}
10631
10632
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000010633Object* CompilationCacheTable::Lookup(String* src) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010634 StringKey key(src);
10635 int entry = FindEntry(&key);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010636 if (entry == kNotFound) return GetHeap()->undefined_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010637 return get(EntryToIndex(entry) + 1);
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010638}
10639
10640
ricow@chromium.org83aa5492011-02-07 12:42:56 +000010641Object* CompilationCacheTable::LookupEval(String* src,
10642 Context* context,
10643 StrictModeFlag strict_mode) {
10644 StringSharedKey key(src, context->closure()->shared(), strict_mode);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010645 int entry = FindEntry(&key);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010646 if (entry == kNotFound) return GetHeap()->undefined_value();
ager@chromium.org381abbb2009-02-25 13:23:22 +000010647 return get(EntryToIndex(entry) + 1);
10648}
10649
10650
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010651Object* CompilationCacheTable::LookupRegExp(String* src,
10652 JSRegExp::Flags flags) {
10653 RegExpKey key(src, flags);
10654 int entry = FindEntry(&key);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010655 if (entry == kNotFound) return GetHeap()->undefined_value();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010656 return get(EntryToIndex(entry) + 1);
10657}
10658
10659
lrn@chromium.org303ada72010-10-27 09:33:13 +000010660MaybeObject* CompilationCacheTable::Put(String* src, Object* value) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010661 StringKey key(src);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010662 Object* obj;
10663 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
10664 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10665 }
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010666
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000010667 CompilationCacheTable* cache =
10668 reinterpret_cast<CompilationCacheTable*>(obj);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010669 int entry = cache->FindInsertionEntry(key.Hash());
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010670 cache->set(EntryToIndex(entry), src);
10671 cache->set(EntryToIndex(entry) + 1, value);
10672 cache->ElementAdded();
10673 return cache;
10674}
10675
10676
lrn@chromium.org303ada72010-10-27 09:33:13 +000010677MaybeObject* CompilationCacheTable::PutEval(String* src,
10678 Context* context,
ricow@chromium.org83aa5492011-02-07 12:42:56 +000010679 SharedFunctionInfo* value) {
10680 StringSharedKey key(src,
10681 context->closure()->shared(),
10682 value->strict_mode() ? kStrictMode : kNonStrictMode);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010683 Object* obj;
10684 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
10685 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10686 }
ager@chromium.org381abbb2009-02-25 13:23:22 +000010687
10688 CompilationCacheTable* cache =
10689 reinterpret_cast<CompilationCacheTable*>(obj);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010690 int entry = cache->FindInsertionEntry(key.Hash());
ager@chromium.org381abbb2009-02-25 13:23:22 +000010691
lrn@chromium.org303ada72010-10-27 09:33:13 +000010692 Object* k;
10693 { MaybeObject* maybe_k = key.AsObject();
10694 if (!maybe_k->ToObject(&k)) return maybe_k;
10695 }
ager@chromium.org381abbb2009-02-25 13:23:22 +000010696
10697 cache->set(EntryToIndex(entry), k);
10698 cache->set(EntryToIndex(entry) + 1, value);
10699 cache->ElementAdded();
10700 return cache;
10701}
10702
10703
lrn@chromium.org303ada72010-10-27 09:33:13 +000010704MaybeObject* CompilationCacheTable::PutRegExp(String* src,
10705 JSRegExp::Flags flags,
10706 FixedArray* value) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010707 RegExpKey key(src, flags);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010708 Object* obj;
10709 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
10710 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10711 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010712
10713 CompilationCacheTable* cache =
10714 reinterpret_cast<CompilationCacheTable*>(obj);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010715 int entry = cache->FindInsertionEntry(key.Hash());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000010716 // We store the value in the key slot, and compare the search key
10717 // to the stored value with a custon IsMatch function during lookups.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010718 cache->set(EntryToIndex(entry), value);
10719 cache->set(EntryToIndex(entry) + 1, value);
10720 cache->ElementAdded();
10721 return cache;
10722}
10723
10724
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010725void CompilationCacheTable::Remove(Object* value) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010726 Object* null_value = GetHeap()->null_value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010727 for (int entry = 0, size = Capacity(); entry < size; entry++) {
10728 int entry_index = EntryToIndex(entry);
10729 int value_index = entry_index + 1;
10730 if (get(value_index) == value) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010731 fast_set(this, entry_index, null_value);
10732 fast_set(this, value_index, null_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010733 ElementRemoved();
10734 }
10735 }
10736 return;
10737}
10738
10739
ager@chromium.org236ad962008-09-25 09:45:57 +000010740// SymbolsKey used for HashTable where key is array of symbols.
10741class SymbolsKey : public HashTableKey {
10742 public:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010743 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
ager@chromium.org236ad962008-09-25 09:45:57 +000010744
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010745 bool IsMatch(Object* symbols) {
10746 FixedArray* o = FixedArray::cast(symbols);
ager@chromium.org236ad962008-09-25 09:45:57 +000010747 int len = symbols_->length();
10748 if (o->length() != len) return false;
10749 for (int i = 0; i < len; i++) {
10750 if (o->get(i) != symbols_->get(i)) return false;
10751 }
10752 return true;
10753 }
10754
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010755 uint32_t Hash() { return HashForObject(symbols_); }
ager@chromium.org236ad962008-09-25 09:45:57 +000010756
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010757 uint32_t HashForObject(Object* obj) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010758 FixedArray* symbols = FixedArray::cast(obj);
10759 int len = symbols->length();
10760 uint32_t hash = 0;
ager@chromium.org236ad962008-09-25 09:45:57 +000010761 for (int i = 0; i < len; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010762 hash ^= String::cast(symbols->get(i))->Hash();
ager@chromium.org236ad962008-09-25 09:45:57 +000010763 }
10764 return hash;
10765 }
10766
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010767 Object* AsObject() { return symbols_; }
ager@chromium.org236ad962008-09-25 09:45:57 +000010768
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010769 private:
ager@chromium.org236ad962008-09-25 09:45:57 +000010770 FixedArray* symbols_;
10771};
10772
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010773
ager@chromium.org236ad962008-09-25 09:45:57 +000010774Object* MapCache::Lookup(FixedArray* array) {
10775 SymbolsKey key(array);
10776 int entry = FindEntry(&key);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010777 if (entry == kNotFound) return GetHeap()->undefined_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010778 return get(EntryToIndex(entry) + 1);
ager@chromium.org236ad962008-09-25 09:45:57 +000010779}
10780
10781
lrn@chromium.org303ada72010-10-27 09:33:13 +000010782MaybeObject* MapCache::Put(FixedArray* array, Map* value) {
ager@chromium.org236ad962008-09-25 09:45:57 +000010783 SymbolsKey key(array);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010784 Object* obj;
10785 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
10786 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10787 }
ager@chromium.org236ad962008-09-25 09:45:57 +000010788
10789 MapCache* cache = reinterpret_cast<MapCache*>(obj);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010790 int entry = cache->FindInsertionEntry(key.Hash());
ager@chromium.org236ad962008-09-25 09:45:57 +000010791 cache->set(EntryToIndex(entry), array);
10792 cache->set(EntryToIndex(entry) + 1, value);
10793 cache->ElementAdded();
10794 return cache;
10795}
10796
10797
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010798template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010799MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
10800 Object* obj;
10801 { MaybeObject* maybe_obj =
10802 HashTable<Shape, Key>::Allocate(at_least_space_for);
10803 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010804 }
lrn@chromium.org303ada72010-10-27 09:33:13 +000010805 // Initialize the next enumeration index.
10806 Dictionary<Shape, Key>::cast(obj)->
10807 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010808 return obj;
10809}
10810
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010811
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010812template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010813MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010814 Heap* heap = Dictionary<Shape, Key>::GetHeap();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010815 int length = HashTable<Shape, Key>::NumberOfElements();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010816
10817 // Allocate and initialize iteration order array.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010818 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010819 { MaybeObject* maybe_obj = heap->AllocateFixedArray(length);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010820 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10821 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010822 FixedArray* iteration_order = FixedArray::cast(obj);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010823 for (int i = 0; i < length; i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010824 iteration_order->set(i, Smi::FromInt(i));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010825 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010826
10827 // Allocate array with enumeration order.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010828 { MaybeObject* maybe_obj = heap->AllocateFixedArray(length);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010829 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10830 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010831 FixedArray* enumeration_order = FixedArray::cast(obj);
10832
10833 // Fill the enumeration order array with property details.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010834 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010835 int pos = 0;
10836 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010837 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010838 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010839 }
10840 }
10841
10842 // Sort the arrays wrt. enumeration order.
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010843 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010844
10845 // Overwrite the enumeration_order with the enumeration indices.
10846 for (int i = 0; i < length; i++) {
10847 int index = Smi::cast(iteration_order->get(i))->value();
10848 int enum_index = PropertyDetails::kInitialIndex + i;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010849 enumeration_order->set(index, Smi::FromInt(enum_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010850 }
10851
10852 // Update the dictionary with new indices.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010853 capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010854 pos = 0;
10855 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010856 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010857 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
10858 PropertyDetails details = DetailsAt(i);
10859 PropertyDetails new_details =
10860 PropertyDetails(details.attributes(), details.type(), enum_index);
10861 DetailsAtPut(i, new_details);
10862 }
10863 }
10864
10865 // Set the next enumeration index.
10866 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
10867 return this;
10868}
10869
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010870template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010871MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000010872 // Check whether there are enough enumeration indices to add n elements.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010873 if (Shape::kIsEnumerable &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010874 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
10875 // If not, we generate new indices for the properties.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010876 Object* result;
10877 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
10878 if (!maybe_result->ToObject(&result)) return maybe_result;
10879 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010880 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010881 return HashTable<Shape, Key>::EnsureCapacity(n, key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010882}
10883
10884
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010885void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010886 // Do nothing if the interval [from, to) is empty.
10887 if (from >= to) return;
10888
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010889 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010890 int removed_entries = 0;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010891 Object* sentinel = heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010892 int capacity = Capacity();
10893 for (int i = 0; i < capacity; i++) {
10894 Object* key = KeyAt(i);
10895 if (key->IsNumber()) {
10896 uint32_t number = static_cast<uint32_t>(key->Number());
10897 if (from <= number && number < to) {
karlklose@chromium.org44bc7082011-04-11 12:33:05 +000010898 SetEntry(i, sentinel, sentinel);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010899 removed_entries++;
10900 }
10901 }
10902 }
10903
10904 // Update the number of elements.
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +000010905 ElementsRemoved(removed_entries);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010906}
10907
10908
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010909template<typename Shape, typename Key>
10910Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +000010911 JSReceiver::DeleteMode mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010912 Heap* heap = Dictionary<Shape, Key>::GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010913 PropertyDetails details = DetailsAt(entry);
ager@chromium.orge2902be2009-06-08 12:21:35 +000010914 // Ignore attributes if forcing a deletion.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +000010915 if (details.IsDontDelete() && mode != JSReceiver::FORCE_DELETION) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010916 return heap->false_value();
ager@chromium.orge2902be2009-06-08 12:21:35 +000010917 }
karlklose@chromium.org44bc7082011-04-11 12:33:05 +000010918 SetEntry(entry, heap->null_value(), heap->null_value());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010919 HashTable<Shape, Key>::ElementRemoved();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010920 return heap->true_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010921}
10922
10923
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010924template<typename Shape, typename Key>
ager@chromium.org04921a82011-06-27 13:21:41 +000010925MaybeObject* Dictionary<Shape, Key>::Shrink(Key key) {
10926 return HashTable<Shape, Key>::Shrink(key);
10927}
10928
10929
10930template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010931MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010932 int entry = this->FindEntry(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010933
10934 // If the entry is present set the value;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010935 if (entry != Dictionary<Shape, Key>::kNotFound) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010936 ValueAtPut(entry, value);
10937 return this;
10938 }
10939
10940 // Check whether the dictionary should be extended.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010941 Object* obj;
10942 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
10943 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10944 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010945
lrn@chromium.org303ada72010-10-27 09:33:13 +000010946 Object* k;
10947 { MaybeObject* maybe_k = Shape::AsObject(key);
10948 if (!maybe_k->ToObject(&k)) return maybe_k;
10949 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010950 PropertyDetails details = PropertyDetails(NONE, NORMAL);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010951 return Dictionary<Shape, Key>::cast(obj)->
10952 AddEntry(key, value, details, Shape::Hash(key));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010953}
10954
10955
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010956template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010957MaybeObject* Dictionary<Shape, Key>::Add(Key key,
10958 Object* value,
10959 PropertyDetails details) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010960 // Valdate key is absent.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010961 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010962 // Check whether the dictionary should be extended.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010963 Object* obj;
10964 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
10965 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10966 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010967 return Dictionary<Shape, Key>::cast(obj)->
10968 AddEntry(key, value, details, Shape::Hash(key));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010969}
10970
10971
10972// Add a key, value pair to the dictionary.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010973template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010974MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key,
10975 Object* value,
10976 PropertyDetails details,
10977 uint32_t hash) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010978 // Compute the key object.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010979 Object* k;
10980 { MaybeObject* maybe_k = Shape::AsObject(key);
10981 if (!maybe_k->ToObject(&k)) return maybe_k;
10982 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010983
10984 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010985 // Insert element at empty or deleted entry
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000010986 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010987 // Assign an enumeration index to the property and update
10988 // SetNextEnumerationIndex.
10989 int index = NextEnumerationIndex();
10990 details = PropertyDetails(details.attributes(), details.type(), index);
10991 SetNextEnumerationIndex(index + 1);
10992 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010993 SetEntry(entry, k, value, details);
10994 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
10995 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
10996 HashTable<Shape, Key>::ElementAdded();
10997 return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010998}
10999
11000
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011001void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011002 // If the dictionary requires slow elements an element has already
11003 // been added at a high index.
11004 if (requires_slow_elements()) return;
11005 // Check if this index is high enough that we should require slow
11006 // elements.
11007 if (key > kRequiresSlowElementsLimit) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000011008 set_requires_slow_elements();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011009 return;
11010 }
11011 // Update max key value.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000011012 Object* max_index_object = get(kMaxNumberKeyIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011013 if (!max_index_object->IsSmi() || max_number_key() < key) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011014 FixedArray::set(kMaxNumberKeyIndex,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011015 Smi::FromInt(key << kRequiresSlowElementsTagSize));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011016 }
11017}
11018
11019
lrn@chromium.org303ada72010-10-27 09:33:13 +000011020MaybeObject* NumberDictionary::AddNumberEntry(uint32_t key,
11021 Object* value,
11022 PropertyDetails details) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011023 UpdateMaxNumberKey(key);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000011024 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011025 return Add(key, value, details);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011026}
11027
11028
lrn@chromium.org303ada72010-10-27 09:33:13 +000011029MaybeObject* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011030 UpdateMaxNumberKey(key);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011031 return AtPut(key, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011032}
11033
11034
lrn@chromium.org303ada72010-10-27 09:33:13 +000011035MaybeObject* NumberDictionary::Set(uint32_t key,
11036 Object* value,
11037 PropertyDetails details) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011038 int entry = FindEntry(key);
11039 if (entry == kNotFound) return AddNumberEntry(key, value, details);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011040 // Preserve enumeration index.
11041 details = PropertyDetails(details.attributes(),
11042 details.type(),
11043 DetailsAt(entry).index());
lrn@chromium.org303ada72010-10-27 09:33:13 +000011044 MaybeObject* maybe_object_key = NumberDictionaryShape::AsObject(key);
11045 Object* object_key;
11046 if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
whesse@chromium.org4a5224e2010-10-20 12:37:07 +000011047 SetEntry(entry, object_key, value, details);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011048 return this;
11049}
11050
11051
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000011052
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011053template<typename Shape, typename Key>
11054int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
11055 PropertyAttributes filter) {
11056 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011057 int result = 0;
11058 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011059 Object* k = HashTable<Shape, Key>::KeyAt(i);
11060 if (HashTable<Shape, Key>::IsKey(k)) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011061 PropertyDetails details = DetailsAt(i);
11062 if (details.IsDeleted()) continue;
11063 PropertyAttributes attr = details.attributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011064 if ((attr & filter) == 0) result++;
11065 }
11066 }
11067 return result;
11068}
11069
11070
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011071template<typename Shape, typename Key>
11072int Dictionary<Shape, Key>::NumberOfEnumElements() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011073 return NumberOfElementsFilterAttributes(
11074 static_cast<PropertyAttributes>(DONT_ENUM));
11075}
11076
11077
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011078template<typename Shape, typename Key>
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011079void Dictionary<Shape, Key>::CopyKeysTo(
11080 FixedArray* storage,
11081 PropertyAttributes filter,
11082 typename Dictionary<Shape, Key>::SortMode sort_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011083 ASSERT(storage->length() >= NumberOfEnumElements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011084 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011085 int index = 0;
11086 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011087 Object* k = HashTable<Shape, Key>::KeyAt(i);
11088 if (HashTable<Shape, Key>::IsKey(k)) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011089 PropertyDetails details = DetailsAt(i);
11090 if (details.IsDeleted()) continue;
11091 PropertyAttributes attr = details.attributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011092 if ((attr & filter) == 0) storage->set(index++, k);
11093 }
11094 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011095 if (sort_mode == Dictionary<Shape, Key>::SORTED) {
11096 storage->SortPairs(storage, index);
11097 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011098 ASSERT(storage->length() >= index);
11099}
11100
11101
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011102void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
11103 FixedArray* sort_array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011104 ASSERT(storage->length() >= NumberOfEnumElements());
11105 int capacity = Capacity();
11106 int index = 0;
11107 for (int i = 0; i < capacity; i++) {
11108 Object* k = KeyAt(i);
11109 if (IsKey(k)) {
11110 PropertyDetails details = DetailsAt(i);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011111 if (details.IsDeleted() || details.IsDontEnum()) continue;
11112 storage->set(index, k);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011113 sort_array->set(index, Smi::FromInt(details.index()));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011114 index++;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011115 }
11116 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +000011117 storage->SortPairs(sort_array, sort_array->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011118 ASSERT(storage->length() >= index);
11119}
11120
11121
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011122template<typename Shape, typename Key>
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011123void Dictionary<Shape, Key>::CopyKeysTo(
11124 FixedArray* storage,
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000011125 int index,
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011126 typename Dictionary<Shape, Key>::SortMode sort_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011127 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
11128 static_cast<PropertyAttributes>(NONE)));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011129 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011130 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011131 Object* k = HashTable<Shape, Key>::KeyAt(i);
11132 if (HashTable<Shape, Key>::IsKey(k)) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011133 PropertyDetails details = DetailsAt(i);
11134 if (details.IsDeleted()) continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011135 storage->set(index++, k);
11136 }
11137 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011138 if (sort_mode == Dictionary<Shape, Key>::SORTED) {
11139 storage->SortPairs(storage, index);
11140 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011141 ASSERT(storage->length() >= index);
11142}
11143
11144
11145// Backwards lookup (slow).
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011146template<typename Shape, typename Key>
11147Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
11148 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011149 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011150 Object* k = HashTable<Shape, Key>::KeyAt(i);
11151 if (Dictionary<Shape, Key>::IsKey(k)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000011152 Object* e = ValueAt(i);
11153 if (e->IsJSGlobalPropertyCell()) {
11154 e = JSGlobalPropertyCell::cast(e)->value();
11155 }
11156 if (e == value) return k;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011157 }
11158 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011159 Heap* heap = Dictionary<Shape, Key>::GetHeap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011160 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011161}
11162
11163
lrn@chromium.org303ada72010-10-27 09:33:13 +000011164MaybeObject* StringDictionary::TransformPropertiesToFastFor(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011165 JSObject* obj, int unused_property_fields) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011166 // Make sure we preserve dictionary representation if there are too many
11167 // descriptors.
11168 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
11169
11170 // Figure out if it is necessary to generate new enumeration indices.
11171 int max_enumeration_index =
11172 NextEnumerationIndex() +
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011173 (DescriptorArray::kMaxNumberOfDescriptors -
11174 NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011175 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
lrn@chromium.org303ada72010-10-27 09:33:13 +000011176 Object* result;
11177 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
11178 if (!maybe_result->ToObject(&result)) return maybe_result;
11179 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011180 }
11181
11182 int instance_descriptor_length = 0;
11183 int number_of_fields = 0;
11184
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011185 Heap* heap = GetHeap();
11186
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011187 // Compute the length of the instance descriptor.
11188 int capacity = Capacity();
11189 for (int i = 0; i < capacity; i++) {
11190 Object* k = KeyAt(i);
11191 if (IsKey(k)) {
11192 Object* value = ValueAt(i);
11193 PropertyType type = DetailsAt(i).type();
11194 ASSERT(type != FIELD);
11195 instance_descriptor_length++;
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011196 if (type == NORMAL &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011197 (!value->IsJSFunction() || heap->InNewSpace(value))) {
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011198 number_of_fields += 1;
11199 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011200 }
11201 }
11202
11203 // Allocate the instance descriptor.
lrn@chromium.org303ada72010-10-27 09:33:13 +000011204 Object* descriptors_unchecked;
11205 { MaybeObject* maybe_descriptors_unchecked =
11206 DescriptorArray::Allocate(instance_descriptor_length);
11207 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
11208 return maybe_descriptors_unchecked;
11209 }
11210 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000011211 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011212
ager@chromium.org32912102009-01-16 10:38:43 +000011213 int inobject_props = obj->map()->inobject_properties();
11214 int number_of_allocated_fields =
11215 number_of_fields + unused_property_fields - inobject_props;
ricow@chromium.orgeb7c1442010-10-04 08:54:21 +000011216 if (number_of_allocated_fields < 0) {
11217 // There is enough inobject space for all fields (including unused).
11218 number_of_allocated_fields = 0;
11219 unused_property_fields = inobject_props - number_of_fields;
11220 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011221
11222 // Allocate the fixed array for the fields.
lrn@chromium.org303ada72010-10-27 09:33:13 +000011223 Object* fields;
11224 { MaybeObject* maybe_fields =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011225 heap->AllocateFixedArray(number_of_allocated_fields);
lrn@chromium.org303ada72010-10-27 09:33:13 +000011226 if (!maybe_fields->ToObject(&fields)) return maybe_fields;
11227 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011228
11229 // Fill in the instance descriptor and the fields.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011230 int next_descriptor = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011231 int current_offset = 0;
11232 for (int i = 0; i < capacity; i++) {
11233 Object* k = KeyAt(i);
11234 if (IsKey(k)) {
11235 Object* value = ValueAt(i);
11236 // Ensure the key is a symbol before writing into the instance descriptor.
lrn@chromium.org303ada72010-10-27 09:33:13 +000011237 Object* key;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011238 { MaybeObject* maybe_key = heap->LookupSymbol(String::cast(k));
lrn@chromium.org303ada72010-10-27 09:33:13 +000011239 if (!maybe_key->ToObject(&key)) return maybe_key;
11240 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011241 PropertyDetails details = DetailsAt(i);
11242 PropertyType type = details.type();
ager@chromium.org32912102009-01-16 10:38:43 +000011243
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011244 if (value->IsJSFunction() && !heap->InNewSpace(value)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011245 ConstantFunctionDescriptor d(String::cast(key),
11246 JSFunction::cast(value),
11247 details.attributes(),
11248 details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011249 descriptors->Set(next_descriptor++, &d);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011250 } else if (type == NORMAL) {
ager@chromium.org32912102009-01-16 10:38:43 +000011251 if (current_offset < inobject_props) {
11252 obj->InObjectPropertyAtPut(current_offset,
11253 value,
11254 UPDATE_WRITE_BARRIER);
11255 } else {
11256 int offset = current_offset - inobject_props;
11257 FixedArray::cast(fields)->set(offset, value);
11258 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011259 FieldDescriptor d(String::cast(key),
11260 current_offset++,
11261 details.attributes(),
11262 details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011263 descriptors->Set(next_descriptor++, &d);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011264 } else if (type == CALLBACKS) {
11265 CallbacksDescriptor d(String::cast(key),
11266 value,
11267 details.attributes(),
11268 details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011269 descriptors->Set(next_descriptor++, &d);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011270 } else {
11271 UNREACHABLE();
11272 }
11273 }
11274 }
11275 ASSERT(current_offset == number_of_fields);
11276
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000011277 descriptors->Sort();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011278 // Allocate new map.
lrn@chromium.org303ada72010-10-27 09:33:13 +000011279 Object* new_map;
11280 { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
11281 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
11282 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011283
11284 // Transform the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011285 obj->set_map(Map::cast(new_map));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000011286 obj->map()->set_instance_descriptors(descriptors);
11287 obj->map()->set_unused_property_fields(unused_property_fields);
11288
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011289 obj->set_properties(FixedArray::cast(fields));
11290 ASSERT(obj->IsJSObject());
11291
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000011292 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
ager@chromium.org32912102009-01-16 10:38:43 +000011293 // Check that it really works.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011294 ASSERT(obj->HasFastProperties());
ager@chromium.org32912102009-01-16 10:38:43 +000011295
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011296 return obj;
11297}
11298
11299
vegorov@chromium.org7943d462011-08-01 11:41:52 +000011300Object* ObjectHashTable::Lookup(JSObject* key) {
11301 // If the object does not have an identity hash, it was never used as a key.
11302 MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::OMIT_CREATION);
11303 if (maybe_hash->IsFailure()) return GetHeap()->undefined_value();
11304 int entry = FindEntry(key);
11305 if (entry == kNotFound) return GetHeap()->undefined_value();
11306 return get(EntryToIndex(entry) + 1);
11307}
11308
11309
11310MaybeObject* ObjectHashTable::Put(JSObject* key, Object* value) {
11311 // Make sure the key object has an identity hash code.
11312 int hash;
11313 { MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::ALLOW_CREATION);
11314 if (maybe_hash->IsFailure()) return maybe_hash;
11315 hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value();
11316 }
11317 int entry = FindEntry(key);
11318
11319 // Check whether to perform removal operation.
11320 if (value->IsUndefined()) {
11321 if (entry == kNotFound) return this;
11322 RemoveEntry(entry);
11323 return Shrink(key);
11324 }
11325
11326 // Key is already in table, just overwrite value.
11327 if (entry != kNotFound) {
11328 set(EntryToIndex(entry) + 1, value);
11329 return this;
11330 }
11331
11332 // Check whether the hash table should be extended.
11333 Object* obj;
11334 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
11335 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
11336 }
11337 ObjectHashTable* table = ObjectHashTable::cast(obj);
11338 table->AddEntry(table->FindInsertionEntry(hash), key, value);
11339 return table;
11340}
11341
11342
11343void ObjectHashTable::AddEntry(int entry, JSObject* key, Object* value) {
11344 set(EntryToIndex(entry), key);
11345 set(EntryToIndex(entry) + 1, value);
11346 ElementAdded();
11347}
11348
11349
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000011350void ObjectHashTable::RemoveEntry(int entry, Heap* heap) {
11351 set_null(heap, EntryToIndex(entry));
11352 set_null(heap, EntryToIndex(entry) + 1);
vegorov@chromium.org7943d462011-08-01 11:41:52 +000011353 ElementRemoved();
11354}
11355
11356
ager@chromium.org65dad4b2009-04-23 08:48:43 +000011357#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011358// Check if there is a break point at this code position.
11359bool DebugInfo::HasBreakPoint(int code_position) {
11360 // Get the break point info object for this code position.
11361 Object* break_point_info = GetBreakPointInfo(code_position);
11362
11363 // If there is no break point info object or no break points in the break
11364 // point info object there is no break point at this code position.
11365 if (break_point_info->IsUndefined()) return false;
11366 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
11367}
11368
11369
11370// Get the break point info object for this code position.
11371Object* DebugInfo::GetBreakPointInfo(int code_position) {
11372 // Find the index of the break point info object for this code position.
11373 int index = GetBreakPointInfoIndex(code_position);
11374
11375 // Return the break point info object if any.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011376 if (index == kNoBreakPointInfo) return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011377 return BreakPointInfo::cast(break_points()->get(index));
11378}
11379
11380
11381// Clear a break point at the specified code position.
11382void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
11383 int code_position,
11384 Handle<Object> break_point_object) {
11385 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
11386 if (break_point_info->IsUndefined()) return;
11387 BreakPointInfo::ClearBreakPoint(
11388 Handle<BreakPointInfo>::cast(break_point_info),
11389 break_point_object);
11390}
11391
11392
11393void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
11394 int code_position,
11395 int source_position,
11396 int statement_position,
11397 Handle<Object> break_point_object) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011398 Isolate* isolate = Isolate::Current();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011399 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
11400 if (!break_point_info->IsUndefined()) {
11401 BreakPointInfo::SetBreakPoint(
11402 Handle<BreakPointInfo>::cast(break_point_info),
11403 break_point_object);
11404 return;
11405 }
11406
11407 // Adding a new break point for a code position which did not have any
11408 // break points before. Try to find a free slot.
11409 int index = kNoBreakPointInfo;
11410 for (int i = 0; i < debug_info->break_points()->length(); i++) {
11411 if (debug_info->break_points()->get(i)->IsUndefined()) {
11412 index = i;
11413 break;
11414 }
11415 }
11416 if (index == kNoBreakPointInfo) {
11417 // No free slot - extend break point info array.
11418 Handle<FixedArray> old_break_points =
11419 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011420 Handle<FixedArray> new_break_points =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011421 isolate->factory()->NewFixedArray(
11422 old_break_points->length() +
11423 Debug::kEstimatedNofBreakPointsInFunction);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +000011424
11425 debug_info->set_break_points(*new_break_points);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011426 for (int i = 0; i < old_break_points->length(); i++) {
11427 new_break_points->set(i, old_break_points->get(i));
11428 }
11429 index = old_break_points->length();
11430 }
11431 ASSERT(index != kNoBreakPointInfo);
11432
11433 // Allocate new BreakPointInfo object and set the break point.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011434 Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast(
11435 isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011436 new_break_point_info->set_code_position(Smi::FromInt(code_position));
11437 new_break_point_info->set_source_position(Smi::FromInt(source_position));
11438 new_break_point_info->
11439 set_statement_position(Smi::FromInt(statement_position));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011440 new_break_point_info->set_break_point_objects(
11441 isolate->heap()->undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011442 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
11443 debug_info->break_points()->set(index, *new_break_point_info);
11444}
11445
11446
11447// Get the break point objects for a code position.
11448Object* DebugInfo::GetBreakPointObjects(int code_position) {
11449 Object* break_point_info = GetBreakPointInfo(code_position);
11450 if (break_point_info->IsUndefined()) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011451 return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011452 }
11453 return BreakPointInfo::cast(break_point_info)->break_point_objects();
11454}
11455
11456
11457// Get the total number of break points.
11458int DebugInfo::GetBreakPointCount() {
11459 if (break_points()->IsUndefined()) return 0;
11460 int count = 0;
11461 for (int i = 0; i < break_points()->length(); i++) {
11462 if (!break_points()->get(i)->IsUndefined()) {
11463 BreakPointInfo* break_point_info =
11464 BreakPointInfo::cast(break_points()->get(i));
11465 count += break_point_info->GetBreakPointCount();
11466 }
11467 }
11468 return count;
11469}
11470
11471
11472Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
11473 Handle<Object> break_point_object) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011474 Heap* heap = debug_info->GetHeap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011475 if (debug_info->break_points()->IsUndefined()) return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011476 for (int i = 0; i < debug_info->break_points()->length(); i++) {
11477 if (!debug_info->break_points()->get(i)->IsUndefined()) {
11478 Handle<BreakPointInfo> break_point_info =
11479 Handle<BreakPointInfo>(BreakPointInfo::cast(
11480 debug_info->break_points()->get(i)));
11481 if (BreakPointInfo::HasBreakPointObject(break_point_info,
11482 break_point_object)) {
11483 return *break_point_info;
11484 }
11485 }
11486 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011487 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011488}
11489
11490
11491// Find the index of the break point info object for the specified code
11492// position.
11493int DebugInfo::GetBreakPointInfoIndex(int code_position) {
11494 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
11495 for (int i = 0; i < break_points()->length(); i++) {
11496 if (!break_points()->get(i)->IsUndefined()) {
11497 BreakPointInfo* break_point_info =
11498 BreakPointInfo::cast(break_points()->get(i));
11499 if (break_point_info->code_position()->value() == code_position) {
11500 return i;
11501 }
11502 }
11503 }
11504 return kNoBreakPointInfo;
11505}
11506
11507
11508// Remove the specified break point object.
11509void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
11510 Handle<Object> break_point_object) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011511 Isolate* isolate = Isolate::Current();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011512 // If there are no break points just ignore.
11513 if (break_point_info->break_point_objects()->IsUndefined()) return;
11514 // If there is a single break point clear it if it is the same.
11515 if (!break_point_info->break_point_objects()->IsFixedArray()) {
11516 if (break_point_info->break_point_objects() == *break_point_object) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011517 break_point_info->set_break_point_objects(
11518 isolate->heap()->undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011519 }
11520 return;
11521 }
11522 // If there are multiple break points shrink the array
11523 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
11524 Handle<FixedArray> old_array =
11525 Handle<FixedArray>(
11526 FixedArray::cast(break_point_info->break_point_objects()));
11527 Handle<FixedArray> new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011528 isolate->factory()->NewFixedArray(old_array->length() - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011529 int found_count = 0;
11530 for (int i = 0; i < old_array->length(); i++) {
11531 if (old_array->get(i) == *break_point_object) {
11532 ASSERT(found_count == 0);
11533 found_count++;
11534 } else {
11535 new_array->set(i - found_count, old_array->get(i));
11536 }
11537 }
11538 // If the break point was found in the list change it.
11539 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
11540}
11541
11542
11543// Add the specified break point object.
11544void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
11545 Handle<Object> break_point_object) {
11546 // If there was no break point objects before just set it.
11547 if (break_point_info->break_point_objects()->IsUndefined()) {
11548 break_point_info->set_break_point_objects(*break_point_object);
11549 return;
11550 }
11551 // If the break point object is the same as before just ignore.
11552 if (break_point_info->break_point_objects() == *break_point_object) return;
11553 // If there was one break point object before replace with array.
11554 if (!break_point_info->break_point_objects()->IsFixedArray()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011555 Handle<FixedArray> array = FACTORY->NewFixedArray(2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011556 array->set(0, break_point_info->break_point_objects());
11557 array->set(1, *break_point_object);
11558 break_point_info->set_break_point_objects(*array);
11559 return;
11560 }
11561 // If there was more than one break point before extend array.
11562 Handle<FixedArray> old_array =
11563 Handle<FixedArray>(
11564 FixedArray::cast(break_point_info->break_point_objects()));
11565 Handle<FixedArray> new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011566 FACTORY->NewFixedArray(old_array->length() + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011567 for (int i = 0; i < old_array->length(); i++) {
11568 // If the break point was there before just ignore.
11569 if (old_array->get(i) == *break_point_object) return;
11570 new_array->set(i, old_array->get(i));
11571 }
11572 // Add the new break point.
11573 new_array->set(old_array->length(), *break_point_object);
11574 break_point_info->set_break_point_objects(*new_array);
11575}
11576
11577
11578bool BreakPointInfo::HasBreakPointObject(
11579 Handle<BreakPointInfo> break_point_info,
11580 Handle<Object> break_point_object) {
11581 // No break point.
11582 if (break_point_info->break_point_objects()->IsUndefined()) return false;
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000011583 // Single break point.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011584 if (!break_point_info->break_point_objects()->IsFixedArray()) {
11585 return break_point_info->break_point_objects() == *break_point_object;
11586 }
11587 // Multiple break points.
11588 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
11589 for (int i = 0; i < array->length(); i++) {
11590 if (array->get(i) == *break_point_object) {
11591 return true;
11592 }
11593 }
11594 return false;
11595}
11596
11597
11598// Get the number of break points.
11599int BreakPointInfo::GetBreakPointCount() {
11600 // No break point.
11601 if (break_point_objects()->IsUndefined()) return 0;
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000011602 // Single break point.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011603 if (!break_point_objects()->IsFixedArray()) return 1;
11604 // Multiple break points.
11605 return FixedArray::cast(break_point_objects())->length();
11606}
ager@chromium.org65dad4b2009-04-23 08:48:43 +000011607#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011608
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011609
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011610} } // namespace v8::internal